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