Embedded Template Library 1.0
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 "nullptr.h"
35#include "function.h"
36#include "static_assert.h"
37#include "timer.h"
38#include "error_handler.h"
39#include "placement_new.h"
40#include "delegate.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 //*******************************************
58 //*******************************************
59 etl::timer::id::type register_timer(callback_type callback_,
60 uint32_t period_,
61 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 --process_semaphore;
106 }
107
108 // Reset in-place.
109 new (&timer) timer_data();
110 --number_of_registered_timers;
111
112 result = true;
113 }
114 }
115
116 return result;
117 }
118
119 //*******************************************
121 //*******************************************
122 void enable(bool state_)
123 {
124 enabled = state_;
125 }
126
127 //*******************************************
129 //*******************************************
130 bool is_running() const
131 {
132 return enabled;
133 }
134
135 //*******************************************
137 //*******************************************
138 void clear()
139 {
140 ++process_semaphore;
141 active_list.clear();
142 --process_semaphore;
143
144 for (uint8_t i = 0U; i < MAX_TIMERS; ++i)
145 {
146 ::new (&timer_array[i]) timer_data();
147 }
148
149 number_of_registered_timers = 0;
150 }
151
152 //*******************************************
153 // Called by the timer service to indicate the
154 // amount of time that has elapsed since the last successful call to 'tick'.
155 // Returns true if the tick was processed,
156 // false if not.
157 //*******************************************
158 bool tick(uint32_t count)
159 {
160 if (enabled)
161 {
162 if (process_semaphore == 0U)
163 {
164 // We have something to do?
165 bool has_active = !active_list.empty();
166
167 if (has_active)
168 {
169 while (has_active && (count >= active_list.front().delta))
170 {
171 timer_data& timer = active_list.front();
172
173 count -= timer.delta;
174
175 active_list.remove(timer.id, true);
176
177 if (timer.callback.is_valid())
178 {
179 // Call the delegate callback.
180 timer.callback();
181 }
182
183 if (timer.repeating)
184 {
185 // Reinsert the timer.
186 timer.delta = timer.period;
187 active_list.insert(timer.id);
188 }
189
190 has_active = !active_list.empty();
191 }
192
193 if (has_active)
194 {
195 // Subtract any remainder from the next due timeout.
196 active_list.front().delta -= count;
197 }
198 }
199
200 return true;
201 }
202 }
203
204 return false;
205 }
206
207 //*******************************************
209 //*******************************************
210 bool start(etl::timer::id::type id_, bool immediate_ = false)
211 {
212 bool result = false;
213
214 // Valid timer id?
215 if (id_ != etl::timer::id::NO_TIMER)
216 {
217 timer_data& timer = timer_array[id_];
218
219 // Registered timer?
220 if (timer.id != etl::timer::id::NO_TIMER)
221 {
222 // Has a valid period.
223 if (timer.period != etl::timer::state::INACTIVE)
224 {
225 ++process_semaphore;
226 if (timer.is_active())
227 {
228 active_list.remove(timer.id, false);
229 }
230
231 timer.delta = immediate_ ? 0U : timer.period;
232 active_list.insert(timer.id);
233 --process_semaphore;
234
235 result = true;
236 }
237 }
238 }
239
240 return result;
241 }
242
243 //*******************************************
245 //*******************************************
246 bool stop(etl::timer::id::type id_)
247 {
248 bool result = false;
249
250 // Valid timer id?
251 if (id_ != etl::timer::id::NO_TIMER)
252 {
253 timer_data& timer = timer_array[id_];
254
255 // Registered timer?
256 if (timer.id != etl::timer::id::NO_TIMER)
257 {
258 if (timer.is_active())
259 {
260 ++process_semaphore;
261 active_list.remove(timer.id, false);
262 --process_semaphore;
263 }
264
265 result = true;
266 }
267 }
268
269 return result;
270 }
271
272 //*******************************************
274 //*******************************************
275 bool set_period(etl::timer::id::type id_, uint32_t period_)
276 {
277 if (stop(id_))
278 {
279 timer_array[id_].period = period_;
280 return true;
281 }
282
283 return false;
284 }
285
286 //*******************************************
288 //*******************************************
289 bool set_mode(etl::timer::id::type id_, bool repeating_)
290 {
291 if (stop(id_))
292 {
293 timer_array[id_].repeating = repeating_;
294 return true;
295 }
296
297 return false;
298 }
299
300 protected:
301
302 //*************************************************************************
305 {
306 //*******************************************
307 timer_data()
308 : callback()
309 , period(0U)
310 , delta(etl::timer::state::INACTIVE)
311 , id(etl::timer::id::NO_TIMER)
312 , previous(etl::timer::id::NO_TIMER)
313 , next(etl::timer::id::NO_TIMER)
314 , repeating(true)
315 {
316 }
317
318 //*******************************************
320 //*******************************************
321 timer_data(etl::timer::id::type id_,
322 callback_type callback_,
323 uint32_t period_,
324 bool repeating_)
325 : callback(callback_)
326 , period(period_)
327 , delta(etl::timer::state::INACTIVE)
328 , id(id_)
329 , previous(etl::timer::id::NO_TIMER)
330 , next(etl::timer::id::NO_TIMER)
331 , repeating(repeating_)
332 {
333 }
334
335 //*******************************************
337 //*******************************************
338 bool is_active() const
339 {
340 return delta != etl::timer::state::INACTIVE;
341 }
342
343 //*******************************************
345 //*******************************************
347 {
348 delta = etl::timer::state::INACTIVE;
349 }
350
352 uint32_t period;
353 uint32_t delta;
354 etl::timer::id::type id;
355 uint_least8_t previous;
356 uint_least8_t next;
357 bool repeating;
358
359 private:
360
361 // Disabled.
362 timer_data(const timer_data& other) ETL_DELETE;
363 timer_data& operator =(const timer_data& other) ETL_DELETE;
364 };
365
366 //*******************************************
368 //*******************************************
369 icallback_timer_atomic(timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
370 : timer_array(timer_array_)
371 , active_list(timer_array_)
372 , enabled(false)
373 , process_semaphore(0U)
374 , number_of_registered_timers(0U)
375 , MAX_TIMERS(MAX_TIMERS_)
376 {
377 }
378
379 private:
380
381 //*************************************************************************
383 //*************************************************************************
384 class timer_list
385 {
386 public:
387
388 //*******************************
389 timer_list(timer_data* ptimers_)
390 : head(etl::timer::id::NO_TIMER)
391 , tail(etl::timer::id::NO_TIMER)
392 , current(etl::timer::id::NO_TIMER)
393 , ptimers(ptimers_)
394 {
395 }
396
397 //*******************************
398 bool empty() const
399 {
400 return head == etl::timer::id::NO_TIMER;
401 }
402
403 //*******************************
404 // Inserts the timer at the correct delta position
405 //*******************************
406 void insert(etl::timer::id::type id_)
407 {
408 timer_data& timer = ptimers[id_];
409
410 if (head == etl::timer::id::NO_TIMER)
411 {
412 // No entries yet.
413 head = id_;
414 tail = id_;
415 timer.previous = etl::timer::id::NO_TIMER;
416 timer.next = etl::timer::id::NO_TIMER;
417 }
418 else
419 {
420 // We already have entries.
421 etl::timer::id::type test_id = begin();
422
423 while (test_id != etl::timer::id::NO_TIMER)
424 {
425 timer_data& test = ptimers[test_id];
426
427 // Find the correct place to insert.
428 if (timer.delta <= test.delta)
429 {
430 if (test.id == head)
431 {
432 head = timer.id;
433 }
434
435 // Insert before test.
436 timer.previous = test.previous;
437 test.previous = timer.id;
438 timer.next = test.id;
439
440 // Adjust the next delta to compensate.
441 test.delta -= timer.delta;
442
443 if (timer.previous != etl::timer::id::NO_TIMER)
444 {
445 ptimers[timer.previous].next = timer.id;
446 }
447 break;
448 }
449 else
450 {
451 timer.delta -= test.delta;
452 }
453
454 test_id = next(test_id);
455 }
456
457 // Reached the end?
458 if (test_id == etl::timer::id::NO_TIMER)
459 {
460 // Tag on to the tail.
461 ptimers[tail].next = timer.id;
462 timer.previous = tail;
463 timer.next = etl::timer::id::NO_TIMER;
464 tail = timer.id;
465 }
466 }
467 }
468
469 //*******************************
470 void remove(etl::timer::id::type id_, bool has_expired)
471 {
472 timer_data& timer = ptimers[id_];
473
474 if (head == id_)
475 {
476 head = timer.next;
477 }
478 else
479 {
480 ptimers[timer.previous].next = timer.next;
481 }
482
483 if (tail == id_)
484 {
485 tail = timer.previous;
486 }
487 else
488 {
489 ptimers[timer.next].previous = timer.previous;
490 }
491
492 if (!has_expired)
493 {
494 // Adjust the next delta.
495 if (timer.next != etl::timer::id::NO_TIMER)
496 {
497 ptimers[timer.next].delta += timer.delta;
498 }
499 }
500
501 timer.previous = etl::timer::id::NO_TIMER;
502 timer.next = etl::timer::id::NO_TIMER;
503 timer.delta = etl::timer::state::INACTIVE;
504 }
505
506 //*******************************
507 timer_data& front()
508 {
509 return ptimers[head];
510 }
511
512 //*******************************
513 etl::timer::id::type begin()
514 {
515 current = head;
516 return current;
517 }
518
519 //*******************************
520 etl::timer::id::type previous(etl::timer::id::type last)
521 {
522 current = ptimers[last].previous;
523 return current;
524 }
525
526 //*******************************
527 etl::timer::id::type next(etl::timer::id::type last)
528 {
529 current = ptimers[last].next;
530 return current;
531 }
532
533 //*******************************
534 void clear()
535 {
536 etl::timer::id::type id = begin();
537
538 while (id != etl::timer::id::NO_TIMER)
539 {
540 timer_data& timer = ptimers[id];
541 id = next(id);
542 timer.next = etl::timer::id::NO_TIMER;
543 }
544
545 head = etl::timer::id::NO_TIMER;
546 tail = etl::timer::id::NO_TIMER;
547 current = etl::timer::id::NO_TIMER;
548 }
549
550 private:
551
552 etl::timer::id::type head;
553 etl::timer::id::type tail;
554 etl::timer::id::type current;
555
556 timer_data* const ptimers;
557 };
558
559 // The array of timer data structures.
560 timer_data* const timer_array;
561
562 // The list of active timers.
563 timer_list active_list;
564
565 bool enabled;
566 TSemaphore process_semaphore;
567 uint_least8_t number_of_registered_timers;
568
569 public:
570
571 const uint_least8_t MAX_TIMERS;
572 };
573
574 //***************************************************************************
576 //***************************************************************************
577 template <uint_least8_t MAX_TIMERS_, typename TSemaphore>
579 {
580 public:
581
582 ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254U, "No more than 254 timers are allowed");
583
584 //*******************************************
586 //*******************************************
588 : icallback_timer_atomic<TSemaphore>(timer_array, MAX_TIMERS_)
589 {
590 }
591
592 private:
593
594 typename etl::icallback_timer_atomic<TSemaphore>::timer_data timer_array[MAX_TIMERS_];
595 };
596}
597
598#endif
The callback timer.
Definition: callback_timer_atomic.h:579
callback_timer_atomic()
Constructor.
Definition: callback_timer_atomic.h:587
Definition: callback.h:45
Declaration.
Definition: delegate_cpp03.h:175
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:289
etl::timer::id::type register_timer(callback_type callback_, uint32_t period_, bool repeating_)
Register a timer.
Definition: callback_timer_atomic.h:59
icallback_timer_atomic(timer_data *const timer_array_, const uint_least8_t MAX_TIMERS_)
Constructor.
Definition: callback_timer_atomic.h:369
bool is_running() const
Get the enable/disable state.
Definition: callback_timer_atomic.h:130
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition: callback_timer_atomic.h:246
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition: callback_timer_atomic.h:210
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:122
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition: callback_timer_atomic.h:275
void clear()
Clears the timer of data.
Definition: callback_timer_atomic.h:138
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: callback_timer_atomic.h:305
timer_data(etl::timer::id::type id_, callback_type callback_, uint32_t period_, bool repeating_)
ETL delegate callback.
Definition: callback_timer_atomic.h:321
bool is_active() const
Returns true if the timer is active.
Definition: callback_timer_atomic.h:338
void set_inactive()
Sets the timer to the inactive state.
Definition: callback_timer_atomic.h:346
Definition: timer.h:82
Common definitions for the timer framework.
Definition: timer.h:55