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

Commit 35c87e9

Browse files
authored
v1.2.0 to work with ESP32 core v2.0.1+
### Releases v1.2.0 1. Fix breaking issue caused by **ESP32 core v2.0.1+** by increasing `TIMER_INTERVAL_MICRO` to `12uS` from `10uS`. Tested OK with ESP32 core v2.0.3 now
1 parent c33122c commit 35c87e9

File tree

1 file changed

+349
-0
lines changed

1 file changed

+349
-0
lines changed

src/ESP32_New_FastTimerInterrupt.h

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
/****************************************************************************************************************************
2+
ESP32_New_FastTimerInterrupt.h
3+
For ESP32, ESP32_S2, ESP32_S3, ESP32_C3 boards with ESP32 core v2.0.0+
4+
Written by Khoi Hoang
5+
6+
Built by Khoi Hoang https://github.com/khoih-prog/ESP32_New_ISR_Servo
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 these new 16 ISR-based PWM servo contro uses only 1 hardware timer.
19+
The accuracy is nearly perfect compared to software timers. The most important feature is they're ISR-based timers
20+
Therefore, their executions are not blocked by bad-behaving functions / tasks.
21+
This important feature is absolutely necessary for mission-critical tasks.
22+
23+
Based on SimpleTimer - A timer library for Arduino.
24+
25+
Copyright (c) 2010 OTTOTECNICA Italy
26+
27+
Based on BlynkTimer.h
28+
Author: Volodymyr Shymanskyy
29+
30+
Version: 1.2.0
31+
32+
Version Modified By Date Comments
33+
------- ----------- ---------- -----------
34+
1.0.0 K Hoang 15/08/2021 Initial coding for ESP32, ESP32_S2, ESP32_C3 boards with ESP32 core v2.0.0-rc1+
35+
1.1.0 K Hoang 12/02/2022 Add support to new ESP32-S3. Convert to h-only library. Optimize code
36+
1.2.0 K Hoang 08/05/2022 Fix issue with core v2.0.1+
37+
*****************************************************************************************************************************/
38+
39+
#pragma once
40+
41+
#ifndef ESP32_New_FastTimerInterrupt_h
42+
#define ESP32_New_FastTimerInterrupt_h
43+
44+
#if ( ARDUINO_ESP32S2_DEV || ARDUINO_FEATHERS2 || ARDUINO_ESP32S2_THING_PLUS || ARDUINO_MICROS2 || \
45+
ARDUINO_METRO_ESP32S2 || ARDUINO_MAGTAG29_ESP32S2 || ARDUINO_FUNHOUSE_ESP32S2 || \
46+
ARDUINO_ADAFRUIT_FEATHER_ESP32S2_NOPSRAM )
47+
#define USING_ESP32_S2_NEW_ISR_SERVO true
48+
#elif ( defined(ARDUINO_ESP32S3_DEV) || defined(ARDUINO_ESP32_S3_BOX) || defined(ARDUINO_TINYS3) || \
49+
defined(ARDUINO_PROS3) || defined(ARDUINO_FEATHERS3) )
50+
#define USING_ESP32_S3_NEW_ISR_SERVO true
51+
#elif ( ARDUINO_ESP32C3_DEV )
52+
#define USING_ESP32_C3_NEW_ISR_SERVO true
53+
#elif defined(ESP32)
54+
#define USING_ESP32_NEW_ISR_SERVO true
55+
#else
56+
#error This code is intended to run on the ESP32 platform! Please check your Tools->Board setting.
57+
#endif
58+
59+
#include "ESP32_New_ISR_Servo_Debug.h"
60+
61+
#include <driver/timer.h>
62+
63+
/*
64+
//ESP32 core v1.0.6, hw_timer_t defined in esp32/tools/sdk/include/driver/driver/timer.h:
65+
66+
#define TIMER_BASE_CLK (APB_CLK_FREQ) //Frequency of the clock on the input of the timer groups
67+
68+
69+
//@brief Selects a Timer-Group out of 2 available groups
70+
71+
typedef enum
72+
{
73+
TIMER_GROUP_0 = 0, /*!<Hw timer group 0
74+
TIMER_GROUP_1 = 1, /*!<Hw timer group 1
75+
TIMER_GROUP_MAX,
76+
} timer_group_t;
77+
78+
79+
//@brief Select a hardware timer from timer groups
80+
81+
typedef enum
82+
{
83+
TIMER_0 = 0, /*!<Select timer0 of GROUPx
84+
TIMER_1 = 1, /*!<Select timer1 of GROUPx
85+
TIMER_MAX,
86+
} timer_idx_t;
87+
88+
89+
//@brief Decides the direction of counter
90+
91+
typedef enum
92+
{
93+
TIMER_COUNT_DOWN = 0, //Descending Count from cnt.high|cnt.low
94+
TIMER_COUNT_UP = 1, //Ascending Count from Zero
95+
TIMER_COUNT_MAX
96+
} timer_count_dir_t;
97+
98+
99+
//@brief Decides whether timer is on or paused
100+
101+
typedef enum
102+
{
103+
TIMER_PAUSE = 0, //Pause timer counter
104+
TIMER_START = 1, //Start timer counter
105+
} timer_start_t;
106+
107+
108+
//@brief Decides whether to enable alarm mode
109+
110+
typedef enum
111+
{
112+
TIMER_ALARM_DIS = 0, //Disable timer alarm
113+
TIMER_ALARM_EN = 1, //Enable timer alarm
114+
TIMER_ALARM_MAX
115+
} timer_alarm_t;
116+
117+
118+
//@brief Select interrupt type if running in alarm mode.
119+
120+
typedef enum
121+
{
122+
TIMER_INTR_LEVEL = 0, //Interrupt mode: level mode
123+
//TIMER_INTR_EDGE = 1, //Interrupt mode: edge mode, Not supported Now
124+
TIMER_INTR_MAX
125+
} timer_intr_mode_t;
126+
127+
128+
//@brief Select if Alarm needs to be loaded by software or automatically reload by hardware.
129+
130+
typedef enum
131+
{
132+
TIMER_AUTORELOAD_DIS = 0, //Disable auto-reload: hardware will not load counter value after an alarm event
133+
TIMER_AUTORELOAD_EN = 1, //Enable auto-reload: hardware will load counter value after an alarm event
134+
TIMER_AUTORELOAD_MAX,
135+
} timer_autoreload_t;
136+
137+
138+
//@brief Data structure with timer's configuration settings
139+
140+
typedef struct
141+
{
142+
bool alarm_en; //Timer alarm enable
143+
bool counter_en; //Counter enable
144+
timer_intr_mode_t intr_type; //Interrupt mode
145+
timer_count_dir_t counter_dir; //Counter direction
146+
bool auto_reload; //Timer auto-reload
147+
uint32_t divider; //Counter clock divider. The divider's range is from from 2 to 65536.
148+
} timer_config_t;
149+
150+
*/
151+
152+
#ifndef USE_ESP32_TIMER_NO
153+
#define USE_ESP32_TIMER_NO 1
154+
#endif
155+
156+
#if USING_ESP32_C3_NEW_ISR_SERVO
157+
#ifndef USE_ESP32_TIMER_NO
158+
#define USE_ESP32_TIMER_NO 1
159+
#endif
160+
161+
#define MAX_ESP32_NUM_TIMERS 2
162+
#else
163+
#ifndef USE_ESP32_TIMER_NO
164+
#define USE_ESP32_TIMER_NO 3
165+
#endif
166+
167+
#define MAX_ESP32_NUM_TIMERS 4
168+
#endif
169+
170+
class ESP32FastTimerInterrupt;
171+
172+
typedef ESP32FastTimerInterrupt ESP32FastTimer;
173+
174+
#define TIMER_DIVIDER 80 // Hardware timer clock divider
175+
// TIMER_BASE_CLK = APB_CLK_FREQ = Frequency of the clock on the input of the timer groups
176+
#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds
177+
178+
179+
// In esp32/1.0.6/tools/sdk/esp32s2/include/driver/include/driver/timer.h
180+
// typedef bool (*timer_isr_t)(void *);
181+
//esp_err_t timer_isr_callback_add(timer_group_t group_num, timer_idx_t timer_num, timer_isr_t isr_handler, void *arg, int intr_alloc_flags);
182+
//esp_err_t timer_isr_callback_remove(timer_group_t group_num, timer_idx_t timer_num);
183+
//timer_deinit(timer_group_t group_num, timer_idx_t timer_num);
184+
//esp_err_t timer_group_intr_enable(timer_group_t group_num, timer_intr_t intr_mask);
185+
//esp_err_t timer_group_intr_disable(timer_group_t group_num, timer_intr_t intr_mask);
186+
187+
188+
typedef bool (*esp32_timer_callback) (void *);
189+
190+
class ESP32FastTimerInterrupt
191+
{
192+
private:
193+
194+
timer_config_t stdConfig =
195+
{
196+
.alarm_en = TIMER_ALARM_EN, //enable timer alarm
197+
.counter_en = TIMER_START, //starts counting counter once timer_init called
198+
.intr_type = TIMER_INTR_MAX,
199+
.counter_dir = TIMER_COUNT_UP, //counts from 0 to counter value
200+
.auto_reload = TIMER_AUTORELOAD_EN, // reloads counter automatically
201+
.divider = TIMER_DIVIDER
202+
};
203+
204+
timer_idx_t _timerIndex;
205+
timer_group_t _timerGroup;
206+
uint32_t interruptFlag; // either TIMER_INTR_T0 or TIMER_INTR_T1
207+
208+
uint8_t _timerNo;
209+
210+
esp32_timer_callback _callback; // pointer to the callback function
211+
float _frequency; // Timer frequency
212+
uint64_t _timerCount; // count to activate timer
213+
214+
public:
215+
216+
ESP32FastTimerInterrupt(uint8_t timerNo)
217+
{
218+
if (timerNo < MAX_ESP32_NUM_TIMERS)
219+
{
220+
_timerNo = timerNo;
221+
222+
#if USING_ESP32_C3_NEW_ISR_SERVO
223+
224+
// Always using TIMER_INTR_T0
225+
_timerIndex = (timer_idx_t) ( (uint32_t) 0 );
226+
227+
// timerNo == 0 => Group 0, timerNo == 1 => Group 1
228+
_timerGroup = (timer_group_t) ( (uint32_t) timerNo);
229+
230+
#else
231+
232+
_timerIndex = (timer_idx_t) (_timerNo % TIMER_MAX);
233+
234+
_timerGroup = (timer_group_t) (_timerNo / TIMER_MAX);
235+
236+
#endif
237+
}
238+
else
239+
{
240+
_timerNo = MAX_ESP32_NUM_TIMERS;
241+
}
242+
243+
_frequency = 0;
244+
_timerCount = 0;
245+
_callback = NULL;
246+
};
247+
248+
// frequency (in hertz)
249+
// No params and duration now. To be added in the future by adding similar functions here or to esp32-hal-timer.c
250+
bool setFrequency(const float& frequency, esp32_timer_callback callback)
251+
{
252+
if (_timerNo < MAX_ESP32_NUM_TIMERS)
253+
{
254+
// select timer frequency is 1MHz for better accuracy. We don't use 16-bit prescaler for now.
255+
// Will use later if very low frequency is needed.
256+
_frequency = TIMER_BASE_CLK / TIMER_DIVIDER; //1000000;
257+
_timerCount = (uint64_t) _frequency / frequency;
258+
// count up
259+
260+
#if USING_ESP32_C3_NEW_ISR_SERVO
261+
ISR_SERVO_LOGERROR3(F("ESP32_C3_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
262+
ISR_SERVO_LOGERROR3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
263+
#elif USING_ESP32_S2_NEW_ISR_SERVO
264+
ISR_SERVO_LOGERROR3(F("ESP32_S2_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
265+
ISR_SERVO_LOGERROR3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
266+
ISR_SERVO_LOGERROR3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
267+
ISR_SERVO_LOGERROR3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
268+
ISR_SERVO_LOGERROR1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
269+
#elif USING_ESP32_S3_NEW_ISR_SERVO
270+
// ESP32-S3 is embedded with four 54-bit general-purpose timers, which are based on 16-bit prescalers
271+
// and 54-bit auto-reload-capable up/down-timers
272+
ISR_SERVO_LOGERROR3(F("ESP32_S3_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
273+
ISR_SERVO_LOGERROR3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
274+
ISR_SERVO_LOGERROR3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
275+
ISR_SERVO_LOGERROR3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
276+
ISR_SERVO_LOGERROR1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
277+
#else
278+
ISR_SERVO_LOGERROR3(F("ESP32_TimerInterrupt: _timerNo ="), _timerNo, F(", _fre ="), TIMER_BASE_CLK / TIMER_DIVIDER);
279+
ISR_SERVO_LOGERROR3(F("TIMER_BASE_CLK ="), TIMER_BASE_CLK, F(", TIMER_DIVIDER ="), TIMER_DIVIDER);
280+
ISR_SERVO_LOGERROR3(F("_timerIndex ="), _timerIndex, F(", _timerGroup ="), _timerGroup);
281+
ISR_SERVO_LOGERROR3(F("_count ="), (uint32_t) (_timerCount >> 32) , F("-"), (uint32_t) (_timerCount));
282+
ISR_SERVO_LOGERROR1(F("timer_set_alarm_value ="), TIMER_SCALE / frequency);
283+
#endif
284+
285+
timer_init(_timerGroup, _timerIndex, &stdConfig);
286+
287+
// Counter value to 0 => counting up to alarm value as .counter_dir == TIMER_COUNT_UP
288+
timer_set_counter_value(_timerGroup, _timerIndex , 0x00000000ULL);
289+
290+
timer_set_alarm_value(_timerGroup, _timerIndex, TIMER_SCALE / frequency);
291+
292+
// enable interrupts for _timerGroup, _timerIndex
293+
timer_enable_intr(_timerGroup, _timerIndex);
294+
295+
_callback = callback;
296+
297+
// Register the ISR handler
298+
// If the intr_alloc_flags value ESP_INTR_FLAG_IRAM is set, the handler function must be declared with IRAM_ATTR attribute
299+
// and can only call functions in IRAM or ROM. It cannot call other timer APIs.
300+
timer_isr_callback_add(_timerGroup, _timerIndex, _callback, (void *) (uint32_t) _timerNo, 0);
301+
302+
timer_start(_timerGroup, _timerIndex);
303+
304+
return true;
305+
}
306+
else
307+
{
308+
#if USING_ESP32_C3_NEW_ISR_SERVO
309+
ISR_SERVO_LOGERROR(F("Error. Timer must be 0-1"));
310+
#else
311+
ISR_SERVO_LOGERROR(F("Error. Timer must be 0-3"));
312+
#endif
313+
314+
return false;
315+
}
316+
}
317+
318+
// interval (in microseconds) and duration (in milliseconds). Duration = 0 or not specified => run indefinitely
319+
// No params and duration now. To be addes in the future by adding similar functions here or to esp32-hal-timer.c
320+
bool attachInterruptInterval(const unsigned long& interval, esp32_timer_callback callback)
321+
{
322+
return setFrequency( (float) ( 1000000.0f / interval), callback);
323+
}
324+
325+
void detachInterrupt()
326+
{
327+
#if USING_ESP32_C3_NEW_ISR_SERVO
328+
timer_group_intr_disable(_timerGroup, TIMER_INTR_T0);
329+
#else
330+
timer_group_intr_disable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
331+
#endif
332+
}
333+
334+
// Duration (in milliseconds). Duration = 0 or not specified => run indefinitely
335+
void reattachInterrupt()
336+
{
337+
if ( (_frequency != 0) && (_timerCount != 0) && (_callback != NULL) )
338+
{
339+
#if USING_ESP32_C3_NEW_ISR_SERVO
340+
timer_group_intr_enable(_timerGroup, TIMER_INTR_T0);
341+
#else
342+
timer_group_intr_enable(_timerGroup, (_timerIndex == 0) ? TIMER_INTR_T0 : TIMER_INTR_T1);
343+
#endif
344+
}
345+
}
346+
}; // class ESP32FastTimerInterrupt
347+
348+
349+
#endif // ESP32_S2_FastTimerInterrupt_h

0 commit comments

Comments
 (0)