arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
AudioTimerESP32.h
1#pragma once
2
3#if defined(ESP32) && defined(ARDUINO)
4#include <esp_task_wdt.h>
5
6#include "AudioTools/CoreAudio/AudioTimer/AudioTimerBase.h"
7
8namespace audio_tools {
9
16class UserCallback {
17 public:
18 void setup(repeating_timer_callback_t my_callback, void *user_data,
19 bool lock) {
20 TRACED();
21 this->my_callback = my_callback;
22 this->user_data = user_data;
23 this->lock = lock; // false when called from Task
24 }
25
26 IRAM_ATTR void call() {
27 if (my_callback != nullptr) {
28 if (lock) portENTER_CRITICAL_ISR(&timerMux);
29 my_callback(user_data);
30 if (lock) portEXIT_CRITICAL_ISR(&timerMux);
31 }
32 }
33
34 protected:
35 repeating_timer_callback_t my_callback = nullptr;
36 void *user_data = nullptr;
37 portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
38 bool lock;
39
40} static *simpleUserCallback = nullptr;
41
42static IRAM_ATTR void userCallback0() { simpleUserCallback[0].call(); }
43static IRAM_ATTR void userCallback1() { simpleUserCallback[1].call(); }
44static IRAM_ATTR void userCallback2() { simpleUserCallback[2].call(); }
45static IRAM_ATTR void userCallback3() { simpleUserCallback[3].call(); }
46
53class TimerCallback {
54 public:
55 TimerCallback() {
56 TRACED();
57 timerMux = portMUX_INITIALIZER_UNLOCKED;
58 p_handler_task = nullptr;
59 }
60
61 void setup(TaskHandle_t handler_task) {
62 TRACED();
63 p_handler_task = handler_task;
64 assert(handler_task != nullptr);
65 }
66
67 IRAM_ATTR void call() {
68 if (p_handler_task != nullptr) {
69 // A mutex protects the handler from reentry (which shouldn't happen, but
70 // just in case)
71 portENTER_CRITICAL_ISR(&timerMux);
72 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
73 vTaskNotifyGiveFromISR(p_handler_task, &xHigherPriorityTaskWoken);
74 if (xHigherPriorityTaskWoken) {
75 portYIELD_FROM_ISR();
76 }
77 portEXIT_CRITICAL_ISR(&timerMux);
78 }
79 }
80
81 protected:
82 portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
83 TaskHandle_t p_handler_task = nullptr;
84
85} static *timerCallbackArray = nullptr;
86
87static IRAM_ATTR void timerCallback0() { timerCallbackArray[0].call(); }
88static IRAM_ATTR void timerCallback1() { timerCallbackArray[1].call(); }
89static IRAM_ATTR void timerCallback2() { timerCallbackArray[2].call(); }
90static IRAM_ATTR void timerCallback3() { timerCallbackArray[3].call(); }
91
102class TimerAlarmRepeatingDriverESP32 : public TimerAlarmRepeatingDriverBase {
103 public:
104 TimerAlarmRepeatingDriverESP32() {
105 setTimerFunction(DirectTimerCallback);
106 setTimer(0);
107 }
108
109 TimerAlarmRepeatingDriverESP32(int timer, TimerFunction function) {
110 setTimerFunction(function);
111 setTimer(timer);
112 }
113
114 void setTimer(int id) override {
115 if (id >= 0 && id < 4) {
116 this->timer_id = id;
117 handler_task = nullptr;
118 } else {
119 LOGE("Invalid timer id %d", timer_id);
120 }
121 }
122
123 void setTimerFunction(TimerFunction function = DirectTimerCallback) override {
124 this->function = function;
125 }
126
128 bool begin(repeating_timer_callback_t callback_f, uint32_t time,
129 TimeUnit unit = MS) override {
130 TRACED();
131
132 // we determine the time in microseconds
133 switch (unit) {
134 case MS:
135 timeUs = time * 1000;
136 break;
137 case US:
138 timeUs = time;
139 break;
140 case HZ:
141 // convert hz to time in us
142 timeUs = AudioTime::toTimeUs(time);
143 break;
144 }
145#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)
146 LOGI("Timer every: %u us", timeUs);
147 adc_timer = timerBegin(timer_id, 80,
148 true); // divider=80 -> 1000000 calls per second
149#else
150 uint32_t freq = AudioTime::AudioTime::toRateUs(timeUs);
151 LOGI("Timer freq: %u hz", (unsigned)freq);
152 adc_timer = timerBegin(1000000); // divider=80 -> 1000000 calls per second
153#endif
154 switch (function) {
155 case DirectTimerCallback:
156 setupDirectTimerCallback(callback_f);
157 break;
158
159 case TimerCallbackInThread:
160 setupTimerCallbackInThread(callback_f);
161 break;
162
163 case SimpleThreadLoop:
164 setupSimpleThreadLoop(callback_f);
165 break;
166 }
167
168 started = true;
169 return true;
170 }
171
173 bool end() override {
174 TRACED();
175 if (started) {
176 timerDetachInterrupt(adc_timer);
177 timerEnd(adc_timer);
178 if (handler_task != nullptr) {
179 vTaskDelete(handler_task);
180 handler_task = nullptr;
181 }
182 }
183 started = false;
184 return true;
185 }
186
187 void setCore(int core) { this->core = core; }
188
191 void setIsSave(bool is_save) override {
192 setTimerFunction(is_save ? TimerCallbackInThread : DirectTimerCallback);
193 }
194
195 protected:
196 int timer_id = 0;
197 volatile bool started = false;
198 TaskHandle_t handler_task = nullptr;
199 hw_timer_t *adc_timer = nullptr; // our timer
200 UserCallback user_callback; // for task
201 TimerFunction function;
202 int core = 1;
203 int priority = 1; // configMAX_PRIORITIES - 1;
204 uint32_t timeUs;
205
207 void attach(hw_timer_t *timer, void (*cb)()) {
208#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
209 timerAttachInterrupt(timer, cb);
210#else
211 timerAttachInterrupt(timer, cb, false);
212#endif
213 }
214
216 void setupDirectTimerCallback(repeating_timer_callback_t callback_f) {
217 TRACED();
218 // We start the timer which executes the callbacks directly
219 if (simpleUserCallback == nullptr) {
220 simpleUserCallback = new UserCallback[4];
221 }
222 simpleUserCallback[timer_id].setup(callback_f, object, true);
223
224 if (timer_id == 0)
225 attach(adc_timer, userCallback0);
226 else if (timer_id == 1)
227 attach(adc_timer, userCallback1);
228 else if (timer_id == 2)
229 attach(adc_timer, userCallback2);
230 else if (timer_id == 3)
231 attach(adc_timer, userCallback3);
232
233#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)
234 timerAlarmWrite(adc_timer, timeUs, true);
235 timerAlarmEnable(adc_timer);
236
237#else
238 // timerStart(adc_timer);
239 // timerWrite(adc_timer,timeUs);
240 timerAlarm(adc_timer, timeUs, true, 0);
241#endif
242 }
243
246 void setupTimerCallbackInThread(repeating_timer_callback_t callback_f) {
247 TRACED();
248 // we start the timer which runs the callback in a seprate task
249 if (timerCallbackArray == nullptr) {
250 timerCallbackArray = new TimerCallback[4];
251 }
252
253 if (timer_id == 0)
254 attach(adc_timer, timerCallback0);
255 else if (timer_id == 1)
256 attach(adc_timer, timerCallback1);
257 else if (timer_id == 2)
258 attach(adc_timer, timerCallback2);
259 else if (timer_id == 3)
260 attach(adc_timer, timerCallback3);
261
262 // we record the callback method and user data
263 user_callback.setup(callback_f, object, false);
264 // setup the timercallback
265 xTaskCreatePinnedToCore(complexTaskHandler, "TimerAlarmRepeatingTask",
266 configMINIMAL_STACK_SIZE + 10000, &user_callback,
267 priority, &handler_task, core);
268 LOGI("Task created on core %d", core);
269 timerCallbackArray[timer_id].setup(handler_task);
270
271#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 1, 0)
272 timerAlarmWrite(adc_timer, timeUs, true);
273 timerAlarmEnable(adc_timer);
274#else
275 timerAlarm(adc_timer, timeUs, true, 0); // set time in us
276#endif
277 }
278
280 void setupSimpleThreadLoop(repeating_timer_callback_t callback_f) {
281 TRACED();
282 user_callback.setup(callback_f, object, false);
283 xTaskCreatePinnedToCore(simpleTaskLoop, "TimerAlarmRepeatingTask",
284 configMINIMAL_STACK_SIZE + 10000, this, priority,
285 &handler_task, core);
286 LOGI("Task created on core %d", core);
287 }
288
291 static void complexTaskHandler(void *param) {
292 TRACEI();
293 UserCallback *cb = (UserCallback *)param;
294
295 while (true) {
296 // Sleep until the ISR gives us something to do
297 uint32_t thread_notification =
298 ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000));
299 // Do something complex and CPU-intensive
300 if (thread_notification) {
301 cb->call();
302 }
303 delay(0);
304 }
305 }
306
309 static void simpleTaskLoop(void *param) {
310 TRACEI();
311 TimerAlarmRepeatingDriverESP32 *ta =
312 (TimerAlarmRepeatingDriverESP32 *)param;
313
314 while (true) {
315 unsigned long end = micros() + ta->timeUs;
316 ta->user_callback.call();
317 long waitUs = end - micros();
318 long waitMs = waitUs / 1000;
319 waitUs = waitUs - (waitMs * 1000);
320 delay(waitMs);
321 waitUs = end - micros();
322 if (waitUs > 0) {
323 delayMicroseconds(waitUs);
324 }
325 }
326 }
327};
328
330using TimerAlarmRepeatingDriver = TimerAlarmRepeatingDriverESP32;
331
332} // namespace audio_tools
333
334#endif
static uint32_t toTimeUs(uint32_t samplingRate, uint8_t limit=10)
converts sampling rate to delay in microseconds (μs)
Definition AudioTypes.h:240
TimeUnit
Time Units.
Definition AudioTypes.h:46
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
TimerAlarmRepeatingDriverAVR TimerAlarmRepeatingDriver
use TimerAlarmRepeating!
Definition AudioTimerAVR.h:94