diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e1eaa2..ff24106 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 -* `SAMD` Core Version (e.g. Arduino SAMD core v1.8.12, Adafruit SAMD core v1.7.7, Seeed Studio SAMD v1.8.2, Sparkfun SAMD v1.8.1) +* `SAMD` Core Version (e.g. Arduino SAMD core v1.8.13, Adafruit SAMD core v1.7.10, Seeed Studio SAMD v1.8.2, Sparkfun SAMD v1.8.1) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce * Anything that might be relevant in your opinion, such as: @@ -27,9 +27,9 @@ Please ensure to specify the following: ``` Arduino IDE version: 1.8.19 -Arduino SAMD Core Version 1.8.12 +Arduino SAMD Core Version 1.8.13 OS: Ubuntu 20.04 LTS -Linux xy-Inspiron-3593 5.4.0-96-generic #109-Ubuntu SMP Wed Jan 12 16:49:16 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux +Linux xy-Inspiron-3593 5.13.0-40-generic #45~20.04.1-Ubuntu SMP Mon Apr 4 09:38:31 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered a crash while trying to use the Timer Interrupt. @@ -50,3 +50,4 @@ There are usually some outstanding feature requests in the [existing issues list ### Sending Pull Requests Pull Requests with changes and fixes are also welcome! + diff --git a/README.md b/README.md index 41ac6bc..7580239 100644 --- a/README.md +++ b/README.md @@ -155,15 +155,12 @@ The catch is **your function is now part of an ISR (Interrupt Service Routine), 1. [`Arduino IDE 1.8.19+` for Arduino](https://github.com/arduino/Arduino). [![GitHub release](https://img.shields.io/github/release/arduino/Arduino.svg)](https://github.com/arduino/Arduino/releases/latest) 2. [`Arduino SAMD core 1.8.13+`](https://github.com/arduino/ArduinoCore-samd) for SAMD ARM Cortex-M0+ boards. [![GitHub release](https://img.shields.io/github/release/arduino/ArduinoCore-samd.svg)](https://github.com/arduino/ArduinoCore-samd/releases/latest) - 3. [`Adafruit SAMD core 1.7.9+`](https://github.com/adafruit/ArduinoCore-samd) for SAMD ARM Cortex-M0+ and M4 boards (Nano 33 IoT, etc.). [![GitHub release](https://img.shields.io/github/release/adafruit/ArduinoCore-samd.svg)](https://github.com/adafruit/ArduinoCore-samd/releases/latest) + 3. [`Adafruit SAMD core 1.7.10+`](https://github.com/adafruit/ArduinoCore-samd) for SAMD ARM Cortex-M0+ and M4 boards (Nano 33 IoT, etc.). [![GitHub release](https://img.shields.io/github/release/adafruit/ArduinoCore-samd.svg)](https://github.com/adafruit/ArduinoCore-samd/releases/latest) 4. [`Seeeduino SAMD core 1.8.2+`](https://github.com/Seeed-Studio/ArduinoCore-samd) for SAMD21/SAMD51 boards (XIAO M0, Wio Terminal, etc.). [![Latest release](https://img.shields.io/github/release/Seeed-Studio/ArduinoCore-samd.svg)](https://github.com/Seeed-Studio/ArduinoCore-samd/releases/latest/) 5. [`Sparkfun SAMD core 1.8.1+`](https://github.com/sparkfun/Arduino_Boards) for SAMD21/SAMD51 boards (SparkFun_RedBoard_Turbo, SparkFun_SAMD51_Thing_Plus, etc.). 6. [`Blynk library 1.0.1`](https://github.com/blynkkk/blynk-library/releases). [![Latest release](https://img.shields.io/github/release/blynkkk/blynk-library.svg)](https://github.com/blynkkk/blynk-library/releases/latest/) to use with some examples. Don't use Blynk beta versions. 7. To use with some examples, depending on which Ethernet card you're using: - - [`Ethernet library v2.0.0+`](https://github.com/arduino-libraries/Ethernet) for W5100, W5200 and W5500. [![GitHub release](https://img.shields.io/github/release/arduino-libraries/Ethernet.svg)](https://github.com/arduino-libraries/Ethernet/releases/latest) - - [`EthernetLarge library v2.0.0+`](https://github.com/OPEnSLab-OSU/EthernetLarge) for W5100, W5200 and W5500. - - [`Ethernet2 library v1.0.4+`](https://github.com/khoih-prog/Ethernet2) for W5500. [![GitHub release](https://img.shields.io/github/release/adafruit/Ethernet2.svg)](https://github.com/adafruit/Ethernet2/releases/latest) - - [`Ethernet3 library v1.5.5+`](https://github.com/sstaub/Ethernet3) for W5500/WIZ550io/WIZ850io/USR-ES1 with Wiznet W5500 chip. [![GitHub release](https://img.shields.io/github/release/sstaub/Ethernet3.svg)](https://github.com/sstaub/Ethernet3/releases/latest) + - [`Ethernet_Generic library v2.1.0+`](https://github.com/khoih-prog/Ethernet_Generic) for W5100, W5200 and W5500/WIZ550io/WIZ850io/USR-ES1 with Wiznet W5500 chip. [![GitHub release](https://img.shields.io/github/release/khoih-prog/Ethernet_Generic.svg)](https://github.com/khoih-prog/Ethernet_Generic/releases/latest) - [`EthernetENC library v2.0.2+`](https://github.com/jandrassy/EthernetENC) for ENC28J60. [![GitHub release](https://img.shields.io/github/release/jandrassy/EthernetENC.svg)](https://github.com/jandrassy/EthernetENC/releases/latest). **New and Better** - [`UIPEthernet library v2.0.11+`](https://github.com/UIPEthernet/UIPEthernet) for ENC28J60. [![GitHub release](https://img.shields.io/github/release/UIPEthernet/UIPEthernet.svg)](https://github.com/UIPEthernet/UIPEthernet/releases/latest) 7. To use with some examples @@ -193,7 +190,7 @@ Another way to install is to: 1. Install [VS Code](https://code.visualstudio.com/) 2. Install [PlatformIO](https://platformio.org/platformio-ide) -3. Install [**SAMD_TimerInterrupt** library](https://platformio.org/lib/show/11396/SAMD_TimerInterrupt) or [**SAMD_TimerInterrupt** library](https://platformio.org/lib/show/11412/SAMD_TimerInterrupt) by using [Library Manager](https://platformio.org/lib/show/11412/SAMD_TimerInterrupt/installation). Search for **SAMD_TimerInterrupt** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) +3. Install [**SAMD_TimerInterrupt** library](https://registry.platformio.org/libraries/khoih-prog/SAMD_TimerInterrupt) by using [Library Manager](https://registry.platformio.org/libraries/khoih-prog/SAMD_TimerInterrupt/installation). Search for **SAMD_TimerInterrupt** in [Platform.io Author's Libraries](https://platformio.org/lib/search?query=author:%22Khoi%20Hoang%22) 4. Use included [platformio.ini](platformio/platformio.ini) file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at [Project Configuration File](https://docs.platformio.org/page/projectconf.html) --- @@ -203,13 +200,13 @@ Another way to install is to: #### 1. For Arduino SAMD boards - ***To be able to compile, run and automatically detect and display BOARD_NAME on Arduino SAMD (Nano-33-IoT, etc) boards***, you have to copy the whole [Arduino SAMD Packages_Patches](Packages_Patches/arduino/hardware/samd/1.8.12) directory into Arduino SAMD directory (~/.arduino15/packages/arduino/hardware/samd/1.8.12). + ***To be able to compile, run and automatically detect and display BOARD_NAME on Arduino SAMD (Nano-33-IoT, etc) boards***, you have to copy the whole [Arduino SAMD Packages_Patches](Packages_Patches/arduino/hardware/samd/1.8.13) directory into Arduino SAMD directory (~/.arduino15/packages/arduino/hardware/samd/1.8.13). #### For core version v1.8.10+ -Supposing the Arduino SAMD version is 1.8.12. Now only one file must be copied into the directory: +Supposing the Arduino SAMD version is 1.8.13. Now only one file must be copied into the directory: -- `~/.arduino15/packages/arduino/hardware/samd/1.8.12/platform.txt` +- `~/.arduino15/packages/arduino/hardware/samd/1.8.13/platform.txt` Whenever a new version is installed, remember to copy this files into the new version directory. For example, new version is x.yy.zz @@ -242,13 +239,13 @@ Whenever the above-mentioned compiler error issue is fixed with the new Arduino #### 2. For Adafruit SAMD boards - ***To be able to compile, run and automatically detect and display BOARD_NAME on Adafruit SAMD (Itsy-Bitsy M4, etc) boards***, you have to copy the whole [Adafruit SAMD Packages_Patches](Packages_Patches/adafruit/hardware/samd/1.7.6) directory into Adafruit samd directory (~/.arduino15/packages/adafruit/hardware/samd/1.7.6). + ***To be able to compile, run and automatically detect and display BOARD_NAME on Adafruit SAMD (Itsy-Bitsy M4, etc) boards***, you have to copy the whole [Adafruit SAMD Packages_Patches](Packages_Patches/adafruit/hardware/samd/1.7.9) directory into Adafruit samd directory (~/.arduino15/packages/adafruit/hardware/samd/1.7.9). -Supposing the Adafruit SAMD core version is 1.7.6. This file must be copied into the directory: +Supposing the Adafruit SAMD core version is 1.7.9. This file must be copied into the directory: -- `~/.arduino15/packages/adafruit/hardware/samd/1.7.6/platform.txt` -- `~/.arduino15/packages/adafruit/hardware/samd/1.7.6/cores/arduino/Print.h` -- `~/.arduino15/packages/adafruit/hardware/samd/1.7.6/cores/arduino/Print.cpp` +- `~/.arduino15/packages/adafruit/hardware/samd/1.7.9/platform.txt` +- `~/.arduino15/packages/adafruit/hardware/samd/1.7.9/cores/arduino/Print.h` +- `~/.arduino15/packages/adafruit/hardware/samd/1.7.9/cores/arduino/Print.cpp` Whenever a new version is installed, remember to copy this file into the new version directory. For example, new version is x.yy.zz This file must be copied into the directory: @@ -540,7 +537,7 @@ While software timer, **programmed for 2s, is activated after 7.937s !!!**. Then ``` Starting ISR_Timer_Complex_WiFiNINA on SAMD_NANO_33_IOT -SAMDTimerInterrupt v1.6.0 +SAMDTimerInterrupt v1.7.0 CPU Frequency = 48 MHz [TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48 [TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00 @@ -605,7 +602,7 @@ The following is the sample terminal output when running example [**TimerInterru ``` Starting TimerInterruptTest on ITSYBITSY_M4 -SAMDTimerInterrupt v1.6.0 +SAMDTimerInterrupt v1.7.0 CPU Frequency = 48 MHz [TISR] SAMDTimerInterrupt: F_CPU (MHz) = 120 , TIMER_HZ = 48 [TISR] TC_Timer::startTimer _Timer = 0x 0x4101c000 , TC3 = 0x 0x4101c000 @@ -683,7 +680,7 @@ The following is the sample terminal output when running example [**Argument_Non ``` Starting Argument_None on SAMD_NANO_33_IOT -SAMDTimerInterrupt v1.6.0 +SAMDTimerInterrupt v1.7.0 CPU Frequency = 48 MHz [TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48 [TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00 @@ -733,7 +730,7 @@ In this example, 16 independent ISR Timers are used, yet utilized just one Hardw ``` Starting ISR_16_Timers_Array on SAMD_NANO_33_IOT -SAMDTimerInterrupt v1.6.0 +SAMDTimerInterrupt v1.7.0 CPU Frequency = 48 MHz [TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48 [TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00 @@ -857,7 +854,7 @@ The following is the sample terminal output when running example [Change_Interva ``` Starting Change_Interval on SAMD_NANO_33_IOT -SAMDTimerInterrupt v1.6.0 +SAMDTimerInterrupt v1.7.0 CPU Frequency = 48 MHz [TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48 [TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00 @@ -921,7 +918,7 @@ The following is the sample terminal output when running example [RepeatedAttach ``` Starting RepeatedAttachInterrupt_uS on SEEED_XIAO_M0 -SAMDTimerInterrupt v1.6.0 +SAMDTimerInterrupt v1.7.0 CPU Frequency = 48 MHz [TISR] _period = 19995 , frequency = 50.01 [TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48 @@ -1029,6 +1026,7 @@ Submit issues to: [SAMD_TimerInterrupt issues](https://github.com/khoih-prog/SAM 6. Fix `multiple-definitions` linker error 7. Add support to many more boards, such as `SAMD21E1xA`, `SAMD21G1xA` and`SAMD21J1xA` 8. Optimize library code by using `reference-passing` instead of `value-passing` +9. Optimize code for setInterval() of SAMD21 TC3 --- --- @@ -1044,6 +1042,7 @@ Many thanks for everyone for bug reporting, new feature suggesting, testing and 5. Thanks to [generationmake](https://github.com/generationmake) to make a PR in [change variable period from unsigned long to float #7](https://github.com/khoih-prog/SAMD_TimerInterrupt/pull/7) leading to new release v1.5.0. 6. Thanks to [Alexander Golovanov](https://github.com/homeodor) to propose a PR in [Add more SAMD21 #10](https://github.com/khoih-prog/SAMD_TimerInterrupt/pull/10) leading to the support of many new boards in new release v1.6.0 7. Thanks to [Will Powell](https://github.com/WillPowellUk) to report the bug in [Multiple Definition Error Not fixed by swapping src_cpp or src_h with src #9](https://github.com/khoih-prog/SAMD_TimerInterrupt/issues/9) leading to new release v1.6.0. +8. Thanks to [Dave Hooper](https://github.com/stripwax) to report the bug and propose the fix in [setInterval on a running timer results in a period significantly longer than the specified period #17](https://github.com/khoih-prog/SAMD_TimerInterrupt/issues/17) leading to new release v1.7.0. @@ -1057,6 +1056,7 @@ Many thanks for everyone for bug reporting, new feature suggesting, testing and +
WillPowellUk
Will Powell

stripwax
Dave Hooper

diff --git a/changelog.md b/changelog.md index 84adfb7..38290e3 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ ## Table of Contents * [Changelog](#changelog) + * [Releases v1.7.0](#releases-v170) * [Releases v1.6.0](#releases-v160) * [Releases v1.5.0](#releases-v150) * [Releases v1.4.0](#releases-v140) @@ -28,6 +29,11 @@ ## Changelog +### Releases v1.7.0 + +1. Optimize code for setInterval() of SAMD21 TC3. Check [setInterval on a running timer results in a period significantly longer than the specified period #17](https://github.com/khoih-prog/SAMD_TimerInterrupt/issues/17) +2. Update `Packages_Patches` + ### Releases v1.6.0 1. Fix `multiple-definitions` linker error. Drop `src_cpp` and `src_h` directories diff --git a/library.json b/library.json index 88ccd1c..67571a5 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "SAMD_TimerInterrupt", - "version": "1.6.0", + "version": "1.7.0", "keywords": "timing, device, control, timer, interrupt, hardware, isr, isr-based, hardware-timer, isr-timer, isr-based-timer, mission-critical, accuracy, precise, non-blocking, samd, samd21, samd51, nano-33-iot", "description": "This library enables you to use Interrupt from Hardware Timers on SAMD-based boards. It now supports 16 ISR-based timers, while consuming only 1 Hardware Timer. Timers' interval is very long (ulong millisecs). The most important feature is they're ISR-based timers. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware timers, using interrupt, still work even if other functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy.", "authors": diff --git a/library.properties b/library.properties index 0260080..f60346b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SAMD_TimerInterrupt -version=1.6.0 +version=1.7.0 author=Khoi Hoang maintainer=Khoi Hoang sentence=This library enables you to use Interrupt from Hardware Timers on SAMD-based boards such as SAMD21 Nano-33-IoT, Adafruit SAMD51 Itsy-Bitsy M4, SeeedStudio XIAO, Sparkfun SAMD51_MICROMOD, etc. diff --git a/platformio/platformio.ini b/platformio/platformio.ini index 055ade8..cae56a6 100644 --- a/platformio/platformio.ini +++ b/platformio/platformio.ini @@ -36,6 +36,8 @@ upload_speed = 921600 ; Checks for the compatibility with frameworks and dev/platforms lib_compat_mode = strict +lib_ldf_mode = chain+ +;lib_ldf_mode = deep+ lib_deps = diff --git a/src/SAMDTimerInterrupt.h b/src/SAMDTimerInterrupt.h index 5c2d981..5ff8805 100644 --- a/src/SAMDTimerInterrupt.h +++ b/src/SAMDTimerInterrupt.h @@ -16,7 +16,7 @@ Based on BlynkTimer.h Author: Volodymyr Shymanskyy - Version: 1.6.0 + Version: 1.7.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.4.0 K.Hoang 02/06/2021 Fix SAMD21 rare bug caused by not fully init Prescaler 1.5.0 K.Hoang 08/10/2021 Improve frequency precision by using float instead of ulong 1.6.0 K.Hoang 20/01/2022 Fix `multiple-definitions` linker error. Add support to many more boards + 1.7.0 K.Hoang 25/04/2022 Optimize code for setInterval() of SAMD21 TC3 *****************************************************************************************************************************/ /* SAMD21 diff --git a/src/SAMDTimerInterrupt.hpp b/src/SAMDTimerInterrupt.hpp index 1012e2d..768074e 100644 --- a/src/SAMDTimerInterrupt.hpp +++ b/src/SAMDTimerInterrupt.hpp @@ -16,7 +16,7 @@ Based on BlynkTimer.h Author: Volodymyr Shymanskyy - Version: 1.6.0 + Version: 1.7.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.4.0 K.Hoang 02/06/2021 Fix SAMD21 rare bug caused by not fully init Prescaler 1.5.0 K.Hoang 08/10/2021 Improve frequency precision by using float instead of ulong 1.6.0 K.Hoang 20/01/2022 Fix `multiple-definitions` linker error. Add support to many more boards + 1.7.0 K.Hoang 25/04/2022 Optimize code for setInterval() of SAMD21 TC3 *****************************************************************************************************************************/ /* SAMD21 @@ -72,7 +73,10 @@ #if defined(ARDUINO_QWIIC_MICRO) #define BOARD_NAME "Sparkfun SAMD21_QWIIC_MICRO" - #warning BOARD_NAME == Sparkfun SAMD21_QWIIC_MICRO + + #if (_TIMERINTERRUPT_LOGLEVEL_ > 2) + #warning BOARD_NAME == Sparkfun SAMD21_QWIIC_MICRO + #endif #elif defined(__SAMD21E15A__) #define BOARD_NAME "__SAMD21E15A__" #elif defined(__SAMD21E16A__) @@ -103,7 +107,10 @@ #endif - #warning Using SAMD21 Hardware Timer + #if (_TIMERINTERRUPT_LOGLEVEL_ > 2) + #warning Using SAMD21 Hardware Timer + #endif + #elif ( defined(__SAMD51__) || defined(__SAMD51J20A__) || defined(__SAMD51J19A__) || defined(__SAMD51G19A__) || defined(__SAMD51P19A__) ) #define TIMER_INTERRUPT_USING_SAMD51 true @@ -111,10 +118,17 @@ #if defined(ARDUINO_SAMD51_THING_PLUS) #define BOARD_NAME "Sparkfun SAMD51_THING_PLUS" - #warning BOARD_NAME == Sparkfun SAMD51_THING_PLUS + + #if (_TIMERINTERRUPT_LOGLEVEL_ > 2) + #warning BOARD_NAME == Sparkfun SAMD51_THING_PLUS + #endif + #elif defined(ARDUINO_SAMD51_MICROMOD) #define BOARD_NAME "Sparkfun SAMD51_MICROMOD" - #warning BOARD_NAME == Sparkfun SAMD51_MICROMOD + + #if (_TIMERINTERRUPT_LOGLEVEL_ > 2) + #warning BOARD_NAME == Sparkfun SAMD51_MICROMOD + #endif #elif defined(__SAMD51J20A__) #define BOARD_NAME "__SAMD51J20A__" #elif defined(__SAMD51J19A__) @@ -129,7 +143,9 @@ #endif - #warning Using SAMD51 Hardware Timer + #if (_TIMERINTERRUPT_LOGLEVEL_ > 2) + #warning Using SAMD51 Hardware Timer + #endif #else #error Unknown board #endif @@ -147,13 +163,13 @@ #include "Arduino.h" #ifndef SAMD_TIMER_INTERRUPT_VERSION - #define SAMD_TIMER_INTERRUPT_VERSION "SAMDTimerInterrupt v1.6.0" + #define SAMD_TIMER_INTERRUPT_VERSION "SAMDTimerInterrupt v1.7.0" #define SAMD_TIMER_INTERRUPT_VERSION_MAJOR 1 - #define SAMD_TIMER_INTERRUPT_VERSION_MINOR 6 + #define SAMD_TIMER_INTERRUPT_VERSION_MINOR 7 #define SAMD_TIMER_INTERRUPT_VERSION_PATCH 0 - #define SAMD_TIMER_INTERRUPT_VERSION_INT 1006000 + #define SAMD_TIMER_INTERRUPT_VERSION_INT 1007000 #endif #include "TimerInterrupt_Generic_Debug.h" @@ -162,6 +178,8 @@ //////////////////////////////////////////////////// +//////////////////////////////////////////////////// + #if (TIMER_INTERRUPT_USING_SAMD51) typedef enum @@ -178,105 +196,130 @@ typedef void (*timerCallback) (); #define SAMD_TC3 ((TcCount16*) _SAMDTimer) -static inline void TC3_wait_for_sync() +////////////////////////////////////////// + +static inline void TC3_wait_for_sync() { while (TC3->COUNT16.SYNCBUSY.reg != 0); } +///////////////////////////////////////////////////////////// + class SAMDTimerInterrupt { private: SAMDTimerNumber _timerNumber; - + // point to timer struct, (TcCount16*) TC3 for SAMD51 void* _SAMDTimer = NULL; - + timerCallback _callback; // pointer to the callback function - float _frequency; // Timer frequency - - float _period; - int _prescaler; - int _compareValue; + + // _prescaler = (1 << new_prescaler) + uint8_t new_prescaler; + + uint16_t _compareValue; public: SAMDTimerInterrupt(const SAMDTimerNumber& timerNumber) { _timerNumber = timerNumber; - + if (_timerNumber == TIMER_TC3) { - _SAMDTimer = (TcCount16*) TC3; + _SAMDTimer = (TcCount16*) TC3; } - - _callback = NULL; + + _callback = NULL; }; - + + ////////////////////////////////////////// + ~SAMDTimerInterrupt() { } + ////////////////////////////////////////// + bool setFrequency(const float& frequency, timerCallback callback); + bool _setPeriod(const float& _period, timerCallback callback); + + ////////////////////////////////////////// // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely // No params and duration now. To be addes in the future by adding similar functions here or to SAMD-hal-timer.c bool setInterval(const unsigned long& interval, timerCallback callback) { - return setFrequency((float) (1000000.0f / interval), callback); + return _setPeriod(interval, callback); } + ////////////////////////////////////////// + bool attachInterrupt(const float& frequency, timerCallback callback) { return setFrequency(frequency, callback); } + ////////////////////////////////////////// + // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely // No params and duration now. To be addes in the future by adding similar functions here or to SAMD-hal-timer.c bool attachInterruptInterval(const unsigned long& interval, timerCallback callback) { - return setFrequency( (float) ( 1000000.0f / interval), callback); + return _setPeriod(interval, callback); } + ////////////////////////////////////////// + void detachInterrupt() { // Disable Interrupt if (_timerNumber == TIMER_TC3) { - NVIC_DisableIRQ(TC3_IRQn); + NVIC_DisableIRQ(TC3_IRQn); } } + ////////////////////////////////////////// + void disableTimer() { // Disable Timer if (_timerNumber == TIMER_TC3) - { + { // Disable TC3 TC3->COUNT16.CTRLA.bit.ENABLE = 0; } } + ////////////////////////////////////////// + // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely void reattachInterrupt() { // Disable Interrupt if (_timerNumber == TIMER_TC3) { - NVIC_EnableIRQ(TC3_IRQn); + NVIC_EnableIRQ(TC3_IRQn); } } + ////////////////////////////////////////// + // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely void enableTimer() - { + { // Enable Timer if (_timerNumber == TIMER_TC3) - { + { // Enable TC3 SAMD_TC3->CTRLA.reg |= TC_CTRLA_ENABLE; } } + ////////////////////////////////////////// + // Just stop clock source, clear the count void stopTimer() { @@ -284,98 +327,110 @@ class SAMDTimerInterrupt disableTimer(); } + ////////////////////////////////////////// + // Just reconnect clock source, start current count from 0 void restartTimer() { // TODO, clear the count enableTimer(); } - - private: - + + ///////////////////////////////////////////////////////////////////////// + + private: + void setPeriod_TIMER_TC3(const float& period) { uint32_t TC_CTRLA_PRESCALER_DIVN = 1; - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; - TC3_wait_for_sync(); - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV1024; - TC3_wait_for_sync(); - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV256; - TC3_wait_for_sync(); - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV64; - TC3_wait_for_sync(); - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV16; - TC3_wait_for_sync(); - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV4; - TC3_wait_for_sync(); - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV2; - TC3_wait_for_sync(); - TC3->COUNT16.CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV1; + TC3->COUNT16.CTRLA.reg &= ~( TC_CTRLA_ENABLE | TC_CTRLA_PRESCALER_DIV1024 | TC_CTRLA_PRESCALER_DIV256 | TC_CTRLA_PRESCALER_DIV64 | \ + TC_CTRLA_PRESCALER_DIV16 | TC_CTRLA_PRESCALER_DIV4 | TC_CTRLA_PRESCALER_DIV2 | TC_CTRLA_PRESCALER_DIV1); TC3_wait_for_sync(); - if (period > 300000) + uint8_t old_prescaler = new_prescaler; + + new_prescaler = 0; + + if (period > 300000) { TC_CTRLA_PRESCALER_DIVN = TC_CTRLA_PRESCALER_DIV1024; - _prescaler = 1024; - } - else if (80000 < period && period <= 300000) + new_prescaler = 10; + } + else if (80000 < period && period <= 300000) { TC_CTRLA_PRESCALER_DIVN = TC_CTRLA_PRESCALER_DIV256; - _prescaler = 256; - } - else if (20000 < period && period <= 80000) + new_prescaler = 8; + } + else if (20000 < period && period <= 80000) { TC_CTRLA_PRESCALER_DIVN = TC_CTRLA_PRESCALER_DIV64; - _prescaler = 64; - } - else if (10000 < period && period <= 20000) + new_prescaler = 6; + } + else if (10000 < period && period <= 20000) { TC_CTRLA_PRESCALER_DIVN = TC_CTRLA_PRESCALER_DIV16; - _prescaler = 16; - } - else if (5000 < period && period <= 10000) + new_prescaler = 4; + } + else if (5000 < period && period <= 10000) { TC_CTRLA_PRESCALER_DIVN = TC_CTRLA_PRESCALER_DIV8; - _prescaler = 8; - } - else if (2500 < period && period <= 5000) + new_prescaler = 3; + } + else if (2500 < period && period <= 5000) { TC_CTRLA_PRESCALER_DIVN = TC_CTRLA_PRESCALER_DIV4; - _prescaler = 4; - } - else if (1000 < period && period <= 2500) { + new_prescaler = 2; + } + else if (1000 < period && period <= 2500) + { TC_CTRLA_PRESCALER_DIVN = TC_CTRLA_PRESCALER_DIV2; - _prescaler = 2; - } - else if (period <= 1000) + new_prescaler = 1; + } + else { TC_CTRLA_PRESCALER_DIVN = TC_CTRLA_PRESCALER_DIV1; - _prescaler = 1; + new_prescaler = 0; } - + TC3->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIVN; TC3_wait_for_sync(); - _compareValue = (int)(TIMER_HZ / (_prescaler/(period / 1000000.0))) - 1; + _compareValue = (uint16_t)(TIMER_HZ / ((1 << new_prescaler) / (period / 1000000.0))) - 1; + + uint16_t counter = TC3->COUNT16.CTRLA.reg; + + // scale up (or down) counter to match the new prescaler + // if new prescaler is lower, then scale up counter (but not higher than _compareValue) + // if new prescaler is higher, then scale down the counter (but not lower than 0) + if (new_prescaler < old_prescaler) + { + counter <<= (old_prescaler - new_prescaler); + + if (counter > _compareValue) + counter = _compareValue; + } + else // new_prescaler > old_prescaler + { + counter >>= (new_prescaler - old_prescaler); + } + + TC3->COUNT16.COUNT.reg = counter; + TC3_wait_for_sync(); - // Make sure the count is in a proportional position to where it was - // to prevent any jitter or disconnect when changing the compare value. - TC3->COUNT16.COUNT.reg = map(TC3->COUNT16.COUNT.reg, 0, - TC3->COUNT16.CC[0].reg, 0, _compareValue); TC3->COUNT16.CC[0].reg = _compareValue; TC3_wait_for_sync(); TC3->COUNT16.CTRLA.bit.ENABLE = 1; TC3_wait_for_sync(); - - TISR_LOGDEBUG3(F("SAMD51 TC3 period ="), period, F(", _prescaler ="), _prescaler); + + TISR_LOGDEBUG3(F("SAMD51 TC3 period ="), period, F(", _prescaler ="), 1 << new_prescaler); TISR_LOGDEBUG1(F("_compareValue ="), _compareValue); } }; // class SAMDTimerInterrupt -//////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////// #elif (TIMER_INTERRUPT_USING_SAMD21) @@ -395,123 +450,149 @@ typedef void (*timerCallback) (); #define SAMD_TC3 ((TcCount16*) _SAMDTimer) #define SAMD_TCC ((Tcc*) _SAMDTimer) +///////////////////////////////////////////////////////// + class SAMDTimerInterrupt { private: SAMDTimerNumber _timerNumber; - + // point to timer struct, (TcCount16*) TC3 or (Tcc*) TCC0 for SAMD21 void* _SAMDTimer = NULL; - + timerCallback _callback; // pointer to the callback function - float _frequency; // Timer frequency - //uint32_t _timerCount; // count to activate timer - - float _period; - int _prescaler; - int _compareValue; + + uint16_t _compareValue; + + // _prescaler = (1 << new_prescaler) + uint8_t new_prescaler; + + bool initialized; public: - SAMDTimerInterrupt(const SAMDTimerNumber& timerNumber) + SAMDTimerInterrupt(const SAMDTimerNumber& timerNumber) : initialized(false) { _timerNumber = timerNumber; - + if (_timerNumber == TIMER_TC3) { - _SAMDTimer = (TcCount16*) TC3; + _SAMDTimer = (TcCount16*) TC3; } else if (_timerNumber == TIMER_TCC) { - _SAMDTimer = (Tcc*) TCC0; + _SAMDTimer = (Tcc*) TCC0; } - - _callback = NULL; + + _callback = NULL; }; - + + ////////////////////////////////////////// + ~SAMDTimerInterrupt() { } - + + ////////////////////////////////////////// + bool setFrequency(const float& frequency, timerCallback callback); + bool _setPeriod(const float& _period, timerCallback callback); + + ////////////////////////////////////////// // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely // No params and duration now. To be addes in the future by adding similar functions here or to SAMD-hal-timer.c bool setInterval(const unsigned long& interval, timerCallback callback) { - return setFrequency((float) (1000000.0f / interval), callback); + return _setPeriod(interval, callback); } + ////////////////////////////////////////// + bool attachInterrupt(const float& frequency, timerCallback callback) { return setFrequency(frequency, callback); } + ////////////////////////////////////////// + // interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely // No params and duration now. To be addes in the future by adding similar functions here or to SAMD-hal-timer.c bool attachInterruptInterval(const unsigned long& interval, timerCallback callback) { - return setFrequency( (float) ( 1000000.0f / interval), callback); + return _setPeriod(interval, callback); } + ////////////////////////////////////////// + void detachInterrupt() { // Disable Interrupt if (_timerNumber == TIMER_TC3) { - NVIC_DisableIRQ(TC3_IRQn); + NVIC_DisableIRQ(TC3_IRQn); } else if (_timerNumber == TIMER_TCC) { - NVIC_DisableIRQ(TCC0_IRQn); + NVIC_DisableIRQ(TCC0_IRQn); } } + ////////////////////////////////////////// + void disableTimer() { // Disable Timer if (_timerNumber == TIMER_TC3) - { + { // Disable TC3 SAMD_TC3->CTRLA.reg &= ~TC_CTRLA_ENABLE; + while (SAMD_TC3->STATUS.bit.SYNCBUSY); } else if (_timerNumber == TIMER_TCC) - { + { // Disable TCC SAMD_TCC->CTRLA.reg &= ~TCC_CTRLA_ENABLE; } } + ////////////////////////////////////////// + // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely void reattachInterrupt() { // Disable Interrupt if (_timerNumber == TIMER_TC3) { - NVIC_EnableIRQ(TC3_IRQn); + NVIC_EnableIRQ(TC3_IRQn); } else if (_timerNumber == TIMER_TCC) { - NVIC_EnableIRQ(TCC0_IRQn); + NVIC_EnableIRQ(TCC0_IRQn); } } + ////////////////////////////////////////// + // Duration (in milliseconds). Duration = 0 or not specified => run indefinitely void enableTimer() - { + { // Enable Timer if (_timerNumber == TIMER_TC3) - { + { // Enable TC3 SAMD_TC3->CTRLA.reg |= TC_CTRLA_ENABLE; + while (SAMD_TC3->STATUS.bit.SYNCBUSY); } else if (_timerNumber == TIMER_TCC) - { + { // Enable TCC SAMD_TCC->CTRLA.reg |= TCC_CTRLA_ENABLE; } } + ////////////////////////////////////////// + // Just stop clock source, clear the count void stopTimer() { @@ -519,189 +600,210 @@ class SAMDTimerInterrupt disableTimer(); } + ////////////////////////////////////////// + // Just reconnect clock source, start current count from 0 void restartTimer() { // TODO, clear the count enableTimer(); } - - private: - + + ///////////////////////////////////////////////////////////////////// + + private: + + ////////////////////////////////////////// + + inline byte getPrescalerBitShift(uint16_t ctrla) + { + // prescaler is stored in bits 8 thru 11 of ctrla, so we need to shift 8 bits right and mask off lower 4 bits + // conveniently, the value is the number of bits to shift i.e. value==0x0A => shift is 10 bits (1024 div) + return (ctrla >> 8) & 0x000f; + } + + ////////////////////////////////////////// + void setPeriod_TIMER_TC3(const float& period) { - TcCount16* _Timer = (TcCount16*) _SAMDTimer; - - _Timer->CTRLA.reg &= ~TC_CTRLA_ENABLE; - while (_Timer->STATUS.bit.SYNCBUSY == 1); - _Timer->CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV1024; - while (_Timer->STATUS.bit.SYNCBUSY == 1); - _Timer->CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV256; - while (_Timer->STATUS.bit.SYNCBUSY == 1); - _Timer->CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV64; - while (_Timer->STATUS.bit.SYNCBUSY == 1); - _Timer->CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV16; - while (_Timer->STATUS.bit.SYNCBUSY == 1); - _Timer->CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV4; - while (_Timer->STATUS.bit.SYNCBUSY == 1); - _Timer->CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV2; - while (_Timer->STATUS.bit.SYNCBUSY == 1); - _Timer->CTRLA.reg &= ~TC_CTRLA_PRESCALER_DIV1; - while (_Timer->STATUS.bit.SYNCBUSY == 1); - - if (period > 300000) - { - // Set prescaler to 1024 - _Timer->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1024; - _prescaler = 1024; - } - else if (80000 < period && period <= 300000) - { - // Set prescaler to 256 - _Timer->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV256; - _prescaler = 256; - } - else if (20000 < period && period <= 80000) - { - // Set prescaler to 64 - _Timer->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV64; - _prescaler = 64; - } - else if (10000 < period && period <= 20000) - { - // Set prescaler to 16 - _Timer->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV16; - _prescaler = 16; - } - else if (5000 < period && period <= 10000) - { - // Set prescaler to 8 - _Timer->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV8; - _prescaler = 8; - } - else if (2500 < period && period <= 5000) - { - // Set prescaler to 4 - _Timer->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV4; - _prescaler = 4; - } - else if (1000 < period && period <= 2500) - { - // Set prescaler to 2 - _Timer->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV2; - _prescaler = 2; - } - else if (period <= 1000) - { - // Set prescaler to 1 - _Timer->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1; - _prescaler = 1; - } - - while (_Timer->STATUS.bit.SYNCBUSY == 1); - - _compareValue = (int)(TIMER_HZ / (_prescaler / (period / 1000000.0))) - 1; + TcCount16* _Timer = (TcCount16*) TC3; + uint16_t ctrla = _Timer->CTRLA.reg; + bool was_enabled = (ctrla & TC_CTRLA_ENABLE); - - // Make sure the count is in a proportional position to where it was - // to prevent any jitter or disconnect when changing the compare value. - _Timer->COUNT.reg = map(_Timer->COUNT.reg, 0, _Timer->CC[0].reg, 0, _compareValue); + // get old prescaler from ctrla, convert to bit shift + uint8_t old_prescaler = getPrescalerBitShift(ctrla); + uint8_t new_prescaler = 0; + + if (period > 300000) + { + // Set prescaler to 1024 + new_prescaler = 10; + } + else if (80000 < period && period <= 300000) + { + // Set prescaler to 256 + new_prescaler = 8; + } + else if (20000 < period && period <= 80000) + { + // Set prescaler to 64 + new_prescaler = 6; + } + else if (10000 < period && period <= 20000) + { + // Set prescaler to 16 + new_prescaler = 4; + } + else if (5000 < period && period <= 10000) + { + // Set prescaler to 8 + new_prescaler = 3; + } + else if (2500 < period && period <= 5000) + { + // Set prescaler to 4 + new_prescaler = 2; + } + else if (1000 < period && period <= 2500) + { + // Set prescaler to 2 + new_prescaler = 1; + } + else + { + // Set prescaler to 1 + new_prescaler = 0; + } + + // mask out old prescaler value, and set the new prescaler value + ctrla = (ctrla & 0xf0ff) | ((new_prescaler) << 8); + + _compareValue = (uint16_t)(TIMER_HZ / ((1 << new_prescaler) / (period / 1000000.0))) - 1; + + if (new_prescaler != old_prescaler && was_enabled) + { + // Make sure the count is in a proportional position to where it was + // to prevent any jitter or disconnect when changing the compare value. + // But we only need to do this if the precaler changed, AND if the timer was enabled. + + _Timer->READREQ.reg = TC_READREQ_RREQ | TC_READREQ_ADDR(0x10); // 0x10 is the offset of the 16-bit count register + while (_Timer->STATUS.bit.SYNCBUSY); + + uint16_t counter = _Timer->COUNT.reg; + + // cannot update ctrla at the same time as the enabled bit is set + _Timer->CTRLA.reg &= ~TC_CTRLA_ENABLE; + while (_Timer->STATUS.bit.SYNCBUSY); + + // need to synchronise the count and cc values + _Timer->READREQ.reg = TC_READREQ_RREQ | TC_READREQ_ADDR(0x18); // 0x18 is the offset of the 16-bit CC0 register + while (_Timer->STATUS.bit.SYNCBUSY); + + _Timer->CC[0].reg = (uint16_t)(-1); // max, so that changing Count doesn't end up wrapping it back to zero again due to the continuous comparison + + // scale up (or down) counter to match the new prescaler + // if new prescaler is lower, then scale up counter (but no higher than _compareValue) + // if new prescaler is higher, then scale down the counter (but no lower than 0) + if (new_prescaler < old_prescaler) + { + counter <<= (old_prescaler - new_prescaler); + + if (counter > _compareValue) + counter = _compareValue; + } + else + { + counter >>= (new_prescaler - old_prescaler); + } + + _Timer->COUNT.reg = counter; + while (_Timer->STATUS.bit.SYNCBUSY); + } + + // in all cases, we need to set the (new) CC, and set the (new) prescaler and (re)enable the timer _Timer->CC[0].reg = _compareValue; - - while (_Timer->STATUS.bit.SYNCBUSY == 1); - - TISR_LOGDEBUG3(F("SAMD21 TC3 period ="), period, F(", _prescaler ="), _prescaler); + while (_Timer->STATUS.bit.SYNCBUSY); + + _Timer->CTRLA.reg = ctrla | TC_CTRLA_ENABLE; + while (_Timer->STATUS.bit.SYNCBUSY); + + TISR_LOGDEBUG3(F("SAMD21 TC3 period ="), period, F(", _prescaler ="), 1 << new_prescaler); TISR_LOGDEBUG1(F("_compareValue ="), _compareValue); } - + + ////////////////////////////////////////// + void setPeriod_TIMER_TCC(const float& period) { Tcc* _Timer = (Tcc*) _SAMDTimer; - - _Timer->CTRLA.reg &= ~TCC_CTRLA_ENABLE; - while (_Timer->SYNCBUSY.bit.ENABLE == 1); - _Timer->CTRLA.reg &= ~TCC_CTRLA_PRESCALER_DIV1024; - while (_Timer->SYNCBUSY.bit.ENABLE == 1); - _Timer->CTRLA.reg &= ~TCC_CTRLA_PRESCALER_DIV256; - while (_Timer->SYNCBUSY.bit.ENABLE == 1); - _Timer->CTRLA.reg &= ~TCC_CTRLA_PRESCALER_DIV64; - while (_Timer->SYNCBUSY.bit.ENABLE == 1); - _Timer->CTRLA.reg &= ~TCC_CTRLA_PRESCALER_DIV16; - while (_Timer->SYNCBUSY.bit.ENABLE == 1); - _Timer->CTRLA.reg &= ~TCC_CTRLA_PRESCALER_DIV4; - while (_Timer->SYNCBUSY.bit.ENABLE == 1); - _Timer->CTRLA.reg &= ~TCC_CTRLA_PRESCALER_DIV2; - while (_Timer->SYNCBUSY.bit.ENABLE == 1); - _Timer->CTRLA.reg &= ~TCC_CTRLA_PRESCALER_DIV1; + + _Timer->CTRLA.reg &= ~( TCC_CTRLA_ENABLE | TCC_CTRLA_PRESCALER_DIV1024 | TCC_CTRLA_PRESCALER_DIV256 | TCC_CTRLA_PRESCALER_DIV64 | \ + TCC_CTRLA_PRESCALER_DIV16 | TCC_CTRLA_PRESCALER_DIV4 | TCC_CTRLA_PRESCALER_DIV2 | TCC_CTRLA_PRESCALER_DIV1); while (_Timer->SYNCBUSY.bit.ENABLE == 1); - - if (period > 300000) - { - // Set prescaler to 1024 - _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1024; - _prescaler = 1024; - } - else if (80000 < period && period <= 300000) - { - // Set prescaler to 256 - _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV256; - _prescaler = 256; - } - else if (20000 < period && period <= 80000) - { - // Set prescaler to 64 - _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV64; - _prescaler = 64; - } - else if (10000 < period && period <= 20000) - { - // Set prescaler to 16 - _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV16; - _prescaler = 16; - } - else if (5000 < period && period <= 10000) - { - // Set prescaler to 8 - _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV8; - _prescaler = 8; - } - else if (2500 < period && period <= 5000) - { - // Set prescaler to 4 - _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV4; - _prescaler = 4; - } - else if (1000 < period && period <= 2500) - { - // Set prescaler to 2 - _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV2; - _prescaler = 2; - } - else if (period <= 1000) - { - // Set prescaler to 1 - _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1; - _prescaler = 1; - } - - _compareValue = (int)(TIMER_HZ / (_prescaler / (period / 1000000))) - 1; - - _Timer->PER.reg = _compareValue; - - while (_Timer->SYNCBUSY.bit.PER == 1); - // Make sure the count is in a proportional position to where it was - // to prevent any jitter or disconnect when changing the compare value. - //_Timer->COUNT.reg = map(_Timer->COUNT.reg, 0, _Timer->CC[0].reg, 0, _compareValue); + if (period > 300000) + { + // Set prescaler to 1024 + _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1024; + new_prescaler = 10; + } + else if (80000 < period && period <= 300000) + { + // Set prescaler to 256 + _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV256; + new_prescaler = 8; + } + else if (20000 < period && period <= 80000) + { + // Set prescaler to 64 + _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV64; + new_prescaler = 6; + } + else if (10000 < period && period <= 20000) + { + // Set prescaler to 16 + _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV16; + new_prescaler = 4; + } + else if (5000 < period && period <= 10000) + { + // Set prescaler to 8 + _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV8; + new_prescaler = 3; + } + else if (2500 < period && period <= 5000) + { + // Set prescaler to 4 + _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV4; + new_prescaler = 2; + } + else if (1000 < period && period <= 2500) + { + // Set prescaler to 2 + _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV2; + new_prescaler = 1; + } + else + { + // Set prescaler to 1 + _Timer->CTRLA.reg |= TCC_CTRLA_PRESCALER_DIV1; + new_prescaler = 0; + } + + _compareValue = (uint16_t)(TIMER_HZ / ((1 << new_prescaler) / (period / 1000000.0))) - 1; + + _Timer->PER.reg = _compareValue; + while (_Timer->SYNCBUSY.bit.PER == 1); _Timer->CC[0].reg = 0xFFF; - //_Timer->CC[0].reg = _compareValue; - while (_Timer->SYNCBUSY.bit.CC0 == 1); - - TISR_LOGDEBUG3(F("SAMD21 TCC period ="), period, F(", _prescaler ="), _prescaler); + + TISR_LOGDEBUG3(F("SAMD21 TCC period ="), period, F(", _prescaler ="), (1 << new_prescaler)); TISR_LOGDEBUG1(F("_compareValue ="), _compareValue); - } + } + + ////////////////////////////////////////// + }; // class SAMDTimerInterrupt #endif // #if (TIMER_INTERRUPT_USING_SAMD51) diff --git a/src/SAMDTimerInterrupt_Impl.h b/src/SAMDTimerInterrupt_Impl.h index fab0740..7f9d08e 100644 --- a/src/SAMDTimerInterrupt_Impl.h +++ b/src/SAMDTimerInterrupt_Impl.h @@ -16,7 +16,7 @@ Based on BlynkTimer.h Author: Volodymyr Shymanskyy - Version: 1.6.0 + Version: 1.7.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -29,6 +29,7 @@ 1.4.0 K.Hoang 02/06/2021 Fix SAMD21 rare bug caused by not fully init Prescaler 1.5.0 K.Hoang 08/10/2021 Improve frequency precision by using float instead of ulong 1.6.0 K.Hoang 20/01/2022 Fix `multiple-definitions` linker error. Add support to many more boards + 1.7.0 K.Hoang 25/04/2022 Optimize code for setInterval() of SAMD21 TC3 *****************************************************************************************************************************/ /* SAMD21 @@ -53,189 +54,211 @@ #if (TIMER_INTERRUPT_USING_SAMD51) - timerCallback TC3_callback; +timerCallback TC3_callback; - //#define SAMD_TC3 ((TcCount16*) _SAMDTimer) +//#define SAMD_TC3 ((TcCount16*) _SAMDTimer) - void TC3_Handler() +//////////////////////////////////////////////////////// + +void TC3_Handler() +{ + // If this interrupt is due to the compare register matching the timer count + if (TC3->COUNT16.INTFLAG.bit.MC0 == 1) { - // If this interrupt is due to the compare register matching the timer count - if (TC3->COUNT16.INTFLAG.bit.MC0 == 1) - { - TC3->COUNT16.INTFLAG.bit.MC0 = 1; - (*TC3_callback)(); - } + TC3->COUNT16.INTFLAG.bit.MC0 = 1; + (*TC3_callback)(); } +} - // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely - // No params and duration now. To be addes in the future by adding similar functions here or to SAMD-hal-timer.c - bool SAMDTimerInterrupt::setFrequency(const float& frequency, timerCallback callback) +//////////////////////////////////////////////////////// + +bool SAMDTimerInterrupt::_setPeriod(const float& _period, timerCallback callback) +{ + if (_timerNumber == TIMER_TC3) { - _period = (1000000.0f / frequency); - - if (_timerNumber == TIMER_TC3) - { - TISR_LOGWARN3(F("SAMDTimerInterrupt: F_CPU (MHz) ="), F_CPU/1000000, F(", TIMER_HZ ="), TIMER_HZ/1000000); - TISR_LOGWARN3(F("TC_Timer::startTimer _Timer = 0x"), String((uint32_t) _SAMDTimer, HEX), F(", TC3 = 0x"), String((uint32_t) TC3, HEX)); - - // Enable the TC bus clock, use clock generator 0 - GCLK->PCHCTRL[TC3_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); - - while (GCLK->SYNCBUSY.reg > 0); - - TC3->COUNT16.CTRLA.bit.ENABLE = 0; - - // Use match mode so that the timer counter resets when the count matches the - // compare register - TC3->COUNT16.WAVE.bit.WAVEGEN = TC_WAVE_WAVEGEN_MFRQ; - TC3_wait_for_sync(); - - // Enable the compare interrupt - TC3->COUNT16.INTENSET.reg = 0; - TC3->COUNT16.INTENSET.bit.MC0 = 1; - - // Enable IRQ - NVIC_EnableIRQ(TC3_IRQn); + TISR_LOGWARN3(F("SAMDTimerInterrupt: F_CPU (MHz) ="), F_CPU / 1000000, F(", TIMER_HZ ="), TIMER_HZ / 1000000); + TISR_LOGWARN3(F("TC_Timer::startTimer _Timer = 0x"), String((uint32_t) _SAMDTimer, HEX), F(", TC3 = 0x"), String((uint32_t) TC3, HEX)); - //func1 = f; - _callback = callback; - TC3_callback = callback; + // Enable the TC bus clock, use clock generator 0 + GCLK->PCHCTRL[TC3_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK1_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); + while (GCLK->SYNCBUSY.reg > 0); - //setPeriod(period); - setPeriod_TIMER_TC3(_period); - - return true; - } - else - return false; + TC3->COUNT16.CTRLA.bit.ENABLE = 0; + + // Use match mode so that the timer counter resets when the count matches the + // compare register + TC3->COUNT16.WAVE.bit.WAVEGEN = TC_WAVE_WAVEGEN_MFRQ; + TC3_wait_for_sync(); + + // Enable the compare interrupt + TC3->COUNT16.INTENSET.reg = 0; + TC3->COUNT16.INTENSET.bit.MC0 = 1; + + // Enable IRQ + NVIC_EnableIRQ(TC3_IRQn); + + //func1 = f; + _callback = callback; + TC3_callback = callback; + + //setPeriod(period); + setPeriod_TIMER_TC3(_period); + + return true; } + else + return false; +} + +//////////////////////////////////////////////////////// +// frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely +// No params and duration now. To be addes in the future by adding similar functions here or to SAMD-hal-timer.c +bool SAMDTimerInterrupt::setFrequency(const float& frequency, timerCallback callback) +{ + float _period = (1000000.0f / frequency); + return _setPeriod(_period, callback); +} //////////////////////////////////////////////////////// #elif (TIMER_INTERRUPT_USING_SAMD21) - timerCallback TC3_callback; - timerCallback TCC_callback; +timerCallback TC3_callback; +timerCallback TCC_callback; //////////////////////////////////////////////////////// +void TC3_Handler() +{ + // get timer struct + TcCount16* TC = (TcCount16*) TC3; - void TC3_Handler() + // If the compare register matching the timer count, trigger this interrupt + if (TC->INTFLAG.bit.MC0 == 1) { - // get timer struct - TcCount16* TC = (TcCount16*) TC3; - - // If the compare register matching the timer count, trigger this interrupt - if (TC->INTFLAG.bit.MC0 == 1) - { - TC->INTFLAG.bit.MC0 = 1; - (*TC3_callback)(); - } + TC->INTFLAG.bit.MC0 = 1; + (*TC3_callback)(); } +} + +//////////////////////////////////////////////////////// - void TCC0_Handler() +void TCC0_Handler() +{ + // get timer struct + Tcc* TC = (Tcc*) TCC0; + + // If the compare register matching the timer count, trigger this interrupt + if (TC->INTFLAG.bit.MC0 == 1) { - // get timer struct - Tcc* TC = (Tcc*) TCC0; - - // If the compare register matching the timer count, trigger this interrupt - if (TC->INTFLAG.bit.MC0 == 1) - { - // A compare to cc0 caused the interrupt - TC->INTFLAG.bit.MC0 = 1; // writing a one clears the flag ovf flag - } + // A compare to cc0 caused the interrupt + TC->INTFLAG.bit.MC0 = 1; // writing a one clears the flag ovf flag + } - if (TC->INTFLAG.bit.OVF == 1) - { - (*TCC_callback)(); - - TC->INTFLAG.bit.OVF = 1; - } + if (TC->INTFLAG.bit.OVF == 1) + { + (*TCC_callback)(); + + TC->INTFLAG.bit.OVF = 1; } - - // frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely - // No params and duration now. To be addes in the future by adding similar functions here or to SAMD-hal-timer.c - bool SAMDTimerInterrupt::setFrequency(const float& frequency, timerCallback callback) +} + +//////////////////////////////////////////////////////// + +bool SAMDTimerInterrupt::_setPeriod(const float& _period, timerCallback callback) +{ + TISR_LOGDEBUG1(F("_period ="), _period); + + if (_timerNumber == TIMER_TC3) { - _period = (1000000.0f / frequency); - - TISR_LOGDEBUG3(F("_period ="), _period, F(", frequency ="), frequency); + noInterrupts(); - if (_timerNumber == TIMER_TC3) - { + if (!initialized) + { REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID (GCM_TCC2_TC3)); - - while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); - - TISR_LOGWARN3(F("SAMDTimerInterrupt: F_CPU (MHz) ="), F_CPU/1000000, F(", TIMER_HZ ="), TIMER_HZ/1000000); + while ( GCLK->STATUS.bit.SYNCBUSY); + + TISR_LOGWARN3(F("SAMDTimerInterrupt: F_CPU (MHz) ="), F_CPU / 1000000, F(", TIMER_HZ ="), TIMER_HZ / 1000000); TISR_LOGWARN3(F("TC3_Timer::startTimer _Timer = 0x"), String((uint32_t) _SAMDTimer, HEX), F(", TC3 = 0x"), String((uint32_t) TC3, HEX)); - + SAMD_TC3->CTRLA.reg &= ~TC_CTRLA_ENABLE; // Use the 16-bit timer SAMD_TC3->CTRLA.reg |= TC_CTRLA_MODE_COUNT16; - - while (SAMD_TC3->STATUS.bit.SYNCBUSY == 1); + while (SAMD_TC3->STATUS.bit.SYNCBUSY); // Use match mode so that the timer counter resets when the count matches the compare register SAMD_TC3->CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; - - while (SAMD_TC3->STATUS.bit.SYNCBUSY == 1); - - setPeriod_TIMER_TC3(_period); + while (SAMD_TC3->STATUS.bit.SYNCBUSY); // Enable the compare interrupt SAMD_TC3->INTENSET.reg = 0; SAMD_TC3->INTENSET.bit.MC0 = 1; + _callback = callback; + TC3_callback = callback; + NVIC_EnableIRQ(TC3_IRQn); - SAMD_TC3->CTRLA.reg |= TC_CTRLA_ENABLE; - - while (SAMD_TC3->STATUS.bit.SYNCBUSY == 1); + setPeriod_TIMER_TC3(_period); // this also enables the timer + initialized = true; + } + else + { + setPeriod_TIMER_TC3(_period); _callback = callback; TC3_callback = callback; } - else if (_timerNumber == TIMER_TCC) - { - REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TCC0_TCC1)); - while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); - - TISR_LOGWARN3(F("SAMDTimerInterrupt: F_CPU (MHz) ="), F_CPU/1000000, F(", TIMER_HZ ="), TIMER_HZ/1000000); - TISR_LOGWARN3(F("TCC_Timer::startTimer _Timer = 0x"), String((uint32_t) _SAMDTimer, HEX), F(", TCC0 = 0x"), String((uint32_t) TCC0, HEX)); - - SAMD_TCC->CTRLA.reg &= ~TCC_CTRLA_ENABLE; // Disable TC - - while (SAMD_TCC->SYNCBUSY.bit.ENABLE == 1); // wait for sync - - setPeriod_TIMER_TCC(_period); + interrupts(); + } + else if (_timerNumber == TIMER_TCC) + { + REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TCC0_TCC1)); + while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); - // Use match mode so that the timer counter resets when the count matches the compare register - SAMD_TCC->WAVE.reg |= TCC_WAVE_WAVEGEN_NFRQ; // Set wave form configuration - - while (SAMD_TCC->SYNCBUSY.bit.WAVE == 1); // wait for sync + TISR_LOGWARN3(F("SAMDTimerInterrupt: F_CPU (MHz) ="), F_CPU / 1000000, F(", TIMER_HZ ="), TIMER_HZ / 1000000); + TISR_LOGWARN3(F("TCC_Timer::startTimer _Timer = 0x"), String((uint32_t) _SAMDTimer, HEX), F(", TCC0 = 0x"), String((uint32_t) TCC0, HEX)); - // Enable the compare interrupt - SAMD_TCC->INTENSET.reg = 0; - SAMD_TCC->INTENSET.bit.OVF = 1; - SAMD_TCC->INTENSET.bit.MC0 = 1; + SAMD_TCC->CTRLA.reg &= ~TCC_CTRLA_ENABLE; // Disable TC + while (SAMD_TCC->SYNCBUSY.bit.ENABLE == 1); // wait for sync - NVIC_EnableIRQ(TCC0_IRQn); + setPeriod_TIMER_TCC(_period); - SAMD_TCC->CTRLA.reg |= TCC_CTRLA_ENABLE; - - while (SAMD_TCC->SYNCBUSY.bit.ENABLE == 1); // wait for sync + // Use match mode so that the timer counter resets when the count matches the compare register + SAMD_TCC->WAVE.reg |= TCC_WAVE_WAVEGEN_NFRQ; // Set wave form configuration + while (SAMD_TCC->SYNCBUSY.bit.WAVE == 1); // wait for sync - _callback = callback; - TCC_callback = callback; - } - - return true; + // Enable the compare interrupt + SAMD_TCC->INTENSET.reg = 0; + SAMD_TCC->INTENSET.bit.OVF = 1; + SAMD_TCC->INTENSET.bit.MC0 = 1; + + NVIC_EnableIRQ(TCC0_IRQn); + + SAMD_TCC->CTRLA.reg |= TCC_CTRLA_ENABLE; + while (SAMD_TCC->SYNCBUSY.bit.ENABLE == 1); // wait for sync + + _callback = callback; + TCC_callback = callback; } + + return true; +} + +//////////////////////////////////////////////////////// + +// frequency (in hertz) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely +// No params and duration now. To be addes in the future by adding similar functions here or to SAMD-hal-timer.c +bool SAMDTimerInterrupt::setFrequency(const float& frequency, timerCallback callback) +{ + float _period = (1000000.0f / frequency); + TISR_LOGDEBUG3(F("_period ="), _period, F(", frequency ="), frequency); + return _setPeriod(_period, callback); +} #endif // #if (TIMER_INTERRUPT_USING_SAMD51) diff --git a/src/SAMD_ISR_Timer-Impl.h b/src/SAMD_ISR_Timer-Impl.h index cbfc1cf..c24218b 100644 --- a/src/SAMD_ISR_Timer-Impl.h +++ b/src/SAMD_ISR_Timer-Impl.h @@ -19,7 +19,7 @@ Based on BlynkTimer.h Author: Volodymyr Shymanskyy - Version: 1.6.0 + Version: 1.7.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -32,6 +32,7 @@ 1.4.0 K.Hoang 02/06/2021 Fix SAMD21 rare bug caused by not fully init Prescaler 1.5.0 K.Hoang 08/10/2021 Improve frequency precision by using float instead of ulong 1.6.0 K.Hoang 20/01/2022 Fix `multiple-definitions` linker error. Add support to many more boards + 1.7.0 K.Hoang 25/04/2022 Optimize code for setInterval() of SAMD21 TC3 *****************************************************************************************************************************/ #pragma once diff --git a/src/SAMD_ISR_Timer.h b/src/SAMD_ISR_Timer.h index 490e1f8..fd499ee 100644 --- a/src/SAMD_ISR_Timer.h +++ b/src/SAMD_ISR_Timer.h @@ -19,7 +19,7 @@ Based on BlynkTimer.h Author: Volodymyr Shymanskyy - Version: 1.6.0 + Version: 1.7.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -32,6 +32,7 @@ 1.4.0 K.Hoang 02/06/2021 Fix SAMD21 rare bug caused by not fully init Prescaler 1.5.0 K.Hoang 08/10/2021 Improve frequency precision by using float instead of ulong 1.6.0 K.Hoang 20/01/2022 Fix `multiple-definitions` linker error. Add support to many more boards + 1.7.0 K.Hoang 25/04/2022 Optimize code for setInterval() of SAMD21 TC3 *****************************************************************************************************************************/ #pragma once diff --git a/src/SAMD_ISR_Timer.hpp b/src/SAMD_ISR_Timer.hpp index fc281ec..c044d5d 100644 --- a/src/SAMD_ISR_Timer.hpp +++ b/src/SAMD_ISR_Timer.hpp @@ -19,7 +19,7 @@ Based on BlynkTimer.h Author: Volodymyr Shymanskyy - Version: 1.6.0 + Version: 1.7.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -32,6 +32,7 @@ 1.4.0 K.Hoang 02/06/2021 Fix SAMD21 rare bug caused by not fully init Prescaler 1.5.0 K.Hoang 08/10/2021 Improve frequency precision by using float instead of ulong 1.6.0 K.Hoang 20/01/2022 Fix `multiple-definitions` linker error. Add support to many more boards + 1.7.0 K.Hoang 25/04/2022 Optimize code for setInterval() of SAMD21 TC3 *****************************************************************************************************************************/ #pragma once @@ -51,13 +52,13 @@ #endif #ifndef SAMD_TIMER_INTERRUPT_VERSION - #define SAMD_TIMER_INTERRUPT_VERSION "SAMDTimerInterrupt v1.6.0" + #define SAMD_TIMER_INTERRUPT_VERSION "SAMDTimerInterrupt v1.7.0" #define SAMD_TIMER_INTERRUPT_VERSION_MAJOR 1 - #define SAMD_TIMER_INTERRUPT_VERSION_MINOR 6 + #define SAMD_TIMER_INTERRUPT_VERSION_MINOR 7 #define SAMD_TIMER_INTERRUPT_VERSION_PATCH 0 - #define SAMD_TIMER_INTERRUPT_VERSION_INT 1006000 + #define SAMD_TIMER_INTERRUPT_VERSION_INT 1007000 #endif #include "TimerInterrupt_Generic_Debug.h" diff --git a/src/TimerInterrupt_Generic_Debug.h b/src/TimerInterrupt_Generic_Debug.h index 8a43d36..864a436 100644 --- a/src/TimerInterrupt_Generic_Debug.h +++ b/src/TimerInterrupt_Generic_Debug.h @@ -12,7 +12,7 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.6.0 + Version: 1.7.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -25,6 +25,7 @@ 1.4.0 K.Hoang 02/06/2021 Fix SAMD21 rare bug caused by not fully init Prescaler 1.5.0 K.Hoang 08/10/2021 Improve frequency precision by using float instead of ulong 1.6.0 K.Hoang 20/01/2022 Fix `multiple-definitions` linker error. Add support to many more boards + 1.7.0 K.Hoang 25/04/2022 Optimize code for setInterval() of SAMD21 TC3 *****************************************************************************************************************************/ #pragma once