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 @@
+
---
---
## 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