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#if ETL_USING_CPP11
143 //*******************************************
145 //*******************************************
147 callback_type& callback_,
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;
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.
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 {
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.
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 {
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 //*******************************
329 {
330 current = head;
331 return current;
332 }
333
334 //*******************************
336 {
337 current = ptimers[last].previous;
338 return current;
339 }
340
341 //*******************************
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
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 //*******************************************
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.
406 ++registered_timers;
407 id = i;
408 break;
409 }
410 }
411 }
412
413 return id;
414 }
415
416 //*******************************************
418 //*******************************************
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.
438 ++registered_timers;
439 id = i;
440 break;
441 }
442 }
443 }
444
445 return id;
446 }
447
448 //*******************************************
450 //*******************************************
451#if ETL_USING_CPP11
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.
471 ++registered_timers;
472 id = i;
473 break;
474 }
475 }
476 }
477
478 return id;
479 }
480#endif
481
482 //*******************************************
484 //*******************************************
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 {
498 active_list.remove(timer.id, false);
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 {
535 active_list.clear();
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 {
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 //*******************************************
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 {
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);
643
644 result = true;
645 }
646 }
647 }
648
649 return result;
650 }
651
652 //*******************************************
654 //*******************************************
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 {
670 active_list.remove(timer.id, false);
672 }
673
674 result = true;
675 }
676 }
677
678 return result;
679 }
680
681 //*******************************************
683 //*******************************************
685 {
686 if (stop(id_))
687 {
688 timer_array[id_].period = period_;
689 return true;
690 }
691
692 return false;
693 }
694
695 //*******************************************
697 //*******************************************
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 //*******************************************
715 : timer_array(timer_array_),
716 active_list(timer_array_),
717 enabled(false),
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)
739#else
740 #if ETL_HAS_ATOMIC
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
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
pair holds two objects of arbitrary type
Definition utility.h:164
Definition timer.h:82
Common definitions for the timer framework.
Definition timer.h:55