diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0560abc..69444dd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -26,11 +26,10 @@ Please ensure to specify the following: ### Example ``` -Arduino IDE version: 1.8.13 +Arduino IDE version: 1.8.16 Arduino SAMDUE Core Version 1.6.12 OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.4.0-86-generic #97-Ubuntu SMP Fri Sep 17 19:19:40 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux - +Linux xy-Inspiron-3593 5.4.0-90-generic #101-Ubuntu SMP Fri Oct 15 20:00:55 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered a crash while trying to use the Timer Interrupt. diff --git a/README.md b/README.md index c5aec01..2388799 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,18 @@ * [1. Init ISR_PWM](#1-init-ISR_PWM) * [2. Set PWM Frequency, dutycycle, attach irqCallbackStartFunc and irqCallbackStopFunc functions](#2-Set-PWM-Frequency-dutycycle-attach-irqCallbackStartFunc-and-irqCallbackStopFunc-functions) * [Examples](#examples) - * [ 1. ISR_8_PWMs_Array](examples/ISR_8_PWMs_Array) - * [ 2. ISR_8_PWMs_Array_Complex](examples/ISR_8_PWMs_Array_Complex) - * [ 3. ISR_8_PWMs_Array_Simple](examples/ISR_8_PWMs_Array_Simple) + * [ 1. ISR_8_PWMs_Array](examples/ISR_8_PWMs_Array) + * [ 2. ISR_8_PWMs_Array_Complex](examples/ISR_8_PWMs_Array_Complex) + * [ 3. ISR_8_PWMs_Array_Simple](examples/ISR_8_PWMs_Array_Simple) + * [ 4. ISR_Changing_PWM](examples/ISR_Changing_PWM) + * [ 5. ISR_Modify_PWM](examples/ISR_Modify_PWM) * [Example ISR_8_PWMs_Array_Complex](#Example-ISR_8_PWMs_Array_Complex) * [Debug Terminal Output Samples](#debug-terminal-output-samples) * [1. ISR_8_PWMs_Array_Complex on SAM_DUE](#1-ISR_8_PWMs_Array_Complex-on-SAM_DUE) * [2. ISR_8_PWMs_Array on SAM_DUE](#2-isr_8_pwms_array-on-SAM_DUE) * [3. ISR_8_PWMs_Array_Simple on SAM_DUE](#3-ISR_8_PWMs_Array_Simple-on-SAM_DUE) + * [4. ISR_Modify_PWM on SAM_DUE](#4-ISR_Modify_PWM-on-SAM_DUE) + * [5. ISR_Changing_PWM on SAM_DUE](#5-ISR_Changing_PWM-on-SAM_DUE) * [Debug](#debug) * [Troubleshooting](#troubleshooting) * [Issues](#issues) @@ -52,7 +56,7 @@ ### Features -This library enables you to use ISR-based PWM channels on **SAM_DUE** boards, using [`Arduino SAM core`](https://github.com/arduino/ArduinoCore-sam), to create and output PWM any GPIO pin. Because this library doesn't use the powerful purely hardware-controlled PWM with many limitations, the maximum PWM frequency is currently limited at **1000Hz**, which is still suitable for many real-life applications. +This library enables you to use ISR-based PWM channels on **SAM_DUE** boards, using [`Arduino SAM core`](https://github.com/arduino/ArduinoCore-sam), to create and output PWM any GPIO pin. Because this library doesn't use the powerful purely hardware-controlled PWM with many limitations, the maximum PWM frequency is currently limited at **1000Hz**, which is still suitable for many real-life applications. Now you can also modify PWM settings on-the-fly. --- @@ -97,7 +101,7 @@ The catch is **your function is now part of an ISR (Interrupt Service Routine), ### Currently supported Boards -1. ****SAM_DUE** boards**, using [`Arduino SAM core`](https://github.com/arduino/ArduinoCore-sam) +1. **SAM_DUE** boards**, using [`Arduino SAM core`](https://github.com/arduino/ArduinoCore-sam) --- @@ -236,7 +240,9 @@ void setup() 1. [ISR_8_PWMs_Array](examples/ISR_8_PWMs_Array) 2. [ISR_8_PWMs_Array_Complex](examples/ISR_8_PWMs_Array_Complex) - 3. [ISR_8_PWMs_Array_Simple](examples/ISR_8_PWMs_Array_Simple) + 3. [ISR_8_PWMs_Array_Simple](examples/ISR_8_PWMs_Array_Simple) + 4. [ISR_Changing_PWM](examples/ISR_Changing_PWM) + 5. [ISR_Modify_PWM](examples/ISR_Modify_PWM) --- @@ -355,11 +361,10 @@ volatile unsigned long previousMicrosStart [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, volatile unsigned long deltaMicrosStop [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, 0, 0, 0 }; volatile unsigned long previousMicrosStop [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - // You can assign any interval for any timer here, in Hz -uint32_t PWM_Freq[NUMBER_ISR_PWMS] = +double PWM_Freq[NUMBER_ISR_PWMS] = { - 1, 2, 3, 5, 10, 20, 30, 50 + 1.0f, 2.0f, 3.0f, 5.0f, 10.0f, 20.0f, 30.0f, 50.0f }; // You can assign any duty-cycle for any PWM channel here, in % @@ -678,8 +683,7 @@ The following is the sample terminal output when running example [ISR_8_PWMs_Arr ``` Starting ISR_8_PWMs_Array_Complex on SAM_DUE -SAMDUE_Slow_PWM v1.0.0 -CPU Frequency = 84 MHz +SAMDUE_Slow_PWM v1.1.0 Timer Frequency = 84 MHz [PWM] Using Timer( 0 ) = TC0 [PWM] Channel = 0 , IRQ = TC0_IRQn @@ -720,8 +724,7 @@ The following is the sample terminal output when running example [**ISR_8_PWMs_A ``` Starting ISR_8_PWMs_Array on SAM_DUE -SAMDUE_Slow_PWM v1.0.0 -CPU Frequency = 84 MHz +SAMDUE_Slow_PWM v1.1.0 Timer Frequency = 84 MHz [PWM] Using Timer( 0 ) = TC0 [PWM] Channel = 0 , IRQ = TC0_IRQn @@ -744,8 +747,7 @@ The following is the sample terminal output when running example [**ISR_8_PWMs_A ``` Starting ISR_8_PWMs_Array_Simple on SAM_DUE -SAMDUE_Slow_PWM v1.0.0 -CPU Frequency = 84 MHz +SAMDUE_Slow_PWM v1.1.0 Timer Frequency = 84 MHz [PWM] Using Timer( 0 ) = TC0 [PWM] Channel = 0 , IRQ = TC0_IRQn @@ -760,6 +762,58 @@ Channel : 6 Period : 33333 OnTime : 13333 Start_Time : 2009415 Channel : 7 Period : 20000 OnTime : 9000 Start_Time : 2009415 ``` +--- + +### 4. ISR_Modify_PWM on SAM_DUE + +The following is the sample terminal output when running example [ISR_Modify_PWM](examples/ISR_Modify_PWM) on **SAM_DUE** to demonstrate how to modify PWM settings on-the-fly without deleting the PWM channel + +``` +Starting ISR_Modify_PWM on SAM_DUE +SAMDUE_Slow_PWM v1.1.0 +CPU Frequency = 84 MHz +Timer Frequency = 84 MHz +[PWM] Using Timer( 0 ) = TC0 +[PWM] Channel = 0 , IRQ = TC0_IRQn +ITimer attached to Timer(0) +Using PWM Freq = 1.00, PWM DutyCycle = 10 +Channel : 0 Period : 1000000 OnTime : 100000 Start_Time : 2012463 +Channel : 0 Period : 500000 OnTime : 450000 Start_Time : 12019042 +Channel : 0 Period : 1000000 OnTime : 100000 Start_Time : 22020042 +Channel : 0 Period : 500000 OnTime : 450000 Start_Time : 32021042 +``` + +--- + +### 5. ISR_Changing_PWM on SAM_DUE + +The following is the sample terminal output when running example [ISR_Changing_PWM](examples/ISR_Changing_PWM) on **SAM_DUE** to demonstrate how to modify PWM settings on-the-fly by deleting the PWM channel and reinit the PWM channel + +``` +Starting ISR_Changing_PWM on SAM_DUE +SAMDUE_Slow_PWM v1.1.0 +CPU Frequency = 84 MHz +Timer Frequency = 84 MHz +[PWM] Using Timer( 0 ) = TC0 +[PWM] Channel = 0 , IRQ = TC0_IRQn +ITimer attached to Timer(0) +Using PWM Freq = 1.00, PWM DutyCycle = 50 +Channel : 0 Period : 1000000 OnTime : 500000 Start_Time : 2012611 +Using PWM Freq = 2.00, PWM DutyCycle = 90 +Channel : 0 Period : 500000 OnTime : 450000 Start_Time : 12018520 +Using PWM Freq = 1.00, PWM DutyCycle = 50 +Channel : 0 Period : 1000000 OnTime : 500000 Start_Time : 22019530 +Using PWM Freq = 2.00, PWM DutyCycle = 90 +Channel : 0 Period : 500000 OnTime : 450000 Start_Time : 32020520 +Using PWM Freq = 1.00, PWM DutyCycle = 50 +Channel : 0 Period : 1000000 OnTime : 500000 Start_Time : 42021531 +Using PWM Freq = 2.00, PWM DutyCycle = 90 +Channel : 0 Period : 500000 OnTime : 450000 Start_Time : 52022520 +Using PWM Freq = 1.00, PWM DutyCycle = 50 +Channel : 0 Period : 1000000 OnTime : 500000 Start_Time : 62023531 +Using PWM Freq = 2.00, PWM DutyCycle = 90 +Channel : 0 Period : 500000 OnTime : 450000 Start_Time : 72024520 +``` --- --- @@ -804,6 +858,7 @@ Submit issues to: [SAMDUE_Slow_PWM issues](https://github.com/khoih-prog/SAMDUE_ 1. Basic hardware multi-channel PWM for **SAM_DUE**, etc. using [`Arduino SAM core`](https://github.com/arduino/ArduinoCore-sam) 2. Add Table of Contents +3. Add functions to modify PWM settings on-the-fly --- --- diff --git a/changelog.md b/changelog.md index 9472402..4819f3a 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ ## Table of Contents * [Changelog](#changelog) + * [Releases v1.1.0](#releases-v110) * [Initial Releases v1.0.0](#Initial-Releases-v100) --- @@ -19,6 +20,11 @@ ## Changelog +### Releases v1.1.0 + +1. Add functions to modify PWM settings on-the-fly +2. Add example to demo how to modify PWM settings on-the-fly + ### Initial Releases v1.0.0 1. Initial coding to support **SAM_DUE**, etc. using [`Arduino SAM core`](https://github.com/arduino/ArduinoCore-sam) 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 5c61c14..6cde81d 100644 --- a/examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino +++ b/examples/ISR_8_PWMs_Array/ISR_8_PWMs_Array.ino @@ -11,12 +11,6 @@ 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.0 - - Version Modified By Date Comments - ------- ----------- ---------- ----------- - 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE *****************************************************************************************************************************/ #if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) @@ -26,9 +20,7 @@ // These define's must be placed at the beginning before #include "ESP32_PWM.h" // _PWM_LOGLEVEL_ from 0 to 4 // Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. -#define _PWM_LOGLEVEL_ 3 - -//#define USING_MICROS_RESOLUTION true //false +#define _PWM_LOGLEVEL_ 4 #include "SAMDUE_Slow_PWM.h" @@ -87,11 +79,10 @@ uint32_t PWM_Pin[] = #define NUMBER_ISR_PWMS ( sizeof(PWM_Pin) / sizeof(uint32_t) ) - // You can assign any interval for any timer here, in Hz -uint32_t PWM_Freq[NUMBER_ISR_PWMS] = +double PWM_Freq[NUMBER_ISR_PWMS] = { - 1, 2, 3, 5, 10, 20, 30, 50 + 1.0f, 2.0f, 3.0f, 5.0f, 10.0f, 20.0f, 30.0f, 50.0f }; // You can assign any duty-cycle for any PWM channel here, in % 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 8233b59..e08a5d4 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 @@ -11,12 +11,6 @@ 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.0 - - Version Modified By Date Comments - ------- ----------- ---------- ----------- - 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE *****************************************************************************************************************************/ #if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) @@ -28,8 +22,6 @@ // Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. #define _PWM_LOGLEVEL_ 3 -//#define USING_MICROS_RESOLUTION true //false - #include "SAMDUE_Slow_PWM.h" #include // https://github.com/jfturcot/SimpleTimer @@ -129,11 +121,10 @@ volatile unsigned long previousMicrosStart [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, volatile unsigned long deltaMicrosStop [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, 0, 0, 0 }; volatile unsigned long previousMicrosStop [NUMBER_ISR_PWMS] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - // You can assign any interval for any timer here, in Hz -uint32_t PWM_Freq[NUMBER_ISR_PWMS] = +double PWM_Freq[NUMBER_ISR_PWMS] = { - 1, 2, 3, 5, 10, 20, 30, 50 + 1.0f, 2.0f, 3.0f, 5.0f, 10.0f, 20.0f, 30.0f, 50.0f }; // You can assign any duty-cycle for any PWM channel here, in % 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 fd3aec8..18638cf 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 @@ -11,12 +11,6 @@ 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.0 - - Version Modified By Date Comments - ------- ----------- ---------- ----------- - 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE *****************************************************************************************************************************/ #if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) @@ -28,8 +22,6 @@ // Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. #define _PWM_LOGLEVEL_ 3 -//#define USING_MICROS_RESOLUTION true //false - #include "SAMDUE_Slow_PWM.h" #include // https://github.com/jfturcot/SimpleTimer @@ -87,11 +79,10 @@ uint32_t PWM_Pin[] = #define NUMBER_ISR_PWMS ( sizeof(PWM_Pin) / sizeof(uint32_t) ) - // You can assign any interval for any timer here, in Hz -uint32_t PWM_Freq[NUMBER_ISR_PWMS] = +double PWM_Freq[NUMBER_ISR_PWMS] = { - 1, 2, 3, 5, 10, 20, 30, 50 + 1.0f, 2.0f, 3.0f, 5.0f, 10.0f, 20.0f, 30.0f, 50.0f }; // You can assign any duty-cycle for any PWM channel here, in % diff --git a/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino b/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino new file mode 100644 index 0000000..2fe8c6c --- /dev/null +++ b/examples/ISR_Changing_PWM/ISR_Changing_PWM.ino @@ -0,0 +1,149 @@ +/**************************************************************************************************************************** + ISR_Changing_PWM.ino + For Arduino SAM_DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_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 megaAVR-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. +*****************************************************************************************************************************/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This is designed only for Arduino SAM_DUE board! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP32_PWM.h" +// _PWM_LOGLEVEL_ from 0 to 4 +// Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define _PWM_LOGLEVEL_ 3 + +#include "SAMDUE_Slow_PWM.h" + +#define LED_OFF HIGH +#define LED_ON LOW + +#ifndef LED_BUILTIN + #define LED_BUILTIN 13 +#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 3 +#endif + +#define USING_HW_TIMER_INTERVAL_MS false //true + +// Don't change these numbers to make higher Timer freq. System can hang +#define HW_TIMER_INTERVAL_US 10L +#define HW_TIMER_INTERVAL_FREQ 100000L + +volatile uint32_t startMicros = 0; + +// Init SAMDUE_Slow_PWM, each can service 16 different ISR-based PWM channels +SAMDUE_Slow_PWM ISR_PWM; + +////////////////////////////////////////////////////// + +void TimerHandler() +{ + ISR_PWM.run(); +} + +////////////////////////////////////////////////////// + +#define USING_PWM_FREQUENCY false //true + +////////////////////////////////////////////////////// + +// You can assign pins here. Be carefull to select good pin to use or crash +uint32_t PWM_Pin = LED_BUILTIN; + +// You can assign any interval for any timer here, in Hz +double PWM_Freq1 = 1.0f; +// You can assign any interval for any timer here, in Hz +double PWM_Freq2 = 2.0f; + +// You can assign any interval for any timer here, in microseconds +uint32_t PWM_Period1 = 1000000 / PWM_Freq1; +// You can assign any interval for any timer here, in microseconds +uint32_t PWM_Period2 = 1000000 / PWM_Freq2; + +// You can assign any duty_cycle for any PWM here, from 0-100 +uint32_t PWM_DutyCycle1 = 50; +// You can assign any duty_cycle for any PWM here, from 0-100 +uint32_t PWM_DutyCycle2 = 90; + +// Channel number used to identify associated channel +int channelNum; + +//////////////////////////////////////////////// + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); Serial.print(F(" attached to Timer(")); Serial.print(timerNumber); Serial.println(F(")")); + + return timerNumber; +} + +//////////////////////////////////////////////// + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(2000); + + Serial.print(F("\nStarting ISR_Changing_PWM on ")); Serial.println(BOARD_NAME); + Serial.println(SAMDUE_SLOW_PWM_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + Serial.print(F("Timer Frequency = ")); Serial.print(SystemCoreClock / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + attachDueInterrupt(HW_TIMER_INTERVAL_US, TimerHandler, "ITimer"); +} + +void loop() +{ + Serial.print(F("Using PWM Freq = ")); Serial.print(PWM_Freq1); Serial.print(F(", PWM DutyCycle = ")); Serial.println(PWM_DutyCycle1); + +#if USING_PWM_FREQUENCY + // You can use this with PWM_Freq in Hz + channelNum = ISR_PWM.setPWM(PWM_Pin, PWM_Freq1, PWM_DutyCycle1); +#else + // Or using period in microsecs resolution + channelNum = ISR_PWM.setPWM_Period(PWM_Pin, PWM_Period1, PWM_DutyCycle1); +#endif + + delay(10000); + + ISR_PWM.deleteChannel((unsigned) channelNum); + + Serial.print(F("Using PWM Freq = ")); Serial.print(PWM_Freq2); Serial.print(F(", PWM DutyCycle = ")); Serial.println(PWM_DutyCycle2); + +#if USING_PWM_FREQUENCY + // You can use this with PWM_Freq in Hz + channelNum = ISR_PWM.setPWM(PWM_Pin, PWM_Freq2, PWM_DutyCycle2); +#else + // Or using period in microsecs resolution + channelNum = ISR_PWM.setPWM_Period(PWM_Pin, PWM_Period2, PWM_DutyCycle2); +#endif + + delay(10000); + + ISR_PWM.deleteChannel((unsigned) channelNum); +} diff --git a/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino b/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino new file mode 100644 index 0000000..848cf4a --- /dev/null +++ b/examples/ISR_Modify_PWM/ISR_Modify_PWM.ino @@ -0,0 +1,184 @@ +/**************************************************************************************************************************** + ISR_Modify_PWM.ino + For Arduino SAM_DUE boards + Written by Khoi Hoang + + Built by Khoi Hoang https://github.com/khoih-prog/SAMDUE_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 megaAVR-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. +*****************************************************************************************************************************/ + +#if !( defined(ARDUINO_SAM_DUE) || defined(__SAM3X8E__) ) + #error This is designed only for Arduino SAM_DUE board! Please check your Tools->Board setting. +#endif + +// These define's must be placed at the beginning before #include "ESP32_PWM.h" +// _PWM_LOGLEVEL_ from 0 to 4 +// Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system. +#define _PWM_LOGLEVEL_ 3 + +#include "SAMDUE_Slow_PWM.h" + +#define LED_OFF HIGH +#define LED_ON LOW + +#ifndef LED_BUILTIN + #define LED_BUILTIN 13 +#endif + +#ifndef LED_BLUE + #define LED_BLUE 2 +#endif + +#ifndef LED_RED + #define LED_RED 3 +#endif + +#define USING_HW_TIMER_INTERVAL_MS false //true + +// Don't change these numbers to make higher Timer freq. System can hang +#define HW_TIMER_INTERVAL_US 10L +#define HW_TIMER_INTERVAL_FREQ 100000L + +volatile uint32_t startMicros = 0; + +// Init SAMDUE_Slow_PWM, each can service 16 different ISR-based PWM channels +SAMDUE_Slow_PWM ISR_PWM; + +////////////////////////////////////////////////////// + +void TimerHandler() +{ + ISR_PWM.run(); +} + +////////////////////////////////////////////////////// + +#define USING_PWM_FREQUENCY false //true + +////////////////////////////////////////////////////// + +// You can assign pins here. Be carefull to select good pin to use or crash +uint32_t PWM_Pin = LED_BUILTIN; + +// You can assign any interval for any timer here, in Hz +double PWM_Freq1 = 1.0f; +// You can assign any interval for any timer here, in Hz +double PWM_Freq2 = 2.0f; + +// You can assign any interval for any timer here, in microseconds +uint32_t PWM_Period1 = 1000000 / PWM_Freq1; +// You can assign any interval for any timer here, in microseconds +uint32_t PWM_Period2 = 1000000 / PWM_Freq2; + +// You can assign any duty_cycle for any PWM here, from 0-100 +uint32_t PWM_DutyCycle1 = 10; +// You can assign any duty_cycle for any PWM here, from 0-100 +uint32_t PWM_DutyCycle2 = 90; + +// Channel number used to identify associated channel +int channelNum; + +//////////////////////////////////////////////// + +uint16_t attachDueInterrupt(double microseconds, timerCallback callback, const char* TimerName) +{ + DueTimerInterrupt dueTimerInterrupt = DueTimer.getAvailable(); + + dueTimerInterrupt.attachInterruptInterval(microseconds, callback); + + uint16_t timerNumber = dueTimerInterrupt.getTimerNumber(); + + Serial.print(TimerName); Serial.print(F(" attached to Timer(")); Serial.print(timerNumber); Serial.println(F(")")); + + return timerNumber; +} + +//////////////////////////////////////////////// + +void setup() +{ + Serial.begin(115200); + while (!Serial); + + delay(2000); + + Serial.print(F("\nStarting ISR_Modify_PWM on ")); Serial.println(BOARD_NAME); + Serial.println(SAMDUE_SLOW_PWM_VERSION); + Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz")); + Serial.print(F("Timer Frequency = ")); Serial.print(SystemCoreClock / 1000000); Serial.println(F(" MHz")); + + // Interval in microsecs + attachDueInterrupt(HW_TIMER_INTERVAL_US, TimerHandler, "ITimer"); + + Serial.print(F("Using PWM Freq = ")); Serial.print(PWM_Freq1); Serial.print(F(", PWM DutyCycle = ")); Serial.println(PWM_DutyCycle1); + +#if USING_PWM_FREQUENCY + // You can use this with PWM_Freq in Hz + ISR_PWM.setPWM(PWM_Pin, PWM_Freq1, PWM_DutyCycle1); +#else + // Or using period in microsecs resolution + channelNum = ISR_PWM.setPWM_Period(PWM_Pin, PWM_Period1, PWM_DutyCycle1); +#endif +} + +//////////////////////////////////////////////// + +void changePWM() +{ + static uint8_t count = 1; + + double PWM_Freq; + uint32_t PWM_DutyCycle; + + if (count++ % 2) + { + PWM_Freq = PWM_Freq2; + PWM_DutyCycle = PWM_DutyCycle2; + } + else + { + PWM_Freq = PWM_Freq1; + PWM_DutyCycle = PWM_DutyCycle1; + } + + // You can use this with PWM_Freq in Hz + if (!ISR_PWM.modifyPWMChannel(channelNum, PWM_Pin, PWM_Freq, PWM_DutyCycle)) + { + Serial.println(F("modifyPWMChannel error for PWM_Period")); + } +} + +//////////////////////////////////////////////// + +void changingPWM() +{ + static unsigned long changingPWM_timeout = 0; + + static unsigned long current_millis; + +#define CHANGING_PWM_INTERVAL 10000L + + current_millis = millis(); + + // changePWM every CHANGING_PWM_INTERVAL (10) seconds. + if ( (current_millis > changingPWM_timeout) ) + { + if (changingPWM_timeout > 0) + changePWM(); + + changingPWM_timeout = current_millis + CHANGING_PWM_INTERVAL; + } +} + +//////////////////////////////////////////////// + +void loop() +{ + changingPWM(); +} diff --git a/keywords.txt b/keywords.txt index 9c880cd..ac06920 100644 --- a/keywords.txt +++ b/keywords.txt @@ -36,6 +36,8 @@ init KEYWORD2 run KEYWORD2 setPWM KEYWORD2 setPWM_Period KEYWORD2 +modifyPWMChannel KEYWORD2 +modifyPWMChannel_Period KEYWORD2 deleteChannel KEYWORD2 restartChannel KEYWORD2 isEnabled KEYWORD2 diff --git a/library.json b/library.json index 13386fd..f674877 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "SAMDUE_Slow_PWM", - "version": "1.0.0", + "version": "1.1.0", "keywords": "timer, interrupt, hardware, isr, isr-based, pwm, isr-based-pwm, timing, control, device, hardware-timer, mission-critical, accuracy, precise, sam, due, sam-due, sam3x8e, atmel-sam", - "description": "This library enables you to use ISR-based PWM channels on an Arduino SAM_DUE board to create and output PWM any GPIO pin. It now supports 16 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they're ISR-based PWM channels, supporting lower PWM frequencies with suitable accuracy. Their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These ISR-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using millis() or micros(). That's necessary if you need to control devices requiring high precision", + "description": "This library enables you to use ISR-based PWM channels on an Arduino SAM_DUE board to create and output PWM any GPIO pin. It now supports 16 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they're ISR-based PWM channels, supporting lower PWM frequencies with suitable accuracy. Their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These ISR-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using millis() or micros(). That's necessary if you need to control devices requiring high precision. Now you can change the PWM settings on-the-fly", "authors": { "name": "Khoi Hoang", diff --git a/library.properties b/library.properties index 718ca1e..12d10e7 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=SAMDUE_Slow_PWM -version=1.0.0 +version=1.1.0 author=Khoi Hoang maintainer=Khoi Hoang sentence=This library enables you to use ISR-based PWM channels on an Arduino SAM_DUE board to create and output PWM any GPIO pin. -paragraph=It now supports 16 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they're ISR-based PWM channels, supporting lower PWM frequencies with suitable accuracy. Their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These ISR-based PWMs, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWM using millis() or micros(). That's necessary if you need to control devices requiring high precision +paragraph=It now supports 16 ISR-based PWM channels, while consuming only 1 Hardware Timer. PWM channel interval can be very long (ulong microsecs / millisecs). The most important feature is they 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 category=Device Control url=https://github.com/khoih-prog/SAMDUE_Slow_PWM architectures=sam,atmelsam diff --git a/src/PWM_Generic_Debug.h b/src/PWM_Generic_Debug.h index 3a95154..00afb64 100644 --- a/src/PWM_Generic_Debug.h +++ b/src/PWM_Generic_Debug.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once diff --git a/src/SAMDUE_Slow_PWM.h b/src/SAMDUE_Slow_PWM.h index 81a5b6a..080556a 100644 --- a/src/SAMDUE_Slow_PWM.h +++ b/src/SAMDUE_Slow_PWM.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once @@ -37,7 +38,7 @@ #endif #ifndef SAMDUE_SLOW_PWM_VERSION - #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.0.0") + #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.1.0") #endif #ifndef _PWM_LOGLEVEL_ diff --git a/src/SAMDUE_Slow_PWM_ISR.h b/src/SAMDUE_Slow_PWM_ISR.h index 008bbd7..5661e1d 100644 --- a/src/SAMDUE_Slow_PWM_ISR.h +++ b/src/SAMDUE_Slow_PWM_ISR.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once @@ -33,7 +34,7 @@ #endif #ifndef SAMDUE_SLOW_PWM_VERSION - #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.0.0") + #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.1.0") #endif #ifndef _PWM_LOGLEVEL_ @@ -80,30 +81,61 @@ class SAMDUE_SLOW_PWM_ISR ////////////////////////////////////////////////////////////////// // PWM - void setPWM(uint32_t pin, uint32_t frequency, uint32_t dutycycle, timer_callback StartCallback = nullptr, + // Return the channelNum if OK, -1 if error + int setPWM(uint32_t pin, double frequency, uint32_t dutycycle, timer_callback StartCallback = nullptr, timer_callback StopCallback = nullptr) { uint32_t period = 0; - + if ( ( frequency != 0 ) && ( frequency <= 1000 ) ) { - // period in us - period = 1000000.0f / frequency; + // period in us + period = 1000000.0f / frequency; } else { - PWM_LOGERROR(F("Error: Invalid frequency, max is 1000Hz")); + PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz"); + + return -1; } - setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); + return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); } - //period in us - void setPWM_Period(uint32_t pin, uint32_t period, uint32_t dutycycle, timer_callback StartCallback = nullptr, - timer_callback StopCallback = nullptr) + // period in us + // Return the channelNum if OK, -1 if error + int setPWM_Period(uint32_t pin, uint32_t period, uint32_t dutycycle, timer_callback StartCallback = nullptr, + timer_callback StopCallback = nullptr) { - setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); - } + return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); + } + + ////////////////////////////////////////////////////////////////// + + // low level function to modify a PWM channel + // returns the true on success or false on failure + bool modifyPWMChannel(unsigned channelNum, uint32_t pin, double frequency, uint32_t dutycycle) + { + uint32_t period = 0; + + if ( ( frequency > 0 ) && ( frequency <= 1000 ) ) + { + // period in us + period = 1000000.0f / frequency; + } + else + { + PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz"); + return false; + } + + return modifyPWMChannel_Period(channelNum, pin, period, dutycycle); + } + + ////////////////////////////////////////////////////////////////// + + //period in us + bool modifyPWMChannel_Period(unsigned channelNum, uint32_t pin, uint32_t period, uint32_t dutycycle); ////////////////////////////////////////////////////////////////// diff --git a/src/SAMDUE_Slow_PWM_ISR.hpp b/src/SAMDUE_Slow_PWM_ISR.hpp index bd90cf4..ee13442 100644 --- a/src/SAMDUE_Slow_PWM_ISR.hpp +++ b/src/SAMDUE_Slow_PWM_ISR.hpp @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once @@ -170,6 +171,8 @@ int SAMDUE_SLOW_PWM_ISR::setupPWMChannel(uint32_t pin, uint32_t period, uint32_t digitalWrite(pin, HIGH); SAM_DUE_PWM[channelNum].pinHigh = true; + SAM_DUE_PWM[channelNum].prevTime = timeNow(); + SAM_DUE_PWM[channelNum].callbackStart = cbStartFunc; SAM_DUE_PWM[channelNum].callbackStop = cbStopFunc; @@ -185,6 +188,44 @@ int SAMDUE_SLOW_PWM_ISR::setupPWMChannel(uint32_t pin, uint32_t period, uint32_t return channelNum; } +/////////////////////////////////////////////////// + +bool SAMDUE_SLOW_PWM_ISR::modifyPWMChannel_Period(unsigned channelNum, uint32_t pin, uint32_t period, uint32_t dutycycle) +{ + // Invalid input, such as period = 0, etc + if ( (period == 0) || (dutycycle > 100) ) + { + PWM_LOGERROR("Error: Invalid period or dutycycle"); + return false; + } + + if (channelNum > MAX_NUMBER_CHANNELS) + { + PWM_LOGERROR("Error: channelNum > MAX_NUMBER_CHANNELS"); + return false; + } + + if (SAM_DUE_PWM[channelNum].pin != pin) + { + PWM_LOGERROR("Error: channelNum and pin mismatched"); + return false; + } + + SAM_DUE_PWM[channelNum].period = period; + SAM_DUE_PWM[channelNum].onTime = ( period * dutycycle ) / 100; + + digitalWrite(pin, HIGH); + SAM_DUE_PWM[channelNum].pinHigh = true; + + SAM_DUE_PWM[channelNum].prevTime = timeNow(); + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); PWM_LOGINFO0("\tPeriod : "); PWM_LOGINFO0(SAM_DUE_PWM[channelNum].period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(SAM_DUE_PWM[channelNum].onTime); PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(SAM_DUE_PWM[channelNum].prevTime); + + return true; +} + + /////////////////////////////////////////////////// void SAMDUE_SLOW_PWM_ISR::deleteChannel(unsigned channelNum) diff --git a/src_cpp/PWM_Generic_Debug.h b/src_cpp/PWM_Generic_Debug.h index 3a95154..00afb64 100644 --- a/src_cpp/PWM_Generic_Debug.h +++ b/src_cpp/PWM_Generic_Debug.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once diff --git a/src_cpp/SAMDUE_Slow_PWM.h b/src_cpp/SAMDUE_Slow_PWM.h index 81a5b6a..080556a 100644 --- a/src_cpp/SAMDUE_Slow_PWM.h +++ b/src_cpp/SAMDUE_Slow_PWM.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once @@ -37,7 +38,7 @@ #endif #ifndef SAMDUE_SLOW_PWM_VERSION - #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.0.0") + #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.1.0") #endif #ifndef _PWM_LOGLEVEL_ diff --git a/src_cpp/SAMDUE_Slow_PWM_ISR.cpp b/src_cpp/SAMDUE_Slow_PWM_ISR.cpp index 009b165..4d5dd71 100644 --- a/src_cpp/SAMDUE_Slow_PWM_ISR.cpp +++ b/src_cpp/SAMDUE_Slow_PWM_ISR.cpp @@ -12,16 +12,18 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ -#include #include "SAMDUE_Slow_PWM_ISR.h" +#include + /////////////////////////////////////////////////// @@ -166,6 +168,8 @@ int SAMDUE_SLOW_PWM_ISR::setupPWMChannel(uint32_t pin, uint32_t period, uint32_t digitalWrite(pin, HIGH); SAM_DUE_PWM[channelNum].pinHigh = true; + SAM_DUE_PWM[channelNum].prevTime = timeNow(); + SAM_DUE_PWM[channelNum].callbackStart = cbStartFunc; SAM_DUE_PWM[channelNum].callbackStop = cbStopFunc; @@ -181,6 +185,44 @@ int SAMDUE_SLOW_PWM_ISR::setupPWMChannel(uint32_t pin, uint32_t period, uint32_t return channelNum; } +/////////////////////////////////////////////////// + +bool SAMDUE_SLOW_PWM_ISR::modifyPWMChannel_Period(unsigned channelNum, uint32_t pin, uint32_t period, uint32_t dutycycle) +{ + // Invalid input, such as period = 0, etc + if ( (period == 0) || (dutycycle > 100) ) + { + PWM_LOGERROR("Error: Invalid period or dutycycle"); + return false; + } + + if (channelNum > MAX_NUMBER_CHANNELS) + { + PWM_LOGERROR("Error: channelNum > MAX_NUMBER_CHANNELS"); + return false; + } + + if (SAM_DUE_PWM[channelNum].pin != pin) + { + PWM_LOGERROR("Error: channelNum and pin mismatched"); + return false; + } + + SAM_DUE_PWM[channelNum].period = period; + SAM_DUE_PWM[channelNum].onTime = ( period * dutycycle ) / 100; + + digitalWrite(pin, HIGH); + SAM_DUE_PWM[channelNum].pinHigh = true; + + SAM_DUE_PWM[channelNum].prevTime = timeNow(); + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); PWM_LOGINFO0("\tPeriod : "); PWM_LOGINFO0(SAM_DUE_PWM[channelNum].period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(SAM_DUE_PWM[channelNum].onTime); PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(SAM_DUE_PWM[channelNum].prevTime); + + return true; +} + + /////////////////////////////////////////////////// void SAMDUE_SLOW_PWM_ISR::deleteChannel(unsigned channelNum) @@ -305,3 +347,4 @@ unsigned SAMDUE_SLOW_PWM_ISR::getnumChannels() } + diff --git a/src_cpp/SAMDUE_Slow_PWM_ISR.h b/src_cpp/SAMDUE_Slow_PWM_ISR.h index 58771ab..8e1d212 100644 --- a/src_cpp/SAMDUE_Slow_PWM_ISR.h +++ b/src_cpp/SAMDUE_Slow_PWM_ISR.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once @@ -33,7 +34,7 @@ #endif #ifndef SAMDUE_SLOW_PWM_VERSION - #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.0.0") + #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.1.0") #endif #ifndef _PWM_LOGLEVEL_ @@ -80,30 +81,61 @@ class SAMDUE_SLOW_PWM_ISR ////////////////////////////////////////////////////////////////// // PWM - void setPWM(uint32_t pin, uint32_t frequency, uint32_t dutycycle, timer_callback StartCallback = nullptr, + // Return the channelNum if OK, -1 if error + int setPWM(uint32_t pin, double frequency, uint32_t dutycycle, timer_callback StartCallback = nullptr, timer_callback StopCallback = nullptr) { uint32_t period = 0; - + if ( ( frequency != 0 ) && ( frequency <= 1000 ) ) { - // period in us - period = 1000000.0f / frequency; + // period in us + period = 1000000.0f / frequency; } else { - PWM_LOGERROR(F("Error: Invalid frequency, max is 1000Hz")); + PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz"); + + return -1; } - setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); + return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); } - //period in us - void setPWM_Period(uint32_t pin, uint32_t period, uint32_t dutycycle, timer_callback StartCallback = nullptr, - timer_callback StopCallback = nullptr) + // period in us + // Return the channelNum if OK, -1 if error + int setPWM_Period(uint32_t pin, uint32_t period, uint32_t dutycycle, timer_callback StartCallback = nullptr, + timer_callback StopCallback = nullptr) { - setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); - } + return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); + } + + ////////////////////////////////////////////////////////////////// + + // low level function to modify a PWM channel + // returns the true on success or false on failure + bool modifyPWMChannel(unsigned channelNum, uint32_t pin, double frequency, uint32_t dutycycle) + { + uint32_t period = 0; + + if ( ( frequency > 0 ) && ( frequency <= 1000 ) ) + { + // period in us + period = 1000000.0f / frequency; + } + else + { + PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz"); + return false; + } + + return modifyPWMChannel_Period(channelNum, pin, period, dutycycle); + } + + ////////////////////////////////////////////////////////////////// + + //period in us + bool modifyPWMChannel_Period(unsigned channelNum, uint32_t pin, uint32_t period, uint32_t dutycycle); ////////////////////////////////////////////////////////////////// diff --git a/src_h/PWM_Generic_Debug.h b/src_h/PWM_Generic_Debug.h index 3a95154..00afb64 100644 --- a/src_h/PWM_Generic_Debug.h +++ b/src_h/PWM_Generic_Debug.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once diff --git a/src_h/SAMDUE_Slow_PWM.h b/src_h/SAMDUE_Slow_PWM.h index 81a5b6a..080556a 100644 --- a/src_h/SAMDUE_Slow_PWM.h +++ b/src_h/SAMDUE_Slow_PWM.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once @@ -37,7 +38,7 @@ #endif #ifndef SAMDUE_SLOW_PWM_VERSION - #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.0.0") + #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.1.0") #endif #ifndef _PWM_LOGLEVEL_ diff --git a/src_h/SAMDUE_Slow_PWM_ISR.h b/src_h/SAMDUE_Slow_PWM_ISR.h index 008bbd7..5661e1d 100644 --- a/src_h/SAMDUE_Slow_PWM_ISR.h +++ b/src_h/SAMDUE_Slow_PWM_ISR.h @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once @@ -33,7 +34,7 @@ #endif #ifndef SAMDUE_SLOW_PWM_VERSION - #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.0.0") + #define SAMDUE_SLOW_PWM_VERSION F("SAMDUE_Slow_PWM v1.1.0") #endif #ifndef _PWM_LOGLEVEL_ @@ -80,30 +81,61 @@ class SAMDUE_SLOW_PWM_ISR ////////////////////////////////////////////////////////////////// // PWM - void setPWM(uint32_t pin, uint32_t frequency, uint32_t dutycycle, timer_callback StartCallback = nullptr, + // Return the channelNum if OK, -1 if error + int setPWM(uint32_t pin, double frequency, uint32_t dutycycle, timer_callback StartCallback = nullptr, timer_callback StopCallback = nullptr) { uint32_t period = 0; - + if ( ( frequency != 0 ) && ( frequency <= 1000 ) ) { - // period in us - period = 1000000.0f / frequency; + // period in us + period = 1000000.0f / frequency; } else { - PWM_LOGERROR(F("Error: Invalid frequency, max is 1000Hz")); + PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz"); + + return -1; } - setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); + return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); } - //period in us - void setPWM_Period(uint32_t pin, uint32_t period, uint32_t dutycycle, timer_callback StartCallback = nullptr, - timer_callback StopCallback = nullptr) + // period in us + // Return the channelNum if OK, -1 if error + int setPWM_Period(uint32_t pin, uint32_t period, uint32_t dutycycle, timer_callback StartCallback = nullptr, + timer_callback StopCallback = nullptr) { - setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); - } + return setupPWMChannel(pin, period, dutycycle, (void *) StartCallback, (void *) StopCallback); + } + + ////////////////////////////////////////////////////////////////// + + // low level function to modify a PWM channel + // returns the true on success or false on failure + bool modifyPWMChannel(unsigned channelNum, uint32_t pin, double frequency, uint32_t dutycycle) + { + uint32_t period = 0; + + if ( ( frequency > 0 ) && ( frequency <= 1000 ) ) + { + // period in us + period = 1000000.0f / frequency; + } + else + { + PWM_LOGERROR("Error: Invalid frequency, max is 1000Hz"); + return false; + } + + return modifyPWMChannel_Period(channelNum, pin, period, dutycycle); + } + + ////////////////////////////////////////////////////////////////// + + //period in us + bool modifyPWMChannel_Period(unsigned channelNum, uint32_t pin, uint32_t period, uint32_t dutycycle); ////////////////////////////////////////////////////////////////// diff --git a/src_h/SAMDUE_Slow_PWM_ISR.hpp b/src_h/SAMDUE_Slow_PWM_ISR.hpp index bd90cf4..ee13442 100644 --- a/src_h/SAMDUE_Slow_PWM_ISR.hpp +++ b/src_h/SAMDUE_Slow_PWM_ISR.hpp @@ -12,11 +12,12 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.0.0 + Version: 1.1.0 Version Modified By Date Comments ------- ----------- ---------- ----------- 1.0.0 K.Hoang 29/09/2021 Initial coding for Arduino SAM_DUE + 1.1.0 K Hoang 10/11/2021 Add functions to modify PWM settings on-the-fly *****************************************************************************************************************************/ #pragma once @@ -170,6 +171,8 @@ int SAMDUE_SLOW_PWM_ISR::setupPWMChannel(uint32_t pin, uint32_t period, uint32_t digitalWrite(pin, HIGH); SAM_DUE_PWM[channelNum].pinHigh = true; + SAM_DUE_PWM[channelNum].prevTime = timeNow(); + SAM_DUE_PWM[channelNum].callbackStart = cbStartFunc; SAM_DUE_PWM[channelNum].callbackStop = cbStopFunc; @@ -185,6 +188,44 @@ int SAMDUE_SLOW_PWM_ISR::setupPWMChannel(uint32_t pin, uint32_t period, uint32_t return channelNum; } +/////////////////////////////////////////////////// + +bool SAMDUE_SLOW_PWM_ISR::modifyPWMChannel_Period(unsigned channelNum, uint32_t pin, uint32_t period, uint32_t dutycycle) +{ + // Invalid input, such as period = 0, etc + if ( (period == 0) || (dutycycle > 100) ) + { + PWM_LOGERROR("Error: Invalid period or dutycycle"); + return false; + } + + if (channelNum > MAX_NUMBER_CHANNELS) + { + PWM_LOGERROR("Error: channelNum > MAX_NUMBER_CHANNELS"); + return false; + } + + if (SAM_DUE_PWM[channelNum].pin != pin) + { + PWM_LOGERROR("Error: channelNum and pin mismatched"); + return false; + } + + SAM_DUE_PWM[channelNum].period = period; + SAM_DUE_PWM[channelNum].onTime = ( period * dutycycle ) / 100; + + digitalWrite(pin, HIGH); + SAM_DUE_PWM[channelNum].pinHigh = true; + + SAM_DUE_PWM[channelNum].prevTime = timeNow(); + + PWM_LOGINFO0("Channel : "); PWM_LOGINFO0(channelNum); PWM_LOGINFO0("\tPeriod : "); PWM_LOGINFO0(SAM_DUE_PWM[channelNum].period); + PWM_LOGINFO0("\t\tOnTime : "); PWM_LOGINFO0(SAM_DUE_PWM[channelNum].onTime); PWM_LOGINFO0("\tStart_Time : "); PWM_LOGINFOLN0(SAM_DUE_PWM[channelNum].prevTime); + + return true; +} + + /////////////////////////////////////////////////// void SAMDUE_SLOW_PWM_ISR::deleteChannel(unsigned channelNum)