Skip to content
This repository was archived by the owner on Jan 29, 2023. It is now read-only.

Commit bd85c79

Browse files
authored
v2.0.1 to add one-shot examples, etc.
### Releases v2.0.1 1. Add example [ISR_16_Timers_Array_Complex_OneShot](examples/ISR_16_Timers_Array_Complex_OneShot) to demo how to use `one-shot ISR-based timer` in complex case 2. Add example [ISR_16_Timers_Array_OneShot](examples/ISR_16_Timers_Array_OneShot) to demo how to use `one-shot ISR-based timer` 3. Modify example [multiFileProject](examples/multiFileProject) to demo for more complex multiple-file project 4. Further optimize code by using passing by `reference` instead of by `value`
1 parent 24e320f commit bd85c79

File tree

6 files changed

+743
-16
lines changed

6 files changed

+743
-16
lines changed

examples/ISR_16_Timers_Array_Complex/ISR_16_Timers_Array_Complex.ino

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ typedef struct
122122
unsigned long previousMillis;
123123
} ISRTimerData;
124124

125-
// In NRF52, avoid doing something fancy in ISR, for example Serial.print()
125+
// In ESP32, avoid doing something fancy in ISR, for example Serial.print()
126126
// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment
127127
// Or you can get this run-time error / crash
128128

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
/****************************************************************************************************************************
2+
ISR_16_Timers_Array_Complex_OneShot.ino
3+
For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.2+
4+
Written by Khoi Hoang
5+
6+
Built by Khoi Hoang https://github.com/khoih-prog/ESP32TimerInterrupt
7+
Licensed under MIT license
8+
9+
The ESP32, ESP32_S2, ESP32_S3, ESP32_C3 have two timer groups, TIMER_GROUP_0 and TIMER_GROUP_1
10+
1) each group of ESP32, ESP32_S2, ESP32_S3 has two general purpose hardware timers, TIMER_0 and TIMER_1
11+
2) each group of ESP32_C3 has ony one general purpose hardware timer, TIMER_0
12+
13+
All the timers are based on 64-bit counters (except 54-bit counter for ESP32_S3 counter) and 16 bit prescalers.
14+
The timer counters can be configured to count up or down and support automatic reload and software reload.
15+
They can also generate alarms when they reach a specific value, defined by the software.
16+
The value of the counter can be read by the software program.
17+
18+
Now even you use all these new 16 ISR-based timers,with their maximum interval practically unlimited (limited only by
19+
unsigned long miliseconds), you just consume only one ESP32-S2 timer and avoid conflicting with other cores' tasks.
20+
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
21+
Therefore, their executions are not blocked by bad-behaving functions / tasks.
22+
This important feature is absolutely necessary for mission-critical tasks.
23+
*****************************************************************************************************************************/
24+
/*
25+
Notes:
26+
Special design is necessary to share data between interrupt code and the rest of your program.
27+
Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume
28+
variable can not spontaneously change. Because your function may change variables while your program is using them,
29+
the compiler needs this hint. But volatile alone is often not enough.
30+
When accessing shared variables, usually interrupts must be disabled. Even with volatile,
31+
if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly.
32+
If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled
33+
or the entire sequence of your code which accesses the data.
34+
35+
This example will demonstrate the nearly perfect accuracy compared to software timers by printing the actual elapsed millisecs.
36+
Being ISR-based timers, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet
37+
and Blynk services. You can also have many (up to 16) timers to use.
38+
This non-being-blocked important feature is absolutely necessary for mission-critical tasks.
39+
You'll see blynkTimer is blocked while connecting to WiFi / Internet / Blynk, and elapsed time is very unaccurate
40+
In this super simple example, you don't see much different after Blynk is connected, because of no competing task is
41+
written
42+
*/
43+
44+
#if !defined( ESP32 )
45+
#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
46+
#endif
47+
48+
// These define's must be placed at the beginning before #include "ESP32TimerInterrupt.h"
49+
#define _TIMERINTERRUPT_LOGLEVEL_ 4
50+
51+
// To be included only in main(), .ino with setup() to avoid `Multiple Definitions` Linker Error
52+
#include "ESP32TimerInterrupt.h"
53+
54+
#include <SimpleTimer.h> // https://github.com/jfturcot/SimpleTimer
55+
56+
// Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868
57+
58+
#ifndef LED_BUILTIN
59+
#define LED_BUILTIN 2
60+
#endif
61+
62+
#ifndef LED_BLUE
63+
#define LED_BLUE 25
64+
#endif
65+
66+
#ifndef LED_RED
67+
#define LED_RED 27
68+
#endif
69+
70+
#define HW_TIMER_INTERVAL_US 10000L
71+
72+
volatile uint32_t startMillis = 0;
73+
74+
// Init ESP32 timer 1
75+
ESP32Timer ITimer(1);
76+
77+
// Init ESP32_ISR_Timer
78+
ESP32_ISR_Timer ISR_Timer;
79+
80+
#define LED_TOGGLE_INTERVAL_MS 2000L
81+
82+
// With core v2.0.0+, you can't use Serial.print/println in ISR or crash.
83+
// and you can't use float calculation inside ISR
84+
// Only OK in core v1.0.6-
85+
bool IRAM_ATTR TimerHandler(void * timerNo)
86+
{
87+
static bool toggle = false;
88+
static int timeRun = 0;
89+
90+
ISR_Timer.run();
91+
92+
// Toggle LED every LED_TOGGLE_INTERVAL_MS = 2000ms = 2s
93+
if (++timeRun == ((LED_TOGGLE_INTERVAL_MS * 1000) / HW_TIMER_INTERVAL_US) )
94+
{
95+
timeRun = 0;
96+
97+
//timer interrupt toggles pin LED_BUILTIN
98+
digitalWrite(LED_BUILTIN, toggle);
99+
toggle = !toggle;
100+
}
101+
102+
return true;
103+
}
104+
105+
/////////////////////////////////////////////////
106+
107+
#define NUMBER_ISR_TIMERS 16
108+
109+
typedef void (*irqCallback) ();
110+
111+
/////////////////////////////////////////////////
112+
113+
#define USE_COMPLEX_STRUCT true
114+
115+
#if USE_COMPLEX_STRUCT
116+
117+
typedef struct
118+
{
119+
irqCallback irqCallbackFunc;
120+
uint32_t TimerInterval;
121+
unsigned long deltaMillis;
122+
unsigned long previousMillis;
123+
} ISRTimerData;
124+
125+
// In ESP32, avoid doing something fancy in ISR, for example Serial.print()
126+
// The pure simple Serial.prints here are just for demonstration and testing. Must be eliminate in working environment
127+
// Or you can get this run-time error / crash
128+
129+
void doingSomething(int index);
130+
131+
#else
132+
133+
volatile unsigned long deltaMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
134+
volatile unsigned long previousMillis [NUMBER_ISR_TIMERS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
135+
136+
// You can assign any interval for any timer here, in milliseconds
137+
uint32_t TimerInterval[NUMBER_ISR_TIMERS] =
138+
{
139+
5000L, 10000L, 15000L, 20000L, 25000L, 30000L, 35000L, 40000L,
140+
45000L, 50000L, 55000L, 60000L, 65000L, 70000L, 75000L, 80000L
141+
};
142+
143+
void doingSomething(int index)
144+
{
145+
unsigned long currentMillis = millis();
146+
147+
deltaMillis[index] = currentMillis - previousMillis[index];
148+
previousMillis[index] = currentMillis;
149+
}
150+
151+
#endif
152+
153+
////////////////////////////////////
154+
// Shared
155+
////////////////////////////////////
156+
157+
void doingSomething0()
158+
{
159+
doingSomething(0);
160+
}
161+
162+
void doingSomething1()
163+
{
164+
doingSomething(1);
165+
}
166+
167+
void doingSomething2()
168+
{
169+
doingSomething(2);
170+
}
171+
172+
void doingSomething3()
173+
{
174+
doingSomething(3);
175+
}
176+
177+
void doingSomething4()
178+
{
179+
doingSomething(4);
180+
}
181+
182+
void doingSomething5()
183+
{
184+
doingSomething(5);
185+
}
186+
187+
void doingSomething6()
188+
{
189+
doingSomething(6);
190+
}
191+
192+
void doingSomething7()
193+
{
194+
doingSomething(7);
195+
}
196+
197+
void doingSomething8()
198+
{
199+
doingSomething(8);
200+
}
201+
202+
void doingSomething9()
203+
{
204+
doingSomething(9);
205+
}
206+
207+
void doingSomething10()
208+
{
209+
doingSomething(10);
210+
}
211+
212+
void doingSomething11()
213+
{
214+
doingSomething(11);
215+
}
216+
217+
void doingSomething12()
218+
{
219+
doingSomething(12);
220+
}
221+
222+
void doingSomething13()
223+
{
224+
doingSomething(13);
225+
}
226+
227+
void doingSomething14()
228+
{
229+
doingSomething(14);
230+
}
231+
232+
void doingSomething15()
233+
{
234+
doingSomething(15);
235+
}
236+
237+
#if USE_COMPLEX_STRUCT
238+
239+
ISRTimerData curISRTimerData[NUMBER_ISR_TIMERS] =
240+
{
241+
//irqCallbackFunc, TimerInterval, deltaMillis, previousMillis
242+
{ doingSomething0, 5000L, 0, 0 },
243+
{ doingSomething1, 10000L, 0, 0 },
244+
{ doingSomething2, 15000L, 0, 0 },
245+
{ doingSomething3, 20000L, 0, 0 },
246+
{ doingSomething4, 25000L, 0, 0 },
247+
{ doingSomething5, 30000L, 0, 0 },
248+
{ doingSomething6, 35000L, 0, 0 },
249+
{ doingSomething7, 40000L, 0, 0 },
250+
{ doingSomething8, 45000L, 0, 0 },
251+
{ doingSomething9, 50000L, 0, 0 },
252+
{ doingSomething10, 55000L, 0, 0 },
253+
{ doingSomething11, 60000L, 0, 0 },
254+
{ doingSomething12, 65000L, 0, 0 },
255+
{ doingSomething13, 70000L, 0, 0 },
256+
{ doingSomething14, 75000L, 0, 0 },
257+
{ doingSomething15, 80000L, 0, 0 }
258+
};
259+
260+
void doingSomething(int index)
261+
{
262+
unsigned long currentMillis = millis();
263+
264+
curISRTimerData[index].deltaMillis = currentMillis - curISRTimerData[index].previousMillis;
265+
curISRTimerData[index].previousMillis = currentMillis;
266+
}
267+
268+
#else
269+
270+
irqCallback irqCallbackFunc[NUMBER_ISR_TIMERS] =
271+
{
272+
doingSomething0, doingSomething1, doingSomething2, doingSomething3,
273+
doingSomething4, doingSomething5, doingSomething6, doingSomething7,
274+
doingSomething8, doingSomething9, doingSomething10, doingSomething11,
275+
doingSomething12, doingSomething13, doingSomething14, doingSomething15
276+
};
277+
278+
#endif
279+
///////////////////////////////////////////
280+
281+
#define SIMPLE_TIMER_MS 2000L
282+
283+
// Init SimpleTimer
284+
SimpleTimer simpleTimer;
285+
286+
// Here is software Timer, you can do somewhat fancy stuffs without many issues.
287+
// But always avoid
288+
// 1. Long delay() it just doing nothing and pain-without-gain wasting CPU power.Plan and design your code / strategy ahead
289+
// 2. Very long "do", "while", "for" loops without predetermined exit time.
290+
void simpleTimerDoingSomething2s()
291+
{
292+
static unsigned long previousMillis = startMillis;
293+
294+
unsigned long currMillis = millis();
295+
296+
Serial.print(F("SimpleTimer : ")); Serial.print(SIMPLE_TIMER_MS / 1000);
297+
Serial.print(F(", ms : ")); Serial.print(currMillis);
298+
Serial.print(F(", Dms : ")); Serial.println(currMillis - previousMillis);
299+
300+
for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
301+
{
302+
#if USE_COMPLEX_STRUCT
303+
Serial.print(F("Timer : ")); Serial.print(i);
304+
Serial.print(F(", programmed : ")); Serial.print(curISRTimerData[i].TimerInterval);
305+
Serial.print(F(", actual : ")); Serial.println(curISRTimerData[i].deltaMillis);
306+
// reset to 0 to be sure the timer is running one-shot or permanent
307+
curISRTimerData[i].deltaMillis = 0;
308+
#else
309+
Serial.print(F("Timer : ")); Serial.print(i);
310+
Serial.print(F(", programmed : ")); Serial.print(TimerInterval[i]);
311+
Serial.print(F(", actual : ")); Serial.println(deltaMillis[i]);
312+
// reset to 0 to be sure the timer is running one-shot or permanent
313+
deltaMillis[i] = 0;
314+
#endif
315+
}
316+
317+
previousMillis = currMillis;
318+
}
319+
320+
void setup()
321+
{
322+
pinMode(LED_BUILTIN, OUTPUT);
323+
324+
Serial.begin(115200);
325+
while (!Serial);
326+
327+
delay(200);
328+
329+
Serial.print(F("\nStarting ISR_16_Timers_Array_Complex_OneShot on ")); Serial.println(ARDUINO_BOARD);
330+
Serial.println(ESP32_TIMER_INTERRUPT_VERSION);
331+
Serial.print(F("CPU Frequency = ")); Serial.print(F_CPU / 1000000); Serial.println(F(" MHz"));
332+
333+
// Interval in microsecs
334+
if (ITimer.attachInterruptInterval(HW_TIMER_INTERVAL_US, TimerHandler))
335+
{
336+
startMillis = millis();
337+
Serial.print(F("Starting ITimer OK, millis() = ")); Serial.println(startMillis);
338+
}
339+
else
340+
Serial.println(F("Can't set ITimer. Select another freq. or timer"));
341+
342+
startMillis = millis();
343+
344+
// Just to demonstrate, don't use too many ISR Timers if not absolutely necessary
345+
// You can use up to 16 timer for each ISR_Timer
346+
for (uint16_t i = 0; i < NUMBER_ISR_TIMERS; i++)
347+
{
348+
#if USE_COMPLEX_STRUCT
349+
curISRTimerData[i].previousMillis = startMillis;
350+
// Set even timer one shot, odd timers run forever
351+
if ( i % 2 )
352+
ISR_Timer.setInterval(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc);
353+
else
354+
ISR_Timer.setTimeout(curISRTimerData[i].TimerInterval, curISRTimerData[i].irqCallbackFunc);
355+
#else
356+
previousMillis[i] = millis();
357+
358+
// Set even timer one shot, odd timers run forever
359+
if ( i % 2 )
360+
ISR_Timer.setInterval(TimerInterval[i], irqCallbackFunc[i]);
361+
else
362+
ISR_Timer.setTimeout(TimerInterval[i], irqCallbackFunc[i]);
363+
#endif
364+
}
365+
366+
// You need this timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary.
367+
simpleTimer.setInterval(SIMPLE_TIMER_MS, simpleTimerDoingSomething2s);
368+
}
369+
370+
#define BLOCKING_TIME_MS 10000L
371+
372+
void loop()
373+
{
374+
// This unadvised blocking task is used to demonstrate the blocking effects onto the execution and accuracy to Software timer
375+
// You see the time elapse of ISR_Timer still accurate, whereas very unaccurate for Software Timer
376+
// The time elapse for 2000ms software timer now becomes 3000ms (BLOCKING_TIME_MS)
377+
// While that of ISR_Timer is still prefect.
378+
delay(BLOCKING_TIME_MS);
379+
380+
// You need this Software timer for non-critical tasks. Avoid abusing ISR if not absolutely necessary
381+
// You don't need to and never call ISR_Timer.run() here in the loop(). It's already handled by ISR timer.
382+
simpleTimer.run();
383+
}

0 commit comments

Comments
 (0)