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