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, false);
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 , ptimers(ptimers_)
476 {
477 }
478
479 //*******************************
480 bool empty() const
481 {
482 return head == etl::timer::id::NO_TIMER;
483 }
484
485 //*******************************
486 // Inserts the timer at the correct delta position
487 //*******************************
488 void insert(etl::timer::id::type id_)
489 {
490 timer_data& timer = ptimers[id_];
491
492 if (head == etl::timer::id::NO_TIMER)
493 {
494 // No entries yet.
495 head = id_;
496 tail = id_;
497 timer.previous = etl::timer::id::NO_TIMER;
498 timer.next = etl::timer::id::NO_TIMER;
499 }
500 else
501 {
502 // We already have entries.
503 etl::timer::id::type test_id = begin();
504
505 while (test_id != etl::timer::id::NO_TIMER)
506 {
507 timer_data& test = ptimers[test_id];
508
509 // Find the correct place to insert.
510 if (timer.delta <= test.delta)
511 {
512 if (test.id == head)
513 {
514 head = timer.id;
515 }
516
517 // Insert before test.
518 timer.previous = test.previous;
519 test.previous = timer.id;
520 timer.next = test.id;
521
522 // Adjust the next delta to compensate.
523 test.delta -= timer.delta;
524
525 if (timer.previous != etl::timer::id::NO_TIMER)
526 {
527 ptimers[timer.previous].next = timer.id;
528 }
529 break;
530 }
531 else
532 {
533 timer.delta -= test.delta;
534 }
535
536 test_id = next(test_id);
537 }
538
539 // Reached the end?
540 if (test_id == etl::timer::id::NO_TIMER)
541 {
542 // Tag on to the tail.
543 ptimers[tail].next = timer.id;
544 timer.previous = tail;
545 timer.next = etl::timer::id::NO_TIMER;
546 tail = timer.id;
547 }
548 }
549 }
550
551 //*******************************
552 void remove(etl::timer::id::type id_, bool has_expired)
553 {
554 timer_data& timer = ptimers[id_];
555
556 if (head == id_)
557 {
558 head = timer.next;
559 }
560 else
561 {
562 ptimers[timer.previous].next = timer.next;
563 }
564
565 if (tail == id_)
566 {
567 tail = timer.previous;
568 }
569 else
570 {
571 ptimers[timer.next].previous = timer.previous;
572 }
573
574 if (!has_expired)
575 {
576 // Adjust the next delta.
577 if (timer.next != etl::timer::id::NO_TIMER)
578 {
579 ptimers[timer.next].delta += timer.delta;
580 }
581 }
582
583 timer.previous = etl::timer::id::NO_TIMER;
584 timer.next = etl::timer::id::NO_TIMER;
585 timer.delta = etl::timer::state::Inactive;
586 }
587
588 //*******************************
589 timer_data& front()
590 {
591 return ptimers[head];
592 }
593
594 //*******************************
595 const timer_data& front() const
596 {
597 return ptimers[head];
598 }
599
600 //*******************************
601 etl::timer::id::type begin()
602 {
603 return head;
604 }
605
606 //*******************************
607 etl::timer::id::type next(etl::timer::id::type last)
608 {
609 return ptimers[last].next;
610 }
611
612 //*******************************
613 void clear()
614 {
615 etl::timer::id::type id = begin();
616
617 while (id != etl::timer::id::NO_TIMER)
618 {
619 timer_data& timer = ptimers[id];
620 id = next(id);
621 timer.next = etl::timer::id::NO_TIMER;
622 }
623
624 head = etl::timer::id::NO_TIMER;
625 tail = etl::timer::id::NO_TIMER;
626 }
627
628 private:
629
630 etl::timer::id::type head;
631 etl::timer::id::type tail;
632
633 timer_data* const ptimers;
634 };
635
636 // The array of timer data structures.
637 timer_data* const timer_array;
638
639 // The list of active timers.
640 timer_list active_list;
641
642 bool enabled;
643 uint_least8_t number_of_registered_timers;
644
645 event_callback_type insert_callback;
646 event_callback_type remove_callback;
647
648 public:
649
650 const uint_least8_t Max_Timers;
651 };
652
653 //***************************************************************************
655 //***************************************************************************
656 template <uint_least8_t Max_Timers_, typename TInterruptGuard>
658 {
659 public:
660
661 ETL_STATIC_ASSERT(Max_Timers_ <= 254, "No more than 254 timers are allowed");
662
663 typedef typename imessage_timer_interrupt<TInterruptGuard>::callback_type callback_type;
664
665 //*******************************************
667 //*******************************************
669 : imessage_timer_interrupt<TInterruptGuard>(timer_array, Max_Timers_)
670 {
671 }
672
673 private:
674
675 typename etl::imessage_timer_interrupt<TInterruptGuard>::timer_data timer_array[Max_Timers_];
676 };
677} // namespace etl
678
679#endif
Declaration.
Definition delegate_cpp03.h:191
This is the base of all message routers.
Definition message_router.h:141
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:73
message_timer_interrupt()
Constructor.
Definition message_timer_interrupt.h:668
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