Embedded Template Library 1.0
Loading...
Searching...
No Matches
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 //*******************************************
110 void (*p_callback_)(),
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 //*******************************************
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 //*******************************************
144 //*******************************************
148 bool repeating_)
149 : p_callback(reinterpret_cast<void*>(&callback_)),
150 period(period_),
151 delta(etl::timer::state::Inactive),
152 id(id_),
153 previous(etl::timer::id::NO_TIMER),
154 next(etl::timer::id::NO_TIMER),
155 repeating(repeating_),
156 cbk_type(DELEGATE)
157 {
158 }
159
160 //*******************************************
162 //*******************************************
163 bool is_active() const
164 {
165 return delta != etl::timer::state::Inactive;
166 }
167
168 //*******************************************
170 //*******************************************
172 {
173 delta = etl::timer::state::Inactive;
174 }
175
176 void* p_callback;
177 uint32_t period;
178 uint32_t delta;
180 uint_least8_t previous;
181 uint_least8_t next;
182 bool repeating;
183 callback_type_id cbk_type;
184
185 private:
186
187 // Disabled.
190 };
191
192 namespace private_callback_timer
193 {
194 //*************************************************************************
196 //*************************************************************************
197 class list
198 {
199 public:
200
201 //*******************************
203 : head(etl::timer::id::NO_TIMER),
204 tail(etl::timer::id::NO_TIMER),
205 current(etl::timer::id::NO_TIMER),
206 ptimers(ptimers_)
207 {
208 }
209
210 //*******************************
211 bool empty() const
212 {
213 return head == etl::timer::id::NO_TIMER;
214 }
215
216 //*******************************
217 // Inserts the timer at the correct delta position
218 //*******************************
219 void insert(etl::timer::id::type id_)
220 {
222
223 if (head == etl::timer::id::NO_TIMER)
224 {
225 // No entries yet.
226 head = id_;
227 tail = id_;
228 timer.previous = etl::timer::id::NO_TIMER;
229 timer.next = etl::timer::id::NO_TIMER;
230 }
231 else
232 {
233 // We already have entries.
235
236 while (test_id != etl::timer::id::NO_TIMER)
237 {
238 etl::callback_timer_data& test = ptimers[test_id];
239
240 // Find the correct place to insert.
241 if (timer.delta <= test.delta)
242 {
243 if (test.id == head)
244 {
245 head = timer.id;
246 }
247
248 // Insert before test.
249 timer.previous = test.previous;
250 test.previous = timer.id;
251 timer.next = test.id;
252
253 // Adjust the next delta to compensate.
254 test.delta -= timer.delta;
255
256 if (timer.previous != etl::timer::id::NO_TIMER)
257 {
258 ptimers[timer.previous].next = timer.id;
259 }
260 break;
261 }
262 else
263 {
264 timer.delta -= test.delta;
265 }
266
267 test_id = next(test_id);
268 }
269
270 // Reached the end?
271 if (test_id == etl::timer::id::NO_TIMER)
272 {
273 // Tag on to the tail.
274 ptimers[tail].next = timer.id;
275 timer.previous = tail;
276 timer.next = etl::timer::id::NO_TIMER;
277 tail = timer.id;
278 }
279 }
280 }
281
282 //*******************************
283 void remove(etl::timer::id::type id_, bool has_expired)
284 {
286
287 if (head == id_)
288 {
289 head = timer.next;
290 }
291 else
292 {
293 ptimers[timer.previous].next = timer.next;
294 }
295
296 if (tail == id_)
297 {
298 tail = timer.previous;
299 }
300 else
301 {
302 ptimers[timer.next].previous = timer.previous;
303 }
304
305 if (!has_expired)
306 {
307 // Adjust the next delta.
308 if (timer.next != etl::timer::id::NO_TIMER)
309 {
310 ptimers[timer.next].delta += timer.delta;
311 }
312 }
313
314 timer.previous = etl::timer::id::NO_TIMER;
315 timer.next = etl::timer::id::NO_TIMER;
316 timer.delta = etl::timer::state::Inactive;
317 }
318
319 //*******************************
321 {
322 return ptimers[head];
323 }
324
325 //*******************************
326 const etl::callback_timer_data& front() const
327 {
328 return ptimers[head];
329 }
330
331 //*******************************
333 {
334 current = head;
335 return current;
336 }
337
338 //*******************************
340 {
341 current = ptimers[last].previous;
342 return current;
343 }
344
345 //*******************************
347 {
348 current = ptimers[last].next;
349 return current;
350 }
351
352 //*******************************
353 void clear()
354 {
355 etl::timer::id::type id = begin();
356
357 while (id != etl::timer::id::NO_TIMER)
358 {
359 etl::callback_timer_data& timer = ptimers[id];
360 id = next(id);
361 timer.next = etl::timer::id::NO_TIMER;
362 }
363
364 head = etl::timer::id::NO_TIMER;
365 tail = etl::timer::id::NO_TIMER;
366 current = etl::timer::id::NO_TIMER;
367 }
368
369 private:
370
373 etl::timer::id::type current;
374
375 etl::callback_timer_data* const ptimers;
376 };
377 }
378
379 //***************************************************************************
381 //***************************************************************************
383 {
384 public:
385
386 typedef etl::delegate<void(void)> callback_type;
387
388 //*******************************************
390 //*******************************************
393 bool repeating_)
394 {
395 etl::timer::id::type id = etl::timer::id::NO_TIMER;
396
397 bool is_space = (registered_timers < MAX_TIMERS);
398
399 if (is_space)
400 {
401 // Search for the free space.
402 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
403 {
404 etl::callback_timer_data& timer = timer_array[i];
405
406 if (timer.id == etl::timer::id::NO_TIMER)
407 {
408 // Create in-place.
410 ++registered_timers;
411 id = i;
412 break;
413 }
414 }
415 }
416
417 return id;
418 }
419
420 //*******************************************
422 //*******************************************
425 bool repeating_)
426 {
427 etl::timer::id::type id = etl::timer::id::NO_TIMER;
428
429 bool is_space = (registered_timers < MAX_TIMERS);
430
431 if (is_space)
432 {
433 // Search for the free space.
434 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
435 {
436 etl::callback_timer_data& timer = timer_array[i];
437
438 if (timer.id == etl::timer::id::NO_TIMER)
439 {
440 // Create in-place.
442 ++registered_timers;
443 id = i;
444 break;
445 }
446 }
447 }
448
449 return id;
450 }
451
452 //*******************************************
454 //*******************************************
455#if ETL_USING_CPP11
458 bool repeating_)
459 {
460 etl::timer::id::type id = etl::timer::id::NO_TIMER;
461
462 bool is_space = (registered_timers < MAX_TIMERS);
463
464 if (is_space)
465 {
466 // Search for the free space.
467 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
468 {
469 etl::callback_timer_data& timer = timer_array[i];
470
471 if (timer.id == etl::timer::id::NO_TIMER)
472 {
473 // Create in-place.
475 ++registered_timers;
476 id = i;
477 break;
478 }
479 }
480 }
481
482 return id;
483 }
484#endif
485
486 //*******************************************
488 //*******************************************
490 {
491 bool result = false;
492
493 if (id_ != etl::timer::id::NO_TIMER)
494 {
495 etl::callback_timer_data& timer = timer_array[id_];
496
497 if (timer.id != etl::timer::id::NO_TIMER)
498 {
499 if (timer.is_active())
500 {
502 active_list.remove(timer.id, false);
504 }
505
506 // Reset in-place.
507 new (&timer) callback_timer_data();
508 --registered_timers;
509
510 result = true;
511 }
512 }
513
514 return result;
515 }
516
517 //*******************************************
519 //*******************************************
520 void enable(bool state_)
521 {
522 enabled = state_;
523 }
524
525 //*******************************************
527 //*******************************************
528 bool is_running() const
529 {
530 return enabled;
531 }
532
533 //*******************************************
535 //*******************************************
536 void clear()
537 {
539 active_list.clear();
541
542 for (int i = 0; i < MAX_TIMERS; ++i)
543 {
544 ::new (&timer_array[i]) callback_timer_data();
545 }
546
547 registered_timers = 0;
548 }
549
550 //*******************************************
551 // Called by the timer service to indicate the
552 // amount of time that has elapsed since the last successful call to 'tick'.
553 // Returns true if the tick was processed,
554 // false if not.
555 //*******************************************
556 bool tick(uint32_t count)
557 {
558 if (enabled)
559 {
561 {
562 // We have something to do?
563 bool has_active = !active_list.empty();
564
565 if (has_active)
566 {
567 while (has_active && (count >= active_list.front().delta))
568 {
569 etl::callback_timer_data& timer = active_list.front();
570
571 count -= timer.delta;
572
573 active_list.remove(timer.id, true);
574
575 if (timer.repeating)
576 {
577 // Reinsert the timer.
578 timer.delta = timer.period;
579 active_list.insert(timer.id);
580 }
581
582 if (timer.p_callback != ETL_NULLPTR)
583 {
584 if (timer.cbk_type == callback_timer_data::C_CALLBACK)
585 {
586 // Call the C callback.
587 reinterpret_cast<void(*)()>(timer.p_callback)();
588 }
589 else if(timer.cbk_type == callback_timer_data::IFUNCTION)
590 {
591 // Call the function wrapper callback.
592 (*reinterpret_cast<etl::ifunction<void>*>(timer.p_callback))();
593 }
594 else if(timer.cbk_type == callback_timer_data::DELEGATE)
595 {
596 // Call the delegate callback.
597 (*reinterpret_cast<callback_type*>(timer.p_callback))();
598 }
599 }
600
601 has_active = !active_list.empty();
602 }
603
604 if (has_active)
605 {
606 // Subtract any remainder from the next due timeout.
607 active_list.front().delta -= count;
608 }
609 }
610
611 return true;
612 }
613 }
614
615 return false;
616 }
617
618 //*******************************************
620 //*******************************************
622 {
623 bool result = false;
624
625 // Valid timer id?
626 if (id_ != etl::timer::id::NO_TIMER)
627 {
628 etl::callback_timer_data& timer = timer_array[id_];
629
630 // Registered timer?
631 if (timer.id != etl::timer::id::NO_TIMER)
632 {
633 // Has a valid period.
634 if (timer.period != etl::timer::state::Inactive)
635 {
637 if (timer.is_active())
638 {
639 active_list.remove(timer.id, false);
640 }
641
642 timer.delta = immediate_ ? 0 : timer.period;
643 active_list.insert(timer.id);
645
646 result = true;
647 }
648 }
649 }
650
651 return result;
652 }
653
654 //*******************************************
656 //*******************************************
658 {
659 bool result = false;
660
661 // Valid timer id?
662 if (id_ != etl::timer::id::NO_TIMER)
663 {
664 etl::callback_timer_data& timer = timer_array[id_];
665
666 // Registered timer?
667 if (timer.id != etl::timer::id::NO_TIMER)
668 {
669 if (timer.is_active())
670 {
672 active_list.remove(timer.id, false);
674 }
675
676 result = true;
677 }
678 }
679
680 return result;
681 }
682
683 //*******************************************
685 //*******************************************
687 {
688 if (stop(id_))
689 {
690 timer_array[id_].period = period_;
691 return true;
692 }
693
694 return false;
695 }
696
697 //*******************************************
699 //*******************************************
701 {
702 if (stop(id_))
703 {
704 timer_array[id_].repeating = repeating_;
705 return true;
706 }
707
708 return false;
709 }
710
711 //*******************************************
713 //*******************************************
714 bool has_active_timer() const
715 {
716 return !active_list.empty();
717 }
718
719 //*******************************************
722 //*******************************************
724 {
725 uint32_t delta = static_cast<uint32_t>(etl::timer::interval::No_Active_Interval);
726
727 if (has_active_timer())
728 {
729 delta = active_list.front().delta;
730 }
731
732 return delta;
733 }
734
735 //*******************************************
738 //*******************************************
740 {
741 // Valid timer id?
742 if (is_valid_timer_id(id_))
743 {
744 if (has_active_timer())
745 {
746 const etl::callback_timer_data& timer = timer_array[id_];
747
748 // Registered timer?
749 if (timer.id != etl::timer::id::NO_TIMER)
750 {
751 return timer.is_active();
752 }
753 }
754 }
755
756 return false;
757 }
758
759 protected:
760
761 //*******************************************
763 //*******************************************
765 : timer_array(timer_array_),
766 active_list(timer_array_),
767 enabled(false),
769 process_semaphore(0),
770#endif
771 registered_timers(0),
772 MAX_TIMERS(MAX_TIMERS_)
773 {
774 }
775
776 private:
777
778 //*******************************************
780 //*******************************************
781 bool is_valid_timer_id(etl::timer::id::type id_) const
782 {
783 return (id_ < MAX_TIMERS);
784 }
785
786 // The array of timer data structures.
787 callback_timer_data* const timer_array;
788
789 // The list of active timers.
790 private_callback_timer::list active_list;
791
792 volatile bool enabled;
793#if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK)
794
795#if defined(ETL_TIMER_SEMAPHORE_TYPE)
796 typedef ETL_TIMER_SEMAPHORE_TYPE timer_semaphore_t;
797#else
798 #if ETL_HAS_ATOMIC
799 typedef etl::atomic_uint16_t timer_semaphore_t;
800 #else
801 #error No atomic type available
802 #endif
803#endif
804
805 mutable etl::timer_semaphore_t process_semaphore;
806#endif
807 uint_least8_t registered_timers;
808
809 public:
810
811 const uint_least8_t MAX_TIMERS;
812 };
813
814 //***************************************************************************
816 //***************************************************************************
817 template <const uint_least8_t MAX_TIMERS_>
819 {
820 public:
821
822 ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
823
824 //*******************************************
826 //*******************************************
828 : icallback_timer(timer_array, MAX_TIMERS_)
829 {
830 }
831
832 private:
833
834 callback_timer_data timer_array[MAX_TIMERS_];
835 };
836}
837
838#undef ETL_DISABLE_TIMER_UPDATES
839#undef ETL_ENABLE_TIMER_UPDATES
840#undef ETL_TIMER_UPDATES_ENABLED
841
842#endif
The callback timer.
Definition callback_timer.h:819
callback_timer()
Constructor.
Definition callback_timer.h:827
Declaration.
Definition delegate_cpp03.h:175
Interface for callback timer.
Definition callback_timer.h:383
bool unregister_timer(etl::timer::id::type id_)
Register a timer.
Definition callback_timer.h:489
bool is_running() const
Get the enable/disable state.
Definition callback_timer.h:528
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition callback_timer.h:686
etl::timer::id::type register_timer(void(*p_callback_)(), uint32_t period_, bool repeating_)
Register a timer.
Definition callback_timer.h:391
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition callback_timer.h:621
icallback_timer(callback_timer_data *const timer_array_, const uint_least8_t MAX_TIMERS_)
Constructor.
Definition callback_timer.h:764
bool set_mode(etl::timer::id::type id_, bool repeating_)
Sets a timer's mode.
Definition callback_timer.h:700
uint32_t time_to_next() const
Definition callback_timer.h:723
bool has_active_timer() const
Check if there is an active timer.
Definition callback_timer.h:714
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition callback_timer.h:657
void enable(bool state_)
Enable/disable the timer.
Definition callback_timer.h:520
etl::timer::id::type register_timer(etl::ifunction< void > &callback_, uint32_t period_, bool repeating_)
Register a timer.
Definition callback_timer.h:423
bool is_active(etl::timer::id::type id_) const
Definition callback_timer.h:739
void clear()
Clears the timer of data.
Definition callback_timer.h:536
A specialised intrusive linked list for timer data.
Definition callback_timer.h:198
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:163
void set_inactive()
Sets the timer to the inactive state.
Definition callback_timer.h:171
callback_timer_data(etl::timer::id::type id_, callback_type &callback_, uint32_t period_, bool repeating_)
ETL delegate callback.
Definition callback_timer.h:145
pair holds two objects of arbitrary type
Definition utility.h:164
Definition timer.h:88
Common definitions for the timer framework.
Definition timer.h:55