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