Embedded Template Library 1.0
callback_timer_interrupt.h
1/******************************************************************************
2The MIT License(MIT)
3
4Embedded Template Library.
5https://github.com/ETLCPP/etl
6https://www.etlcpp.com
7
8Copyright(c) 2022 John Wellbelove
9
10Permission is hereby granted, free of charge, to any person obtaining a copy
11of this software and associated documentation files(the "Software"), to deal
12in the Software without restriction, including without limitation the rights
13to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
14copies of the Software, and to permit persons to whom the Software is
15furnished to do so, subject to the following conditions :
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26SOFTWARE.
27******************************************************************************/
28
29#ifndef ETL_CALLBACK_TIMER_INTERRUPT_INCLUDED
30#define ETL_CALLBACK_TIMER_INTERRUPT_INCLUDED
31
32#include "platform.h"
33#include "algorithm.h"
34#include "nullptr.h"
35#include "delegate.h"
36#include "static_assert.h"
37#include "timer.h"
38#include "error_handler.h"
39#include "placement_new.h"
40
41#include <stdint.h>
42
43namespace etl
44{
45 //***************************************************************************
47 //***************************************************************************
48 template <typename TInterruptGuard>
50 {
51 public:
52
53 typedef etl::delegate<void(void)> callback_type;
54
55 //*******************************************
57 //*******************************************
58 etl::timer::id::type register_timer(const callback_type& callback_,
59 uint32_t period_,
60 bool repeating_)
61 {
62 etl::timer::id::type id = etl::timer::id::NO_TIMER;
63
64 bool is_space = (number_of_registered_timers < MAX_TIMERS);
65
66 if (is_space)
67 {
68 // Search for the free space.
69 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
70 {
71 timer_data& timer = timer_array[i];
72
73 if (timer.id == etl::timer::id::NO_TIMER)
74 {
75 TInterruptGuard guard;
76 (void)guard; // Silence 'unused variable warnings.
77
78 // Create in-place.
79 new (&timer) timer_data(i, callback_, period_, repeating_);
80 ++number_of_registered_timers;
81 id = i;
82 break;
83 }
84 }
85 }
86
87 return id;
88 }
89
90 //*******************************************
92 //*******************************************
93 bool unregister_timer(etl::timer::id::type id_)
94 {
95 bool result = false;
96
97 if (id_ != etl::timer::id::NO_TIMER)
98 {
99 timer_data& timer = timer_array[id_];
100
101 if (timer.id != etl::timer::id::NO_TIMER)
102 {
103 if (timer.is_active())
104 {
105 TInterruptGuard guard;
106 (void)guard; // Silence 'unused variable warnings.
107
108 active_list.remove(timer.id, false);
109 }
110
111 // Reset in-place.
112 new (&timer) timer_data();
113 --number_of_registered_timers;
114
115 result = true;
116 }
117 }
118
119 return result;
120 }
121
122 //*******************************************
124 //*******************************************
125 void enable(bool state_)
126 {
127 enabled = state_;
128 }
129
130 //*******************************************
132 //*******************************************
133 bool is_running() const
134 {
135 return enabled;
136 }
137
138 //*******************************************
140 //*******************************************
141 void clear()
142 {
143 {
144 TInterruptGuard guard;
145 (void)guard; // Silence 'unused variable warnings.
146
147 active_list.clear();
148 number_of_registered_timers = 0;
149 }
150
151 for (uint8_t i = 0U; i < MAX_TIMERS; ++i)
152 {
153 ::new (&timer_array[i]) timer_data();
154 }
155 }
156
157 //*******************************************
158 // Called by the timer service to indicate the
159 // amount of time that has elapsed since the last successful call to 'tick'.
160 // Returns true if the tick was processed,
161 // false if not.
162 //*******************************************
163 bool tick(uint32_t count)
164 {
165 if (enabled)
166 {
167 // We have something to do?
168 bool has_active = !active_list.empty();
169
170 if (has_active)
171 {
172 while (has_active && (count >= active_list.front().delta))
173 {
174 timer_data& timer = active_list.front();
175
176 count -= timer.delta;
177
178 active_list.remove(timer.id, true);
179
180 if (timer.callback.is_valid())
181 {
182 timer.callback();
183 }
184
185 if (timer.repeating)
186 {
187 // Reinsert the timer.
188 timer.delta = timer.period;
189 active_list.insert(timer.id);
190 }
191
192 has_active = !active_list.empty();
193 }
194
195 if (has_active)
196 {
197 // Subtract any remainder from the next due timeout.
198 active_list.front().delta -= count;
199 }
200 }
201
202 return true;
203 }
204
205 return false;
206 }
207
208 //*******************************************
210 //*******************************************
211 bool start(etl::timer::id::type id_, bool immediate_ = false)
212 {
213 bool result = false;
214
215 // Valid timer id?
216 if (id_ != etl::timer::id::NO_TIMER)
217 {
218 timer_data& timer = timer_array[id_];
219
220 // Registered timer?
221 if (timer.id != etl::timer::id::NO_TIMER)
222 {
223 // Has a valid period.
224 if (timer.period != etl::timer::state::INACTIVE)
225 {
226 TInterruptGuard guard;
227 (void)guard; // Silence 'unused variable warnings.
228
229 if (timer.is_active())
230 {
231 active_list.remove(timer.id, false);
232 }
233
234 timer.delta = immediate_ ? 0U : timer.period;
235 active_list.insert(timer.id);
236
237 result = true;
238 }
239 }
240 }
241
242 return result;
243 }
244
245 //*******************************************
247 //*******************************************
248 bool stop(etl::timer::id::type id_)
249 {
250 bool result = false;
251
252 // Valid timer id?
253 if (id_ != etl::timer::id::NO_TIMER)
254 {
255 timer_data& timer = timer_array[id_];
256
257 // Registered timer?
258 if (timer.id != etl::timer::id::NO_TIMER)
259 {
260 if (timer.is_active())
261 {
262 TInterruptGuard guard;
263 (void)guard; // Silence 'unused variable warnings.
264
265 active_list.remove(timer.id, false);
266 }
267
268 result = true;
269 }
270 }
271
272 return result;
273 }
274
275 //*******************************************
277 //*******************************************
278 bool set_period(etl::timer::id::type id_, uint32_t period_)
279 {
280 if (stop(id_))
281 {
282 timer_array[id_].period = period_;
283 return true;
284 }
285
286 return false;
287 }
288
289 //*******************************************
291 //*******************************************
292 bool set_mode(etl::timer::id::type id_, bool repeating_)
293 {
294 if (stop(id_))
295 {
296 timer_array[id_].repeating = repeating_;
297 return true;
298 }
299
300 return false;
301 }
302
303 protected:
304
305 //*************************************************************************
308 {
309 //*******************************************
310 timer_data()
311 : callback()
312 , period(0U)
313 , delta(etl::timer::state::INACTIVE)
314 , id(etl::timer::id::NO_TIMER)
315 , previous(etl::timer::id::NO_TIMER)
316 , next(etl::timer::id::NO_TIMER)
317 , repeating(true)
318 {
319 }
320
321 //*******************************************
323 //*******************************************
324 timer_data(etl::timer::id::type id_,
325 callback_type callback_,
326 uint32_t period_,
327 bool repeating_)
328 : callback(callback_)
329 , period(period_)
330 , delta(etl::timer::state::INACTIVE)
331 , id(id_)
332 , previous(etl::timer::id::NO_TIMER)
333 , next(etl::timer::id::NO_TIMER)
334 , repeating(repeating_)
335 {
336 }
337
338 //*******************************************
340 //*******************************************
341 bool is_active() const
342 {
343 return delta != etl::timer::state::INACTIVE;
344 }
345
346 //*******************************************
348 //*******************************************
350 {
351 delta = etl::timer::state::INACTIVE;
352 }
353
355 uint32_t period;
356 uint32_t delta;
357 etl::timer::id::type id;
358 uint_least8_t previous;
359 uint_least8_t next;
360 bool repeating;
361
362 private:
363
364 // Disabled.
365 timer_data(const timer_data& other) ETL_DELETE;
366 timer_data& operator =(const timer_data& other) ETL_DELETE;
367 };
368
369 //*******************************************
371 //*******************************************
372 icallback_timer_interrupt(timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
373 : timer_array(timer_array_),
374 active_list(timer_array_),
375 enabled(false),
376 number_of_registered_timers(0U),
377 MAX_TIMERS(MAX_TIMERS_)
378 {
379 }
380
381 private:
382
383 //*************************************************************************
385 //*************************************************************************
386 class timer_list
387 {
388 public:
389
390 //*******************************
391 timer_list(timer_data* ptimers_)
392 : head(etl::timer::id::NO_TIMER)
393 , tail(etl::timer::id::NO_TIMER)
394 , current(etl::timer::id::NO_TIMER)
395 , ptimers(ptimers_)
396 {
397 }
398
399 //*******************************
400 bool empty() const
401 {
402 return head == etl::timer::id::NO_TIMER;
403 }
404
405 //*******************************
406 // Inserts the timer at the correct delta position
407 //*******************************
408 void insert(etl::timer::id::type id_)
409 {
410 timer_data& timer = ptimers[id_];
411
412 if (head == etl::timer::id::NO_TIMER)
413 {
414 // No entries yet.
415 head = id_;
416 tail = id_;
417 timer.previous = etl::timer::id::NO_TIMER;
418 timer.next = etl::timer::id::NO_TIMER;
419 }
420 else
421 {
422 // We already have entries.
423 etl::timer::id::type test_id = begin();
424
425 while (test_id != etl::timer::id::NO_TIMER)
426 {
427 timer_data& test = ptimers[test_id];
428
429 // Find the correct place to insert.
430 if (timer.delta <= test.delta)
431 {
432 if (test.id == head)
433 {
434 head = timer.id;
435 }
436
437 // Insert before test.
438 timer.previous = test.previous;
439 test.previous = timer.id;
440 timer.next = test.id;
441
442 // Adjust the next delta to compensate.
443 test.delta -= timer.delta;
444
445 if (timer.previous != etl::timer::id::NO_TIMER)
446 {
447 ptimers[timer.previous].next = timer.id;
448 }
449 break;
450 }
451 else
452 {
453 timer.delta -= test.delta;
454 }
455
456 test_id = next(test_id);
457 }
458
459 // Reached the end?
460 if (test_id == etl::timer::id::NO_TIMER)
461 {
462 // Tag on to the tail.
463 ptimers[tail].next = timer.id;
464 timer.previous = tail;
465 timer.next = etl::timer::id::NO_TIMER;
466 tail = timer.id;
467 }
468 }
469 }
470
471 //*******************************
472 void remove(etl::timer::id::type id_, bool has_expired)
473 {
474 timer_data& timer = ptimers[id_];
475
476 if (head == id_)
477 {
478 head = timer.next;
479 }
480 else
481 {
482 ptimers[timer.previous].next = timer.next;
483 }
484
485 if (tail == id_)
486 {
487 tail = timer.previous;
488 }
489 else
490 {
491 ptimers[timer.next].previous = timer.previous;
492 }
493
494 if (!has_expired)
495 {
496 // Adjust the next delta.
497 if (timer.next != etl::timer::id::NO_TIMER)
498 {
499 ptimers[timer.next].delta += timer.delta;
500 }
501 }
502
503 timer.previous = etl::timer::id::NO_TIMER;
504 timer.next = etl::timer::id::NO_TIMER;
505 timer.delta = etl::timer::state::INACTIVE;
506 }
507
508 //*******************************
509 timer_data& front()
510 {
511 return ptimers[head];
512 }
513
514 //*******************************
515 etl::timer::id::type begin()
516 {
517 current = head;
518 return current;
519 }
520
521 //*******************************
522 etl::timer::id::type previous(etl::timer::id::type last)
523 {
524 current = ptimers[last].previous;
525 return current;
526 }
527
528 //*******************************
529 etl::timer::id::type next(etl::timer::id::type last)
530 {
531 current = ptimers[last].next;
532 return current;
533 }
534
535 //*******************************
536 void clear()
537 {
538 etl::timer::id::type id = begin();
539
540 while (id != etl::timer::id::NO_TIMER)
541 {
542 timer_data& timer = ptimers[id];
543 id = next(id);
544 timer.next = etl::timer::id::NO_TIMER;
545 }
546
547 head = etl::timer::id::NO_TIMER;
548 tail = etl::timer::id::NO_TIMER;
549 current = etl::timer::id::NO_TIMER;
550 }
551
552 private:
553
554 etl::timer::id::type head;
555 etl::timer::id::type tail;
556 etl::timer::id::type current;
557
558 timer_data* const ptimers;
559 };
560
561 // The array of timer data structures.
562 timer_data* const timer_array;
563
564 // The list of active timers.
565 timer_list active_list;
566
567 bool enabled;
568 uint_least8_t number_of_registered_timers;
569
570 public:
571
572 const uint_least8_t MAX_TIMERS;
573 };
574
575 //***************************************************************************
577 //***************************************************************************
578 template <uint_least8_t MAX_TIMERS_, typename TInterruptGuard>
580 {
581 public:
582
583 ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254U, "No more than 254 timers are allowed");
584
586
587 //*******************************************
589 //*******************************************
591 : icallback_timer_interrupt<TInterruptGuard>(timer_array, MAX_TIMERS_)
592 {
593 }
594
595 private:
596
597 typename icallback_timer_interrupt<TInterruptGuard>::timer_data timer_array[MAX_TIMERS_];
598 };
599}
600
601#endif
The callback timer.
Definition: callback_timer_interrupt.h:580
callback_timer_interrupt()
Constructor.
Definition: callback_timer_interrupt.h:590
Definition: callback.h:45
Declaration.
Definition: delegate_cpp03.h:175
Interface for callback timer.
Definition: callback_timer_interrupt.h:50
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition: callback_timer_interrupt.h:248
icallback_timer_interrupt(timer_data *const timer_array_, const uint_least8_t MAX_TIMERS_)
Constructor.
Definition: callback_timer_interrupt.h:372
bool is_running() const
Get the enable/disable state.
Definition: callback_timer_interrupt.h:133
bool set_mode(etl::timer::id::type id_, bool repeating_)
Sets a timer's mode.
Definition: callback_timer_interrupt.h:292
void enable(bool state_)
Enable/disable the timer.
Definition: callback_timer_interrupt.h:125
etl::timer::id::type register_timer(const callback_type &callback_, uint32_t period_, bool repeating_)
Register a timer.
Definition: callback_timer_interrupt.h:58
bool unregister_timer(etl::timer::id::type id_)
Unregister a timer.
Definition: callback_timer_interrupt.h:93
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition: callback_timer_interrupt.h:211
void clear()
Clears the timer of data.
Definition: callback_timer_interrupt.h:141
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition: callback_timer_interrupt.h:278
ETL_CONSTEXPR14 TIterator remove(TIterator first, TIterator last, const T &value)
Definition: algorithm.h:1934
bitset_ext
Definition: absolute.h:38
ETL_CONSTEXPR TContainer::iterator begin(TContainer &container)
Definition: iterator.h:931
The configuration of a timer.
Definition: callback_timer_interrupt.h:308
void set_inactive()
Sets the timer to the inactive state.
Definition: callback_timer_interrupt.h:349
bool is_active() const
Returns true if the timer is active.
Definition: callback_timer_interrupt.h:341
timer_data(etl::timer::id::type id_, callback_type callback_, uint32_t period_, bool repeating_)
ETL delegate callback.
Definition: callback_timer_interrupt.h:324
Definition: timer.h:82
Common definitions for the timer framework.
Definition: timer.h:55