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