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