- Notifications
You must be signed in to change notification settings - Fork7
This library enables you to use Interrupt from Hardware Timers on an STM32H7-based Portenta_H7 board. 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 func…
License
khoih-prog/Portenta_H7_TimerInterrupt
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
- Important Change from v1.4.0
- Why do we need this Portenta_H7_TimerInterrupt library
- Changelog
- Prerequisites
- Installation
- Packages' Patches
- HOWTO Fix
Multiple Definitions
Linker Error - More useful Information about STM32 Timers
- Available Timers for Portenta_H7
- From v1.2.1
- Usage
- Examples
- Example ISR_16_Timers_Array_Complex
- Debug Terminal Output Samples
- Debug
- Troubleshooting
- Issues
- TO DO
- DONE
- Contributions and Thanks
- Contributing
- License
- Copyright
Please have a look atHOWTO FixMultiple Definitions
Linker Error
Why do we need thisPortenta_H7_TimerInterrupt library
This library enables you to use Interrupt from Hardware Timers on anSTM32H7-based Portenta_H7 board.
AsHardware Timers are rare, and very precious assets of any board, this library now enables you to use up to16 ISR-based Timers, while consuming only 1 Hardware Timer. Timers' interval is very long (ulong millisecs).
Now with these new16 ISR-based timers, the maximum interval ispractically unlimited (limited only by unsigned long milliseconds) whilethe accuracy is nearly perfect compared to software timers.
The most important feature is they're ISR-based timers. Therefore, their executions arenot blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks.
TheISR_16_Timers_Array_Complex example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs of each type of timers.
ThePWM_Multi_Args will demonstrate the usage of multichannel PWM using multiple Hardware Timers. The 6 independent Hardware Timers are usedto control 6 different PWM outputs, with totally independent frequencies and dutycycles.
Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or 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 taskinloop()
, using delay() function as an example. The elapsed time then is very unaccurate
Imagine you have a system with amission-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 inloop()
. But what if another function isblocking theloop()
orsetup()
.
So your functionmight 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 withInterrupt to call your function.
These hardware timers, using interrupt, still work even if other functions are blocking. Moreover, they are much moreprecise (certainly depending on clock frequency accuracy) than other software timers usingmillis()
ormicros()
. That's necessary if you need to measure some data requiring better accuracy.
Functions using normal software timers, relying onloop()
and callingmillis()
, won't work if theloop()
orsetup()
is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services.
The catch isyour function is now part of an ISR (Interrupt Service Routine), and must be lean / mean, and follow certain rules. More to read on:
- Portenta_H7 boards such as Portenta_H7 Rev2 ABX00042, etc., usingArduinoCore mbed_portenta core
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 asvolatile 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.
Arduino IDE 1.8.19+
for Arduino.ArduinoCore-mbed mbed_portenta core 3.4.1+
for ArduinoPortenta_H7 boards, such asPortenta_H7 Rev2 ABX00042, etc..- To use with certain example, depending on which Ethernet card you're using:
Ethernet_Generic library v2.7.1+
for W5100, W5200 and W5500.EthernetENC library v2.0.3+
for ENC28J60..New and Better
UIPEthernet library v2.0.12+
for ENC28J60.
- To use with certain example
SimpleTimer library
forISR_16_Timers_Array andISR_16_Timers_Array_Complex examples.
The best and easiest way is to useArduino Library Manager
. Search forPortenta_H7_TimerInterrupt, then select / install the latest version.You can also use this link for more detailed instructions.
Another way to install is to:
- Navigate toPortenta_H7_TimerInterrupt page.
- Download the latest release
Portenta_H7_TimerInterrupt-master.zip
. - Extract the zip file to
Portenta_H7_TimerInterrupt-master
directory - Copy whole
Portenta_H7_TimerInterrupt-master
folder to Arduino libraries' directory such as~/Arduino/libraries/
.
- InstallVS Code
- InstallPlatformIO
- InstallPortenta_H7_TimerInterrupt library by usingLibrary Manager. Search forPortenta_H7_TimerInterrupt inPlatform.io Author's Libraries
- Use includedplatformio.ini file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples atProject Configuration File
To be able to upload firmware to Portenta_H7 using Arduino IDE in Linux (Ubuntu, etc.), you have to copy the fileportenta_post_install.sh into mbed_portenta directory (~/.arduino15/packages/arduino/hardware/mbed_portenta/3.4.1/portenta_post_install.sh).
Then run the following command usingsudo
$ cd ~/.arduino15/packages/arduino/hardware/mbed_portenta/3.4.1$ chmod 755 portenta_post_install.sh$ sudo ./portenta_post_install.sh
This will create the file/etc/udev/rules.d/49-portenta_h7.rules
as follows:
# Portenta H7 bootloader mode UDEV rulesSUBSYSTEMS=="usb", ATTRS{idVendor}=="2341", ATTRS{idProduct}=="035b", GROUP="plugdev", MODE="0666"
Supposing the ArduinoCore-mbed core version is 3.4.1. Now only one file must be copied into the directory:
~/.arduino15/packages/arduino/hardware/mbed_portenta/3.4.1/portenta_post_install.sh
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/mbed_portenta/x.yy.zz/portenta_post_install.sh
The current library implementation, usingxyz-Impl.h
instead of standardxyz.cpp
, possibly creates certainMultiple Definitions
Linker error in certain use cases.
You can include these.hpp
or.h
files
// Can be included as many times as necessary, without `Multiple Definitions` Linker Error#include"Portenta_H7_TimerInterrupt.h"//https://github.com/khoih-prog/Portenta_H7_TimerInterrupt// Can be included as many times as necessary, without `Multiple Definitions` Linker Error#include"Portenta_H7_ISR_Timer.hpp"//https://github.com/khoih-prog/Portenta_H7_TimerInterrupt
in many files. But be sure to use the following.h
filein just 1.h
,.cpp
or.ino
file, which mustnot be included in any other file, to avoidMultiple Definitions
Linker Error
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error#include"Portenta_H7_ISR_Timer.h"//https://github.com/khoih-prog/Portenta_H7_TimerInterrupt
The Timers of STM32s are numerous, yet very sophisticated and powerful.
In general, across the STM32 microcontrollers families, the timer peripherals that have the same name also have the same features set, but there are a few exceptions.
The general purpose timers embedded by the STM32 microcontrollers share the same backbone structure; they differ only on the level of features embedded by a given timer peripheral.
The level of features integration for a given timer peripheral is decided based on the applications field that it targets.
The timer peripherals can be classified as:
• Advanced-configuration timers like TIM1 and TIM8 among others.• General-purpose configuration timers like TIM2 and TIM3 among others• Lite-configuration timers like TIM9, TIM10, TIM12 and TIM16 among others• Basic-configuration timers like TIM6 and TIM7 among others.
More information can be found atEmbedded-Lab STM32 TIMERS
To be sure which Timer is available for the board you're using, check the Core Package's related files. For example, forPortenta_H7 using STM32H747XI, check this file:
~/.arduino15/packages/STM32/hardware/stm32/2.0.0/system/Drivers/CMSIS/Device/ST/STM32H7xx/Include/stm32h7xx.h
The information will be as follows:
typedefstruct{ __IOuint32_t CR1;/*!< TIM control register 1, Address offset: 0x00*/ __IOuint32_t CR2;/*!< TIM control register 2, Address offset: 0x04*/ __IOuint32_t SMCR;/*!< TIM slave mode control register, Address offset: 0x08*/ __IOuint32_t DIER;/*!< TIM DMA/interrupt enable register, Address offset: 0x0C*/ __IOuint32_t SR;/*!< TIM status register, Address offset: 0x10*/ __IOuint32_t EGR;/*!< TIM event generation register, Address offset: 0x14*/ __IOuint32_t CCMR1;/*!< TIM capture/compare mode register 1, Address offset: 0x18*/ __IOuint32_t CCMR2;/*!< TIM capture/compare mode register 2, Address offset: 0x1C*/ __IOuint32_t CCER;/*!< TIM capture/compare enable register, Address offset: 0x20*/ __IOuint32_t CNT;/*!< TIM counter register, Address offset: 0x24*/ __IOuint32_t PSC;/*!< TIM prescaler, Address offset: 0x28*/ __IOuint32_t ARR;/*!< TIM auto-reload register, Address offset: 0x2C*/ __IOuint32_t RCR;/*!< TIM repetition counter register, Address offset: 0x30*/ __IOuint32_t CCR1;/*!< TIM capture/compare register 1, Address offset: 0x34*/ __IOuint32_t CCR2;/*!< TIM capture/compare register 2, Address offset: 0x38*/ __IOuint32_t CCR3;/*!< TIM capture/compare register 3, Address offset: 0x3C*/ __IOuint32_t CCR4;/*!< TIM capture/compare register 4, Address offset: 0x40*/ __IOuint32_t BDTR;/*!< TIM break and dead-time register, Address offset: 0x44*/ __IOuint32_t DCR;/*!< TIM DMA control register, Address offset: 0x48*/ __IOuint32_t DMAR;/*!< TIM DMA address for full transfer, Address offset: 0x4C*/uint32_t RESERVED1;/*!< Reserved, 0x50*/ __IOuint32_t CCMR3;/*!< TIM capture/compare mode register 3, Address offset: 0x54*/ __IOuint32_t CCR5;/*!< TIM capture/compare register5, Address offset: 0x58*/ __IOuint32_t CCR6;/*!< TIM capture/compare register6, Address offset: 0x5C*/ __IOuint32_t AF1;/*!< TIM alternate function option register 1, Address offset: 0x60*/ __IOuint32_t AF2;/*!< TIM alternate function option register 2, Address offset: 0x64*/ __IOuint32_t TISEL;/*!< TIM Input Selection register, Address offset: 0x68*/} TIM_TypeDef;
and
#definePERIPH_BASE0x40000000UL/*!< Base address of : AHB/ABP Peripherals/*!< Peripheral memory map*/#defineAPB1PERIPH_BASE PERIPH_BASE/*!< APB1 peripherals*//*!< D2_APB1PERIPH peripherals*/#defineTIM2_BASE (D2_APB1PERIPH_BASE +0x0000UL)#defineTIM3_BASE (D2_APB1PERIPH_BASE +0x0400UL)#defineTIM4_BASE (D2_APB1PERIPH_BASE +0x0800UL)#defineTIM5_BASE (D2_APB1PERIPH_BASE +0x0C00UL)#defineTIM6_BASE (D2_APB1PERIPH_BASE +0x1000UL)#defineTIM7_BASE (D2_APB1PERIPH_BASE +0x1400UL)#defineTIM12_BASE (D2_APB1PERIPH_BASE +0x1800UL)#defineTIM13_BASE (D2_APB1PERIPH_BASE +0x1C00UL)#defineTIM14_BASE (D2_APB1PERIPH_BASE +0x2000UL)/*!< APB2 peripherals*/#defineTIM1_BASE (D2_APB2PERIPH_BASE +0x0000UL)#defineTIM8_BASE (D2_APB2PERIPH_BASE +0x0400UL)...#defineTIM9_BASE (APB2PERIPH_BASE +0x4000UL)#defineTIM10_BASE (APB2PERIPH_BASE +0x4400UL)#defineTIM11_BASE (APB2PERIPH_BASE +0x4800UL)...#defineTI15_BASE (D2_APB2PERIPH_BASE +0x4000UL)#defineTIM16_BASE (D2_APB2PERIPH_BASE +0x4400UL)#defineTIM17_BASE (D2_APB2PERIPH_BASE +0x4800UL)...#defineHRTIM1_BASE (D2_APB2PERIPH_BASE +0x7400UL)#defineHRTIM1_TIMA_BASE (HRTIM1_BASE +0x00000080UL)#defineHRTIM1_TIMB_BASE (HRTIM1_BASE +0x00000100UL)#defineHRTIM1_TIMC_BASE (HRTIM1_BASE +0x00000180UL)#defineHRTIM1_TIMD_BASE (HRTIM1_BASE +0x00000200UL)#defineHRTIM1_TIME_BASE (HRTIM1_BASE +0x00000280UL)#defineHRTIM1_COMMON_BASE (HRTIM1_BASE +0x00000380UL)...#defineTIM2 ((TIM_TypeDef *) TIM2_BASE)#defineTIM3 ((TIM_TypeDef *) TIM3_BASE)#defineTIM4 ((TIM_TypeDef *) TIM4_BASE)#defineTIM5 ((TIM_TypeDef *) TIM5_BASE)#defineTIM6 ((TIM_TypeDef *) TIM6_BASE)#defineTIM7 ((TIM_TypeDef *) TIM7_BASE)#defineTIM13 ((TIM_TypeDef *) TIM13_BASE)#defineTIM14 ((TIM_TypeDef *) TIM14_BASE)...#defineTIM1 ((TIM_TypeDef *) TIM1_BASE)#defineTIM8 ((TIM_TypeDef *) TIM8_BASE)...#defineTIM12 ((TIM_TypeDef *) TIM12_BASE)#defineTIM15 ((TIM_TypeDef *) TIM15_BASE)#defineTIM16 ((TIM_TypeDef *) TIM16_BASE)#defineTIM17 ((TIM_TypeDef *) TIM17_BASE)...#defineHRTIM1 ((HRTIM_TypeDef *) HRTIM1_BASE)#defineHRTIM1_TIMA ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMA_BASE)#defineHRTIM1_TIMB ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMB_BASE)#defineHRTIM1_TIMC ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMC_BASE)#defineHRTIM1_TIMD ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMD_BASE)#defineHRTIM1_TIME ((HRTIM_Timerx_TypeDef *) HRTIM1_TIME_BASE)#defineHRTIM1_COMMON ((HRTIM_Common_TypeDef *) HRTIM1_COMMON_BASE)
This is the temporary list for Portenta_H7 Timers which can be used. The available Timers certainly depends on they are being used for other purpose (core, application, libraries, etc.) or not. You have to exhausively test yourself to be sure.
TIM1, TIM4, TIM7, TIM8, TIM12, TIM13, TIM14, TIM15, TIM16, TIM17
TIM9, TIM10, TIM11. Only for STM32F2, STM32F4 or STM32L1
TIM18, TIM19, TIM20, TIM21, TIM22
TIM2, TIM3, TIM5, TIM6
Now with these new16 ISR-based timers
(while consuming only1 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.
TheISR_16_Timers_Array_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 software-based SimpleTimer is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking taskin 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.
// Init timer TIM15Portenta_H7_TimerITimer0(TIM15);
voidTimerHandler0(void){// Doing something here inside ISR}#defineTIMER0_INTERVAL_MS1000// 1s = 1000msvoidsetup(){ ....// Interval in microsecsif (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS *1000, TimerHandler0)) Serial.println("Starting ITimer0 OK, millis() =" +String(millis()));else Serial.println("Can't set ITimer0. Select another freq. or timer");}
// Init timer TIM16Portenta_H7_TimerITimer(TIM16);// Init Portenta_H7_ISR_Timer// Each Portenta_H7_ISR_Timer can service 16 different ISR-based timersPortenta_H7_ISR_Timer ISR_Timer;
// In Portenta_H7, avoid doing something fancy in ISR, for example Serial.print// Or you can get this run-time error / crashvoidTimerHandler(void){ ISR_Timer.run();}#defineHW_TIMER_INTERVAL_US100L#defineTIMER_INTERVAL_2S2000L#defineTIMER_INTERVAL_5S5000L#defineTIMER_INTERVAL_11S11000L#defineTIMER_INTERVAL_101S101000L// In Portenta_H7, avoid doing something fancy in ISR, for example Serial.print// Or you can get this run-time error / crashvoiddoingSomething2s(){// Doing something here inside ISR}voiddoingSomething5s(){// Doing something here inside ISR}voiddoingSomething11s(){// Doing something here inside ISR}voiddoingSomething101s(){// Doing something here inside ISR}voidsetup(){ ....// Interval in microsecsif (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, 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 ISR_Timer.setInterval(TIMER_INTERVAL_2S, doingSomething2s); ISR_Timer.setInterval(TIMER_INTERVAL_5S, doingSomething5s); ISR_Timer.setInterval(TIMER_INTERVAL_11S, doingSomething11s); ISR_Timer.setInterval(TIMER_INTERVAL_101S, doingSomething101s);}
- Argument_None
- Change_Interval.
- ISR_16_Timers_Array
- ISR_16_Timers_Array_Complex.
- SwitchDebounce
- TimerInterruptLEDDemo
- TimerInterruptTest
- multiFileProject.New
ExampleISR_16_Timers_Array_Complex
Portenta_H7_TimerInterrupt/examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino
Lines 35 to 369 in63d4891
#if !( ( defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_PORTENTA_H7_M4) ) && defined(ARDUINO_ARCH_MBED) ) | |
#error This code is designed to run on Portenta_H7 platform! Please check your Tools->Board setting. | |
#endif | |
// These define's must be placed at the beginning before #include "Portenta_H7_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_TIMERINTERRUPT_LOGLEVEL_4 | |
// Can be included as many times as necessary, without `Multiple Definitions` Linker Error | |
#include"Portenta_H7_TimerInterrupt.h" | |
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error | |
#include"Portenta_H7_ISR_Timer.h" | |
#include<SimpleTimer.h>// https://github.com/jfturcot/SimpleTimer | |
#defineLED_OFF HIGH | |
#defineLED_ON LOW | |
#ifndef LED_BUILTIN | |
#defineLED_BUILTIN24//LEDG // Pin 24 control on-board LED_GREEN on Portenta_H7 | |
#endif | |
#ifndef LED_BLUE | |
#defineLED_BLUE25//LEDB // Pin 25 control on-board LED_BLUE on Portenta_H7 | |
#endif | |
#ifndef LED_RED | |
#defineLED_RED23// LEDR // Pin 23 control on-board LED_RED on Portenta_H7 | |
#endif | |
#defineHW_TIMER_INTERVAL_US10000L | |
volatileuint32_t startMillis =0; | |
// Depending on the board, you can select STM32H7 Hardware Timer from TIM1-TIM22 | |
// If you select a Timer not correctly, you'll get a message from compiler | |
// 'TIMxx' was not declared in this scope; did you mean 'TIMyy'? | |
// Portenta_H7 OK : TIM1, TIM4, TIM7, TIM8, TIM12, TIM13, TIM14, TIM15, TIM16, TIM17 | |
// Portenta_H7 Not OK : TIM2, TIM3, TIM5, TIM6, TIM18, TIM19, TIM20, TIM21, TIM22 | |
// Portenta_H7 No timer : TIM9, TIM10, TIM11. Only for STM32F2, STM32F4 and STM32L1 | |
// Portenta_H7 No timer : TIM18, TIM19, TIM20, TIM21, TIM22 | |
// Init timer TIM16 | |
Portenta_H7_TimerITimer(TIM16); | |
// Init Portenta_H7_ISR_Timer | |
// Each Portenta_H7_ISR_Timer can service 16 different ISR-based timers | |
Portenta_H7_ISR_Timer ISR_Timer; | |
#defineLED_TOGGLE_INTERVAL_MS2000L | |
voidTimerHandler() | |
{ | |
staticbool toggle =false; | |
staticint timeRun =0; | |
ISR_Timer.run(); | |
// Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s | |
if (++timeRun == ((LED_TOGGLE_INTERVAL_MS *1000) / HW_TIMER_INTERVAL_US) ) | |
{ | |
timeRun =0; | |
//timer interrupt toggles pin LED_BUILTIN | |
digitalWrite(LED_BUILTIN, toggle); | |
toggle = !toggle; | |
} | |
} | |
///////////////////////////////////////////////// | |
#defineNUMBER_ISR_TIMERS16 | |
typedefvoid (*irqCallback) (); | |
///////////////////////////////////////////////// | |
#defineUSE_COMPLEX_STRUCTtrue | |
#if USE_COMPLEX_STRUCT | |
typedefstruct | |
{ | |
irqCallback irqCallbackFunc; | |
uint32_t TimerInterval; | |
unsignedlong deltaMillis; | |
unsignedlong previousMillis; | |
} ISRTimerData; | |
// In Portenta_H7, avoid doing something fancy in ISR, for example Serial.print | |
// Or you can get this run-time error / crash | |
voiddoingSomething(int index); | |
#else | |
volatileunsignedlong deltaMillis [NUMBER_ISR_TIMERS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; | |
volatileunsignedlong previousMillis [NUMBER_ISR_TIMERS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; | |
// You can assign any interval for any timer here, in milliseconds | |
uint32_t TimerInterval[NUMBER_ISR_TIMERS] = | |
{ | |
5000L,10000L,15000L,20000L,25000L,30000L,35000L,40000L, | |
45000L,50000L,55000L,60000L,65000L,70000L,75000L,80000L | |
}; | |
voiddoingSomething(int index) | |
{ | |
unsignedlong currentMillis =millis(); | |
deltaMillis[index] = currentMillis - previousMillis[index]; | |
previousMillis[index] = currentMillis; | |
} | |
#endif | |
//////////////////////////////////// | |
// Shared | |
//////////////////////////////////// | |
voiddoingSomething0() | |
{ | |
doingSomething(0); | |
} | |
voiddoingSomething1() | |
{ | |
doingSomething(1); | |
} | |
voiddoingSomething2() | |
{ | |
doingSomething(2); | |
} | |
voiddoingSomething3() | |
{ | |
doingSomething(3); | |
} | |
voiddoingSomething4() | |
{ | |
doingSomething(4); | |
} | |
voiddoingSomething5() | |
{ | |
doingSomething(5); | |
} | |
voiddoingSomething6() | |
{ | |
doingSomething(6); | |
} | |
voiddoingSomething7() | |
{ | |
doingSomething(7); | |
} | |
voiddoingSomething8() | |
{ | |
doingSomething(8); | |
} | |
voiddoingSomething9() | |
{ | |
doingSomething(9); | |
} | |
voiddoingSomething10() | |
{ | |
doingSomething(10); | |
} | |
voiddoingSomething11() | |
{ | |
doingSomething(11); | |
} | |
voiddoingSomething12() | |
{ | |
doingSomething(12); | |
} | |
voiddoingSomething13() | |
{ | |
doingSomething(13); | |
} | |
voiddoingSomething14() | |
{ | |
doingSomething(14); | |
} | |
voiddoingSomething15() | |
{ | |
doingSomething(15); | |
} | |
#if USE_COMPLEX_STRUCT | |
ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] = | |
{ | |
//irqCallbackFunc, TimerInterval, deltaMillis, previousMillis | |
{ doingSomething0,5000L,0,0 }, | |
{ doingSomething1,10000L,0,0 }, | |
{ doingSomething2,15000L,0,0 }, | |
{ doingSomething3,20000L,0,0 }, | |
{ doingSomething4,25000L,0,0 }, | |
{ doingSomething5,30000L,0,0 }, | |
{ doingSomething6,35000L,0,0 }, | |
{ doingSomething7,40000L,0,0 }, | |
{ doingSomething8,45000L,0,0 }, | |
{ doingSomething9,50000L,0,0 }, | |
{ doingSomething10,55000L,0,0 }, | |
{ doingSomething11,60000L,0,0 }, | |
{ doingSomething12,65000L,0,0 }, | |
{ doingSomething13,70000L,0,0 }, | |
{ doingSomething14,75000L,0,0 }, | |
{ doingSomething15,80000L,0,0 } | |
}; | |
voiddoingSomething(int index) | |
{ | |
unsignedlong currentMillis =millis(); | |
curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis; | |
curISRTimerData[index].previousMillis = currentMillis; | |
} | |
#else | |
irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] = | |
{ | |
doingSomething0, doingSomething1, doingSomething2, doingSomething3, | |
doingSomething4, doingSomething5, doingSomething6, doingSomething7, | |
doingSomething8, doingSomething9, doingSomething10, doingSomething11, | |
doingSomething12, doingSomething13, doingSomething14, doingSomething15 | |
}; | |
#endif | |
/////////////////////////////////////////// | |
#defineSIMPLE_TIMER_MS2000L | |
// Init SimpleTimer | |
SimpleTimer simpleTimer; | |
// Here is software Timer, you can do somewhat fancy stuffs without many issues. | |
// But always avoid | |
// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead | |
// 2. Very long "do", "while", "for" loops without predetermined exit time. | |
voidsimpleTimerDoingSomething2s() | |
{ | |
staticunsignedlong previousMillis = startMillis; | |
unsignedlong currMillis =millis(); | |
Serial.print(F("SimpleTimer :")); Serial.print(SIMPLE_TIMER_MS /1000); | |
Serial.print(F(", ms :")); Serial.print(currMillis); | |
Serial.print(F(", Dms :")); Serial.println(currMillis - previousMillis); | |
for (uint16_t i =0; i < NUMBER_ISR_TIMERS; i++) | |
{ | |
#if USE_COMPLEX_STRUCT | |
Serial.print(F("Timer :")); Serial.print(i); | |
Serial.print(F(", programmed :")); Serial.print(curISRTimerData[i].TimerInterval); | |
Serial.print(F(", actual :")); Serial.println(curISRTimerData[i].deltaMillis); | |
#else | |
Serial.print(F("Timer :")); Serial.print(i); | |
Serial.print(F(", programmed :")); Serial.print(TimerInterval[i]); | |
Serial.print(F(", actual :")); Serial.println(deltaMillis[i]); | |
#endif | |
} | |
previousMillis = currMillis; | |
} | |
voidsetup() | |
{ | |
pinMode(LED_BUILTIN, OUTPUT); | |
Serial.begin(115200); | |
while (!Serial); | |
delay(100); | |
Serial.print(F("\nStarting ISR_16_Timers_Array_Complex on")); Serial.println(BOARD_NAME); | |
Serial.println(PORTENTA_H7_TIMER_INTERRUPT_VERSION); | |
// Interval in microsecs | |
if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler)) | |
{ | |
startMillis =millis(); | |
Serial.print(F("Starting ITimer OK, millis() =")); Serial.println(startMillis); | |
} | |
else | |
Serial.println(F("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 Portenta_H7_ISR_Timer | |
for (uint16_t i =0; i < NUMBER_ISR_TIMERS; i++) | |
{ | |
#if USE_COMPLEX_STRUCT | |
curISRTimerData[i].previousMillis = startMillis; | |
ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc); | |
#else | |
previousMillis[i] = startMillis; | |
ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]); | |
#endif | |
} | |
// You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary. | |
simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s); | |
} | |
#defineBLOCKING_TIME_MS10000L | |
voidloop() | |
{ | |
// This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer | |
// You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer | |
// The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS) | |
// While that of ISR_Timer is still prefect. | |
delay(BLOCKING_TIME_MS); | |
// You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary | |
// You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer. | |
simpleTimer.run(); | |
} |
The following is the sample terminal output when running exampleISR_16_Timers_Array_Complex onPortenta_H7 to demonstrate the accuracy of ISR Hardware Timer,especially when system is very busy. The ISR timer isprogrammed for 2s, is activated exactly after 2.000s !!!
While software-basedSimpleTimer
,programmed for 2s, is activated after 10.000s !!!.
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_Complex on PORTENTA_H7_M7Portenta_H7_TimerInterrupt v1.4.0[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =100.00, _count = 10000Starting ITimer OK, millis() = 1111SimpleTimer : 2, ms : 11111, Dms : 10000Timer : 0, programmed : 5000, actual : 5004Timer : 1, programmed : 10000, actual : 0Timer : 2, programmed : 15000, actual : 0Timer : 3, programmed : 20000, actual : 0Timer : 4, programmed : 25000, actual : 0Timer : 5, programmed : 30000, actual : 0Timer : 6, programmed : 35000, actual : 0Timer : 7, programmed : 40000, actual : 0Timer : 8, programmed : 45000, actual : 0Timer : 9, programmed : 50000, actual : 0Timer : 10, programmed : 55000, actual : 0Timer : 11, programmed : 60000, actual : 0Timer : 12, programmed : 65000, actual : 0Timer : 13, programmed : 70000, actual : 0Timer : 14, programmed : 75000, actual : 0Timer : 15, programmed : 80000, actual : 0SimpleTimer : 2, ms : 21114, Dms : 10003Timer : 0, programmed : 5000, actual : 4994Timer : 1, programmed : 10000, actual : 10008Timer : 2, programmed : 15000, actual : 15002Timer : 3, programmed : 20000, actual : 0Timer : 4, programmed : 25000, actual : 0Timer : 5, programmed : 30000, actual : 0Timer : 6, programmed : 35000, actual : 0Timer : 7, programmed : 40000, actual : 0Timer : 8, programmed : 45000, actual : 0Timer : 9, programmed : 50000, actual : 0Timer : 10, programmed : 55000, actual : 0Timer : 11, programmed : 60000, actual : 0Timer : 12, programmed : 65000, actual : 0Timer : 13, programmed : 70000, actual : 0Timer : 14, programmed : 75000, actual : 0Timer : 15, programmed : 80000, actual : 0SimpleTimer : 2, ms : 31117, Dms : 10003Timer : 0, programmed : 5000, actual : 5005Timer : 1, programmed : 10000, actual : 9999Timer : 2, programmed : 15000, actual : 15003Timer : 3, programmed : 20000, actual : 20006Timer : 4, programmed : 25000, actual : 25000Timer : 5, programmed : 30000, actual : 30005Timer : 6, programmed : 35000, actual : 0Timer : 7, programmed : 40000, actual : 0Timer : 8, programmed : 45000, actual : 0Timer : 9, programmed : 50000, actual : 0Timer : 10, programmed : 55000, actual : 0Timer : 11, programmed : 60000, actual : 0Timer : 12, programmed : 65000, actual : 0Timer : 13, programmed : 70000, actual : 0Timer : 14, programmed : 75000, actual : 0Timer : 15, programmed : 80000, actual : 0SimpleTimer : 2, ms : 41120, Dms : 10003Timer : 0, programmed : 5000, actual : 4994Timer : 1, programmed : 10000, actual : 9998Timer : 2, programmed : 15000, actual : 15003Timer : 3, programmed : 20000, actual : 19997Timer : 4, programmed : 25000, actual : 25000Timer : 5, programmed : 30000, actual : 30005Timer : 6, programmed : 35000, actual : 35009Timer : 7, programmed : 40000, actual : 40003Timer : 8, programmed : 45000, actual : 0Timer : 9, programmed : 50000, actual : 0Timer : 10, programmed : 55000, actual : 0Timer : 11, programmed : 60000, actual : 0Timer : 12, programmed : 65000, actual : 0Timer : 13, programmed : 70000, actual : 0Timer : 14, programmed : 75000, actual : 0Timer : 15, programmed : 80000, actual : 0SimpleTimer : 2, ms : 51123, Dms : 10003Timer : 0, programmed : 5000, actual : 4995Timer : 1, programmed : 10000, actual : 9999Timer : 2, programmed : 15000, actual : 15002Timer : 3, programmed : 20000, actual : 19997Timer : 4, programmed : 25000, actual : 25002Timer : 5, programmed : 30000, actual : 30005Timer : 6, programmed : 35000, actual : 35009Timer : 7, programmed : 40000, actual : 40003Timer : 8, programmed : 45000, actual : 45007Timer : 9, programmed : 50000, actual : 50002Timer : 10, programmed : 55000, actual : 0Timer : 11, programmed : 60000, actual : 0Timer : 12, programmed : 65000, actual : 0Timer : 13, programmed : 70000, actual : 0Timer : 14, programmed : 75000, actual : 0Timer : 15, programmed : 80000, actual : 0SimpleTimer : 2, ms : 61126, Dms : 10003Timer : 0, programmed : 5000, actual : 4994Timer : 1, programmed : 10000, actual : 9998Timer : 2, programmed : 15000, actual : 14993Timer : 3, programmed : 20000, actual : 19997Timer : 4, programmed : 25000, actual : 25002Timer : 5, programmed : 30000, actual : 29995Timer : 6, programmed : 35000, actual : 35009Timer : 7, programmed : 40000, actual : 40003Timer : 8, programmed : 45000, actual : 45007Timer : 9, programmed : 50000, actual : 50002Timer : 10, programmed : 55000, actual : 55006Timer : 11, programmed : 60000, actual : 60000Timer : 12, programmed : 65000, actual : 0Timer : 13, programmed : 70000, actual : 0Timer : 14, programmed : 75000, actual : 0Timer : 15, programmed : 80000, actual : 0SimpleTimer : 2, ms : 71129, Dms : 10003Timer : 0, programmed : 5000, actual : 5005Timer : 1, programmed : 10000, actual : 10009Timer : 2, programmed : 15000, actual : 14993Timer : 3, programmed : 20000, actual : 19997Timer : 4, programmed : 25000, actual : 25002Timer : 5, programmed : 30000, actual : 29995Timer : 6, programmed : 35000, actual : 35000Timer : 7, programmed : 40000, actual : 40003Timer : 8, programmed : 45000, actual : 45007Timer : 9, programmed : 50000, actual : 50002Timer : 10, programmed : 55000, actual : 55006Timer : 11, programmed : 60000, actual : 60000Timer : 12, programmed : 65000, actual : 65004Timer : 13, programmed : 70000, actual : 70009Timer : 14, programmed : 75000, actual : 0Timer : 15, programmed : 80000, actual : 0SimpleTimer : 2, ms : 81132, Dms : 10003Timer : 0, programmed : 5000, actual : 5005Timer : 1, programmed : 10000, actual : 9999Timer : 2, programmed : 15000, actual : 15003Timer : 3, programmed : 20000, actual : 20008Timer : 4, programmed : 25000, actual : 25001Timer : 5, programmed : 30000, actual : 29995Timer : 6, programmed : 35000, actual : 35000Timer : 7, programmed : 40000, actual : 40005Timer : 8, programmed : 45000, actual : 45007Timer : 9, programmed : 50000, actual : 50002Timer : 10, programmed : 55000, actual : 55006Timer : 11, programmed : 60000, actual : 60000Timer : 12, programmed : 65000, actual : 65004Timer : 13, programmed : 70000, actual : 70009Timer : 14, programmed : 75000, actual : 75003Timer : 15, programmed : 80000, actual : 80008
The following is the sample terminal output when running exampleTimerInterruptTest onPortenta_H7 to demonstrate how to start/stop Hardware Timers.
Starting TimerInterruptTest on PORTENTA_H7_M7Portenta_H7_TimerInterrupt v1.4.0[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =1.00, _count = 1000000Starting ITimer0 OK, millis() = 980[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =0.33, _count = 3000000Starting ITimer1 OK, millis() = 980Stop ITimer0, millis() = 10001Start ITimer0, millis() = 20002Stop ITimer1, millis() = 30001Stop ITimer0, millis() = 30003Start ITimer0, millis() = 40004
The following is the sample terminal output when running exampleArgument_None onPortenta_H7 to demonstrate how to use Multiple Hardware Timers.
Starting Argument_None on PORTENTA_H7_M7Portenta_H7_TimerInterrupt v1.4.0[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =1.00, _count = 1000000Starting ITimer0 OK, millis() = 1259[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =0.50, _count = 2000000Starting ITimer1 OK, millis() = 1259[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =0.20, _count = 5000000Starting ITimer2 OK, millis() = 1260
The following is the sample terminal output when running exampleChange_Interval onPortenta_H7 to demonstrate how to change Timer Interval on-the-fly
Starting Change_Interval on PORTENTA_H7_M7Portenta_H7_TimerInterrupt v1.4.0[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =2.00, _count = 500000Starting Timer0 OK, millis() = 1011[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =1.00, _count = 1000000Starting ITimer1 OK, millis() = 1011Time = 10001, Timer0Count = 18, Timer1Count = 9Time = 20002, Timer0Count = 38, Timer1Count = 19[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =1.00, _count = 1000000[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =0.50, _count = 2000000Changing Interval, Timer0 = 1000, Timer1 = 2000Time = 30003, Timer0Count = 48, Timer1Count = 24Time = 40004, Timer0Count = 58, Timer1Count = 29[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =2.00, _count = 500000[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =1.00, _count = 1000000Changing Interval, Timer0 = 500, Timer1 = 1000Time = 50005, Timer0Count = 76, Timer1Count = 37Time = 60006, Timer0Count = 96, Timer1Count = 47[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =1.00, _count = 1000000[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =0.50, _count = 2000000Changing Interval, Timer0 = 1000, Timer1 = 2000
The following is the sample terminal output when running new exampleISR_16_Timers_Array onPortenta_H7 to demonstrate the accuracy of ISR Hardware Timer,especially when system is very busy or blocked. The 16 independent ISR timers areprogrammed to be activated repetitively after certain intervals, is activated exactly after that programmed interval !!!
While software-basedSimpleTimer
,programmed for 2s, is activated after 10.000s in loop()!!!.
Starting ISR_16_Timers_Array on PORTENTA_H7_M7Portenta_H7_TimerInterrupt v1.4.0[TISR] Timer Input Freq (Hz) = 200000000[TISR] Frequency =10000.00, _count = 100Starting ITimer OK, millis() = 1009simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10000Timer : 0, programmed : 1000, actual : 1000Timer : 1, programmed : 2000, actual : 2000Timer : 2, programmed : 3000, actual : 3000Timer : 3, programmed : 4000, actual : 4000Timer : 4, programmed : 5000, actual : 5000Timer : 5, programmed : 6000, actual : 6000Timer : 6, programmed : 7000, actual : 7000Timer : 7, programmed : 8000, actual : 8000Timer : 8, programmed : 9000, actual : 9000Timer : 9, programmed : 10000, actual : 10000Timer : 10, programmed : 11000, actual : 0Timer : 11, programmed : 12000, actual : 0Timer : 12, programmed : 13000, actual : 0Timer : 13, programmed : 14000, actual : 0Timer : 14, programmed : 15000, actual : 0Timer : 15, programmed : 16000, actual : 0simpleTimerDoingSomething2s: Delta programmed ms = 2000, actual = 10001Timer : 0, programmed : 1000, actual : 1000Timer : 1, programmed : 2000, actual : 2000Timer : 2, programmed : 3000, actual : 3000Timer : 3, programmed : 4000, actual : 4000Timer : 4, programmed : 5000, actual : 5000Timer : 5, programmed : 6000, actual : 6000Timer : 6, programmed : 7000, actual : 7000Timer : 7, programmed : 8000, actual : 8000Timer : 8, programmed : 9000, actual : 9000Timer : 9, programmed : 10000, actual : 10000Timer : 10, programmed : 11000, actual : 11000Timer : 11, programmed : 12000, actual : 12000Timer : 12, programmed : 13000, actual : 13000Timer : 13, programmed : 14000, actual : 14000Timer : 14, programmed : 15000, actual : 15000Timer : 15, programmed : 16000, actual : 16000
The following is the sample terminal output when running new examplePWM_Multi_Args onPortenta_H7 to demonstrate the usage of multichannel PWM using multiple Hardware Timers. The 6 independent Hardware Timers are usedto control 6 different PWM outputs, with totally independent frequencies and dutycycles.
Starting PWM_Multi_Args on PORTENTA_H7_M7Portenta_H7_TimerInterrupt v1.4.0Index = 0, Instance = 0x40010000, channel = 3, TimerIndex = 0Index = 1, Instance = 0x40000800, channel = 1, TimerIndex = 3Index = 2, Instance = 0x40001400, channel = 2, TimerIndex = 6Index = 3, Instance = 0x40010400, channel = 2, TimerIndex = 7Index = 4, Instance = 0x40001800, channel = 1, TimerIndex = 8Index = 5, Instance = 0x40001C00, channel = 3, TimerIndex = 9Index = 6, Instance = 0x40002000, channel = 1, TimerIndex = 10==========================================================================================================Count 0Count 1Count 2Count 3Count 4Count 5Count 6==========================================================================================================1020501001994979942040100199398994198830601502995971491298240801993987961988397650100249498995248649716012029959711932983596570140348696139234806959801603987961591397779539017944889517904474894710019949899519894971994111021954710942188546810935120239597119323865965119301302596471293258564621292414027969613922784695913918150299746149229837456149121603197961591318279531590617033984516903380845016900
Debug is enabled by default on Serial.
You can also change the debugging level (TIMERINTERRUPT_LOGLEVEL) from 0 to 4
// These define's must be placed at the beginning before #include "Portenta_H7_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_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:Portenta_H7_TimerInterrupt issues
- Search for bug and improvement.
- Similar features for remaining Arduino boards such as SAM-DUE
- Basic hardware timers forPortenta_H7.
- More hardware-initiated software-enabled timers
- Longer time interval
- Add Table of Contents
- Fix
multiple-definitions
linker error - Optimize library code by using
reference-passing
instead ofvalue-passing
Many thanks for everyone for bug reporting, new feature suggesting, testing and contributing to the development of this library.
If you want to contribute to this project:
- Report bugs and errors
- Ask for enhancements
- Create issues and pull requests
- Tell other people about this library
- The library is licensed underMIT
Copyright (c) 2021- Khoi Hoang
About
This library enables you to use Interrupt from Hardware Timers on an STM32H7-based Portenta_H7 board. 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 func…
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.