Multiple Definitions
Linker ErrorPlease have a look at HOWTO Fix Multiple Definitions
Linker Error
This library enables you to use Interrupt from Hardware Timers on an SAMD-based board, such as SAMD21 Nano-33-IoT, Adafruit SAMD51 Itsy-Bitsy M4, SparkFun SAMD51 MicroMod, etc.
As Hardware Timers are rare, and very precious assets of any board, this library now enables you to use up to 16 ISR-based Timers, while consuming only 1 Hardware Timer. Timers' interval is very long (ulong millisecs).
Now with these new 16 ISR-based timers, the maximum interval is practically unlimited (limited only by unsigned long milliseconds) while 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.
The ISR_Timer_Complex example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs of each type of timers.
Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet and Blynk services. You can also have many (up to 16)
timers to use.
This non-being-blocked important feature is absolutely necessary for mission-critical tasks.
You'll see blynkTimer Software is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task in loop(), using delay() function as an example. The elapsed time then is very unaccurate
Imagine you have a system with a mission-critical function, measuring water level and control the sump pump or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is blocking the loop() or setup().
So your function might not be executed, and the result would be disastrous.
You'd prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.).
The correct choice is to use a Hardware Timer with Interrupt to call your function.
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.
Functions using normal software timers, relying on loop() and calling millis(), won't work if the loop() or setup() is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services.
The catch is your function is now part of an ISR (Interrupt Service Routine), and must be lean / mean, and follow certain rules. More to read on:
Inside the attached function, delay() won’t work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function.
Typically global variables are used to pass data between an ISR and the main program. To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile.
The maximum time you can use directly on SAMD TC3/TCC Timer is ~1398.11ms = 1.39811s. If you use time larger than 1398.11ms, the wrap-around will happen and you'll get much smaller time.
Arduino IDE 1.8.19+
for Arduino. Arduino SAMD core 1.8.13+
for SAMD ARM Cortex-M0+ boards. Adafruit SAMD core 1.7.11+
for SAMD ARM Cortex-M0+ and M4 boards (Nano 33 IoT, etc.). Seeeduino SAMD core 1.8.3+
for SAMD21/SAMD51 boards (XIAO M0, Wio Terminal, etc.). Sparkfun SAMD core 1.8.7+
for SAMD21/SAMD51 boards (SparkFun_RedBoard_Turbo, SparkFun_SAMD51_Thing_Plus, etc.).Blynk library 1.1.0
. to use with some examples. Don't use Blynk beta versions.Ethernet_Generic library v2.7.1+
for W5100, W5200 and W5500/WIZ550io/WIZ850io/USR-ES1 with Wiznet W5500 chip. EthernetENC library v2.0.3+
for ENC28J60. . New and BetterUIPEthernet library v2.0.12+
for ENC28J60. SimpleTimer library
for ISR_16_Timers_Array and ISR_16_Timers_Array_Complex examples. New to avoid error with old
Sparkfun core.The best and easiest way is to use Arduino Library Manager
. Search for SAMD_TimerInterrupt, then select / install the latest version.
You can also use this link for more detailed instructions.
Another way to install is to:
SAMD_TimerInterrupt-master.zip
.SAMD_TimerInterrupt-master
directory SAMD_TimerInterrupt-master
folder to Arduino libraries' directory such as ~/Arduino/libraries/
.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 directory into Arduino SAMD directory (~/.arduino15/packages/arduino/hardware/samd/1.8.13).
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.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
This file must be copied into the directory:
~/.arduino15/packages/arduino/hardware/samd/x.yy.zz/platform.txt
Supposing the Arduino SAMD version is 1.8.9. These files must be copied into the directory:
~/.arduino15/packages/arduino/hardware/samd/1.8.9/platform.txt
~/.arduino15/packages/arduino/hardware/samd/1.8.9/cores/arduino/Arduino.h
Whenever a new version is installed, remember to copy these files into the new version directory. For example, new version is x.yy.z
These files must be copied into the directory:
~/.arduino15/packages/arduino/hardware/samd/x.yy.z/platform.txt
~/.arduino15/packages/arduino/hardware/samd/x.yy.z/cores/arduino/Arduino.h
This is mandatory to fix the notorious Arduino SAMD compiler error. See Improve Arduino compatibility with the STL (min and max macro)
...\arm-none-eabi\include\c++\7.2.1\bits\stl_algobase.h:243:56: error: macro "min" passed 3 arguments, but takes just 2
min(const _Tp& __a, const _Tp& __b, _Compare __comp)
Whenever the above-mentioned compiler error issue is fixed with the new Arduino SAMD release, you don't need to copy the Arduino.h
file anymore.
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 directory into Adafruit samd directory (~/.arduino15/packages/adafruit/hardware/samd/1.7.11).
Supposing the Adafruit SAMD core version is 1.7.11. This file must be copied into the directory:
~/.arduino15/packages/adafruit/hardware/samd/1.7.11/platform.txt
~/.arduino15/packages/adafruit/hardware/samd/1.7.11/cores/arduino/Print.h
~/.arduino15/packages/adafruit/hardware/samd/1.7.11/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:
~/.arduino15/packages/adafruit/hardware/samd/x.yy.zz/platform.txt
~/.arduino15/packages/adafruit/hardware/samd/x.yy.zz/cores/arduino/Print.h
~/.arduino15/packages/adafruit/hardware/samd/x.yy.zz/cores/arduino/Print.cpp
To be able to compile, run and automatically detect and display BOARD_NAME on Seeeduino SAMD (XIAO M0, Wio Terminal, etc) boards, you have to copy the whole Seeeduino SAMD Packages_Patches directory into Seeeduino samd directory (~/.arduino15/packages/Seeeduino/hardware/samd/1.8.3).
Supposing the Seeeduino SAMD core version is 1.8.3. This file must be copied into the directory:
~/.arduino15/packages/Seeeduino/hardware/samd/1.8.3/platform.txt
~/.arduino15/packages/Seeeduino/hardware/samd/1.8.3/cores/arduino/Arduino.h
~/.arduino15/packages/Seeeduino/hardware/samd/1.8.3/cores/arduino/Print.h
~/.arduino15/packages/Seeeduino/hardware/samd/1.8.3/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:
~/.arduino15/packages/Seeeduino/hardware/samd/x.yy.zz/platform.txt
~/.arduino15/packages/Seeeduino/hardware/samd/x.yy.zz/cores/arduino/Arduino.h
~/.arduino15/packages/Seeeduino/hardware/samd/x.yy.zz/cores/arduino/Print.h
~/.arduino15/packages/Seeeduino/hardware/samd/x.yy.zz/cores/arduino/Print.cpp
If your application requires 2K+ HTML page, the current Ethernet library
must be modified if you are using W5200/W5500 Ethernet shields. W5100 is not supported for 2K+ buffer. If you use boards requiring different CS/SS pin for W5x00 Ethernet shield, for example ESP32, ESP8266, nRF52, etc., you also have to modify the following libraries to be able to specify the CS/SS pin correctly.
To fix Ethernet library
, just copy these following files into the Ethernet library
directory to overwrite the old files:
To fix EthernetLarge library
, just copy these following files into the EthernetLarge library
directory to overwrite the old files:
To fix Ethernet2 library
, just copy these following files into the Ethernet2 library
directory to overwrite the old files:
To add UDP Multicast support, necessary for the UPnP_Generic library:
Ethernet3 library
, just copy these following files into the Ethernet3 library
directory to overwrite the old files:
To be able to compile and run on nRF52 boards with ENC28J60 using UIPEthernet library, you have to copy these following files into the UIPEthernet utility
directory to overwrite the old files:
To fix ESP32 compile error
, just copy the following file into the ESP32
cores/esp32 directory (e.g. ./arduino-1.8.12/hardware/espressif/cores/esp32) to overwrite the old file:
Multiple Definitions
Linker ErrorThe current library implementation, using xyz-Impl.h
instead of standard xyz.cpp
, possibly creates certain Multiple Definitions
Linker error in certain use cases.
You can include .hpp
// Can be included as many times as necessary, without `Multiple Definitions` Linker Error
#include "SAMDTimerInterrupt.hpp" //https://github.com/khoih-prog/SAMD_TimerInterrupt
// Can be included as many times as necessary, without `Multiple Definitions` Linker Error
#include "SAMD_ISR_Timer.hpp" //https://github.com/khoih-prog/SAMD_TimerInterrupt
in many files. But be sure to use the following .h
files in just 1 .h
, .cpp
or .ino
file, which must not be included in any other file, to avoid Multiple Definitions
Linker Error
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "SAMDTimerInterrupt.h" //https://github.com/khoih-prog/SAMD_TimerInterrupt
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "SAMD_ISR_Timer.h" //https://github.com/khoih-prog/SAMD_TimerInterrupt
Now with these new 16 ISR-based timers
(while consuming only 1 hardware timer), the maximum interval is practically unlimited (limited only by unsigned long milliseconds). 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.
The ISR_Timer_Complex
example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs of each type of timers.
Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet and Blynk services. You can also have many (up to 16)
timers to use.
This non-being-blocked important feature is absolutely necessary for mission-critical tasks.
You'll see blynkTimer Software is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task
in loop(), using delay() function as an example. The elapsed time then is very unaccurate
Before using any Timer, you have to make sure the Timer has not been used by any other purpose.
// Select only one to be true for SAMD21. Must must be placed at the beginning before #include "SAMDTimerInterrupt.h"
#define USING_TIMER_TC3 true // Only TC3 can be used for SAMD51
#define USING_TIMER_TC4 false // Not to use with Servo library
#define USING_TIMER_TC5 false
#define USING_TIMER_TCC false
#define USING_TIMER_TCC1 false
#define USING_TIMER_TCC2 false // Don't use this, can crash on some boards
// Uncomment To test if conflict with Servo library
//#include "Servo.h"
/////////////////////////////////////////////////////////////////
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "SAMDTimerInterrupt.h"
//////////////////////////////////////////////
// TC3, TC4, TC5 max permissible TIMER_INTERVAL_MS is 1398.101 ms, larger will overflow, therefore not permitted
// Use TCC, TCC1, TCC2 for longer TIMER_INTERVAL_MS
#define TIMER_INTERVAL_MS 1000
#if USING_TIMER_TC3
#define SELECTED_TIMER TIMER_TC3
#elif USING_TIMER_TC4
#define SELECTED_TIMER TIMER_TC4
#elif USING_TIMER_TC5
#define SELECTED_TIMER TIMER_TC5
#elif USING_TIMER_TCC
#define SELECTED_TIMER TIMER_TCC
#elif USING_TIMER_TCC1
#define SELECTED_TIMER TIMER_TCC1
#elif USING_TIMER_TCC2
#define SELECTED_TIMER TIMER_TCC
#else
#error You have to select 1 Timer
#endif
// Init selected SAMD timer
SAMDTimer ITimer(SELECTED_TIMER);
void TimerHandler()
{
// Doing something here inside ISR
}
// TC3, TC4, TC5 max permissible TIMER_INTERVAL_MS is 1398.101 ms, larger will overflow, therefore not permitted
// Use TCC, TCC1, TCC2 for longer TIMER_INTERVAL_MS
#define TIMER_INTERVAL_MS 1000 // 1s = 1000ms
void setup()
{
....
// Interval in microsecs
if (ITimer.attachInterruptInterval_MS(TIMER_INTERVAL_MS, TimerHandler0))
Serial.println("Starting ITimer OK, millis() = " + String(millis()));
else
Serial.println("Can't set ITimer. Select another freq. or timer");
}
// Select only one to be true for SAMD21. Must must be placed at the beginning before #include "SAMDTimerInterrupt.h"
#define USING_TIMER_TC3 true // Only TC3 can be used for SAMD51
#define USING_TIMER_TC4 false // Not to use with Servo library
#define USING_TIMER_TC5 false
#define USING_TIMER_TCC false
#define USING_TIMER_TCC1 false
#define USING_TIMER_TCC2 false // Don't use this, can crash on some boards
// Uncomment To test if conflict with Servo library
//#include "Servo.h"
/////////////////////////////////////////////////////////////////
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
#include "SAMDTimerInterrupt.h"
//////////////////////////////////////////////
// TC3, TC4, TC5 max permissible TIMER_INTERVAL_MS is 1398.101 ms, larger will overflow, therefore not permitted
// Use TCC, TCC1, TCC2 for longer TIMER_INTERVAL_MS
#define TIMER_INTERVAL_MS 1000
#if USING_TIMER_TC3
#define SELECTED_TIMER TIMER_TC3
#elif USING_TIMER_TC4
#define SELECTED_TIMER TIMER_TC4
#elif USING_TIMER_TC5
#define SELECTED_TIMER TIMER_TC5
#elif USING_TIMER_TCC
#define SELECTED_TIMER TIMER_TCC
#elif USING_TIMER_TCC1
#define SELECTED_TIMER TIMER_TCC1
#elif USING_TIMER_TCC2
#define SELECTED_TIMER TIMER_TCC
#else
#error You have to select 1 Timer
#endif
// Init selected SAMD timer
SAMDTimer ITimer(SELECTED_TIMER);
// Init SAMD_ISR_Timer
// Each SAMD_ISR_Timer can service 16 different ISR-based timers
ISR_Timer SAMD_ISR_Timer;
void TimerHandler(void)
{
ISR_Timer.run();
}
#define HW_TIMER_INTERVAL_MS 50L
#define TIMER_INTERVAL_2S 2000L
#define TIMER_INTERVAL_5S 5000L
#define TIMER_INTERVAL_11S 11000L
#define TIMER_INTERVAL_101S 101000L
// In SAMD, avoid doing something fancy in ISR, for example complex Serial.print with String() argument
// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment
// Or you can get this run-time error / crash
void doingSomething2s()
{
// Doing something here inside ISR
}
void doingSomething5s()
{
// Doing something here inside ISR
}
void doingSomething11s()
{
// Doing something here inside ISR
}
void doingSomething101s()
{
// Doing something here inside ISR
}
void setup()
{
....
// Interval in microsecs
if (ITimer.attachInterruptInterval_MS(HW_TIMER_INTERVAL_MS, TimerHandler))
{
lastMillis = millis();
Serial.println("Starting ITimer OK, millis() = " + String(lastMillis));
}
else
Serial.println("Can't set ITimer correctly. Select another freq. or interval");
// Just to demonstrate, don't use too many ISR Timers if not absolutely necessary
// You can use up to 16 timer for each ISR_Timer
SAMD_ISR_Timer.setInterval(TIMER_INTERVAL_2S, doingSomething2s);
SAMD_ISR_Timer.setInterval(TIMER_INTERVAL_5S, doingSomething5s);
SAMD_ISR_Timer.setInterval(TIMER_INTERVAL_11S, doingSomething11s);
SAMD_ISR_Timer.setInterval(TIMER_INTERVAL_101S, doingSomething101s);
}
The following is the sample terminal output when running example ISR_Timer_Complex_WiFiNINA on SAMD_NANO_33_IOT using Built-in WiFiNINA to demonstrate the accuracy of ISR Hardware Timer, especially when system is very busy. The ISR timer is programmed for 2s, is activated exactly after 2.000s !!!
While software timer, programmed for 2s, is activated after 7.937s !!!. Then in loop(), it's also activated every 3s.
Starting ISR_Timer_Complex_WiFiNINA on SAMD_NANO_33_IOT
SAMDTimerInterrupt v1.10.1
CPU Frequency = 48 MHz
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Starting ITimer OK, millis() = 810
Start Blynk
[1571] WiFiNINA Firmware Version: 1.4.1
[1572] Con2:HueNet1
2s: Delta ms = 2000
[5439] ConW OK
[5439] IP:192.168.2.98
[5439]
___ __ __
/ _ )/ /_ _____ / /__
/ _ / / // / _ \/ '_/
/____/_/\_, /_//_/_/\_\
/___/ v0.6.1 on NANO_33_IOT
[5440] BlynkArduinoClient.connect: Connecting to account.duckdns.org:8080
[5673] Ready (ping: 6ms).
IP = 192.168.2.98
2s: Delta ms = 2000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 7937
2s: Delta ms = 2000
2s: Delta ms = 2000
5s: Delta ms = 5000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3002
2s: Delta ms = 2000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3002
2s: Delta ms = 2000
5s: Delta ms = 5000
2s: Delta ms = 2000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3002
2s: Delta ms = 2000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3004
2s: Delta ms = 2000
5s: Delta ms = 5000
2s: Delta ms = 2000
11s: Delta ms = 11000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3002
2s: Delta ms = 2000
5s: Delta ms = 5000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3003
2s: Delta ms = 2000
2s: Delta ms = 2000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3002
2s: Delta ms = 2000
5s: Delta ms = 5000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3004
2s: Delta ms = 2000
11s: Delta ms = 11000
2s: Delta ms = 2000
blynkDoingSomething2s: Delta programmed ms = 2000, actual = 3002
5s: Delta ms = 5000
2s: Delta ms = 2000
The following is the sample terminal output when running example TimerInterruptTest on Adafruit SAMD51 ITSYBITSY_M4 to demonstrate how to start/stop Hardware Timers.
Starting TimerInterruptTest on ITSYBITSY_M4
SAMDTimerInterrupt v1.10.1
CPU Frequency = 48 MHz
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 120 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 0x4101c000 , TC3 = 0x 0x4101c000
Starting ITimer1 OK, millis() = 1820
ITimer0: millis() = 2820, delta = 1000
ITimer0: millis() = 3820, delta = 1000
ITimer0: millis() = 4820, delta = 1000
Stop ITimer0, millis() = 5001
Start ITimer0, millis() = 10002
ITimer0: millis() = 11002, delta = 1000
ITimer0: millis() = 12002, delta = 1000
ITimer0: millis() = 13002, delta = 1000
ITimer0: millis() = 14002, delta = 1000
ITimer0: millis() = 15002, delta = 1000
Stop ITimer0, millis() = 15003
Start ITimer0, millis() = 20004
ITimer0: millis() = 21004, delta = 1000
ITimer0: millis() = 22004, delta = 1000
ITimer0: millis() = 23004, delta = 1000
ITimer0: millis() = 24004, delta = 1000
ITimer0: millis() = 25004, delta = 1000
Stop ITimer0, millis() = 25005
Start ITimer0, millis() = 30006
ITimer0: millis() = 31006, delta = 1000
ITimer0: millis() = 32006, delta = 1000
ITimer0: millis() = 33006, delta = 1000
ITimer0: millis() = 34006, delta = 1000
ITimer0: millis() = 35006, delta = 1000
Stop ITimer0, millis() = 35007
Start ITimer0, millis() = 40008
ITimer0: millis() = 41008, delta = 1000
ITimer0: millis() = 42008, delta = 1000
ITimer0: millis() = 43008, delta = 1000
ITimer0: millis() = 44008, delta = 1000
ITimer0: millis() = 45008, delta = 1000
Stop ITimer0, millis() = 45009
Start ITimer0, millis() = 50010
ITimer0: millis() = 51010, delta = 1000
ITimer0: millis() = 52010, delta = 1000
ITimer0: millis() = 53010, delta = 1000
ITimer0: millis() = 54010, delta = 1000
ITimer0: millis() = 55010, delta = 1000
Stop ITimer0, millis() = 55011
Start ITimer0, millis() = 60012
ITimer0: millis() = 61012, delta = 1000
ITimer0: millis() = 62012, delta = 1000
ITimer0: millis() = 63012, delta = 1000
ITimer0: millis() = 64012, delta = 1000
ITimer0: millis() = 65012, delta = 1000
Stop ITimer0, millis() = 65013
Start ITimer0, millis() = 70014
ITimer0: millis() = 71014, delta = 1000
ITimer0: millis() = 72014, delta = 1000
ITimer0: millis() = 73014, delta = 1000
ITimer0: millis() = 74014, delta = 1000
ITimer0: millis() = 75014, delta = 1000
Stop ITimer0, millis() = 75015
Start ITimer0, millis() = 80016
ITimer0: millis() = 81016, delta = 1000
ITimer0: millis() = 82016, delta = 1000
ITimer0: millis() = 83016, delta = 1000
ITimer0: millis() = 84016, delta = 1000
ITimer0: millis() = 85016, delta = 1000
Stop ITimer0, millis() = 85017
Start ITimer0, millis() = 90018
ITimer0: millis() = 91018, delta = 1000
The following is the sample terminal output when running example Argument_None on Arduino SAMD21 SAMD_NANO_33_IOT to demonstrate how to start/stop Multiple Hardware Timers.
Starting Argument_None on SAMD_NANO_33_IOT
SAMDTimerInterrupt v1.10.1
CPU Frequency = 48 MHz
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Starting ITimer1 OK, millis() = 910
F_CPU (MHz) = 48
TC_Timer::startTimer _Timer = 0x42002000, TCC0 = 0x42002000
Starting ITimer1 OK, millis() = 911
ITimer0: millis() = 1410, delta = 500
ITimer0: millis() = 1910, delta = 500
ITimer0: millis() = 2410, delta = 500
ITimer0: millis() = 2910, delta = 500
ITimer1: millis() = 2911, delta = 2000
ITimer0: millis() = 3410, delta = 500
ITimer0: millis() = 3910, delta = 500
ITimer0: millis() = 4410, delta = 500
ITimer0: millis() = 4910, delta = 500
ITimer1: millis() = 4911, delta = 2000
ITimer0: millis() = 5410, delta = 500
ITimer0: millis() = 5910, delta = 500
ITimer0: millis() = 6410, delta = 500
ITimer0: millis() = 6910, delta = 500
ITimer1: millis() = 6911, delta = 2000
ITimer0: millis() = 7410, delta = 500
ITimer0: millis() = 7910, delta = 500
ITimer0: millis() = 8410, delta = 500
ITimer0: millis() = 8910, delta = 500
ITimer1: millis() = 8911, delta = 2000
ITimer0: millis() = 9410, delta = 500
ITimer0: millis() = 9910, delta = 500
ITimer0: millis() = 10410, delta = 500
ITimer0: millis() = 10910, delta = 500
ITimer1: millis() = 10911, delta = 2000
ITimer0: millis() = 11410, delta = 500
ITimer0: millis() = 11910, delta = 500
The following is the sample terminal output when running example ISR_16_Timers_Array on Arduino SAMD21 SAMD_NANO_33_IOT to demonstrate the accuracy of ISR Hardware Timer, especially when system is very busy or blocked. The 16 independent ISR timers are programmed to be activated repetitively after certain intervals, is activated exactly after that programmed interval !!!
While software timer, programmed for 2s, is activated after 10.000s in loop()!!!.
In this example, 16 independent ISR Timers are used, yet utilized just one Hardware Timer. The Timer Intervals and Function Pointers are stored in arrays to facilitate the code modification.
Starting ISR_16_Timers_Array on SAMD_NANO_33_IOT
SAMDTimerInterrupt v1.10.1
CPU Frequency = 48 MHz
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Starting ITimer OK, millis() = 1421
1s: Delta ms = 1000, ms = 2421
1s: Delta ms = 1000, ms = 3421
2s: Delta ms = 2000, ms = 3421
1s: Delta ms = 1000, ms = 4421
3s: Delta ms = 3000, ms = 4421
1s: Delta ms = 1000, ms = 5421
2s: Delta ms = 2000, ms = 5421
4s: Delta ms = 4000, ms = 5421
1s: Delta ms = 1000, ms = 6421
5s: Delta ms = 5000, ms = 6421
1s: Delta ms = 1000, ms = 7421
2s: Delta ms = 2000, ms = 7421
3s: Delta ms = 3000, ms = 7421
6s: Delta ms = 6000, ms = 7421
1s: Delta ms = 1000, ms = 8421
7s: Delta ms = 7000, ms = 8421
1s: Delta ms = 1000, ms = 9421
2s: Delta ms = 2000, ms = 9421
4s: Delta ms = 4000, ms = 9421
8s: Delta ms = 8000, ms = 9421
1s: Delta ms = 1000, ms = 10421
3s: Delta ms = 3000, ms = 10421
9s: Delta ms = 9000, ms = 10421
1s: Delta ms = 1000, ms = 11421
2s: Delta ms = 2000, ms = 11421
5s: Delta ms = 5000, ms = 11421
10s: Delta ms = 10000, ms = 11421
simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000
1s: Delta ms = 1000, ms = 12421
11s: Delta ms = 11000, ms = 12421
1s: Delta ms = 1000, ms = 13421
2s: Delta ms = 2000, ms = 13421
3s: Delta ms = 3000, ms = 13421
4s: Delta ms = 4000, ms = 13421
6s: Delta ms = 6000, ms = 13421
12s: Delta ms = 12000, ms = 13421
1s: Delta ms = 1000, ms = 14421
13s: Delta ms = 13000, ms = 14421
1s: Delta ms = 1000, ms = 15421
2s: Delta ms = 2000, ms = 15421
7s: Delta ms = 7000, ms = 15421
14s: Delta ms = 14000, ms = 15421
1s: Delta ms = 1000, ms = 16421
3s: Delta ms = 3000, ms = 16421
5s: Delta ms = 5000, ms = 16421
15s: Delta ms = 15000, ms = 16421
1s: Delta ms = 1000, ms = 17421
2s: Delta ms = 2000, ms = 17421
4s: Delta ms = 4000, ms = 17421
8s: Delta ms = 8000, ms = 17421
16s: Delta ms = 16000, ms = 17421
1s: Delta ms = 1000, ms = 18421
1s: Delta ms = 1000, ms = 19421
2s: Delta ms = 2000, ms = 19421
3s: Delta ms = 3000, ms = 19421
6s: Delta ms = 6000, ms = 19421
9s: Delta ms = 9000, ms = 19421
1s: Delta ms = 1000, ms = 20421
1s: Delta ms = 1000, ms = 21421
2s: Delta ms = 2000, ms = 21421
4s: Delta ms = 4000, ms = 21421
5s: Delta ms = 5000, ms = 21421
10s: Delta ms = 10000, ms = 21421
simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000
1s: Delta ms = 1000, ms = 22421
3s: Delta ms = 3000, ms = 22421
7s: Delta ms = 7000, ms = 22421
1s: Delta ms = 1000, ms = 23421
2s: Delta ms = 2000, ms = 23421
11s: Delta ms = 11000, ms = 23421
1s: Delta ms = 1000, ms = 24421
1s: Delta ms = 1000, ms = 25421
2s: Delta ms = 2000, ms = 25421
3s: Delta ms = 3000, ms = 25421
4s: Delta ms = 4000, ms = 25421
6s: Delta ms = 6000, ms = 25421
8s: Delta ms = 8000, ms = 25421
12s: Delta ms = 12000, ms = 25421
1s: Delta ms = 1000, ms = 26421
5s: Delta ms = 5000, ms = 26421
1s: Delta ms = 1000, ms = 27421
2s: Delta ms = 2000, ms = 27421
13s: Delta ms = 13000, ms = 27421
1s: Delta ms = 1000, ms = 28421
3s: Delta ms = 3000, ms = 28421
9s: Delta ms = 9000, ms = 28421
1s: Delta ms = 1000, ms = 29421
2s: Delta ms = 2000, ms = 29421
4s: Delta ms = 4000, ms = 29421
7s: Delta ms = 7000, ms = 29421
14s: Delta ms = 14000, ms = 29421
1s: Delta ms = 1000, ms = 30421
1s: Delta ms = 1000, ms = 31421
2s: Delta ms = 2000, ms = 31421
3s: Delta ms = 3000, ms = 31421
5s: Delta ms = 5000, ms = 31421
6s: Delta ms = 6000, ms = 31421
10s: Delta ms = 10000, ms = 31421
15s: Delta ms = 15000, ms = 31421
simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000
1s: Delta ms = 1000, ms = 32421
1s: Delta ms = 1000, ms = 33421
2s: Delta ms = 2000, ms = 33421
4s: Delta ms = 4000, ms = 33421
8s: Delta ms = 8000, ms = 33421
16s: Delta ms = 16000, ms = 33421
1s: Delta ms = 1000, ms = 34421
3s: Delta ms = 3000, ms = 34421
11s: Delta ms = 11000, ms = 34421
The following is the sample terminal output when running example Change_Interval on Arduino SAMD21 SAMD_NANO_33_IOT to demonstrate how to change Timer Interval on-the-fly
Starting Change_Interval on SAMD_NANO_33_IOT
SAMDTimerInterrupt v1.10.1
CPU Frequency = 48 MHz
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Starting ITimer OK, millis() = 1131
Time = 10001, TimerCount = 17
Time = 20002, TimerCount = 37
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 1000
Time = 30003, TimerCount = 47
Time = 40004, TimerCount = 57
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 500
Time = 50005, TimerCount = 77
Time = 60006, TimerCount = 97
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 1000
Time = 70007, TimerCount = 107
Time = 80008, TimerCount = 117
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 500
Time = 90009, TimerCount = 137
Time = 100010, TimerCount = 157
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 1000
Time = 110011, TimerCount = 167
Time = 120012, TimerCount = 177
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 500
Time = 130013, TimerCount = 197
Time = 140014, TimerCount = 217
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 1000
Time = 150015, TimerCount = 227
Time = 160016, TimerCount = 237
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 500
Time = 170017, TimerCount = 257
Time = 180018, TimerCount = 277
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
Changing Interval, Timer = 1000
Time = 190019, TimerCount = 287
Time = 200020, TimerCount = 297
The following is the sample terminal output when running example RepeatedAttachInterrupt_uS on SEEED_XIAO_M0 to demonstrate how to change Timer Interval on-the-fly
Starting RepeatedAttachInterrupt_uS on SEEED_XIAO_M0
SAMDTimerInterrupt v1.10.1
CPU Frequency = 48 MHz
[TISR] _period = 19995 , frequency = 50.01
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 19995 , _prescaler = 16
[TISR] _compareValue = 59984
myClockTimer (19995) = 19993 <========== OK===============================<=======OK
[TISR] _period = 19995 , frequency = 50.01
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 19995 , _prescaler = 16
[TISR] _compareValue = 59984
myClockTimer (19995) = 19993 <========== OK===============================<=======OK
[TISR] _period = 19995 , frequency = 50.01
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 19995 , _prescaler = 16
[TISR] _compareValue = 59984
myClockTimer (19995) = 19993 <========== OK===============================<=======OK
[TISR] _period = 19000 , frequency = 52.63
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 19000 , _prescaler = 16
[TISR] _compareValue = 56999
myClockTimer (19000) = 18998
[TISR] _period = 20005 , frequency = 49.99
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 20005 , _prescaler = 64
[TISR] _compareValue = 15002
myClockTimer (20005) = 20002
[TISR] _period = 30000 , frequency = 33.33
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 30000 , _prescaler = 64
[TISR] _compareValue = 22498
myClockTimer (30000) = 29996
[TISR] _period = 19995 , frequency = 50.01
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 19995 , _prescaler = 16
[TISR] _compareValue = 59984
myClockTimer (19995) = 19993 <========== OK===============================<=======OK
[TISR] _period = 30000 , frequency = 33.33
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 30000 , _prescaler = 64
[TISR] _compareValue = 22498
myClockTimer (30000) = 29997
[TISR] _period = 19995 , frequency = 50.01
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 19995 , _prescaler = 16
[TISR] _compareValue = 59984
The following is the sample terminal output when running example SAMD21_MultiTimers on SAMD_NANO_33_IOT to demonstrate demo the how to use all 6 SAMD21 timers simultaneously.
Starting SAMD21_MultiTimers on SAMD_NANO_33_IOT
SAMDTimerInterrupt v1.10.1
CPU Frequency = 48 MHz
[TISR] _period = 20000.00 , frequency = 50.00
[TISR] _timerNumber = 0
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC3_Timer::startTimer _Timer = 0x 42002c00 , TC3 = 0x 42002c00
[TISR] SAMD21 TC3 period = 20000.00 , _prescaler = 16
[TISR] _compareValue = 59999
Starting TIMER_TC3 OK, millis() = 962
[TISR] _period = 50000.00 , frequency = 20.00
[TISR] _timerNumber = 1
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC4_Timer::startTimer _Timer = 0x 42003000 , TC4 = 0x 42003000
[TISR] SAMD21 TC4 period = 50000.00 , _prescaler = 64
[TISR] _compareValue = 37499
Starting TIMER_TC4 OK, millis() = 964
[TISR] _period = 100000.00 , frequency = 10.00
[TISR] _timerNumber = 2
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TC5_Timer::startTimer _Timer = 0x 42003400 , TC5 = 0x 42003400
[TISR] SAMD21 TC5 period = 100000.00 , _prescaler = 256
[TISR] _compareValue = 18749
Starting TIMER_TC5 OK, millis() = 966
[TISR] _period = 200000.00 , frequency = 5.00
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002000 , TCC0 = 0x 42002000
[TISR] SAMD21 TCC period = 200000.00 , _prescaler = 256
[TISR] _compareValue = 37499
Starting TIMER_TCC OK, millis() = 968
[TISR] _period = 500000.00 , frequency = 2.00
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002400 , TCC1 = 0x 42002400
[TISR] SAMD21 TCC1 period = 500000.00 , _prescaler = 1024
[TISR] _compareValue = 23436
Starting TIMER_TCC1 OK, millis() = 970
[TISR] _period = 1000000.00 , frequency = 1.00
[TISR] SAMDTimerInterrupt: F_CPU (MHz) = 48 , TIMER_HZ = 48
[TISR] TCC_Timer::startTimer _Timer = 0x 42002800 , TCC2 = 0x 42002800
[TISR] SAMD21 TCC2 period = 1000000.00 , _prescaler = 1024
[TISR] _compareValue = 46874
========================================
TC3 Actual/Programmed (ms) 20/20
TC4 Actual/Programmed (ms) 50/50
TC5 Actual/Programmed (ms) 100/100
TCC Actual/Programmed (ms) 200/200
TCC1 Actual/Programmed (ms) 500/500
========================================
TC3 Actual/Programmed (ms) 20/20
TC4 Actual/Programmed (ms) 50/50
TC5 Actual/Programmed (ms) 100/100
TCC Actual/Programmed (ms) 200/200
TCC1 Actual/Programmed (ms) 500/500
Debug is enabled by default on Serial.
You can also change the debugging level (_TIMERINTERRUPTLOGLEVEL) from 0 to 4
// These define's must be placed at the beginning before #include "SAMD_TimerInterrupt.h"
// _TIMERINTERRUPT_LOGLEVEL_ from 0 to 4
// Don't define _TIMERINTERRUPT_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
#define TIMER_INTERRUPT_DEBUG 0
#define _TIMERINTERRUPT_LOGLEVEL_ 0
If you get compilation errors, more often than not, you may need to install a newer version of the core for Arduino boards.
Sometimes, the library will only work if you update the board core to the latest version because I am using newly added functions.
Submit issues to: SAMD_TimerInterrupt issues
multiple-definitions
linker errorSAMD21E1xA
, SAMD21G1xA
andSAMD21J1xA
reference-passing
instead of value-passing
setInterval()
of SAMD21 TC3
setInterval()
of SAMD21 TC3 to fix bug when using SAMD21 TC3.attachInterruptInterval_MS()
and setInterval_MS()
Many thanks for everyone for bug reporting, new feature suggesting, testing and contributing to the development of this library.
⭐️ Tamasa |
Dennis van Gils |
generationmake |
thiagothimotti |
generationmake |
Alexander Golovanov |
Will Powell |
Dave Hooper |
Rui Marinheiro |
sriccardi-invn |
If you want to contribute to this project:
Copyright 2020- Khoi Hoang