diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 70ec599..e3084c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ If you don't find anything, please [open a new issue](https://github.com/khoih-p Please ensure to specify the following: * Arduino IDE version (e.g. 1.8.19) or Platform.io version -* `DxCore` or `megaTinyCore` Core Version (e.g. Arduino DxCore core v1.4.10) +* `DxCore` Core Version (e.g. Arduino DxCore core v1.5.1) * Board (e.g. AVR128DA64, AVR128DB48, AVR64DB32, etc.) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce @@ -23,15 +23,15 @@ Please ensure to specify the following: * Operating system (Windows, Ubuntu, etc.) and the output of `uname -a` * Network configuration +Please be educated, civilized and constructive. Disrespective posts against [GitHub Code of Conduct](https://docs.github.com/en/site-policy/github-terms/github-event-code-of-conduct) will be ignored and deleted. ### Example ``` Arduino IDE version: 1.8.19 -Arduino DxCore core v1.4.10 -OS: Ubuntu 20.04 LTS +Arduino DxCore core v1.5.1 Board: Curiosity AVR128DB48 -Linux xy-Inspiron-3593 5.15.0-53-generic #59~20.04.1-Ubuntu SMP Thu Oct 20 15:10:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.15.0-56-generic #62~20.04.1-Ubuntu SMP Tue Nov 22 21:24:20 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered a crash while using this library diff --git a/changelog.md b/changelog.md index bb19a84..7ee9d26 100644 --- a/changelog.md +++ b/changelog.md @@ -10,13 +10,14 @@ Donate to my libraries using BuyMeACoffee + --- --- ## Table of Contents * [Changelog](#changelog) - + * [Release v1.1.0](#release-v110) * [Release v1.0.2](#release-v102) * [Release v1.0.1](#release-v101) * [Initial Release v1.0.0](#initial-release-v100) @@ -26,6 +27,9 @@ ## Changelog +### Release v1.1.0 + +1. Add support to AVRDD (AVR64DD, AVR32DD, AVR16DD, etc.) ### Release v1.0.2 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 c4fc786..c6724d1 100644 --- a/examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino +++ b/examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino @@ -16,8 +16,8 @@ // Important Note: To use drag-and-drop into CURIOSITY virtual drive if you can program via Arduino IDE // For example, check https://ww1.microchip.com/downloads/en/DeviceDoc/AVR128DB48-Curiosity-Nano-HW-UserG-DS50003037A.pdf -#if !( defined(DXCORE) || defined(MEGATINYCORE) ) - #error This is designed only for DXCORE or MEGATINYCORE megaAVR board! Please check your Tools->Board setting +#if !defined(DXCORE) + #error This is designed only for DXCORE megaAVR board! Please check your Tools->Board setting #endif // These define's must be placed at the beginning before #include "Dx_Slow_PWM.h" 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 941721d..0b61df8 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 @@ -16,8 +16,8 @@ // Important Note: To use drag-and-drop into CURIOSITY virtual drive if you can program via Arduino IDE // For example, check https://ww1.microchip.com/downloads/en/DeviceDoc/AVR128DB48-Curiosity-Nano-HW-UserG-DS50003037A.pdf -#if !( defined(DXCORE) || defined(MEGATINYCORE) ) - #error This is designed only for DXCORE or MEGATINYCORE megaAVR board! Please check your Tools->Board setting +#if !defined(DXCORE) + #error This is designed only for DXCORE megaAVR board! Please check your Tools->Board setting #endif // These define's must be placed at the beginning before #include "Dx_Slow_PWM.h" 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 03e3f2b..93f0314 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 @@ -16,8 +16,8 @@ // Important Note: To use drag-and-drop into CURIOSITY virtual drive if you can program via Arduino IDE // For example, check https://ww1.microchip.com/downloads/en/DeviceDoc/AVR128DB48-Curiosity-Nano-HW-UserG-DS50003037A.pdf -#if !( defined(DXCORE) || defined(MEGATINYCORE) ) - #error This is designed only for DXCORE or MEGATINYCORE megaAVR board! Please check your Tools->Board setting +#if !defined(DXCORE) + #error This is designed only for DXCORE megaAVR board! Please check your Tools->Board setting #endif // These define's must be placed at the beginning before #include "Dx_Slow_PWM.h" diff --git a/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino b/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino index a9c006c..7b89a40 100644 --- a/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino +++ b/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino @@ -16,8 +16,8 @@ // Important Note: To use drag-and-drop into CURIOSITY virtual drive if you can program via Arduino IDE // For example, check https://ww1.microchip.com/downloads/en/DeviceDoc/AVR128DB48-Curiosity-Nano-HW-UserG-DS50003037A.pdf -#if !( defined(DXCORE) || defined(MEGATINYCORE) ) - #error This is designed only for DXCORE or MEGATINYCORE megaAVR board! Please check your Tools->Board setting +#if !defined(DXCORE) + #error This is designed only for DXCORE megaAVR board! Please check your Tools->Board setting #endif // These define's must be placed at the beginning before #include "Dx_Slow_PWM.h" diff --git a/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino b/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino index f14f85a..519fab4 100644 --- a/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino +++ b/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino @@ -16,8 +16,8 @@ // Important Note: To use drag-and-drop into CURIOSITY virtual drive if you can program via Arduino IDE // For example, check https://ww1.microchip.com/downloads/en/DeviceDoc/AVR128DB48-Curiosity-Nano-HW-UserG-DS50003037A.pdf -#if !( defined(DXCORE) || defined(MEGATINYCORE) ) - #error This is designed only for DXCORE or MEGATINYCORE megaAVR board! Please check your Tools->Board setting +#if !defined(DXCORE) + #error This is designed only for DXCORE megaAVR board! Please check your Tools->Board setting #endif // These define's must be placed at the beginning before #include "Dx_Slow_PWM.h" diff --git a/examples/multiFileProject/multiFileProject.ino b/examples/multiFileProject/multiFileProject.ino index 517e2b3..8924502 100644 --- a/examples/multiFileProject/multiFileProject.ino +++ b/examples/multiFileProject/multiFileProject.ino @@ -1,18 +1,5 @@ -/**************************************************************************************************************************** - multiFileProject.ino - - For Arduino AVRDx-based boards (AVR128Dx, AVR64Dx, AVR32Dx, etc.) using DxCore - Written by Khoi Hoang - - Built by Khoi Hoang https://github.com/khoih-prog/Dx_Slow_PWM - Licensed under MIT license -*****************************************************************************************************************************/ - -// Important Note: To use drag-and-drop into CURIOSITY virtual drive if you can program via Arduino IDE -// For example, check https://ww1.microchip.com/downloads/en/DeviceDoc/AVR128DB48-Curiosity-Nano-HW-UserG-DS50003037A.pdf - -#if !( defined(DXCORE) || defined(MEGATINYCORE) ) - #error This is designed only for DXCORE or MEGATINYCORE megaAVR board! Please check your Tools->Board setting +#if !defined(DXCORE) + #error This is designed only for DXCORE megaAVR board! Please check your Tools->Board setting #endif #define DX_SLOW_PWM_VERSION_MIN_TARGET F("Dx_Slow_PWM v1.0.1") diff --git a/library.json b/library.json index 8cf79fa..53e3fe1 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "Dx_Slow_PWM", - "version": "1.0.2", + "version": "1.1.0", "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 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.", + "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. Now supporting AVRDD", "authors": { "name": "Khoi Hoang", diff --git a/library.properties b/library.properties index 8c182ce..7398418 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=Dx_Slow_PWM -version=1.0.2 +version=1.1.0 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 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. +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 are 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 is necessary if you need to control devices requiring high precision. Now you can change the PWM settings on-the-fly. Now supporting AVRDD 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 4a40366..431bddd 100644 --- a/src/Dx_Slow_PWM.h +++ b/src/Dx_Slow_PWM.h @@ -5,20 +5,21 @@ Built by Khoi Hoang https://github.com/khoih-prog/Dx_Slow_PWM Licensed under MIT license - + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by unsigned long miliseconds), you just consume only one AVRDx-based timer and avoid conflicting with other cores' tasks. The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.2 + Version: 1.1.0 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 1.0.2 K.Hoang 25/08/2022 Minor cosmetic fix + 1.1.0 K.Hoang 30/12/2022 Add support to AVR DD (AVR64DD, AVR32DDx, AVR16DD, etc.) using breaking DxCore v1.5.1+ *****************************************************************************************************************************/ #pragma once diff --git a/src/Dx_Slow_PWM.hpp b/src/Dx_Slow_PWM.hpp index c12d13a..61e03e9 100644 --- a/src/Dx_Slow_PWM.hpp +++ b/src/Dx_Slow_PWM.hpp @@ -12,13 +12,14 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.2 + Version: 1.1.0 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 1.0.2 K.Hoang 25/08/2022 Minor cosmetic fix + 1.1.0 K.Hoang 30/12/2022 Add support to AVR DD (AVR64DD, AVR32DDx, AVR16DD, etc.) using breaking DxCore v1.5.1+ *****************************************************************************************************************************/ #pragma once @@ -54,13 +55,22 @@ #elif ( defined(__AVR_AVR64DD32__) || defined(__AVR_AVR64DD28__) || defined(__AVR_AVR64DD20__) || defined(__AVR_AVR64DD14__) ) #define BOARD_NAME F("AVR64DD") - #error AVR64DD not supported yet by the DxCore + + #if ( (DXCORE_MAJOR == 1) && (DXCORE_MINOR < 5) ) + #error AVR64DD not supported yet + #endif #elif ( defined(__AVR_AVR32DD32__) || defined(__AVR_AVR32DD28__) || defined(__AVR_AVR32DD20__) || defined(__AVR_AVR32DD14__) ) #define BOARD_NAME F("AVR32DD") - #error AVR32DD not supported yet by the DxCore + + #if ( (DXCORE_MAJOR == 1) && (DXCORE_MINOR < 5) ) + #error AVR32DD not supported yet + #endif #elif ( defined(__AVR_AVR16DD32__) || defined(__AVR_AVR16DD28__) || defined(__AVR_AVR16DD20__) || defined(__AVR_AVR16DD14__) ) #define BOARD_NAME F("AVR16DD") - #error AVR16DD not supported yet by the DxCore + + #if ( (DXCORE_MAJOR == 1) && (DXCORE_MINOR < 5) ) + #error AVR16DD not supported yet + #endif ////////////////////////// __AVR_DU__ ////////////////////////// @@ -87,31 +97,23 @@ #endif #endif // #if !defined(BOARD_NAME) - -#elif defined(MEGATINYCORE) - - #define TIMER_INTERRUPT_USING_MEGATINYCORE true - - #define BOARD_NAME F("MEGATINYCORE Board") - - #error Support for megaTinyCore not ready yet! Please check your Tools->Board setting - + #else - #error This is designed only for AVRDx boards using DxCore or megaTinyCore ! Please check your Tools->Board setting + #error This is designed only for AVRDx boards using DxCore ! Please check your Tools->Board setting #endif /////////////////////////////////////////////////////////////////////////////// #ifndef DX_SLOW_PWM_VERSION - #define DX_SLOW_PWM_VERSION F("Dx_Slow_PWM v1.0.2") + #define DX_SLOW_PWM_VERSION F("Dx_Slow_PWM v1.1.0") #define DX_SLOW_PWM_VERSION_MAJOR 1 - #define DX_SLOW_PWM_VERSION_MINOR 0 - #define DX_SLOW_PWM_VERSION_PATCH 2 + #define DX_SLOW_PWM_VERSION_MINOR 1 + #define DX_SLOW_PWM_VERSION_PATCH 0 - #define DX_SLOW_PWM_VERSION_INT 1000002 + #define DX_SLOW_PWM_VERSION_INT 1001000 #endif #ifndef _PWM_LOGLEVEL_ diff --git a/src/Dx_Slow_PWM_ISR.h b/src/Dx_Slow_PWM_ISR.h index 6c76931..bb56e9e 100644 --- a/src/Dx_Slow_PWM_ISR.h +++ b/src/Dx_Slow_PWM_ISR.h @@ -5,20 +5,21 @@ Built by Khoi Hoang https://github.com/khoih-prog/Dx_Slow_PWM Licensed under MIT license - + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by unsigned long miliseconds), you just consume only one AVRDx-based timer and avoid conflicting with other cores' tasks. The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.2 + Version: 1.1.0 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 1.0.2 K.Hoang 25/08/2022 Minor cosmetic fix + 1.1.0 K.Hoang 30/12/2022 Add support to AVR DD (AVR64DD, AVR32DDx, AVR16DD, etc.) using breaking DxCore v1.5.1+ *****************************************************************************************************************************/ #pragma once diff --git a/src/Dx_Slow_PWM_ISR.hpp b/src/Dx_Slow_PWM_ISR.hpp index 7ab0b22..50b6aea 100644 --- a/src/Dx_Slow_PWM_ISR.hpp +++ b/src/Dx_Slow_PWM_ISR.hpp @@ -12,13 +12,14 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.2 + Version: 1.1.0 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 1.0.2 K.Hoang 25/08/2022 Minor cosmetic fix + 1.1.0 K.Hoang 30/12/2022 Add support to AVR DD (AVR64DD, AVR32DDx, AVR16DD, etc.) using breaking DxCore v1.5.1+ *****************************************************************************************************************************/ #pragma once @@ -54,13 +55,22 @@ #elif ( defined(__AVR_AVR64DD32__) || defined(__AVR_AVR64DD28__) || defined(__AVR_AVR64DD20__) || defined(__AVR_AVR64DD14__) ) #define BOARD_NAME F("AVR64DD") - #error AVR64DD not supported yet by the DxCore + + #if ( (DXCORE_MAJOR == 1) && (DXCORE_MINOR < 5) ) + #error AVR64DD not supported yet + #endif #elif ( defined(__AVR_AVR32DD32__) || defined(__AVR_AVR32DD28__) || defined(__AVR_AVR32DD20__) || defined(__AVR_AVR32DD14__) ) #define BOARD_NAME F("AVR32DD") - #error AVR32DD not supported yet by the DxCore + + #if ( (DXCORE_MAJOR == 1) && (DXCORE_MINOR < 5) ) + #error AVR32DD not supported yet + #endif #elif ( defined(__AVR_AVR16DD32__) || defined(__AVR_AVR16DD28__) || defined(__AVR_AVR16DD20__) || defined(__AVR_AVR16DD14__) ) #define BOARD_NAME F("AVR16DD") - #error AVR16DD not supported yet by the DxCore + + #if ( (DXCORE_MAJOR == 1) && (DXCORE_MINOR < 5) ) + #error AVR16DD not supported yet + #endif ////////////////////////// __AVR_DU__ ////////////////////////// @@ -105,13 +115,13 @@ /////////////////////////////////////////////////////////////////////////////// #ifndef DX_SLOW_PWM_VERSION - #define DX_SLOW_PWM_VERSION F("Dx_Slow_PWM v1.0.2") + #define DX_SLOW_PWM_VERSION F("Dx_Slow_PWM v1.1.0") #define DX_SLOW_PWM_VERSION_MAJOR 1 - #define DX_SLOW_PWM_VERSION_MINOR 0 - #define DX_SLOW_PWM_VERSION_PATCH 2 + #define DX_SLOW_PWM_VERSION_MINOR 1 + #define DX_SLOW_PWM_VERSION_PATCH 0 - #define DX_SLOW_PWM_VERSION_INT 1000002 + #define DX_SLOW_PWM_VERSION_INT 1001000 #endif #ifndef _PWM_LOGLEVEL_ diff --git a/src/Dx_Slow_PWM_ISR_Impl.h b/src/Dx_Slow_PWM_ISR_Impl.h index 08b9fda..b8bccac 100644 --- a/src/Dx_Slow_PWM_ISR_Impl.h +++ b/src/Dx_Slow_PWM_ISR_Impl.h @@ -5,20 +5,21 @@ Built by Khoi Hoang https://github.com/khoih-prog/Dx_Slow_PWM Licensed under MIT license - + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by unsigned long miliseconds), you just consume only one AVRDx-based timer and avoid conflicting with other cores' tasks. The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.2 + Version: 1.1.0 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 1.0.2 K.Hoang 25/08/2022 Minor cosmetic fix + 1.1.0 K.Hoang 30/12/2022 Add support to AVR DD (AVR64DD, AVR32DDx, AVR16DD, etc.) using breaking DxCore v1.5.1+ *****************************************************************************************************************************/ #pragma once @@ -28,19 +29,19 @@ #include -/////////////////////////////////////////////////// +/////////////////////////////////////////////////// uint32_t timeNow() { -#if USING_MICROS_RESOLUTION +#if USING_MICROS_RESOLUTION return ( (uint32_t) micros() ); #else return ( (uint32_t) millis() ); -#endif +#endif } - -/////////////////////////////////////////////////// + +/////////////////////////////////////////////////// DX_SLOW_PWM_ISR::DX_SLOW_PWM_ISR() : numChannels (-1) @@ -49,42 +50,42 @@ DX_SLOW_PWM_ISR::DX_SLOW_PWM_ISR() /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::init() +void DX_SLOW_PWM_ISR::init() { uint32_t currentTime = timeNow(); - - for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) { memset((void*) &PWM[channelNum], 0, sizeof (PWM_t)); PWM[channelNum].prevTime = currentTime; PWM[channelNum].pin = INVALID_MEGA_AVR_PIN; } - + numChannels = 0; } /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::run() -{ +void DX_SLOW_PWM_ISR::run() +{ uint32_t currentTime = timeNow(); - + noInterrupts(); - for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) - { + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + { // If enabled => check // start period / dutyCycle => digitalWrite HIGH // end dutyCycle => digitalWrite LOW - if (PWM[channelNum].enabled) + if (PWM[channelNum].enabled) { - if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) <= PWM[channelNum].onTime ) - { + if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) <= PWM[channelNum].onTime ) + { if (!PWM[channelNum].pinHigh) { digitalWrite(PWM[channelNum].pin, HIGH); PWM[channelNum].pinHigh = true; - + // callbackStart if (PWM[channelNum].callbackStart != nullptr) { @@ -92,13 +93,13 @@ void DX_SLOW_PWM_ISR::run() } } } - else if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) < PWM[channelNum].period ) + else if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) < PWM[channelNum].period ) { if (PWM[channelNum].pinHigh) { digitalWrite(PWM[channelNum].pin, LOW); PWM[channelNum].pinHigh = false; - + // callback when PWM pulse stops (LOW) if (PWM[channelNum].callbackStop != nullptr) { @@ -106,27 +107,25 @@ void DX_SLOW_PWM_ISR::run() } } } - //else - else if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) >= PWM[channelNum].period ) + //else + else if ( (uint32_t) (currentTime - PWM[channelNum].prevTime) >= PWM[channelNum].period ) { PWM[channelNum].prevTime = currentTime; - + #if CHANGING_PWM_END_OF_CYCLE - // Only update whenever having newPeriod if (PWM[channelNum].newPeriod != 0) { PWM[channelNum].period = PWM[channelNum].newPeriod; PWM[channelNum].newPeriod = 0; - + PWM[channelNum].onTime = PWM[channelNum].newOnTime; } - -#endif - } +#endif + } } } - + interrupts(); } @@ -135,16 +134,16 @@ void DX_SLOW_PWM_ISR::run() // find the first available slot // return -1 if none found -int DX_SLOW_PWM_ISR::findFirstFreeSlot() +int DX_SLOW_PWM_ISR::findFirstFreeSlot() { // all slots are used - if (numChannels >= MAX_NUMBER_CHANNELS) + if (numChannels >= MAX_NUMBER_CHANNELS) { return -1; } // return the first slot with no callbackStart (i.e. free) - for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) { if ( (PWM[channelNum].period == 0) && !PWM[channelNum].enabled ) { @@ -158,11 +157,10 @@ int DX_SLOW_PWM_ISR::findFirstFreeSlot() /////////////////////////////////////////////////// -int DX_SLOW_PWM_ISR::setupPWMChannel(const uint32_t& pin, const uint32_t& period, const float& dutycycle, - void* cbStartFunc, void* cbStopFunc) +int DX_SLOW_PWM_ISR::setupPWMChannel(const uint32_t& pin, const uint32_t& period, const float& dutycycle, void* cbStartFunc, void* cbStopFunc) { int channelNum; - + // Invalid input, such as period = 0, etc if ( (period == 0) || (dutycycle < 0.0) || (dutycycle > 100.0) ) { @@ -170,55 +168,50 @@ int DX_SLOW_PWM_ISR::setupPWMChannel(const uint32_t& pin, const uint32_t& period return -1; } - if (numChannels < 0) + if (numChannels < 0) { init(); } - + channelNum = findFirstFreeSlot(); - - if (channelNum < 0) + + if (channelNum < 0) { return -1; } PWM[channelNum].pin = pin; PWM[channelNum].period = period; - + // Must be 0 for new PWM channel PWM[channelNum].newPeriod = 0; - + PWM[channelNum].onTime = ( period * dutycycle ) / 100; - + pinMode(pin, OUTPUT); digitalWrite(pin, HIGH); PWM[channelNum].pinHigh = true; - + PWM[channelNum].prevTime = timeNow(); - + PWM[channelNum].callbackStart = cbStartFunc; PWM[channelNum].callbackStop = cbStopFunc; - - PWM_LOGINFO0("Channel : "); - PWM_LOGINFO0(channelNum); - PWM_LOGINFO0("\t Period : "); - PWM_LOGINFO0(PWM[channelNum].period); - PWM_LOGINFO0("\t\tOnTime : "); - PWM_LOGINFO0(PWM[channelNum].onTime); - PWM_LOGINFO0("\tStart_Time : "); - PWM_LOGINFOLN0(PWM[channelNum].prevTime); - + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); + PWM_LOGINFO0("\t Period : "); PWM_LOGINFO0(PWM[channelNum].period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].onTime); + PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime); + numChannels++; - + PWM[channelNum].enabled = true; - + return channelNum; } /////////////////////////////////////////////////// -bool DX_SLOW_PWM_ISR::modifyPWMChannel_Period(const uint8_t& channelNum, const uint32_t& pin, const uint32_t& period, - const float& dutycycle) +bool DX_SLOW_PWM_ISR::modifyPWMChannel_Period(const uint8_t& channelNum, const uint32_t& pin, const uint32_t& period, const float& dutycycle) { // Invalid input, such as period = 0, etc if ( (period == 0) || (dutycycle < 0.0) || (dutycycle > 100.0) ) @@ -227,62 +220,54 @@ bool DX_SLOW_PWM_ISR::modifyPWMChannel_Period(const uint8_t& channelNum, const u return false; } - if (channelNum > MAX_NUMBER_CHANNELS) + if (channelNum > MAX_NUMBER_CHANNELS) { PWM_LOGERROR("Error: channelNum > MAX_NUMBER_CHANNELS"); return false; } - - if (PWM[channelNum].pin != pin) + + if (PWM[channelNum].pin != pin) { PWM_LOGERROR("Error: channelNum and pin mismatched"); return false; } - + #if CHANGING_PWM_END_OF_CYCLE PWM[channelNum].newPeriod = period; PWM[channelNum].newDutyCycle = dutycycle; PWM[channelNum].newOnTime = ( period * dutycycle ) / 100; - - PWM_LOGINFO0("Channel : "); - PWM_LOGINFO0(channelNum); - PWM_LOGINFO0("\t Period : "); - PWM_LOGINFO0(period); - PWM_LOGINFO0("\t\tOnTime : "); - PWM_LOGINFO0(PWM[channelNum].newOnTime); - PWM_LOGINFO0("\tStart_Time : "); - PWM_LOGINFOLN0(PWM[channelNum].prevTime); - + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); + PWM_LOGINFO0("\t Period : "); PWM_LOGINFO0(period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].newOnTime); + PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime); + #else - PWM[channelNum].period = period; + PWM[channelNum].period = period; PWM[channelNum].onTime = ( period * dutycycle ) / 100; - + digitalWrite(pin, HIGH); PWM[channelNum].pinHigh = true; - + PWM[channelNum].prevTime = timeNow(); - - PWM_LOGINFO0("Channel : "); - PWM_LOGINFO0(channelNum); - PWM_LOGINFO0("\t Period : "); - PWM_LOGINFO0(PWM[channelNum].period); - PWM_LOGINFO0("\t\tOnTime : "); - PWM_LOGINFO0(PWM[channelNum].onTime); - PWM_LOGINFO0("\tStart_Time : "); - PWM_LOGINFOLN0(PWM[channelNum].prevTime); - + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); + PWM_LOGINFO0("\t Period : "); PWM_LOGINFO0(PWM[channelNum].period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(PWM[channelNum].onTime); + PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(PWM[channelNum].prevTime); + #endif - + return true; } /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::deleteChannel(const uint8_t& channelNum) +void DX_SLOW_PWM_ISR::deleteChannel(const uint8_t& channelNum) { // nothing to delete if no timers are in use if ( (channelNum >= MAX_NUMBER_CHANNELS) || (numChannels == 0) ) @@ -294,9 +279,9 @@ void DX_SLOW_PWM_ISR::deleteChannel(const uint8_t& channelNum) if ( (PWM[channelNum].pin != INVALID_MEGA_AVR_PIN) && (PWM[channelNum].period != 0) ) { memset((void*) &PWM[channelNum], 0, sizeof (PWM_t)); - + PWM[channelNum].pin = INVALID_MEGA_AVR_PIN; - + // update number of timers numChannels--; } @@ -304,9 +289,9 @@ void DX_SLOW_PWM_ISR::deleteChannel(const uint8_t& channelNum) /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::restartChannel(const uint8_t& channelNum) +void DX_SLOW_PWM_ISR::restartChannel(const uint8_t& channelNum) { - if (channelNum >= MAX_NUMBER_CHANNELS) + if (channelNum >= MAX_NUMBER_CHANNELS) { return; } @@ -316,9 +301,9 @@ void DX_SLOW_PWM_ISR::restartChannel(const uint8_t& channelNum) /////////////////////////////////////////////////// -bool DX_SLOW_PWM_ISR::isEnabled(const uint8_t& channelNum) +bool DX_SLOW_PWM_ISR::isEnabled(const uint8_t& channelNum) { - if (channelNum >= MAX_NUMBER_CHANNELS) + if (channelNum >= MAX_NUMBER_CHANNELS) { return false; } @@ -328,9 +313,9 @@ bool DX_SLOW_PWM_ISR::isEnabled(const uint8_t& channelNum) /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::enable(const uint8_t& channelNum) +void DX_SLOW_PWM_ISR::enable(const uint8_t& channelNum) { - if (channelNum >= MAX_NUMBER_CHANNELS) + if (channelNum >= MAX_NUMBER_CHANNELS) { return; } @@ -340,9 +325,9 @@ void DX_SLOW_PWM_ISR::enable(const uint8_t& channelNum) /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::disable(const uint8_t& channelNum) +void DX_SLOW_PWM_ISR::disable(const uint8_t& channelNum) { - if (channelNum >= MAX_NUMBER_CHANNELS) + if (channelNum >= MAX_NUMBER_CHANNELS) { return; } @@ -352,11 +337,11 @@ void DX_SLOW_PWM_ISR::disable(const uint8_t& channelNum) /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::enableAll() +void DX_SLOW_PWM_ISR::enableAll() { // Enable all timers with a callbackStart assigned (used) - for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) { if (PWM[channelNum].period != 0) { @@ -367,10 +352,10 @@ void DX_SLOW_PWM_ISR::enableAll() /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::disableAll() +void DX_SLOW_PWM_ISR::disableAll() { // Disable all timers with a callbackStart assigned (used) - for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) + for (uint8_t channelNum = 0; channelNum < MAX_NUMBER_CHANNELS; channelNum++) { if (PWM[channelNum].period != 0) { @@ -381,9 +366,9 @@ void DX_SLOW_PWM_ISR::disableAll() /////////////////////////////////////////////////// -void DX_SLOW_PWM_ISR::toggle(const uint8_t& channelNum) +void DX_SLOW_PWM_ISR::toggle(const uint8_t& channelNum) { - if (channelNum >= MAX_NUMBER_CHANNELS) + if (channelNum >= MAX_NUMBER_CHANNELS) { return; } @@ -393,7 +378,7 @@ void DX_SLOW_PWM_ISR::toggle(const uint8_t& channelNum) /////////////////////////////////////////////////// -int8_t DX_SLOW_PWM_ISR::getnumChannels() +int8_t DX_SLOW_PWM_ISR::getnumChannels() { return numChannels; } diff --git a/src/Dx_Slow_PWM_Impl.h b/src/Dx_Slow_PWM_Impl.h index cbf2685..aa42885 100644 --- a/src/Dx_Slow_PWM_Impl.h +++ b/src/Dx_Slow_PWM_Impl.h @@ -5,20 +5,21 @@ Built by Khoi Hoang https://github.com/khoih-prog/Dx_Slow_PWM Licensed under MIT license - + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by unsigned long miliseconds), you just consume only one AVRDx-based timer and avoid conflicting with other cores' tasks. The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.2 + Version: 1.1.0 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 1.0.2 K.Hoang 25/08/2022 Minor cosmetic fix + 1.1.0 K.Hoang 30/12/2022 Add support to AVR DD (AVR64DD, AVR32DDx, AVR16DD, etc.) using breaking DxCore v1.5.1+ ****************************************************************************************************************************/ #pragma once @@ -34,24 +35,24 @@ /***************************************************************************************** - // From ~/.arduino15/packages/arduino/7.3.0-atmel3.6.1-arduino5/avr/include/avr/iom4809.h +// From ~/.arduino15/packages/arduino/7.3.0-atmel3.6.1-arduino5/avr/include/avr/iom4809.h - //#define TCB0 (*(TCB_t *) 0x0A80) // 16-bit Timer Type B - //#define TCB1 (*(TCB_t *) 0x0A90) // 16-bit Timer Type B - //#define TCB2 (*(TCB_t *) 0x0AA0) // 16-bit Timer Type B - //#define TCB3 (*(TCB_t *) 0x0AB0) // 16-bit Timer Type B +//#define TCB0 (*(TCB_t *) 0x0A80) // 16-bit Timer Type B +//#define TCB1 (*(TCB_t *) 0x0A90) // 16-bit Timer Type B +//#define TCB2 (*(TCB_t *) 0x0AA0) // 16-bit Timer Type B +//#define TCB3 (*(TCB_t *) 0x0AB0) // 16-bit Timer Type B - // - typedef enum TCB_CLKSEL_enum - { +// +typedef enum TCB_CLKSEL_enum +{ TCB_CLKSEL_CLKDIV1_gc = (0x00<<1), // CLK_PER (No Prescaling) TCB_CLKSEL_CLKDIV2_gc = (0x01<<1), // CLK_PER/2 (From Prescaler) TCB_CLKSEL_CLKTCA_gc = (0x02<<1), // Use Clock from TCA - } TCB_CLKSEL_t; +} TCB_CLKSEL_t; - // - typedef enum TCB_CNTMODE_enum - { +// +typedef enum TCB_CNTMODE_enum +{ TCB_CNTMODE_INT_gc = (0x00<<0), // Periodic Interrupt TCB_CNTMODE_TIMEOUT_gc = (0x01<<0), // Periodic Timeout TCB_CNTMODE_CAPT_gc = (0x02<<0), // Input Capture Event @@ -60,47 +61,47 @@ TCB_CNTMODE_FRQPW_gc = (0x05<<0), // Input Capture Frequency and Pulse-Width measurement TCB_CNTMODE_SINGLE_gc = (0x06<<0), // Single Shot TCB_CNTMODE_PWM8_gc = (0x07<<0), // 8-bit PWM - } TCB_CNTMODE_t; +} TCB_CNTMODE_t; *****************************************************************************************/ /***************************************************************************************** - // From https://github.com/SpenceKonde/DxCore/blob/master/megaavr/cores/dxcore/Arduino.h#L455-L462 +// From https://github.com/SpenceKonde/DxCore/blob/master/megaavr/cores/dxcore/Arduino.h#L455-L462 - #define TIMERA0 (0x10) // A "simple" type A timer mapping doesn't get constants for the WO channels. - #define TIMERA1 (0x08) // Formerly 0x11 - giving it a dedicated bit makes the takeover tracking easy and efficient instead of being a morass of tests and bitmath. - #define TIMERB0 (0x20) // TCB0 - #define TIMERB1 (0x21) // TCB1 - #define TIMERB2 (0x22) // TCB2 - #define TIMERB3 (0x23) // TCB3 - #define TIMERB4 (0x24) // TCB4 - #define TIMERD0 (0x70) // If any of these bits match it's potentially on TCD0 +#define TIMERA0 (0x10) // A "simple" type A timer mapping doesn't get constants for the WO channels. +#define TIMERA1 (0x08) // Formerly 0x11 - giving it a dedicated bit makes the takeover tracking easy and efficient instead of being a morass of tests and bitmath. +#define TIMERB0 (0x20) // TCB0 +#define TIMERB1 (0x21) // TCB1 +#define TIMERB2 (0x22) // TCB2 +#define TIMERB3 (0x23) // TCB3 +#define TIMERB4 (0x24) // TCB4 +#define TIMERD0 (0x70) // If any of these bits match it's potentially on TCD0 *****************************************************************************************/ #if defined(TCB4) -#if (_PWM_LOGLEVEL_ > 2) - #warning TCB0-TCB4 Timers available -#endif - -TCB_t* TimerTCB[ NUM_HW_TIMERS ] = { &TCB0, &TCB1, &TCB2, &TCB3, &TCB4 }; - + #if (_PWM_LOGLEVEL_ > 2) + #warning TCB0-TCB4 Timers available + #endif + + TCB_t* TimerTCB[ NUM_HW_TIMERS ] = { &TCB0, &TCB1, &TCB2, &TCB3, &TCB4 }; + #elif defined(TCB3) -#if (_PWM_LOGLEVEL_ > 2) - #warning TCB0-TCB3 Timers available -#endif - -TCB_t* TimerTCB[ NUM_HW_TIMERS ] = { &TCB0, &TCB1, &TCB2, &TCB3 }; - + #if (_PWM_LOGLEVEL_ > 2) + #warning TCB0-TCB3 Timers available + #endif + + TCB_t* TimerTCB[ NUM_HW_TIMERS ] = { &TCB0, &TCB1, &TCB2, &TCB3 }; + #else -#if (_PWM_LOGLEVEL_ > 2) - #warning TCB0-TCB2 Timers available -#endif - -TCB_t* TimerTCB[ NUM_HW_TIMERS ] = { &TCB0, &TCB1, &TCB2 }; - -#endif + #if (_PWM_LOGLEVEL_ > 2) + #warning TCB0-TCB2 Timers available + #endif + + TCB_t* TimerTCB[ NUM_HW_TIMERS ] = { &TCB0, &TCB1, &TCB2 }; + +#endif ////////////////////////////////////////////// @@ -115,62 +116,62 @@ TCB_t* TimerTCB[ NUM_HW_TIMERS ] = { &TCB0, &TCB1, &TCB2 }; #if (_PWM_LOGLEVEL_ > 2) #warning Using no prescaler => FULL_CLOCK (24MHz, 16MHz, etc) #endif - + #define TCB_CLKSEL_VALUE TCB_CLKSEL_DIV1_gc #define CLOCK_PRESCALER 1 - + #elif USING_HALF_CLOCK // Use prescaler 2 => 12/8MHz, etc. #if (_PWM_LOGLEVEL_ > 2) #warning Using prescaler 2 => HALF_CLOCK (12MHz, 8MHz, etc.) #endif - + #define TCB_CLKSEL_VALUE TCB_CLKSEL_DIV2_gc #define CLOCK_PRESCALER 2 - // No TCB_CLKSEL_CLKTCA_gc in DXCore ??? - // Don't use 250KHz now +// No TCB_CLKSEL_CLKTCA_gc in DXCore ??? +// Don't use 250KHz now #elif USING_250KHZ - // Optional, but for clarity - // Use Timer A as clock (prescaler 64) => 250KHz - #define TCB_CLKSEL_VALUE TCB_CLKSEL_CLKTCA_gc - #define CLOCK_PRESCALER 64 - - #error Not OK now. Do not use - + // Optional, but for clarity + // Use Timer A as clock (prescaler 64) => 250KHz + #define TCB_CLKSEL_VALUE TCB_CLKSEL_CLKTCA_gc + #define CLOCK_PRESCALER 64 + + #error Not OK now. Do not use + #else // Use prescaler 2 => 8MHz #if (_PWM_LOGLEVEL_ > 2) #warning Using prescaler 2 => 8MHz #endif - + #define TCB_CLKSEL_VALUE TCB_CLKSEL_DIV2_gc #define CLOCK_PRESCALER 2 - + #endif - + #define CLK_TCB_FREQ ( F_CPU / CLOCK_PRESCALER ) ////////////////////////////////////////////// void TimerInterrupt::init(const int8_t& timer) -{ +{ // Set timer specific stuff // All timers in CTC mode // 8 bit timers will require changing prescalar values, // whereas 16 bit timers are set to either ck/1 or ck/64 prescalar - + noInterrupts(); - + // 16 bit timer TimerTCB[timer]->CTRLB = TCB_CNTMODE_INT_gc; // Use timer compare mode 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 - + _timer = timer; - interrupts(); + interrupts(); } ////////////////////////////////////////////// @@ -182,14 +183,14 @@ void TimerInterrupt::set_CCMP() // set the toggle count, // then turn on the interrupts uint32_t _CCMPValueToUse; - + _CCMPValueToUse = min(MAX_COUNT_16BIT, _CCMPValueRemaining); _CCMPValueRemaining -= _CCMPValueToUse; - + TimerTCB[_timer]->CCMP = _CCMPValueToUse; // Value to compare with. - + TimerTCB[_timer]->INTCTRL = TCB_CAPT_bm; // Enable the interrupt - + // Flag _CCMPValue == 0 => end of long timer if (_CCMPValueRemaining == 0) _timerDone = true; @@ -200,26 +201,25 @@ void TimerInterrupt::set_CCMP() // frequency (in hertz) and duration (in milliseconds). // Return true if frequency is OK with selected timer (CCMPValue is in range) -bool TimerInterrupt::setFrequency(const float& frequency, timer_callback_p callback, const uint32_t& params, - const unsigned long& duration) -{ +bool TimerInterrupt::setFrequency(const float& frequency, timer_callback_p callback, const uint32_t& params, const unsigned long& duration) +{ //frequencyLimit must > 1 float frequencyLimit = frequency * 17179.840; // 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) ) - { + { return false; } - else - { + else + { // Calculate the toggle count. Duration must be at least longer then one cycle if (duration > 0) - { + { _toggle_count = frequency * duration / 1000; - + if (_toggle_count < 1) - { + { return false; } } @@ -227,7 +227,7 @@ bool TimerInterrupt::setFrequency(const float& frequency, timer_callback_p callb { _toggle_count = -1; } - + //Timer0-4 are 16 bit timers, meaning it can store a maximum counter value of 65535. noInterrupts(); @@ -237,14 +237,14 @@ bool TimerInterrupt::setFrequency(const float& frequency, timer_callback_p callb _params = reinterpret_cast(params); _timerDone = false; - + _CCMPValue = _CCMPValueRemaining = (uint32_t) (CLK_TCB_FREQ / frequency); - + // Set the CCMP for the given timer, // set the toggle count, - // then turn on the interrupts + // then turn on the interrupts set_CCMP(); - + interrupts(); return true; @@ -256,12 +256,12 @@ bool TimerInterrupt::setFrequency(const float& frequency, timer_callback_p callb void TimerInterrupt::detachInterrupt() { noInterrupts(); - + // Clear interrupt flag TimerTCB[_timer]->INTFLAGS = TCB_CAPT_bm; TimerTCB[_timer]->INTCTRL &= ~TCB_CAPT_bm; // Disable the interrupt TimerTCB[_timer]->CTRLA &= ~TCB_ENABLE_bm; // Disable timer - + interrupts(); } @@ -281,11 +281,11 @@ void TimerInterrupt::reattachInterrupt(const unsigned long& duration) { _toggle_count = -1; } - - // Set interrupt flag + + // Set interrupt flag TimerTCB[_timer]->INTCTRL |= TCB_CAPT_bm; // Enable the interrupt TimerTCB[_timer]->CTRLA |= TCB_ENABLE_bm; // Enable timer - + interrupts(); } @@ -294,7 +294,7 @@ void TimerInterrupt::reattachInterrupt(const unsigned long& duration) // Just stop clock source, still keep the count // To fix this. void TimerInterrupt::pauseTimer() -{ +{ detachInterrupt(); } @@ -302,7 +302,7 @@ void TimerInterrupt::pauseTimer() // Just reconnect clock source, continue from the current count void TimerInterrupt::resumeTimer() -{ +{ reattachInterrupt(); } @@ -338,102 +338,102 @@ void TimerInterrupt::resumeTimer() //////////////////////////////////////////////////////////////////////////////// #if USE_TIMER_0 -#ifndef TIMER0_INSTANTIATED -// To force pre-instantiate only once -#define TIMER0_INSTANTIATED -TimerInterrupt ITimer0(HW_TIMER_0); - -ISR(TCB0_INT_vect) -{ - long countLocal = ITimer0.getCount(); - - if (ITimer0.getTimer() == 0) - { - if (countLocal != 0) + #ifndef TIMER0_INSTANTIATED + // To force pre-instantiate only once + #define TIMER0_INSTANTIATED + TimerInterrupt ITimer0(HW_TIMER_0); + + ISR(TCB0_INT_vect) { - if (ITimer0.checkTimerDone()) + long countLocal = ITimer0.getCount(); + + if (ITimer0.getTimer() == 0) { - ITimer0.callback(); - - if (ITimer0.get_CCMPValue() > MAX_COUNT_16BIT) + if (countLocal != 0) { - // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT - ITimer0.reload_CCMPValue(); + if (ITimer0.checkTimerDone()) + { + ITimer0.callback(); + + if (ITimer0.get_CCMPValue() > MAX_COUNT_16BIT) + { + // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT + ITimer0.reload_CCMPValue(); + } + + if (countLocal > 0) + ITimer0.setCount(countLocal - 1); + } + else + { + //Deduct _CCMPValue by min(MAX_COUNT_16BIT, _CCMPValue) + // If _CCMPValue == 0, flag _timerDone for next cycle + // If last one (_CCMPValueRemaining < MAX_COUNT_16BIT) => load _CCMP register _CCMPValueRemaining + ITimer0.adjust_CCMPValue(); + } + } + else + { + ITimer0.detachInterrupt(); } - - if (countLocal > 0) - ITimer0.setCount(countLocal - 1); - } - else - { - //Deduct _CCMPValue by min(MAX_COUNT_16BIT, _CCMPValue) - // If _CCMPValue == 0, flag _timerDone for next cycle - // If last one (_CCMPValueRemaining < MAX_COUNT_16BIT) => load _CCMP register _CCMPValueRemaining - ITimer0.adjust_CCMPValue(); } + + // Clear interrupt flag + TCB0.INTFLAGS = TCB_CAPT_bm; } - else - { - ITimer0.detachInterrupt(); - } - } - - // Clear interrupt flag - TCB0.INTFLAGS = TCB_CAPT_bm; -} -#endif //#ifndef TIMER0_INSTANTIATED + #endif //#ifndef TIMER0_INSTANTIATED #endif //#if USE_TIMER_0 ////////////////////////////////////////////// -#if USE_TIMER_1 +#if USE_TIMER_1 #ifndef TIMER1_INSTANTIATED -// To force pre-instantiate only once -#define TIMER1_INSTANTIATED -TimerInterrupt ITimer1(HW_TIMER_1); - -// Timer0 is used for micros(), millis(), delay(), etc and can't be used -// Pre-instantiate - -ISR(TCB1_INT_vect) -{ - long countLocal = ITimer1.getCount(); - - if (ITimer1.getTimer() == 1) + // To force pre-instantiate only once + #define TIMER1_INSTANTIATED + TimerInterrupt ITimer1(HW_TIMER_1); + + // Timer0 is used for micros(), millis(), delay(), etc and can't be used + // Pre-instantiate + + ISR(TCB1_INT_vect) { - if (countLocal != 0) + long countLocal = ITimer1.getCount(); + + if (ITimer1.getTimer() == 1) { - if (ITimer1.checkTimerDone()) + if (countLocal != 0) { - ITimer1.callback(); - - if (ITimer1.get_CCMPValue() > MAX_COUNT_16BIT) - { - // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT if _CCMPValueRemaining > MAX_COUNT_16BIT - ITimer1.reload_CCMPValue(); + if (ITimer1.checkTimerDone()) + { + ITimer1.callback(); + + if (ITimer1.get_CCMPValue() > MAX_COUNT_16BIT) + { + // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT if _CCMPValueRemaining > MAX_COUNT_16BIT + ITimer1.reload_CCMPValue(); + } + + if (countLocal > 0) + ITimer1.setCount(countLocal - 1); } - - if (countLocal > 0) - ITimer1.setCount(countLocal - 1); + else + { + //Deduct _CCMPValue by min(MAX_COUNT_16BIT, _CCMPValue) + // If _CCMPValue == 0, flag _timerDone for next cycle + // If last one (_CCMPValueRemaining < MAX_COUNT_16BIT) => load _CCMP register _CCMPValueRemaining + ITimer1.adjust_CCMPValue(); + } } else - { - //Deduct _CCMPValue by min(MAX_COUNT_16BIT, _CCMPValue) - // If _CCMPValue == 0, flag _timerDone for next cycle - // If last one (_CCMPValueRemaining < MAX_COUNT_16BIT) => load _CCMP register _CCMPValueRemaining - ITimer1.adjust_CCMPValue(); + { + ITimer1.detachInterrupt(); } } - else - { - ITimer1.detachInterrupt(); - } + + // Clear interrupt flag + TCB1.INTFLAGS = TCB_CAPT_bm; } - - // Clear interrupt flag - TCB1.INTFLAGS = TCB_CAPT_bm; -} - + #endif //#ifndef TIMER1_INSTANTIATED #endif //#if USE_TIMER_1 @@ -442,47 +442,47 @@ ISR(TCB1_INT_vect) // Pre-instantiate #if USE_TIMER_2 #ifndef TIMER2_INSTANTIATED -#define TIMER2_INSTANTIATED -TimerInterrupt ITimer2(HW_TIMER_2); - -ISR(TCB2_INT_vect) -{ - long countLocal = ITimer2.getCount(); - - if (ITimer2.getTimer() == 2) + #define TIMER2_INSTANTIATED + TimerInterrupt ITimer2(HW_TIMER_2); + + ISR(TCB2_INT_vect) { - if (countLocal != 0) + long countLocal = ITimer2.getCount(); + + if (ITimer2.getTimer() == 2) { - if (ITimer2.checkTimerDone()) + if (countLocal != 0) { - ITimer2.callback(); + if (ITimer2.checkTimerDone()) + { + ITimer2.callback(); + + if (ITimer2.get_CCMPValue() > MAX_COUNT_16BIT) + { + // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT if _CCMPValueRemaining > MAX_COUNT_16BIT + ITimer2.reload_CCMPValue(); + } + + if (countLocal > 0) + ITimer2.setCount(countLocal - 1); - if (ITimer2.get_CCMPValue() > MAX_COUNT_16BIT) - { - // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT if _CCMPValueRemaining > MAX_COUNT_16BIT - ITimer2.reload_CCMPValue(); } - - if (countLocal > 0) - ITimer2.setCount(countLocal - 1); - - } + else + { + //Deduct _CCMPValue by min(MAX_COUNT_8BIT, _CCMPValue) + // If _CCMPValue == 0, flag _timerDone for next cycle + ITimer2.adjust_CCMPValue(); + } + } else - { - //Deduct _CCMPValue by min(MAX_COUNT_8BIT, _CCMPValue) - // If _CCMPValue == 0, flag _timerDone for next cycle - ITimer2.adjust_CCMPValue(); + { + ITimer2.detachInterrupt(); } } - else - { - ITimer2.detachInterrupt(); - } - } - - // Clear interrupt flag - TCB2.INTFLAGS = TCB_CAPT_bm; -} + + // Clear interrupt flag + TCB2.INTFLAGS = TCB_CAPT_bm; + } #endif //#ifndef TIMER2_INSTANTIATED #endif //#if USE_TIMER_2 @@ -490,102 +490,102 @@ ISR(TCB2_INT_vect) // Pre-instantiate #if ( USE_TIMER_3 && ( ( defined(DX_64_PINS) || defined(DX_48_PINS) ) && defined(TCB3) ) ) -#ifndef TIMER3_INSTANTIATED -// To force pre-instantiate only once -#define TIMER3_INSTANTIATED -TimerInterrupt ITimer3(HW_TIMER_3); - -ISR(TCB3_INT_vect) -{ - long countLocal = ITimer3.getCount(); - - if (ITimer3.getTimer() == 3) - { - if (countLocal != 0) + #ifndef TIMER3_INSTANTIATED + // To force pre-instantiate only once + #define TIMER3_INSTANTIATED + TimerInterrupt ITimer3(HW_TIMER_3); + + ISR(TCB3_INT_vect) { - if (ITimer3.checkTimerDone()) + long countLocal = ITimer3.getCount(); + + if (ITimer3.getTimer() == 3) { - ITimer3.callback(); - - if (ITimer3.get_CCMPValue() > MAX_COUNT_16BIT) + if (countLocal != 0) { - // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT if _CCMPValueRemaining > MAX_COUNT_16BIT - ITimer3.reload_CCMPValue(); + if (ITimer3.checkTimerDone()) + { + ITimer3.callback(); + + if (ITimer3.get_CCMPValue() > MAX_COUNT_16BIT) + { + // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT if _CCMPValueRemaining > MAX_COUNT_16BIT + ITimer3.reload_CCMPValue(); + } + + if (countLocal > 0) + ITimer3.setCount(countLocal - 1); + } + else + { + //Deduct _CCMPValue by min(MAX_COUNT_16BIT, _CCMPValue) + // If _CCMPValue == 0, flag _timerDone for next cycle + // If last one (_CCMPValueRemaining < MAX_COUNT_16BIT) => load _CCMP register _CCMPValueRemaining + ITimer3.adjust_CCMPValue(); + } + } + else + { + ITimer3.detachInterrupt(); } - - if (countLocal > 0) - ITimer3.setCount(countLocal - 1); - } - else - { - //Deduct _CCMPValue by min(MAX_COUNT_16BIT, _CCMPValue) - // If _CCMPValue == 0, flag _timerDone for next cycle - // If last one (_CCMPValueRemaining < MAX_COUNT_16BIT) => load _CCMP register _CCMPValueRemaining - ITimer3.adjust_CCMPValue(); } - } - else - { - ITimer3.detachInterrupt(); - } - } - - // Clear interrupt flag - TCB3.INTFLAGS = TCB_CAPT_bm; -} - -#endif //#ifndef TIMER3_INSTANTIATED + + // Clear interrupt flag + TCB3.INTFLAGS = TCB_CAPT_bm; + } + + #endif //#ifndef TIMER3_INSTANTIATED #endif //#if USE_TIMER_3 ////////////////////////////////////////////// // Pre-instantiate #if ( USE_TIMER_4 && defined(DX_64_PINS) && defined(TCB4) ) -#ifndef TIMER4_INSTANTIATED -// To force pre-instantiate only once -#define TIMER4_INSTANTIATED -TimerInterrupt ITimer4(HW_TIMER_4); - -ISR(TCB4_INT_vect) -{ - long countLocal = ITimer4.getCount(); - - if (ITimer4.getTimer() == 4) - { - if (countLocal != 0) + #ifndef TIMER4_INSTANTIATED + // To force pre-instantiate only once + #define TIMER4_INSTANTIATED + TimerInterrupt ITimer4(HW_TIMER_4); + + ISR(TCB4_INT_vect) { - if (ITimer4.checkTimerDone()) + long countLocal = ITimer4.getCount(); + + if (ITimer4.getTimer() == 4) { - ITimer4.callback(); - - if (ITimer4.get_CCMPValue() > MAX_COUNT_16BIT) + if (countLocal != 0) { - // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT if _CCMPValueRemaining > MAX_COUNT_16BIT - ITimer4.reload_CCMPValue(); + if (ITimer4.checkTimerDone()) + { + ITimer4.callback(); + + if (ITimer4.get_CCMPValue() > MAX_COUNT_16BIT) + { + // To reload _CCMPValueRemaining as well as _CCMP register to MAX_COUNT_16BIT if _CCMPValueRemaining > MAX_COUNT_16BIT + ITimer4.reload_CCMPValue(); + } + + if (countLocal > 0) + ITimer4.setCount(countLocal - 1); + } + else + { + //Deduct _CCMPValue by min(MAX_COUNT_16BIT, _CCMPValue) + // If _CCMPValue == 0, flag _timerDone for next cycle + // If last one (_CCMPValueRemaining < MAX_COUNT_16BIT) => load _CCMP register _CCMPValueRemaining + ITimer4.adjust_CCMPValue(); + } + } + else + { + ITimer4.detachInterrupt(); } - - if (countLocal > 0) - ITimer4.setCount(countLocal - 1); - } - else - { - //Deduct _CCMPValue by min(MAX_COUNT_16BIT, _CCMPValue) - // If _CCMPValue == 0, flag _timerDone for next cycle - // If last one (_CCMPValueRemaining < MAX_COUNT_16BIT) => load _CCMP register _CCMPValueRemaining - ITimer4.adjust_CCMPValue(); } - } - else - { - ITimer4.detachInterrupt(); - } - } - - // Clear interrupt flag - TCB4.INTFLAGS = TCB_CAPT_bm; -} - -#endif //#ifndef TIMER4_INSTANTIATED + + // Clear interrupt flag + TCB4.INTFLAGS = TCB_CAPT_bm; + } + + #endif //#ifndef TIMER4_INSTANTIATED #endif //#if USE_TIMER_4 #endif // DX_SLOW_PWM_IMPL_H diff --git a/src/PWM_Generic_Debug.h b/src/PWM_Generic_Debug.h index aabc01a..defc28a 100644 --- a/src/PWM_Generic_Debug.h +++ b/src/PWM_Generic_Debug.h @@ -5,20 +5,21 @@ Built by Khoi Hoang https://github.com/khoih-prog/Dx_Slow_PWM Licensed under MIT license - + Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by unsigned long miliseconds), you just consume only one AVRDx-based timer and avoid conflicting with other cores' tasks. The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.2 + Version: 1.1.0 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 1.0.2 K.Hoang 25/08/2022 Minor cosmetic fix + 1.1.0 K.Hoang 30/12/2022 Add support to AVR DD (AVR64DD, AVR32DDx, AVR16DD, etc.) using breaking DxCore v1.5.1+ *****************************************************************************************************************************/ #pragma once