From c67b7f2fc34fc176788b283e72d4860edadd97d3 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Thu, 25 Aug 2022 14:33:01 -0400 Subject: [PATCH] v1.0.1 for up to 64 PWM channels ### Release v1.0.1 1. Make `MAX_NUMBER_CHANNELS` configurable to max **64 PWM channels** 2. Remove debug codes possibly causing hang 3. Improve debug to use `Serialx` port automatically according to boards --- README.md | 51 ++++++----- changelog.md | 7 ++ .../ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino | 28 ++++--- .../ISR_8_PWMs_Array_Complex.ino | 19 ++++- .../ISR_8_PWMs_Array_Simple.ino | 28 ++++--- .../ISR_Changing_PWM/ISR_Changing_PWM.ino | 28 ++++--- examples/ISR_Modify_PWM/ISR_Modify_PWM.ino | 26 +++--- examples/multiFileProject/multiFileProject.h | 14 ++++ .../multiFileProject/multiFileProject.ino | 13 +-- keywords.txt | 8 ++ library.json | 4 +- library.properties | 4 +- src/Dx_Slow_PWM.h | 3 +- src/Dx_Slow_PWM.hpp | 9 +- src/Dx_Slow_PWM_ISR.h | 3 +- src/Dx_Slow_PWM_ISR.hpp | 45 ++++++---- src/Dx_Slow_PWM_ISR_Impl.h | 3 +- src/Dx_Slow_PWM_Impl.h | 84 ++++--------------- src/PWM_Generic_Debug.h | 3 +- 19 files changed, 209 insertions(+), 171 deletions(-) diff --git a/README.md b/README.md index ec87a06..d4b37d0 100644 --- a/README.md +++ b/README.md @@ -117,21 +117,21 @@ After drag-and-drop the `Change_Interval.ino.hex` into `CURIOSITY` virtual drive ### Features -This library enables you to use ISR-based PWM channels on Arduino AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.), using [DxCore](https://github.com/SpenceKonde/DxCore), to create and output PWM any GPIO pin. Because this library doesn't use the powerful purely hardware-controlled PWM with many limitations, the maximum PWM frequency is currently limited at **500Hz**, which is still suitable for many real-life applications. Now you can change the PWM settings on-the-fly +This library enables you to use ISR-based PWM channels on Arduino AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.), using [DxCore](https://github.com/SpenceKonde/DxCore), to create and output PWM any GPIO pin. Because this library doesn't use the powerful purely hardware-controlled PWM with many limitations, the maximum PWM frequency is currently limited at **1000Hz**, which is still suitable for many real-life applications. Now you can change the PWM settings on-the-fly --- -This library enables you to use Interrupt from Hardware Timers on AVRDx-based boards to create and output PWM to pins. It now supports 16 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer. PWM interval can be very long (uint64_t microsecs / millisecs). The most important feature is they're ISR-based PWM channels. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware PWM channels, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software PWM using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy. +This library enables you to use Interrupt from Hardware Timers on AVRDx-based boards to create and output PWM to pins. It now supports 64 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer. PWM interval can be very long (uint64_t microsecs / millisecs). The most important feature is they're ISR-based PWM channels. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware PWM channels, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software PWM using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy. -As **Hardware Timers are rare, and very precious assets** of any board, this library now enables you to use up to **16 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer**. Timers' interval is very long (**ulong millisecs**). +As **Hardware Timers are rare, and very precious assets** of any board, this library now enables you to use up to **64 ISR-based synchronized PWM channels, while consuming only 1 Hardware Timer**. Timers' interval is very long (**ulong millisecs**). -Now with these new **16 ISR-based PWM-channels**, the maximum interval is **practically unlimited** (limited only by unsigned long miliseconds) while **the accuracy is nearly perfect** compared to software PWM channels. +Now with these new **64 ISR-based PWM-channels**, the maximum interval is **practically unlimited** (limited only by unsigned long miliseconds) while **the accuracy is nearly perfect** compared to software PWM channels. The most important feature is they're ISR-based PWM channels. Therefore, their executions are **not blocked by bad-behaving functions / tasks**. This important feature is absolutely necessary for mission-critical tasks. The [**ISR_8_PWMs_Array_Complex**](examples/ISR_8_PWMs_Array_Complex) example will demonstrate the nearly perfect accuracy, compared to software PWM, by printing the actual period / duty-cycle in `microsecs` of each of PWM-channels. -Being ISR-based PWM, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or Blynk services. You can also have many `(up to 16)` PWM channels to use. +Being ISR-based PWM, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or Blynk services. You can also have many `(up to 64)` PWM channels to use. This non-being-blocked important feature is absolutely necessary for mission-critical tasks. @@ -206,7 +206,7 @@ The catch is **your function is now part of an ISR (Interrupt Service Routine), 1. [`Arduino IDE 1.8.19+` for Arduino](https://github.com/arduino/Arduino). [![GitHub release](https://img.shields.io/github/release/arduino/Arduino.svg)](https://github.com/arduino/Arduino/releases/latest) 2. [`SpenceKonde DxCore core 1.4.10+`](https://github.com/SpenceKonde/DxCore) for Arduino AVRDx boards. [![GitHub release](https://img.shields.io/github/release/SpenceKonde/DxCore.svg)](https://github.com/SpenceKonde/DxCore/releases/latest). Follow [**DxCore Installation**](https://github.com/SpenceKonde/DxCore/blob/main/Installation.md). 3. To use with certain example - - [`SimpleTimer library`](https://github.com/jfturcot/SimpleTimer) for [ISR_Timers_Array_Simple](examples/ISR_Timers_Array_Simple) and [ISR_16_Timers_Array_Complex](examples/ISR_16_Timers_Array_Complex) examples. + - [`SimpleTimer library`](https://github.com/jfturcot/SimpleTimer) for [ISR_8_PWMs_Array_Simple](examples/ISR_8_PWMs_Array_Simple) and [ISR_8_PWMs_Array_Complex](examples/ISR_8_PWMs_Array_Complex) examples. --- @@ -328,7 +328,7 @@ Before using any Timer, you have to make sure the Timer has not been used by any #error You must select one Timer #endif -// Init Dx_Slow_PWM, each can service 16 different ISR-based PWM channels +// Init Dx_Slow_PWM, each can service 64 different ISR-based PWM channels Dx_Slow_PWM ISR_PWM; ``` @@ -389,7 +389,7 @@ The following is the sample terminal output when running example [ISR_8_PWMs_Arr ``` Starting ISR_8_PWMs_Array_Complex on AVR128DB -Dx_Slow_PWM v1.0.0 +Dx_Slow_PWM v1.0.1 CPU Frequency = 24 MHz TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy Starting ITimer1 OK, micros() = 13691 @@ -431,7 +431,7 @@ The following is the sample terminal output when running example [**ISR_8_PWMs_A ``` Starting ISR_8_PWMs_Array on AVR128DB -Dx_Slow_PWM v1.0.0 +Dx_Slow_PWM v1.0.1 CPU Frequency = 24 MHz TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy Starting ITimer1 OK, micros() = 12894 @@ -445,7 +445,7 @@ The following is the sample terminal output when running example [**ISR_8_PWMs_A ``` Starting ISR_8_PWMs_Array_Simple on AVR128DB -Dx_Slow_PWM v1.0.0 +Dx_Slow_PWM v1.0.1 CPU Frequency = 24 MHz TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy Starting ITimer1 OK, micros() = 14169 @@ -459,7 +459,7 @@ The following is the sample terminal output when running example [ISR_Modify_PWM ``` Starting ISR_Modify_PWM on AVR128DB -Dx_Slow_PWM v1.0.0 +Dx_Slow_PWM v1.0.1 CPU Frequency = 24 MHz TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy Starting ITimer1 OK, micros() = 12823 @@ -474,7 +474,7 @@ The following is the sample terminal output when running example [ISR_Changing_P ``` Starting ISR_Changing_PWM on AVR128DB -Dx_Slow_PWM v1.0.0 +Dx_Slow_PWM v1.0.1 CPU Frequency = 24 MHz TCB Clock Frequency = Full clock (24/16MHz, etc) for highest accuracy Starting ITimer1 OK, micros() = 12998 @@ -526,14 +526,18 @@ Submit issues to: [Dx_Slow_PWM issues](https://github.com/khoih-prog/Dx_Slow_PWM ## DONE -1. Basic hardware multi-channel PWM for **AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore** -2. Add Table of Contents -3. Add functions to modify PWM settings on-the-fly -4. Fix `multiple-definitions` linker error -5. Optimize library code by using `reference-passing` instead of `value-passing` -6. Improve accuracy by using `float`, instead of `uint32_t` for `dutycycle` -7. DutyCycle to be optionally updated at the end current PWM period instead of immediately. -8. Display informational warning only when `_PWM_LOGLEVEL_` > 3 + 1. Basic hardware multi-channel PWM for **AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore** + 2. Add Table of Contents + 3. Add functions to modify PWM settings on-the-fly + 4. Fix `multiple-definitions` linker error + 5. Optimize library code by using `reference-passing` instead of `value-passing` + 6. Improve accuracy by using `float`, instead of `uint32_t` for `dutycycle` + 7. DutyCycle to be optionally updated at the end current PWM period instead of immediately. + 8. Display informational warning only when `_PWM_LOGLEVEL_` > 3 + 9 Make `MAX_NUMBER_CHANNELS` configurable to max **64 PWM channels** +10. Remove debug codes possibly causing hang +11. Improve debug to use `Serialx` port automatically according to boards. + --- --- @@ -543,7 +547,12 @@ Submit issues to: [Dx_Slow_PWM issues](https://github.com/khoih-prog/Dx_Slow_PWM Many thanks for everyone for bug reporting, new feature suggesting, testing and contributing to the development of this library. Especially to these people who have directly or indirectly contributed to this [Dx_TimerInterrupt library](https://github.com/khoih-prog/Dx_TimerInterrupt) 1. Thanks to good work of [Spence Konde (aka Dr. Azzy)](https://github.com/SpenceKonde) for the [DxCore](https://github.com/SpenceKonde/DxCore) and [megaTinyCore](https://github.com/SpenceKonde/megaTinyCore) -2. Thanks to [LaurentR59](https://github.com/LaurentR59) to request the enhancement [Support for DX CORE CPU and MightyCORE CPU possible? #8](https://github.com/khoih-prog/TimerInterrupt_Generic/issues/8) leading to this new library +2. Thanks to [LaurentR59](https://github.com/LaurentR59) to request + +- the enhancement [Support for DX CORE CPU and MightyCORE CPU possible? #8](https://github.com/khoih-prog/TimerInterrupt_Generic/issues/8) leading to this new library +- the enhancement [PWM to drive over 16 channels #1](https://github.com/khoih-prog/Dx_Slow_PWM/issues/1) leading to new v1.0.1 + + diff --git a/changelog.md b/changelog.md index 01ac121..a1a438a 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,7 @@ * [Changelog](#changelog) + * [Release v1.0.1](#release-v101) * [Initial Release v1.0.0](#initial-release-v100) --- @@ -18,6 +19,12 @@ ## Changelog +### Release v1.0.1 + +1. Make `MAX_NUMBER_CHANNELS` configurable to max **64 PWM channels** +2. Remove debug codes possibly causing hang +3. Improve debug to use `Serialx` port automatically according to boards + ### Initial Release v1.0.0 1. Initial release to support Arduino **AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore** diff --git a/examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino b/examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino index a8e7378..4294e0a 100644 --- a/examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino +++ b/examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino @@ -23,7 +23,21 @@ // These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h" // _PWM_LOGLEVEL_ from 0 to 4 // Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. -#define _PWM_LOGLEVEL_ 3 +#define _PWM_LOGLEVEL_ 1 + +#if defined(__AVR_AVR128DA48__) + #define SerialDebug Serial1 +#elif defined(__AVR_AVR128DB48__) + #define SerialDebug Serial3 +#else + // standard Serial + #define SerialDebug Serial +#endif + +#define PWM_GENERIC_DEBUG_PORT SerialDebug + +// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64 +#define MAX_NUMBER_CHANNELS 16 // Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy // Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy @@ -79,15 +93,6 @@ #endif #endif -#if defined(__AVR_AVR128DA48__) - #define SerialDebug Serial1 -#elif defined(__AVR_AVR128DB48__) - #define SerialDebug Serial3 -#else - // standard Serial - #define SerialDebug Serial -#endif - #define USING_HW_TIMER_INTERVAL_MS false //true // Don't change these numbers to make higher Timer freq. System can hang @@ -96,7 +101,7 @@ volatile uint32_t startMicros = 0; -// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels +// Init DX_SLOW_PWM, each can service max 64 different ISR-based PWM channels DX_SLOW_PWM_ISR ISR_PWM; ////////////////////////////////////////////////////// @@ -193,6 +198,7 @@ void setup() SerialDebug.print(F("\nStarting ISR_8_PWMs_Array on ")); SerialDebug.println(BOARD_NAME); SerialDebug.println(DX_SLOW_PWM_VERSION); SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz")); + SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS); SerialDebug.print(F("TCB Clock Frequency = ")); diff --git a/examples/ISR_8_PWMs_Array_Complex/ISR_8_PWMs_Array_Complex.ino b/examples/ISR_8_PWMs_Array_Complex/ISR_8_PWMs_Array_Complex.ino index 5e17401..f8c9c58 100644 --- a/examples/ISR_8_PWMs_Array_Complex/ISR_8_PWMs_Array_Complex.ino +++ b/examples/ISR_8_PWMs_Array_Complex/ISR_8_PWMs_Array_Complex.ino @@ -23,7 +23,21 @@ // These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h" // _PWM_LOGLEVEL_ from 0 to 4 // Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. -#define _PWM_LOGLEVEL_ 3 +#define _PWM_LOGLEVEL_ 1 + +#if defined(__AVR_AVR128DA48__) + #define SerialDebug Serial1 +#elif defined(__AVR_AVR128DB48__) + #define SerialDebug Serial3 +#else + // standard Serial + #define SerialDebug Serial +#endif + +#define PWM_GENERIC_DEBUG_PORT SerialDebug + +// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64 +#define MAX_NUMBER_CHANNELS 16 // Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy // Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy @@ -96,7 +110,7 @@ volatile uint32_t startMicros = 0; -// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels +// Init DX_SLOW_PWM, each can service max 48 different ISR-based PWM channels DX_SLOW_PWM_ISR ISR_PWM; ////////////////////////////////////////////////////// @@ -415,6 +429,7 @@ void setup() SerialDebug.print(F("\nStarting ISR_8_PWMs_Array_Complex on ")); SerialDebug.println(BOARD_NAME); SerialDebug.println(DX_SLOW_PWM_VERSION); SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz")); + SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS); SerialDebug.print(F("TCB Clock Frequency = ")); diff --git a/examples/ISR_8_PWMs_Array_Simple/ISR_8_PWMs_Array_Simple.ino b/examples/ISR_8_PWMs_Array_Simple/ISR_8_PWMs_Array_Simple.ino index 445a91c..7edecf3 100644 --- a/examples/ISR_8_PWMs_Array_Simple/ISR_8_PWMs_Array_Simple.ino +++ b/examples/ISR_8_PWMs_Array_Simple/ISR_8_PWMs_Array_Simple.ino @@ -23,7 +23,21 @@ // These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h" // _PWM_LOGLEVEL_ from 0 to 4 // Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. -#define _PWM_LOGLEVEL_ 4 +#define _PWM_LOGLEVEL_ 1 + +#if defined(__AVR_AVR128DA48__) + #define SerialDebug Serial1 +#elif defined(__AVR_AVR128DB48__) + #define SerialDebug Serial3 +#else + // standard Serial + #define SerialDebug Serial +#endif + +#define PWM_GENERIC_DEBUG_PORT SerialDebug + +// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64 +#define MAX_NUMBER_CHANNELS 16 // Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy // Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy @@ -77,15 +91,6 @@ #endif #endif -#if defined(__AVR_AVR128DA48__) - #define SerialDebug Serial1 -#elif defined(__AVR_AVR128DB48__) - #define SerialDebug Serial3 -#else - // standard Serial - #define SerialDebug Serial -#endif - #define USING_HW_TIMER_INTERVAL_MS false //true // Don't change these numbers to make higher Timer freq. System can hang @@ -94,7 +99,7 @@ volatile uint32_t startMicros = 0; -// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels +// Init DX_SLOW_PWM, each can service max 64 different ISR-based PWM channels DX_SLOW_PWM_ISR ISR_PWM; ////////////////////////////////////////////////////// @@ -146,6 +151,7 @@ void setup() SerialDebug.print(F("\nStarting ISR_8_PWMs_Array_Simple on ")); SerialDebug.println(BOARD_NAME); SerialDebug.println(DX_SLOW_PWM_VERSION); SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz")); + SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS); SerialDebug.print(F("TCB Clock Frequency = ")); diff --git a/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino b/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino index d5b5060..6149f0b 100644 --- a/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino +++ b/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino @@ -23,7 +23,21 @@ // These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h" // _PWM_LOGLEVEL_ from 0 to 4 // Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. -#define _PWM_LOGLEVEL_ 3 +#define _PWM_LOGLEVEL_ 1 + +#if defined(__AVR_AVR128DA48__) + #define SerialDebug Serial1 +#elif defined(__AVR_AVR128DB48__) + #define SerialDebug Serial3 +#else + // standard Serial + #define SerialDebug Serial +#endif + +#define PWM_GENERIC_DEBUG_PORT SerialDebug + +// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64 +#define MAX_NUMBER_CHANNELS 16 // Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy // Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy @@ -77,15 +91,6 @@ #endif #endif -#if defined(__AVR_AVR128DA48__) - #define SerialDebug Serial1 -#elif defined(__AVR_AVR128DB48__) - #define SerialDebug Serial3 -#else - // standard Serial - #define SerialDebug Serial -#endif - #define USING_HW_TIMER_INTERVAL_MS false //true // Don't change these numbers to make higher Timer freq. System can hang @@ -94,7 +99,7 @@ volatile uint32_t startMicros = 0; -// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels +// Init DX_SLOW_PWM, each can service max 64 different ISR-based PWM channels DX_SLOW_PWM_ISR ISR_PWM; ////////////////////////////////////////////////////// @@ -141,6 +146,7 @@ void setup() SerialDebug.print(F("\nStarting ISR_Changing_PWM on ")); SerialDebug.println(BOARD_NAME); SerialDebug.println(DX_SLOW_PWM_VERSION); SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz")); + SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS); SerialDebug.print(F("TCB Clock Frequency = ")); diff --git a/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino b/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino index 7a707c7..4f12645 100644 --- a/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino +++ b/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino @@ -23,7 +23,19 @@ // These define's must be placed at the beginning before #include "megaAVR_Slow_PWM.h" // _PWM_LOGLEVEL_ from 0 to 4 // Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. -#define _PWM_LOGLEVEL_ 3 +#define _PWM_LOGLEVEL_ 1#if defined(__AVR_AVR128DA48__) + #define SerialDebug Serial1 +#elif defined(__AVR_AVR128DB48__) + #define SerialDebug Serial3 +#else + // standard Serial + #define SerialDebug Serial +#endif + +#define PWM_GENERIC_DEBUG_PORT SerialDebug + +// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64 +#define MAX_NUMBER_CHANNELS 16 // Select USING_FULL_CLOCK == true for 24/16MHz to Timer TCBx => shorter timer, but better accuracy // Select USING_HALF_CLOCK == true for 12/ 8MHz to Timer TCBx => shorter timer, but better accuracy @@ -77,15 +89,6 @@ #endif #endif -#if defined(__AVR_AVR128DA48__) - #define SerialDebug Serial1 -#elif defined(__AVR_AVR128DB48__) - #define SerialDebug Serial3 -#else - // standard Serial - #define SerialDebug Serial -#endif - #define USING_HW_TIMER_INTERVAL_MS false //true // Don't change these numbers to make higher Timer freq. System can hang @@ -94,7 +97,7 @@ volatile uint32_t startMicros = 0; -// Init DX_SLOW_PWM, each can service 16 different ISR-based PWM channels +// Init DX_SLOW_PWM, each can service max 64 different ISR-based PWM channels DX_SLOW_PWM_ISR ISR_PWM; ////////////////////////////////////////////////////// @@ -141,6 +144,7 @@ void setup() SerialDebug.print(F("\nStarting ISR_Modify_PWM on ")); SerialDebug.println(BOARD_NAME); SerialDebug.println(DX_SLOW_PWM_VERSION); SerialDebug.print(F("CPU Frequency = ")); SerialDebug.print(F_CPU / 1000000); SerialDebug.println(F(" MHz")); + SerialDebug.print(F("Max number PWM channels = ")); SerialDebug.println(MAX_NUMBER_CHANNELS); SerialDebug.print(F("TCB Clock Frequency = ")); diff --git a/examples/multiFileProject/multiFileProject.h b/examples/multiFileProject/multiFileProject.h index c6b6c74..6479e21 100644 --- a/examples/multiFileProject/multiFileProject.h +++ b/examples/multiFileProject/multiFileProject.h @@ -12,6 +12,20 @@ #pragma once +#if defined(__AVR_AVR128DA48__) + #define SerialDebug Serial1 +#elif defined(__AVR_AVR128DB48__) + #define SerialDebug Serial3 +#else + // standard Serial + #define SerialDebug Serial +#endif + +#define PWM_GENERIC_DEBUG_PORT SerialDebug + +// Be careful when using MAX_NUMBER_CHANNELS > 16. Max pemissible MAX_NUMBER_CHANNELS is 64 +#define MAX_NUMBER_CHANNELS 16 + #define USING_MICROS_RESOLUTION true //false // Default is true, uncomment to false diff --git a/examples/multiFileProject/multiFileProject.ino b/examples/multiFileProject/multiFileProject.ino index 6f008c3..b0c7b31 100644 --- a/examples/multiFileProject/multiFileProject.ino +++ b/examples/multiFileProject/multiFileProject.ino @@ -15,23 +15,14 @@ #error This is designed only for DXCORE or MEGATINYCORE megaAVR board! Please check your Tools->Board setting #endif -#define DX_SLOW_PWM_VERSION_MIN_TARGET F("Dx_Slow_PWM v1.0.0") -#define DX_SLOW_PWM_VERSION_MIN 1000000 +#define DX_SLOW_PWM_VERSION_MIN_TARGET F("Dx_Slow_PWM v1.0.1") +#define DX_SLOW_PWM_VERSION_MIN 1000001 #include "multiFileProject.h" // To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error #include "Dx_Slow_PWM.h" -#if defined(__AVR_AVR128DA48__) - #define SerialDebug Serial1 -#elif defined(__AVR_AVR128DB48__) - #define SerialDebug Serial3 -#else - // standard Serial - #define SerialDebug Serial -#endif - void setup() { SerialDebug.begin(115200); diff --git a/keywords.txt b/keywords.txt index f984e33..980b819 100644 --- a/keywords.txt +++ b/keywords.txt @@ -50,12 +50,20 @@ getNumAvailablePWMChannels KEYWORD2 # Constants (LITERAL1) ####################################### +PWM_GENERIC_DEBUG_PORT LITERAL1 +PWM_DBG_PORT LITERAL1 +_PWM_LOGLEVEL_ LITERAL1 + +BOARD_NAME LITERAL1 + DX_SLOW_PWM_VERSION LITERAL1 DX_SLOW_PWM_VERSION_MAJOR LITERAL1 DX_SLOW_PWM_VERSION_MINOR LITERAL1 DX_SLOW_PWM_VERSION_PATCH LITERAL1 DX_SLOW_PWM_VERSION_INT LITERAL1 +MAX_NUMBER_CHANNELS LITERAL1 + USING_MICROS_RESOLUTION LITERAL1 CHANGING_PWM_END_OF_CYCLE LITERAL1 diff --git a/library.json b/library.json index dd7743c..bfbbce9 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "Dx_Slow_PWM", - "version": "1.0.0", + "version": "1.0.1", "keywords": "timer, interrupt, hardware, isr, isr-based, pwm, isr-based-pwm, timing, control, device, hardware-timer, mission-critical, accuracy, precise, megaavr, avr-da, avr-db, avr-dd, dxcore, avr128dx, avr64dx, avr32dx, megatinycore, dx-timerinterrupt, tcb-timers", - "description": "This library enables you to use ISR-based PWM channels on Arduino AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.), using DxCore, to create and output PWM any GPIO pin. It now supports 16 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they're ISR-based PWM channels, supporting lower PWM frequencies with suitable accuracy. Their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These ISR-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using millis() or micros(). That's necessary if you need to control devices requiring high precision. Now you can change the PWM settings on-the-fly.", + "description": "This library enables you to use ISR-based PWM channels on Arduino AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.), using DxCore, to create and output PWM any GPIO pin. It now supports 64 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they're ISR-based PWM channels, supporting lower PWM frequencies with suitable accuracy. Their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These ISR-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using millis() or micros(). That's necessary if you need to control devices requiring high precision. Now you can change the PWM settings on-the-fly.", "authors": { "name": "Khoi Hoang", diff --git a/library.properties b/library.properties index 33faff7..b3086bc 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=Dx_Slow_PWM -version=1.0.0 +version=1.0.1 author=Khoi Hoang maintainer=Khoi Hoang sentence=This library enables you to use ISR-based PWM channels on Arduino AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.), using DxCore, to create and output PWM any GPIO pin. -paragraph=It now supports 16 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they're ISR-based PWM channels, supporting lower PWM frequencies with suitable accuracy. Their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These ISR-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using millis() or micros(). That's necessary if you need to control devices requiring high precision. Now you can change the PWM settings on-the-fly. +paragraph=It now supports 64 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they're ISR-based PWM channels, supporting lower PWM frequencies with suitable accuracy. Their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These ISR-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using millis() or micros(). That's necessary if you need to control devices requiring high precision. Now you can change the PWM settings on-the-fly. category=Device Control url=https://github.com/khoih-prog/Dx_Slow_PWM architectures=megaavr diff --git a/src/Dx_Slow_PWM.h b/src/Dx_Slow_PWM.h index adc5e44..564a248 100644 --- a/src/Dx_Slow_PWM.h +++ b/src/Dx_Slow_PWM.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.0.1 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 25/08/2022 Initial coding to support AVR Dx (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore + 1.0.1 K.Hoang 25/08/2022 Make MAX_NUMBER_CHANNELS configurable to max 48 PWM channels *****************************************************************************************************************************/ #pragma once diff --git a/src/Dx_Slow_PWM.hpp b/src/Dx_Slow_PWM.hpp index 0dbfbeb..1c34ab8 100644 --- a/src/Dx_Slow_PWM.hpp +++ b/src/Dx_Slow_PWM.hpp @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.0.1 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 25/08/2022 Initial coding to support AVR Dx (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore + 1.0.1 K.Hoang 25/08/2022 Make MAX_NUMBER_CHANNELS configurable to max 64 PWM channels *****************************************************************************************************************************/ #pragma once @@ -103,13 +104,13 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef DX_SLOW_PWM_VERSION - #define DX_SLOW_PWM_VERSION F("Dx_Slow_PWM v1.0.0") + #define DX_SLOW_PWM_VERSION F("Dx_Slow_PWM v1.0.1") #define DX_SLOW_PWM_VERSION_MAJOR 1 #define DX_SLOW_PWM_VERSION_MINOR 0 - #define DX_SLOW_PWM_VERSION_PATCH 0 + #define DX_SLOW_PWM_VERSION_PATCH 1 - #define DX_SLOW_PWM_VERSION_INT 1000000 + #define DX_SLOW_PWM_VERSION_INT 1000001 #endif #ifndef _PWM_LOGLEVEL_ diff --git a/src/Dx_Slow_PWM_ISR.h b/src/Dx_Slow_PWM_ISR.h index 5509bcd..0f712f0 100644 --- a/src/Dx_Slow_PWM_ISR.h +++ b/src/Dx_Slow_PWM_ISR.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.0.1 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 25/08/2022 Initial coding to support AVR Dx (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore + 1.0.1 K.Hoang 25/08/2022 Make MAX_NUMBER_CHANNELS configurable to max 64 PWM channels *****************************************************************************************************************************/ #pragma once diff --git a/src/Dx_Slow_PWM_ISR.hpp b/src/Dx_Slow_PWM_ISR.hpp index 71c7291..3baa71f 100644 --- a/src/Dx_Slow_PWM_ISR.hpp +++ b/src/Dx_Slow_PWM_ISR.hpp @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.0.1 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 25/08/2022 Initial coding to support AVR Dx (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore + 1.0.1 K.Hoang 25/08/2022 Make MAX_NUMBER_CHANNELS configurable to max 64 PWM channels *****************************************************************************************************************************/ #pragma once @@ -103,13 +104,13 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef DX_SLOW_PWM_VERSION - #define DX_SLOW_PWM_VERSION F("Dx_Slow_PWM v1.0.0") + #define DX_SLOW_PWM_VERSION F("Dx_Slow_PWM v1.0.1") #define DX_SLOW_PWM_VERSION_MAJOR 1 #define DX_SLOW_PWM_VERSION_MINOR 0 - #define DX_SLOW_PWM_VERSION_PATCH 0 + #define DX_SLOW_PWM_VERSION_PATCH 1 - #define DX_SLOW_PWM_VERSION_INT 1000000 + #define DX_SLOW_PWM_VERSION_INT 1000001 #endif #ifndef _PWM_LOGLEVEL_ @@ -154,14 +155,24 @@ typedef void (*timer_callback_p)(void *); #define INVALID_MEGA_AVR_PIN 255 +#if !defined(MAX_NUMBER_CHANNELS) + // maximum number of PWM channels + #define MAX_NUMBER_CHANNELS 16 +#else + #if (MAX_NUMBER_CHANNELS > 64) + #undef MAX_NUMBER_CHANNELS + #define MAX_NUMBER_CHANNELS 64 + + #warning Reset too big MAX_NUMBER_CHANNELS to 64 + #endif +#endif + ////////////////////////////////////////////////////////////////// class DX_SLOW_PWM_ISR { - public: - // maximum number of PWM channels -#define MAX_NUMBER_CHANNELS 16 + public: // constructor DX_SLOW_PWM_ISR(); @@ -182,12 +193,14 @@ class DX_SLOW_PWM_ISR if ( ( frequency >= 0.0 ) && ( frequency <= 1000.0 ) ) { #if USING_MICROS_RESOLUTION - // period in us - period = (uint32_t) (1000000.0f / frequency); + // period in us + period = (uint32_t) (1000000.0f / frequency); #else - // period in ms - period = (uint32_t) (1000.0f / frequency); + // period in ms + period = (uint32_t) (1000.0f / frequency); #endif + PWM_LOGDEBUG1(F("Frequency = "), frequency); + } else { @@ -218,12 +231,14 @@ class DX_SLOW_PWM_ISR if ( ( frequency >= 0.0 ) && ( frequency <= 1000.0 ) ) { #if USING_MICROS_RESOLUTION - // period in us - period = (uint32_t) (1000000.0f / frequency); + // period in us + period = (uint32_t) (1000000.0f / frequency); #else - // period in ms - period = (uint32_t) (1000.0f / frequency); + // period in ms + period = (uint32_t) (1000.0f / frequency); #endif + + PWM_LOGDEBUG1(F("Frequency = "), frequency); } else { diff --git a/src/Dx_Slow_PWM_ISR_Impl.h b/src/Dx_Slow_PWM_ISR_Impl.h index 563cb95..624d148 100644 --- a/src/Dx_Slow_PWM_ISR_Impl.h +++ b/src/Dx_Slow_PWM_ISR_Impl.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.0.1 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 25/08/2022 Initial coding to support AVR Dx (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore + 1.0.1 K.Hoang 25/08/2022 Make MAX_NUMBER_CHANNELS configurable to max 64 PWM channels *****************************************************************************************************************************/ #pragma once diff --git a/src/Dx_Slow_PWM_Impl.h b/src/Dx_Slow_PWM_Impl.h index 143e5b5..5bb8282 100644 --- a/src/Dx_Slow_PWM_Impl.h +++ b/src/Dx_Slow_PWM_Impl.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.0.1 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 25/08/2022 Initial coding to support AVR Dx (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore + 1.0.1 K.Hoang 25/08/2022 Make MAX_NUMBER_CHANNELS configurable to max 64 PWM channels ****************************************************************************************************************************/ #pragma once @@ -165,21 +166,10 @@ void TimerInterrupt::init(const int8_t& timer) TimerTCB[timer]->CCMP = MAX_COUNT_16BIT; // Value to compare with. TimerTCB[timer]->INTCTRL &= ~TCB_CAPT_bm; // Disable the interrupt TimerTCB[timer]->CTRLA = TCB_CLKSEL_VALUE | TCB_ENABLE_bm; // Use Timer A as clock, enable timer - - PWM_LOGWARN1(F("TCB"), timer); - - PWM_LOGINFO(F("==================")); - PWM_LOGINFO1(F("Init, Timer = "), timer); - PWM_LOGINFO1(F("CTRLB = "), TimerTCB[timer]->CTRLB); - PWM_LOGINFO1(F("CCMP = "), TimerTCB[timer]->CCMP); - PWM_LOGINFO1(F("INTCTRL = "), TimerTCB[timer]->INTCTRL); - PWM_LOGINFO1(F("CTRLA = "), TimerTCB[timer]->CTRLA); - PWM_LOGINFO(F("==================")); _timer = timer; - interrupts(); - + interrupts(); } ////////////////////////////////////////////// @@ -198,15 +188,7 @@ void TimerInterrupt::set_CCMP() TimerTCB[_timer]->CCMP = _CCMPValueToUse; // Value to compare with. TimerTCB[_timer]->INTCTRL = TCB_CAPT_bm; // Enable the interrupt - - PWM_LOGDEBUG(F("==================")); - PWM_LOGDEBUG1(F("set_CCMP, Timer = "), _timer); - PWM_LOGDEBUG1(F("CTRLB = "), TimerTCB[_timer]->CTRLB); - PWM_LOGDEBUG1(F("CCMP = "), TimerTCB[_timer]->CCMP); - PWM_LOGDEBUG1(F("INTCTRL = "), TimerTCB[_timer]->INTCTRL); - PWM_LOGDEBUG1(F("CTRLA = "), TimerTCB[_timer]->CTRLA); - PWM_LOGDEBUG(F("==================")); - + // Flag _CCMPValue == 0 => end of long timer if (_CCMPValueRemaining == 0) _timerDone = true; @@ -224,9 +206,7 @@ bool TimerInterrupt::setFrequency(const float& frequency, timer_callback_p callb // Limit frequency to larger than (0.00372529 / 64) Hz or interval 17179.840s / 17179840 ms to avoid uint32_t overflow if ((_timer <= 0) || (callback == NULL) || ((frequencyLimit) < 1) ) - { - PWM_LOGDEBUG(F("setFrequency error")); - + { return false; } else @@ -235,14 +215,9 @@ bool TimerInterrupt::setFrequency(const float& frequency, timer_callback_p callb if (duration > 0) { _toggle_count = frequency * duration / 1000; - - PWM_LOGINFO1(F("setFrequency => _toggle_count = "), _toggle_count); - PWM_LOGINFO3(F("Frequency ="), frequency, F(", duration = "), duration); - + if (_toggle_count < 1) - { - PWM_LOGDEBUG(F("setFrequency: _toggle_count < 1 error")); - + { return false; } } @@ -262,10 +237,7 @@ bool TimerInterrupt::setFrequency(const float& frequency, timer_callback_p callb _timerDone = false; _CCMPValue = _CCMPValueRemaining = (uint32_t) (CLK_TCB_FREQ / frequency); - - PWM_LOGINFO3(F("Frequency = "), frequency, F(", CLK_TCB_FREQ = "), CLK_TCB_FREQ); - PWM_LOGINFO1(F("setFrequency: _CCMPValueRemaining = "), _CCMPValueRemaining); - + // Set the CCMP for the given timer, // set the toggle count, // then turn on the interrupts @@ -378,9 +350,7 @@ void TimerInterrupt::resumeTimer() if (countLocal != 0) { if (ITimer0.checkTimerDone()) - { - PWM_LOGDEBUG3(F("T0 callback, _CCMPValueRemaining = "), ITimer0.get_CCMPValueRemaining(), F(", millis = "), millis()); - + { ITimer0.callback(); if (ITimer0.get_CCMPValue() > MAX_COUNT_16BIT) @@ -401,9 +371,7 @@ void TimerInterrupt::resumeTimer() } } else - { - PWM_LOGWARN(F("T0 done")); - + { ITimer0.detachInterrupt(); } } @@ -434,9 +402,7 @@ void TimerInterrupt::resumeTimer() if (countLocal != 0) { if (ITimer1.checkTimerDone()) - { - PWM_LOGDEBUG3(F("T1 callback, _CCMPValueRemaining = "), ITimer1.get_CCMPValueRemaining(), F(", millis = "), millis()); - + { ITimer1.callback(); if (ITimer1.get_CCMPValue() > MAX_COUNT_16BIT) @@ -457,9 +423,7 @@ void TimerInterrupt::resumeTimer() } } else - { - PWM_LOGWARN(F("T1 done")); - + { ITimer1.detachInterrupt(); } } @@ -488,9 +452,7 @@ void TimerInterrupt::resumeTimer() if (countLocal != 0) { if (ITimer2.checkTimerDone()) - { - PWM_LOGDEBUG3(F("T2 callback, _CCMPValueRemaining = "), ITimer2.get_CCMPValueRemaining(), F(", millis = "), millis()); - + { ITimer2.callback(); if (ITimer2.get_CCMPValue() > MAX_COUNT_16BIT) @@ -511,9 +473,7 @@ void TimerInterrupt::resumeTimer() } } else - { - PWM_LOGWARN(F("T2 done")); - + { ITimer2.detachInterrupt(); } } @@ -542,9 +502,7 @@ void TimerInterrupt::resumeTimer() if (countLocal != 0) { if (ITimer3.checkTimerDone()) - { - PWM_LOGDEBUG3(F("T3 callback, _CCMPValueRemaining = "), ITimer3.get_CCMPValueRemaining(), F(", millis = "), millis()); - + { ITimer3.callback(); if (ITimer3.get_CCMPValue() > MAX_COUNT_16BIT) @@ -565,9 +523,7 @@ void TimerInterrupt::resumeTimer() } } else - { - PWM_LOGWARN(F("T3 done")); - + { ITimer3.detachInterrupt(); } } @@ -597,9 +553,7 @@ void TimerInterrupt::resumeTimer() if (countLocal != 0) { if (ITimer4.checkTimerDone()) - { - PWM_LOGDEBUG3(F("T4 callback, _CCMPValueRemaining = "), ITimer4.get_CCMPValueRemaining(), F(", millis = "), millis()); - + { ITimer4.callback(); if (ITimer4.get_CCMPValue() > MAX_COUNT_16BIT) @@ -620,9 +574,7 @@ void TimerInterrupt::resumeTimer() } } else - { - PWM_LOGWARN(F("T4 done")); - + { ITimer4.detachInterrupt(); } } diff --git a/src/PWM_Generic_Debug.h b/src/PWM_Generic_Debug.h index 5c035b3..c07a8a9 100644 --- a/src/PWM_Generic_Debug.h +++ b/src/PWM_Generic_Debug.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.0.1 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 25/08/2022 Initial coding to support AVR Dx (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore + 1.0.1 K.Hoang 25/08/2022 Make MAX_NUMBER_CHANNELS configurable to max 64 PWM channels *****************************************************************************************************************************/ #pragma once