Embedded Template Library 1.0
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 "nullptr.h"
34#include "message_types.h"
35#include "message.h"
36#include "message_router.h"
37#include "message_bus.h"
38#include "static_assert.h"
39#include "timer.h"
40#include "delegate.h"
41#include "algorithm.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 public:
58
59 //*******************************************
61 //*******************************************
62 etl::timer::id::type register_timer(const etl::imessage& message_,
63 etl::imessage_router& router_,
64 uint32_t period_,
65 bool repeating_,
66 etl::message_router_id_t destination_router_id_ = etl::imessage_router::ALL_MESSAGE_ROUTERS)
67 {
68 etl::timer::id::type id = etl::timer::id::NO_TIMER;
69
70 bool is_space = (number_of_registered_timers < MAX_TIMERS);
71
72 if (is_space)
73 {
74 // There's no point adding null message routers.
75 if (!router_.is_null_router())
76 {
77 // Search for the free space.
78 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
79 {
80 timer_data& timer = timer_array[i];
81
82 if (timer.id == etl::timer::id::NO_TIMER)
83 {
84 TInterruptGuard guard;
85 (void)guard; // Silence 'unused variable warnings.
86
87 // Create in-place.
88 new (&timer) timer_data(i, message_, router_, period_, repeating_, destination_router_id_);
89 ++number_of_registered_timers;
90 id = i;
91 break;
92 }
93 }
94 }
95 }
96
97 return id;
98 }
99
100 //*******************************************
102 //*******************************************
103 bool unregister_timer(etl::timer::id::type id_)
104 {
105 bool result = false;
106
107 if (id_ != etl::timer::id::NO_TIMER)
108 {
109 timer_data& timer = timer_array[id_];
110
111 if (timer.id != etl::timer::id::NO_TIMER)
112 {
113 if (timer.is_active())
114 {
115 TInterruptGuard guard;
116 (void)guard; // Silence 'unused variable warnings.
117
118 active_list.remove(timer.id, true);
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
191 if (timer.p_router != ETL_NULLPTR)
192 {
193 timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
194 }
195
196 if (timer.repeating)
197 {
198 // Reinsert the timer.
199 timer.delta = timer.period;
200 active_list.insert(timer.id);
201 }
202
203 has_active = !active_list.empty();
204 }
205
206 if (has_active)
207 {
208 // Subtract any remainder from the next due timeout.
209 active_list.front().delta -= count;
210 }
211 }
212
213 return true;
214 }
215
216 return false;
217 }
218
219 //*******************************************
221 //*******************************************
222 bool start(etl::timer::id::type id_, bool immediate_ = false)
223 {
224 bool result = false;
225
226 // Valid timer id?
227 if (id_ != etl::timer::id::NO_TIMER)
228 {
229 timer_data& timer = timer_array[id_];
230
231 // Registered timer?
232 if (timer.id != etl::timer::id::NO_TIMER)
233 {
234 // Has a valid period.
235 if (timer.period != etl::timer::state::INACTIVE)
236 {
237 TInterruptGuard guard;
238 (void)guard; // Silence 'unused variable warnings.
239
240 if (timer.is_active())
241 {
242 active_list.remove(timer.id, false);
243 }
244
245 timer.delta = immediate_ ? 0 : timer.period;
246 active_list.insert(timer.id);
247
248 result = true;
249 }
250 }
251 }
252
253 return result;
254 }
255
256 //*******************************************
258 //*******************************************
259 bool stop(etl::timer::id::type id_)
260 {
261 bool result = false;
262
263 // Valid timer id?
264 if (id_ != etl::timer::id::NO_TIMER)
265 {
266 timer_data& timer = timer_array[id_];
267
268 // Registered timer?
269 if (timer.id != etl::timer::id::NO_TIMER)
270 {
271 if (timer.is_active())
272 {
273 TInterruptGuard guard;
274 (void)guard; // Silence 'unused variable warnings.
275
276 active_list.remove(timer.id, false);
277 }
278
279 result = true;
280 }
281 }
282
283 return result;
284 }
285
286 //*******************************************
288 //*******************************************
289 bool set_period(etl::timer::id::type id_, uint32_t period_)
290 {
291 if (stop(id_))
292 {
293 timer_array[id_].period = period_;
294 return true;
295 }
296
297 return false;
298 }
299
300 //*******************************************
302 //*******************************************
303 bool set_mode(etl::timer::id::type id_, bool repeating_)
304 {
305 if (stop(id_))
306 {
307 timer_array[id_].repeating = repeating_;
308 return true;
309 }
310
311 return false;
312 }
313
314 protected:
315
316 //*************************************************************************
319 {
320 //*******************************************
321 timer_data()
322 : p_message(ETL_NULLPTR)
323 , p_router(ETL_NULLPTR)
324 , period(0)
325 , delta(etl::timer::state::INACTIVE)
326 , destination_router_id(etl::imessage_bus::ALL_MESSAGE_ROUTERS)
327 , id(etl::timer::id::NO_TIMER)
328 , previous(etl::timer::id::NO_TIMER)
329 , next(etl::timer::id::NO_TIMER)
330 , repeating(true)
331 {
332 }
333
334 //*******************************************
335 timer_data(etl::timer::id::type id_,
336 const etl::imessage& message_,
337 etl::imessage_router& irouter_,
338 uint32_t period_,
339 bool repeating_,
340 etl::message_router_id_t destination_router_id_ = etl::imessage_bus::ALL_MESSAGE_ROUTERS)
341 : p_message(&message_)
342 , p_router(&irouter_)
343 , period(period_)
344 , delta(etl::timer::state::INACTIVE)
345 , destination_router_id(destination_router_id_)
346 , id(id_)
347 , previous(etl::timer::id::NO_TIMER)
348 , next(etl::timer::id::NO_TIMER)
349 , repeating(repeating_)
350 {
351 }
352
353 //*******************************************
355 //*******************************************
356 bool is_active() const
357 {
358 return delta != etl::timer::state::INACTIVE;
359 }
360
361 //*******************************************
363 //*******************************************
365 {
366 delta = etl::timer::state::INACTIVE;
367 }
368
369 const etl::imessage* p_message;
370 etl::imessage_router* p_router;
371 uint32_t period;
372 uint32_t delta;
373 etl::message_router_id_t destination_router_id;
374 etl::timer::id::type id;
375 uint_least8_t previous;
376 uint_least8_t next;
377 bool repeating;
378
379 private:
380
381 // Disabled.
382 timer_data(const timer_data& other);
383 timer_data& operator =(const timer_data& other);
384 };
385
386 //*******************************************
388 //*******************************************
389 imessage_timer_interrupt(timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
390 : timer_array(timer_array_)
391 , active_list(timer_array_)
392 , enabled(false)
393 , number_of_registered_timers(0U)
394 , MAX_TIMERS(MAX_TIMERS_)
395 {
396 }
397
398 //*******************************************
400 //*******************************************
402 {
403 }
404
405 private:
406
407 //*************************************************************************
409 //*************************************************************************
410 class timer_list
411 {
412 public:
413
414 //*******************************
415 timer_list(timer_data* ptimers_)
416 : head(etl::timer::id::NO_TIMER)
417 , tail(etl::timer::id::NO_TIMER)
418 , current(etl::timer::id::NO_TIMER)
419 , ptimers(ptimers_)
420 {
421 }
422
423 //*******************************
424 bool empty() const
425 {
426 return head == etl::timer::id::NO_TIMER;
427 }
428
429 //*******************************
430 // Inserts the timer at the correct delta position
431 //*******************************
432 void insert(etl::timer::id::type id_)
433 {
434 timer_data& timer = ptimers[id_];
435
436 if (head == etl::timer::id::NO_TIMER)
437 {
438 // No entries yet.
439 head = id_;
440 tail = id_;
441 timer.previous = etl::timer::id::NO_TIMER;
442 timer.next = etl::timer::id::NO_TIMER;
443 }
444 else
445 {
446 // We already have entries.
447 etl::timer::id::type test_id = begin();
448
449 while (test_id != etl::timer::id::NO_TIMER)
450 {
451 timer_data& test = ptimers[test_id];
452
453 // Find the correct place to insert.
454 if (timer.delta <= test.delta)
455 {
456 if (test.id == head)
457 {
458 head = timer.id;
459 }
460
461 // Insert before test.
462 timer.previous = test.previous;
463 test.previous = timer.id;
464 timer.next = test.id;
465
466 // Adjust the next delta to compensate.
467 test.delta -= timer.delta;
468
469 if (timer.previous != etl::timer::id::NO_TIMER)
470 {
471 ptimers[timer.previous].next = timer.id;
472 }
473 break;
474 }
475 else
476 {
477 timer.delta -= test.delta;
478 }
479
480 test_id = next(test_id);
481 }
482
483 // Reached the end?
484 if (test_id == etl::timer::id::NO_TIMER)
485 {
486 // Tag on to the tail.
487 ptimers[tail].next = timer.id;
488 timer.previous = tail;
489 timer.next = etl::timer::id::NO_TIMER;
490 tail = timer.id;
491 }
492 }
493 }
494
495 //*******************************
496 void remove(etl::timer::id::type id_, bool has_expired)
497 {
498 timer_data& timer = ptimers[id_];
499
500 if (head == id_)
501 {
502 head = timer.next;
503 }
504 else
505 {
506 ptimers[timer.previous].next = timer.next;
507 }
508
509 if (tail == id_)
510 {
511 tail = timer.previous;
512 }
513 else
514 {
515 ptimers[timer.next].previous = timer.previous;
516 }
517
518 if (!has_expired)
519 {
520 // Adjust the next delta.
521 if (timer.next != etl::timer::id::NO_TIMER)
522 {
523 ptimers[timer.next].delta += timer.delta;
524 }
525 }
526
527 timer.previous = etl::timer::id::NO_TIMER;
528 timer.next = etl::timer::id::NO_TIMER;
529 timer.delta = etl::timer::state::INACTIVE;
530 }
531
532 //*******************************
533 timer_data& front()
534 {
535 return ptimers[head];
536 }
537
538 //*******************************
539 etl::timer::id::type begin()
540 {
541 current = head;
542 return current;
543 }
544
545 //*******************************
546 etl::timer::id::type previous(etl::timer::id::type last)
547 {
548 current = ptimers[last].previous;
549 return current;
550 }
551
552 //*******************************
553 etl::timer::id::type next(etl::timer::id::type last)
554 {
555 current = ptimers[last].next;
556 return current;
557 }
558
559 //*******************************
560 void clear()
561 {
562 etl::timer::id::type id = begin();
563
564 while (id != etl::timer::id::NO_TIMER)
565 {
566 timer_data& timer = ptimers[id];
567 id = next(id);
568 timer.next = etl::timer::id::NO_TIMER;
569 }
570
571 head = etl::timer::id::NO_TIMER;
572 tail = etl::timer::id::NO_TIMER;
573 current = etl::timer::id::NO_TIMER;
574 }
575
576 private:
577
578 etl::timer::id::type head;
579 etl::timer::id::type tail;
580 etl::timer::id::type current;
581
582 timer_data* const ptimers;
583 };
584
585 // The array of timer data structures.
586 timer_data* const timer_array;
587
588 // The list of active timers.
589 timer_list active_list;
590
591 bool enabled;
592 uint_least8_t number_of_registered_timers;
593
594 public:
595
596 const uint_least8_t MAX_TIMERS;
597 };
598
599 //***************************************************************************
601 //***************************************************************************
602 template <uint_least8_t MAX_TIMERS_, typename TInterruptGuard>
604 {
605 public:
606
607 ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
608
610
611 //*******************************************
613 //*******************************************
615 : imessage_timer_interrupt<TInterruptGuard>(timer_array, MAX_TIMERS_)
616 {
617 }
618
619 private:
620
621 typename etl::imessage_timer_interrupt<TInterruptGuard>::timer_data timer_array[MAX_TIMERS_];
622 };
623}
624
625#endif
Declaration.
Definition: delegate_cpp03.h:175
This is the base of all message routers.
Definition: message_router_generator.h:121
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:303
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition: message_timer_interrupt.h:222
bool unregister_timer(etl::timer::id::type id_)
Unregister a timer.
Definition: message_timer_interrupt.h:103
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:62
imessage_timer_interrupt(timer_data *const timer_array_, const uint_least8_t MAX_TIMERS_)
Constructor.
Definition: message_timer_interrupt.h:389
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:259
void enable(bool state_)
Enable/disable the timer.
Definition: message_timer_interrupt.h:135
~imessage_timer_interrupt()
Destructor.
Definition: message_timer_interrupt.h:401
bool is_running() const
Get the enable/disable state.
Definition: message_timer_interrupt.h:143
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition: message_timer_interrupt.h:289
Definition: message.h:69
The message timer.
Definition: message_timer_interrupt.h:604
message_timer_interrupt()
Constructor.
Definition: message_timer_interrupt.h:614
ETL_CONSTEXPR14 TIterator remove(TIterator first, TIterator last, const T &value)
Definition: algorithm.h:1934
bitset_ext
Definition: absolute.h:38
ETL_CONSTEXPR TContainer::iterator begin(TContainer &container)
Definition: iterator.h:931
The configuration of a timer.
Definition: message_timer_interrupt.h:319
bool is_active() const
Returns true if the timer is active.
Definition: message_timer_interrupt.h:356
void set_inactive()
Sets the timer to the inactive state.
Definition: message_timer_interrupt.h:364
Definition: timer.h:82
Common definitions for the timer framework.
Definition: timer.h:55