Embedded Template Library 1.0
callback_timer.h
1/******************************************************************************
2The MIT License(MIT)
3
4Embedded Template Library.
5https://github.com/ETLCPP/etl
6https://www.etlcpp.com
7
8Copyright(c) 2017 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_INCLUDED
30#define ETL_CALLBACK_TIMER_INCLUDED
31
32#include "platform.h"
33#include "algorithm.h"
34#include "nullptr.h"
35#include "function.h"
36#include "static_assert.h"
37#include "timer.h"
38#include "atomic.h"
39#include "error_handler.h"
40#include "placement_new.h"
41#include "delegate.h"
42
43#include <stdint.h>
44
45#if defined(ETL_IN_UNIT_TEST) && ETL_NOT_USING_STL
46 #define ETL_DISABLE_TIMER_UPDATES
47 #define ETL_ENABLE_TIMER_UPDATES
48 #define ETL_TIMER_UPDATES_ENABLED true
49
50 #undef ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK
51 #undef ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK
52#else
53 #if !defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK) && !defined(ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK)
54 #error ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK or ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK not defined
55 #endif
56
57 #if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK) && defined(ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK)
58 #error Only define one of ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK or ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK
59 #endif
60
61 #if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK)
62 #define ETL_DISABLE_TIMER_UPDATES (++process_semaphore)
63 #define ETL_ENABLE_TIMER_UPDATES (--process_semaphore)
64 #define ETL_TIMER_UPDATES_ENABLED (process_semaphore.load() == 0)
65 #endif
66#endif
67
68#if defined(ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK)
69 #if !defined(ETL_CALLBACK_TIMER_DISABLE_INTERRUPTS) || !defined(ETL_CALLBACK_TIMER_ENABLE_INTERRUPTS)
70 #error ETL_CALLBACK_TIMER_DISABLE_INTERRUPTS and/or ETL_CALLBACK_TIMER_ENABLE_INTERRUPTS not defined
71 #endif
72
73 #define ETL_DISABLE_TIMER_UPDATES ETL_CALLBACK_TIMER_DISABLE_INTERRUPTS
74 #define ETL_ENABLE_TIMER_UPDATES ETL_CALLBACK_TIMER_ENABLE_INTERRUPTS
75 #define ETL_TIMER_UPDATES_ENABLED true
76#endif
77
78namespace etl
79{
80 //*************************************************************************
83 {
84 typedef etl::delegate<void(void)> callback_type;
85
86 enum callback_type_id
87 {
88 C_CALLBACK,
89 IFUNCTION,
90 DELEGATE
91 };
92
93 //*******************************************
95 : p_callback(ETL_NULLPTR),
96 period(0),
97 delta(etl::timer::state::INACTIVE),
98 id(etl::timer::id::NO_TIMER),
99 previous(etl::timer::id::NO_TIMER),
100 next(etl::timer::id::NO_TIMER),
101 repeating(true),
102 cbk_type(IFUNCTION)
103 {
104 }
105
106 //*******************************************
108 //*******************************************
109 callback_timer_data(etl::timer::id::type id_,
110 void (*p_callback_)(),
111 uint32_t period_,
112 bool repeating_)
113 : p_callback(reinterpret_cast<void*>(p_callback_)),
114 period(period_),
115 delta(etl::timer::state::INACTIVE),
116 id(id_),
117 previous(etl::timer::id::NO_TIMER),
118 next(etl::timer::id::NO_TIMER),
119 repeating(repeating_),
120 cbk_type(C_CALLBACK)
121 {
122 }
123
124 //*******************************************
126 //*******************************************
127 callback_timer_data(etl::timer::id::type id_,
128 etl::ifunction<void>& callback_,
129 uint32_t period_,
130 bool repeating_)
131 : p_callback(reinterpret_cast<void*>(&callback_)),
132 period(period_),
133 delta(etl::timer::state::INACTIVE),
134 id(id_),
135 previous(etl::timer::id::NO_TIMER),
136 next(etl::timer::id::NO_TIMER),
137 repeating(repeating_),
138 cbk_type(IFUNCTION)
139 {
140 }
141
142#if ETL_USING_CPP11
143 //*******************************************
145 //*******************************************
146 callback_timer_data(etl::timer::id::type id_,
147 callback_type& callback_,
148 uint32_t period_,
149 bool repeating_)
150 : p_callback(reinterpret_cast<void*>(&callback_)),
151 period(period_),
152 delta(etl::timer::state::INACTIVE),
153 id(id_),
154 previous(etl::timer::id::NO_TIMER),
155 next(etl::timer::id::NO_TIMER),
156 repeating(repeating_),
157 cbk_type(DELEGATE)
158 {
159 }
160#endif
161
162 //*******************************************
164 //*******************************************
165 bool is_active() const
166 {
167 return delta != etl::timer::state::INACTIVE;
168 }
169
170 //*******************************************
172 //*******************************************
174 {
175 delta = etl::timer::state::INACTIVE;
176 }
177
178 void* p_callback;
179 uint32_t period;
180 uint32_t delta;
181 etl::timer::id::type id;
182 uint_least8_t previous;
183 uint_least8_t next;
184 bool repeating;
185 callback_type_id cbk_type;
186
187 private:
188
189 // Disabled.
191 callback_timer_data& operator =(const callback_timer_data& other);
192 };
193
194 namespace private_callback_timer
195 {
196 //*************************************************************************
198 //*************************************************************************
199 class list
200 {
201 public:
202
203 //*******************************
205 : head(etl::timer::id::NO_TIMER),
206 tail(etl::timer::id::NO_TIMER),
207 current(etl::timer::id::NO_TIMER),
208 ptimers(ptimers_)
209 {
210 }
211
212 //*******************************
213 bool empty() const
214 {
215 return head == etl::timer::id::NO_TIMER;
216 }
217
218 //*******************************
219 // Inserts the timer at the correct delta position
220 //*******************************
221 void insert(etl::timer::id::type id_)
222 {
223 etl::callback_timer_data& timer = ptimers[id_];
224
225 if (head == etl::timer::id::NO_TIMER)
226 {
227 // No entries yet.
228 head = id_;
229 tail = id_;
230 timer.previous = etl::timer::id::NO_TIMER;
231 timer.next = etl::timer::id::NO_TIMER;
232 }
233 else
234 {
235 // We already have entries.
236 etl::timer::id::type test_id = begin();
237
238 while (test_id != etl::timer::id::NO_TIMER)
239 {
240 etl::callback_timer_data& test = ptimers[test_id];
241
242 // Find the correct place to insert.
243 if (timer.delta <= test.delta)
244 {
245 if (test.id == head)
246 {
247 head = timer.id;
248 }
249
250 // Insert before test.
251 timer.previous = test.previous;
252 test.previous = timer.id;
253 timer.next = test.id;
254
255 // Adjust the next delta to compensate.
256 test.delta -= timer.delta;
257
258 if (timer.previous != etl::timer::id::NO_TIMER)
259 {
260 ptimers[timer.previous].next = timer.id;
261 }
262 break;
263 }
264 else
265 {
266 timer.delta -= test.delta;
267 }
268
269 test_id = next(test_id);
270 }
271
272 // Reached the end?
273 if (test_id == etl::timer::id::NO_TIMER)
274 {
275 // Tag on to the tail.
276 ptimers[tail].next = timer.id;
277 timer.previous = tail;
278 timer.next = etl::timer::id::NO_TIMER;
279 tail = timer.id;
280 }
281 }
282 }
283
284 //*******************************
285 void remove(etl::timer::id::type id_, bool has_expired)
286 {
287 etl::callback_timer_data& timer = ptimers[id_];
288
289 if (head == id_)
290 {
291 head = timer.next;
292 }
293 else
294 {
295 ptimers[timer.previous].next = timer.next;
296 }
297
298 if (tail == id_)
299 {
300 tail = timer.previous;
301 }
302 else
303 {
304 ptimers[timer.next].previous = timer.previous;
305 }
306
307 if (!has_expired)
308 {
309 // Adjust the next delta.
310 if (timer.next != etl::timer::id::NO_TIMER)
311 {
312 ptimers[timer.next].delta += timer.delta;
313 }
314 }
315
316 timer.previous = etl::timer::id::NO_TIMER;
317 timer.next = etl::timer::id::NO_TIMER;
318 timer.delta = etl::timer::state::INACTIVE;
319 }
320
321 //*******************************
323 {
324 return ptimers[head];
325 }
326
327 //*******************************
328 etl::timer::id::type begin()
329 {
330 current = head;
331 return current;
332 }
333
334 //*******************************
335 etl::timer::id::type previous(etl::timer::id::type last)
336 {
337 current = ptimers[last].previous;
338 return current;
339 }
340
341 //*******************************
342 etl::timer::id::type next(etl::timer::id::type last)
343 {
344 current = ptimers[last].next;
345 return current;
346 }
347
348 //*******************************
349 void clear()
350 {
351 etl::timer::id::type id = begin();
352
353 while (id != etl::timer::id::NO_TIMER)
354 {
355 etl::callback_timer_data& timer = ptimers[id];
356 id = next(id);
357 timer.next = etl::timer::id::NO_TIMER;
358 }
359
360 head = etl::timer::id::NO_TIMER;
361 tail = etl::timer::id::NO_TIMER;
362 current = etl::timer::id::NO_TIMER;
363 }
364
365 private:
366
367 etl::timer::id::type head;
368 etl::timer::id::type tail;
369 etl::timer::id::type current;
370
371 etl::callback_timer_data* const ptimers;
372 };
373 }
374
375 //***************************************************************************
377 //***************************************************************************
379 {
380 public:
381
382 typedef etl::delegate<void(void)> callback_type;
383
384 //*******************************************
386 //*******************************************
387 etl::timer::id::type register_timer(void (*p_callback_)(),
388 uint32_t period_,
389 bool repeating_)
390 {
391 etl::timer::id::type id = etl::timer::id::NO_TIMER;
392
393 bool is_space = (registered_timers < MAX_TIMERS);
394
395 if (is_space)
396 {
397 // Search for the free space.
398 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
399 {
400 etl::callback_timer_data& timer = timer_array[i];
401
402 if (timer.id == etl::timer::id::NO_TIMER)
403 {
404 // Create in-place.
405 new (&timer) callback_timer_data(i, p_callback_, period_, repeating_);
406 ++registered_timers;
407 id = i;
408 break;
409 }
410 }
411 }
412
413 return id;
414 }
415
416 //*******************************************
418 //*******************************************
419 etl::timer::id::type register_timer(etl::ifunction<void>& callback_,
420 uint32_t period_,
421 bool repeating_)
422 {
423 etl::timer::id::type id = etl::timer::id::NO_TIMER;
424
425 bool is_space = (registered_timers < MAX_TIMERS);
426
427 if (is_space)
428 {
429 // Search for the free space.
430 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
431 {
432 etl::callback_timer_data& timer = timer_array[i];
433
434 if (timer.id == etl::timer::id::NO_TIMER)
435 {
436 // Create in-place.
437 new (&timer) callback_timer_data(i, callback_, period_, repeating_);
438 ++registered_timers;
439 id = i;
440 break;
441 }
442 }
443 }
444
445 return id;
446 }
447
448 //*******************************************
450 //*******************************************
451#if ETL_USING_CPP11
452 etl::timer::id::type register_timer(callback_type& callback_,
453 uint32_t period_,
454 bool repeating_)
455 {
456 etl::timer::id::type id = etl::timer::id::NO_TIMER;
457
458 bool is_space = (registered_timers < MAX_TIMERS);
459
460 if (is_space)
461 {
462 // Search for the free space.
463 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
464 {
465 etl::callback_timer_data& timer = timer_array[i];
466
467 if (timer.id == etl::timer::id::NO_TIMER)
468 {
469 // Create in-place.
470 new (&timer) callback_timer_data(i, callback_, period_, repeating_);
471 ++registered_timers;
472 id = i;
473 break;
474 }
475 }
476 }
477
478 return id;
479 }
480#endif
481
482 //*******************************************
484 //*******************************************
485 bool unregister_timer(etl::timer::id::type id_)
486 {
487 bool result = false;
488
489 if (id_ != etl::timer::id::NO_TIMER)
490 {
491 etl::callback_timer_data& timer = timer_array[id_];
492
493 if (timer.id != etl::timer::id::NO_TIMER)
494 {
495 if (timer.is_active())
496 {
497 ETL_DISABLE_TIMER_UPDATES;
498 active_list.remove(timer.id, false);
499 ETL_ENABLE_TIMER_UPDATES;
500 }
501
502 // Reset in-place.
503 new (&timer) callback_timer_data();
504 --registered_timers;
505
506 result = true;
507 }
508 }
509
510 return result;
511 }
512
513 //*******************************************
515 //*******************************************
516 void enable(bool state_)
517 {
518 enabled = state_;
519 }
520
521 //*******************************************
523 //*******************************************
524 bool is_running() const
525 {
526 return enabled;
527 }
528
529 //*******************************************
531 //*******************************************
532 void clear()
533 {
534 ETL_DISABLE_TIMER_UPDATES;
535 active_list.clear();
536 ETL_ENABLE_TIMER_UPDATES;
537
538 for (int i = 0; i < MAX_TIMERS; ++i)
539 {
540 ::new (&timer_array[i]) callback_timer_data();
541 }
542
543 registered_timers = 0;
544 }
545
546 //*******************************************
547 // Called by the timer service to indicate the
548 // amount of time that has elapsed since the last successful call to 'tick'.
549 // Returns true if the tick was processed,
550 // false if not.
551 //*******************************************
552 bool tick(uint32_t count)
553 {
554 if (enabled)
555 {
556 if (ETL_TIMER_UPDATES_ENABLED)
557 {
558 // We have something to do?
559 bool has_active = !active_list.empty();
560
561 if (has_active)
562 {
563 while (has_active && (count >= active_list.front().delta))
564 {
565 etl::callback_timer_data& timer = active_list.front();
566
567 count -= timer.delta;
568
569 active_list.remove(timer.id, true);
570
571 if (timer.repeating)
572 {
573 // Reinsert the timer.
574 timer.delta = timer.period;
575 active_list.insert(timer.id);
576 }
577
578 if (timer.p_callback != ETL_NULLPTR)
579 {
580 if (timer.cbk_type == callback_timer_data::C_CALLBACK)
581 {
582 // Call the C callback.
583 reinterpret_cast<void(*)()>(timer.p_callback)();
584 }
585 else if(timer.cbk_type == callback_timer_data::IFUNCTION)
586 {
587 // Call the function wrapper callback.
588 (*reinterpret_cast<etl::ifunction<void>*>(timer.p_callback))();
589 }
590#if ETL_USING_CPP11
591 else if(timer.cbk_type == callback_timer_data::DELEGATE)
592 {
593 // Call the delegate callback.
594 (*reinterpret_cast<callback_type*>(timer.p_callback))();
595 }
596#endif
597 }
598
599 has_active = !active_list.empty();
600 }
601
602 if (has_active)
603 {
604 // Subtract any remainder from the next due timeout.
605 active_list.front().delta -= count;
606 }
607 }
608
609 return true;
610 }
611 }
612
613 return false;
614 }
615
616 //*******************************************
618 //*******************************************
619 bool start(etl::timer::id::type id_, bool immediate_ = false)
620 {
621 bool result = false;
622
623 // Valid timer id?
624 if (id_ != etl::timer::id::NO_TIMER)
625 {
626 etl::callback_timer_data& timer = timer_array[id_];
627
628 // Registered timer?
629 if (timer.id != etl::timer::id::NO_TIMER)
630 {
631 // Has a valid period.
632 if (timer.period != etl::timer::state::INACTIVE)
633 {
634 ETL_DISABLE_TIMER_UPDATES;
635 if (timer.is_active())
636 {
637 active_list.remove(timer.id, false);
638 }
639
640 timer.delta = immediate_ ? 0 : timer.period;
641 active_list.insert(timer.id);
642 ETL_ENABLE_TIMER_UPDATES;
643
644 result = true;
645 }
646 }
647 }
648
649 return result;
650 }
651
652 //*******************************************
654 //*******************************************
655 bool stop(etl::timer::id::type id_)
656 {
657 bool result = false;
658
659 // Valid timer id?
660 if (id_ != etl::timer::id::NO_TIMER)
661 {
662 etl::callback_timer_data& timer = timer_array[id_];
663
664 // Registered timer?
665 if (timer.id != etl::timer::id::NO_TIMER)
666 {
667 if (timer.is_active())
668 {
669 ETL_DISABLE_TIMER_UPDATES;
670 active_list.remove(timer.id, false);
671 ETL_ENABLE_TIMER_UPDATES;
672 }
673
674 result = true;
675 }
676 }
677
678 return result;
679 }
680
681 //*******************************************
683 //*******************************************
684 bool set_period(etl::timer::id::type id_, uint32_t period_)
685 {
686 if (stop(id_))
687 {
688 timer_array[id_].period = period_;
689 return true;
690 }
691
692 return false;
693 }
694
695 //*******************************************
697 //*******************************************
698 bool set_mode(etl::timer::id::type id_, bool repeating_)
699 {
700 if (stop(id_))
701 {
702 timer_array[id_].repeating = repeating_;
703 return true;
704 }
705
706 return false;
707 }
708
709 protected:
710
711 //*******************************************
713 //*******************************************
714 icallback_timer(callback_timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
715 : timer_array(timer_array_),
716 active_list(timer_array_),
717 enabled(false),
718#if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK)
719 process_semaphore(0),
720#endif
721 registered_timers(0),
722 MAX_TIMERS(MAX_TIMERS_)
723 {
724 }
725
726 private:
727
728 // The array of timer data structures.
729 callback_timer_data* const timer_array;
730
731 // The list of active timers.
733
734 volatile bool enabled;
735#if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK)
736
737#if defined(ETL_TIMER_SEMAPHORE_TYPE)
738 typedef ETL_TIMER_SEMAPHORE_TYPE timer_semaphore_t;
739#else
740 #if ETL_HAS_ATOMIC
741 typedef etl::atomic_uint16_t timer_semaphore_t;
742 #else
743 #error No atomic type available
744 #endif
745#endif
746
747 etl::timer_semaphore_t process_semaphore;
748#endif
749 uint_least8_t registered_timers;
750
751 public:
752
753 const uint_least8_t MAX_TIMERS;
754 };
755
756 //***************************************************************************
758 //***************************************************************************
759 template <const uint_least8_t MAX_TIMERS_>
761 {
762 public:
763
764 ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
765
766 //*******************************************
768 //*******************************************
770 : icallback_timer(timer_array, MAX_TIMERS_)
771 {
772 }
773
774 private:
775
776 callback_timer_data timer_array[MAX_TIMERS_];
777 };
778}
779
780#undef ETL_DISABLE_TIMER_UPDATES
781#undef ETL_ENABLE_TIMER_UPDATES
782#undef ETL_TIMER_UPDATES_ENABLED
783
784#endif
The callback timer.
Definition: callback_timer.h:761
callback_timer()
Constructor.
Definition: callback_timer.h:769
Declaration.
Definition: delegate_cpp03.h:175
Interface for callback timer.
Definition: callback_timer.h:379
bool unregister_timer(etl::timer::id::type id_)
Register a timer.
Definition: callback_timer.h:485
bool is_running() const
Get the enable/disable state.
Definition: callback_timer.h:524
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition: callback_timer.h:684
etl::timer::id::type register_timer(void(*p_callback_)(), uint32_t period_, bool repeating_)
Register a timer.
Definition: callback_timer.h:387
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition: callback_timer.h:619
icallback_timer(callback_timer_data *const timer_array_, const uint_least8_t MAX_TIMERS_)
Constructor.
Definition: callback_timer.h:714
bool set_mode(etl::timer::id::type id_, bool repeating_)
Sets a timer's mode.
Definition: callback_timer.h:698
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition: callback_timer.h:655
void enable(bool state_)
Enable/disable the timer.
Definition: callback_timer.h:516
etl::timer::id::type register_timer(etl::ifunction< void > &callback_, uint32_t period_, bool repeating_)
Register a timer.
Definition: callback_timer.h:419
void clear()
Clears the timer of data.
Definition: callback_timer.h:532
A specialised intrusive linked list for timer data.
Definition: callback_timer.h:200
Definition: function.h:73
bitset_ext
Definition: absolute.h:38
The configuration of a timer.
Definition: callback_timer.h:83
callback_timer_data(etl::timer::id::type id_, void(*p_callback_)(), uint32_t period_, bool repeating_)
C function callback.
Definition: callback_timer.h:109
callback_timer_data(etl::timer::id::type id_, etl::ifunction< void > &callback_, uint32_t period_, bool repeating_)
ETL function callback.
Definition: callback_timer.h:127
bool is_active() const
Returns true if the timer is active.
Definition: callback_timer.h:165
void set_inactive()
Sets the timer to the inactive state.
Definition: callback_timer.h:173
Definition: timer.h:82
Common definitions for the timer framework.
Definition: timer.h:55