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