Embedded Template Library 1.0
Loading...
Searching...
No Matches
callback_timer_interrupt.h
1/******************************************************************************
2The MIT License(MIT)
3
4Embedded Template Library.
5https://github.com/ETLCPP/etl
6https://www.etlcpp.com
7
8Copyright(c) 2022 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_INTERRUPT_INCLUDED
30#define ETL_CALLBACK_TIMER_INTERRUPT_INCLUDED
31
32#include "platform.h"
33#include "algorithm.h"
34#include "delegate.h"
35#include "error_handler.h"
36#include "placement_new.h"
37#include "static_assert.h"
38#include "timer.h"
39
40#include <stdint.h>
41
42namespace etl
43{
44 //***************************************************************************
46 //***************************************************************************
47 template <typename TInterruptGuard>
49 {
50 public:
51
52 typedef etl::delegate<void(void)> callback_type;
53
54 typedef etl::delegate<void(etl::timer::id::type)> event_callback_type;
55
56 //*******************************************
58 //*******************************************
59 etl::timer::id::type register_timer(const callback_type& callback_, uint32_t period_, bool repeating_)
60 {
61 etl::timer::id::type id = etl::timer::id::NO_TIMER;
62
63 bool is_space = (number_of_registered_timers < Max_Timers);
64
65 if (is_space)
66 {
67 // Search for the free space.
68 for (uint_least8_t i = 0U; i < Max_Timers; ++i)
69 {
70 timer_data& timer = timer_array[i];
71
72 if (timer.id == etl::timer::id::NO_TIMER)
73 {
74 TInterruptGuard guard;
75 (void)guard; // Silence 'unused variable warnings.
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 TInterruptGuard guard;
105 (void)guard; // Silence 'unused variable warnings.
106
107 active_list.remove(timer.id, false);
108 remove_callback.call_if(timer.id);
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 {
144 TInterruptGuard guard;
145 (void)guard; // Silence 'unused variable warnings.
146
147 active_list.clear();
148 number_of_registered_timers = 0;
149 }
150
151 for (uint8_t i = 0U; i < Max_Timers; ++i)
152 {
153 ::new (&timer_array[i]) timer_data();
154 }
155 }
156
157 //*******************************************
158 // Called by the timer service to indicate the
159 // amount of time that has elapsed since the last successful call to 'tick'.
160 // Returns true if the tick was processed,
161 // false if not.
162 //*******************************************
163 bool tick(uint32_t count)
164 {
165 if (enabled)
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 timer.callback();
184 }
185
186 if (timer.repeating)
187 {
188 // Reinsert the timer.
189 timer.delta = timer.period;
190 active_list.insert(timer.id);
191 insert_callback.call_if(timer.id);
192 }
193
194 has_active = !active_list.empty();
195 }
196
197 if (has_active)
198 {
199 // Subtract any remainder from the next due timeout.
200 active_list.front().delta -= count;
201 }
202 }
203
204 return true;
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 TInterruptGuard guard;
229 (void)guard; // Silence 'unused variable warnings.
230
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
241 result = true;
242 }
243 }
244 }
245
246 return result;
247 }
248
249 //*******************************************
251 //*******************************************
252 bool stop(etl::timer::id::type id_)
253 {
254 bool result = false;
255
256 // Valid timer id?
257 if (id_ != etl::timer::id::NO_TIMER)
258 {
259 timer_data& timer = timer_array[id_];
260
261 // Registered timer?
262 if (timer.id != etl::timer::id::NO_TIMER)
263 {
264 if (timer.is_active())
265 {
266 TInterruptGuard guard;
267 (void)guard; // Silence 'unused variable warnings.
268
269 active_list.remove(timer.id, false);
270 remove_callback.call_if(timer.id);
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 TInterruptGuard guard;
314 (void)guard; // Silence 'unused variable warnings.
315 return !active_list.empty();
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 TInterruptGuard guard;
328 (void)guard; // Silence 'unused variable warnings.
329
330 if (!active_list.empty())
331 {
332 delta = active_list.front().delta;
333 }
334
335 return delta;
336 }
337
338 //*******************************************
341 //*******************************************
342 bool is_active(etl::timer::id::type id_) const
343 {
344 // Valid timer id?
345 if (is_valid_timer_id(id_))
346 {
347 if (has_active_timer())
348 {
349 TInterruptGuard guard;
350 (void)guard; // Silence 'unused variable warnings.
351
352 const timer_data& timer = timer_array[id_];
353
354 // Registered timer?
355 if (timer.id != etl::timer::id::NO_TIMER)
356 {
357 return timer.is_active();
358 }
359 }
360 }
361
362 return false;
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_interrupt(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 , number_of_registered_timers(0U)
464 , Max_Timers(Max_Timers_)
465 {
466 }
467
468 private:
469
470 //*******************************************
472 //*******************************************
473 bool is_valid_timer_id(etl::timer::id::type id_) const
474 {
475 return (id_ < Max_Timers);
476 }
477
478 //*************************************************************************
480 //*************************************************************************
481 class timer_list
482 {
483 public:
484
485 //*******************************
486 timer_list(timer_data* ptimers_)
487 : head(etl::timer::id::NO_TIMER)
488 , tail(etl::timer::id::NO_TIMER)
489 , ptimers(ptimers_)
490 {
491 }
492
493 //*******************************
494 bool empty() const
495 {
496 return head == etl::timer::id::NO_TIMER;
497 }
498
499 //*******************************
500 // Inserts the timer at the correct delta position
501 //*******************************
502 void insert(etl::timer::id::type id_)
503 {
504 timer_data& timer = ptimers[id_];
505
506 if (head == etl::timer::id::NO_TIMER)
507 {
508 // No entries yet.
509 head = id_;
510 tail = id_;
511 timer.previous = etl::timer::id::NO_TIMER;
512 timer.next = etl::timer::id::NO_TIMER;
513 }
514 else
515 {
516 // We already have entries.
517 etl::timer::id::type test_id = begin();
518
519 while (test_id != etl::timer::id::NO_TIMER)
520 {
521 timer_data& test = ptimers[test_id];
522
523 // Find the correct place to insert.
524 if (timer.delta <= test.delta)
525 {
526 if (test.id == head)
527 {
528 head = timer.id;
529 }
530
531 // Insert before test.
532 timer.previous = test.previous;
533 test.previous = timer.id;
534 timer.next = test.id;
535
536 // Adjust the next delta to compensate.
537 test.delta -= timer.delta;
538
539 if (timer.previous != etl::timer::id::NO_TIMER)
540 {
541 ptimers[timer.previous].next = timer.id;
542 }
543 break;
544 }
545 else
546 {
547 timer.delta -= test.delta;
548 }
549
550 test_id = next(test_id);
551 }
552
553 // Reached the end?
554 if (test_id == etl::timer::id::NO_TIMER)
555 {
556 // Tag on to the tail.
557 ptimers[tail].next = timer.id;
558 timer.previous = tail;
559 timer.next = etl::timer::id::NO_TIMER;
560 tail = timer.id;
561 }
562 }
563 }
564
565 //*******************************
566 void remove(etl::timer::id::type id_, bool has_expired)
567 {
568 timer_data& timer = ptimers[id_];
569
570 if (head == id_)
571 {
572 head = timer.next;
573 }
574 else
575 {
576 ptimers[timer.previous].next = timer.next;
577 }
578
579 if (tail == id_)
580 {
581 tail = timer.previous;
582 }
583 else
584 {
585 ptimers[timer.next].previous = timer.previous;
586 }
587
588 if (!has_expired)
589 {
590 // Adjust the next delta.
591 if (timer.next != etl::timer::id::NO_TIMER)
592 {
593 ptimers[timer.next].delta += timer.delta;
594 }
595 }
596
597 timer.previous = etl::timer::id::NO_TIMER;
598 timer.next = etl::timer::id::NO_TIMER;
599 timer.delta = etl::timer::state::Inactive;
600 }
601
602 //*******************************
603 timer_data& front()
604 {
605 return ptimers[head];
606 }
607
608 //*******************************
609 const timer_data& front() const
610 {
611 return ptimers[head];
612 }
613
614 //*******************************
615 etl::timer::id::type begin()
616 {
617 return head;
618 }
619
620 //*******************************
621 etl::timer::id::type next(etl::timer::id::type last)
622 {
623 return ptimers[last].next;
624 }
625
626 //*******************************
627 void clear()
628 {
629 etl::timer::id::type id = begin();
630
631 while (id != etl::timer::id::NO_TIMER)
632 {
633 timer_data& timer = ptimers[id];
634 id = next(id);
635 timer.next = etl::timer::id::NO_TIMER;
636 }
637
638 head = etl::timer::id::NO_TIMER;
639 tail = etl::timer::id::NO_TIMER;
640 }
641
642 private:
643
644 etl::timer::id::type head;
645 etl::timer::id::type tail;
646
647 timer_data* const ptimers;
648 };
649
650 // The array of timer data structures.
651 timer_data* const timer_array;
652
653 // The list of active timers.
654 timer_list active_list;
655
656 bool enabled;
657 uint_least8_t number_of_registered_timers;
658
659 event_callback_type insert_callback;
660 event_callback_type remove_callback;
661
662 public:
663
664 const uint_least8_t Max_Timers;
665 };
666
667 //***************************************************************************
669 //***************************************************************************
670 template <uint_least8_t Max_Timers_, typename TInterruptGuard>
672 {
673 public:
674
675 ETL_STATIC_ASSERT(Max_Timers_ <= 254U, "No more than 254 timers are allowed");
676
677 typedef typename icallback_timer_interrupt<TInterruptGuard>::callback_type callback_type;
678
679 //*******************************************
681 //*******************************************
683 : icallback_timer_interrupt<TInterruptGuard>(timer_array, Max_Timers_)
684 {
685 }
686
687 private:
688
689 typename icallback_timer_interrupt<TInterruptGuard>::timer_data timer_array[Max_Timers_];
690 };
691} // namespace etl
692
693#endif
callback_timer_interrupt()
Constructor.
Definition callback_timer_interrupt.h:682
Definition callback.h:46
Declaration.
Definition delegate_cpp03.h:191
Interface for callback timer.
Definition callback_timer_interrupt.h:49
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition callback_timer_interrupt.h:252
bool is_active(etl::timer::id::type id_) const
Definition callback_timer_interrupt.h:342
void set_insert_callback(event_callback_type insert_)
Set a callback when a timer is inserted on list.
Definition callback_timer_interrupt.h:368
bool is_running() const
Get the enable/disable state.
Definition callback_timer_interrupt.h:133
uint32_t time_to_next() const
Definition callback_timer_interrupt.h:323
bool set_mode(etl::timer::id::type id_, bool repeating_)
Sets a timer's mode.
Definition callback_timer_interrupt.h:297
void enable(bool state_)
Enable/disable the timer.
Definition callback_timer_interrupt.h:125
void set_remove_callback(event_callback_type remove_)
Set a callback when a timer is removed from list.
Definition callback_timer_interrupt.h:376
etl::timer::id::type register_timer(const callback_type &callback_, uint32_t period_, bool repeating_)
Register a timer.
Definition callback_timer_interrupt.h:59
bool unregister_timer(etl::timer::id::type id_)
Unregister a timer.
Definition callback_timer_interrupt.h:92
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition callback_timer_interrupt.h:213
void clear()
Clears the timer of data.
Definition callback_timer_interrupt.h:141
icallback_timer_interrupt(timer_data *const timer_array_, const uint_least8_t Max_Timers_)
Constructor.
Definition callback_timer_interrupt.h:459
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition callback_timer_interrupt.h:283
bool has_active_timer() const
Check if there is an active timer.
Definition callback_timer_interrupt.h:311
Definition absolute.h:40
The configuration of a timer.
Definition callback_timer_interrupt.h:398
void set_inactive()
Sets the timer to the inactive state.
Definition callback_timer_interrupt.h:436
bool is_active() const
Returns true if the timer is active.
Definition callback_timer_interrupt.h:428
timer_data(etl::timer::id::type id_, callback_type callback_, uint32_t period_, bool repeating_)
ETL delegate callback.
Definition callback_timer_interrupt.h:414
Definition timer.h:88
Common definitions for the timer framework.
Definition timer.h:55