Embedded Template Library  1.0
callback_timer.h
1 /******************************************************************************
2 The MIT License(MIT)
3 
4 Embedded Template Library.
5 https://github.com/ETLCPP/etl
6 https://www.etlcpp.com
7 
8 Copyright(c) 2017 jwellbelove
9 
10 Permission is hereby granted, free of charge, to any person obtaining a copy
11 of this software and associated documentation files(the "Software"), to deal
12 in the Software without restriction, including without limitation the rights
13 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
14 copies of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions :
16 
17 The above copyright notice and this permission notice shall be included in all
18 copies or substantial portions of the Software.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 SOFTWARE.
27 ******************************************************************************/
28 
29 #ifndef ETL_CALLBACK_TIMER_INCLUDED
30 #define ETL_CALLBACK_TIMER_INCLUDED
31 
32 #include <stdint.h>
33 
34 #include "platform.h"
35 #include "algorithm.h"
36 #include "nullptr.h"
37 #include "function.h"
38 #include "static_assert.h"
39 #include "timer.h"
40 #include "atomic.h"
41 #include "error_handler.h"
42 #include "placement_new.h"
43 
44 #if ETL_CPP11_SUPPORTED
45  #include "delegate.h"
46 #endif
47 
48 #undef ETL_FILE
49 #define ETL_FILE "43"
50 
51 #if defined(ETL_IN_UNIT_TEST) && ETL_NOT_USING_STL
52  #define ETL_DISABLE_TIMER_UPDATES
53  #define ETL_ENABLE_TIMER_UPDATES
54  #define ETL_TIMER_UPDATES_ENABLED true
55 
56  #undef ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK
57  #undef ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK
58 #else
59  #if !defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK) && !defined(ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK)
60  #error ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK or ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK not defined
61  #endif
62 
63  #if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK) && defined(ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK)
64  #error Only define one of ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK or ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK
65  #endif
66 
67  #if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK)
68  #define ETL_DISABLE_TIMER_UPDATES (++process_semaphore)
69  #define ETL_ENABLE_TIMER_UPDATES (--process_semaphore)
70  #define ETL_TIMER_UPDATES_ENABLED (process_semaphore.load() == 0)
71  #endif
72 #endif
73 
74 #if defined(ETL_CALLBACK_TIMER_USE_INTERRUPT_LOCK)
75  #if !defined(ETL_CALLBACK_TIMER_DISABLE_INTERRUPTS) || !defined(ETL_CALLBACK_TIMER_ENABLE_INTERRUPTS)
76  #error ETL_CALLBACK_TIMER_DISABLE_INTERRUPTS and/or ETL_CALLBACK_TIMER_ENABLE_INTERRUPTS not defined
77  #endif
78 
79  #define ETL_DISABLE_TIMER_UPDATES (ETL_CALLBACK_TIMER_DISABLE_INTERRUPTS)
80  #define ETL_ENABLE_TIMER_UPDATES (ETL_CALLBACK_TIMER_ENABLE_INTERRUPTS)
81  #define ETL_TIMER_UPDATES_ENABLED true
82 #endif
83 
84 namespace etl
85 {
86  //*************************************************************************
89  {
90  enum callback_type
91  {
92  C_CALLBACK,
93  IFUNCTION,
94  DELEGATE
95  };
96 
97  //*******************************************
99  : p_callback(ETL_NULLPTR),
100  period(0),
101  delta(etl::timer::state::INACTIVE),
102  id(etl::timer::id::NO_TIMER),
103  previous(etl::timer::id::NO_TIMER),
104  next(etl::timer::id::NO_TIMER),
105  repeating(true),
106  cbk_type(IFUNCTION)
107  {
108  }
109 
110  //*******************************************
112  //*******************************************
113  callback_timer_data(etl::timer::id::type id_,
114  void (*p_callback_)(),
115  uint32_t period_,
116  bool repeating_)
117  : p_callback(reinterpret_cast<void*>(p_callback_)),
118  period(period_),
119  delta(etl::timer::state::INACTIVE),
120  id(id_),
121  previous(etl::timer::id::NO_TIMER),
122  next(etl::timer::id::NO_TIMER),
123  repeating(repeating_),
124  cbk_type(C_CALLBACK)
125  {
126  }
127 
128  //*******************************************
130  //*******************************************
131  callback_timer_data(etl::timer::id::type id_,
132  etl::ifunction<void>& callback_,
133  uint32_t period_,
134  bool repeating_)
135  : p_callback(reinterpret_cast<void*>(&callback_)),
136  period(period_),
137  delta(etl::timer::state::INACTIVE),
138  id(id_),
139  previous(etl::timer::id::NO_TIMER),
140  next(etl::timer::id::NO_TIMER),
141  repeating(repeating_),
142  cbk_type(IFUNCTION)
143  {
144  }
145 
146 #if ETL_CPP11_SUPPORTED
147  //*******************************************
149  //*******************************************
150  callback_timer_data(etl::timer::id::type id_,
151  etl::delegate<void()>& callback_,
152  uint32_t period_,
153  bool repeating_)
154  : p_callback(reinterpret_cast<void*>(&callback_)),
155  period(period_),
156  delta(etl::timer::state::INACTIVE),
157  id(id_),
158  previous(etl::timer::id::NO_TIMER),
159  next(etl::timer::id::NO_TIMER),
160  repeating(repeating_),
161  cbk_type(DELEGATE)
162  {
163  }
164 #endif
165 
166  //*******************************************
168  //*******************************************
169  bool is_active() const
170  {
171  return delta != etl::timer::state::INACTIVE;
172  }
173 
174  //*******************************************
176  //*******************************************
178  {
179  delta = etl::timer::state::INACTIVE;
180  }
181 
182  void* p_callback;
183  uint32_t period;
184  uint32_t delta;
185  etl::timer::id::type id;
186  uint_least8_t previous;
187  uint_least8_t next;
188  bool repeating;
189  callback_type cbk_type;
190 
191  private:
192 
193  // Disabled.
195  callback_timer_data& operator =(const callback_timer_data& other);
196  };
197 
198  namespace private_callback_timer
199  {
200  //*************************************************************************
202  //*************************************************************************
203  class list
204  {
205  public:
206 
207  //*******************************
208  list(etl::callback_timer_data* ptimers_)
209  : head(etl::timer::id::NO_TIMER),
210  tail(etl::timer::id::NO_TIMER),
211  current(etl::timer::id::NO_TIMER),
212  ptimers(ptimers_)
213  {
214  }
215 
216  //*******************************
217  bool empty() const
218  {
219  return head == etl::timer::id::NO_TIMER;
220  }
221 
222  //*******************************
223  // Inserts the timer at the correct delta position
224  //*******************************
225  void insert(etl::timer::id::type id_)
226  {
227  etl::callback_timer_data& timer = ptimers[id_];
228 
229  if (head == etl::timer::id::NO_TIMER)
230  {
231  // No entries yet.
232  head = id_;
233  tail = id_;
234  timer.previous = etl::timer::id::NO_TIMER;
235  timer.next = etl::timer::id::NO_TIMER;
236  }
237  else
238  {
239  // We already have entries.
240  etl::timer::id::type test_id = begin();
241 
242  while (test_id != etl::timer::id::NO_TIMER)
243  {
244  etl::callback_timer_data& test = ptimers[test_id];
245 
246  // Find the correct place to insert.
247  if (timer.delta <= test.delta)
248  {
249  if (test.id == head)
250  {
251  head = timer.id;
252  }
253 
254  // Insert before test.
255  timer.previous = test.previous;
256  test.previous = timer.id;
257  timer.next = test.id;
258 
259  // Adjust the next delta to compensate.
260  test.delta -= timer.delta;
261 
262  if (timer.previous != etl::timer::id::NO_TIMER)
263  {
264  ptimers[timer.previous].next = timer.id;
265  }
266  break;
267  }
268  else
269  {
270  timer.delta -= test.delta;
271  }
272 
273  test_id = next(test_id);
274  }
275 
276  // Reached the end?
277  if (test_id == etl::timer::id::NO_TIMER)
278  {
279  // Tag on to the tail.
280  ptimers[tail].next = timer.id;
281  timer.previous = tail;
282  timer.next = etl::timer::id::NO_TIMER;
283  tail = timer.id;
284  }
285  }
286  }
287 
288  //*******************************
289  void remove(etl::timer::id::type id_, bool has_expired)
290  {
291  etl::callback_timer_data& timer = ptimers[id_];
292 
293  if (head == id_)
294  {
295  head = timer.next;
296  }
297  else
298  {
299  ptimers[timer.previous].next = timer.next;
300  }
301 
302  if (tail == id_)
303  {
304  tail = timer.previous;
305  }
306  else
307  {
308  ptimers[timer.next].previous = timer.previous;
309  }
310 
311  if (!has_expired)
312  {
313  // Adjust the next delta.
314  if (timer.next != etl::timer::id::NO_TIMER)
315  {
316  ptimers[timer.next].delta += timer.delta;
317  }
318  }
319 
320  timer.previous = etl::timer::id::NO_TIMER;
321  timer.next = etl::timer::id::NO_TIMER;
322  timer.delta = etl::timer::state::INACTIVE;
323  }
324 
325  //*******************************
326  etl::callback_timer_data& front()
327  {
328  return ptimers[head];
329  }
330 
331  //*******************************
332  etl::timer::id::type begin()
333  {
334  current = head;
335  return current;
336  }
337 
338  //*******************************
339  etl::timer::id::type previous(etl::timer::id::type last)
340  {
341  current = ptimers[last].previous;
342  return current;
343  }
344 
345  //*******************************
346  etl::timer::id::type next(etl::timer::id::type last)
347  {
348  current = ptimers[last].next;
349  return current;
350  }
351 
352  //*******************************
353  void clear()
354  {
355  etl::timer::id::type id = begin();
356 
357  while (id != etl::timer::id::NO_TIMER)
358  {
359  etl::callback_timer_data& timer = ptimers[id];
360  id = next(id);
361  timer.next = etl::timer::id::NO_TIMER;
362  }
363 
364  head = etl::timer::id::NO_TIMER;
365  tail = etl::timer::id::NO_TIMER;
366  current = etl::timer::id::NO_TIMER;
367  }
368 
369  private:
370 
371  etl::timer::id::type head;
372  etl::timer::id::type tail;
373  etl::timer::id::type current;
374 
375  etl::callback_timer_data* const ptimers;
376  };
377  }
378 
379  //***************************************************************************
381  //***************************************************************************
383  {
384  public:
385 
386  //*******************************************
388  //*******************************************
389  etl::timer::id::type register_timer(void (*p_callback_)(),
390  uint32_t period_,
391  bool repeating_)
392  {
393  etl::timer::id::type id = etl::timer::id::NO_TIMER;
394 
395  bool is_space = (registered_timers < MAX_TIMERS);
396 
397  if (is_space)
398  {
399  // Search for the free space.
400  for (uint_least8_t i = 0; i < MAX_TIMERS; ++i)
401  {
402  etl::callback_timer_data& timer = timer_array[i];
403 
404  if (timer.id == etl::timer::id::NO_TIMER)
405  {
406  // Create in-place.
407  new (&timer) callback_timer_data(i, p_callback_, period_, repeating_);
408  ++registered_timers;
409  id = i;
410  break;
411  }
412  }
413  }
414 
415  return id;
416  }
417 
418  //*******************************************
420  //*******************************************
421  etl::timer::id::type register_timer(etl::ifunction<void>& callback_,
422  uint32_t period_,
423  bool repeating_)
424  {
425  etl::timer::id::type id = etl::timer::id::NO_TIMER;
426 
427  bool is_space = (registered_timers < MAX_TIMERS);
428 
429  if (is_space)
430  {
431  // Search for the free space.
432  for (uint_least8_t i = 0; i < MAX_TIMERS; ++i)
433  {
434  etl::callback_timer_data& timer = timer_array[i];
435 
436  if (timer.id == etl::timer::id::NO_TIMER)
437  {
438  // Create in-place.
439  new (&timer) callback_timer_data(i, callback_, period_, repeating_);
440  ++registered_timers;
441  id = i;
442  break;
443  }
444  }
445  }
446 
447  return id;
448  }
449 
450  //*******************************************
452  //*******************************************
453 #if ETL_CPP11_SUPPORTED
454  etl::timer::id::type register_timer(etl::delegate<void()>& callback_,
455  uint32_t period_,
456  bool repeating_)
457  {
458  etl::timer::id::type id = etl::timer::id::NO_TIMER;
459 
460  bool is_space = (registered_timers < MAX_TIMERS);
461 
462  if (is_space)
463  {
464  // Search for the free space.
465  for (uint_least8_t i = 0; i < MAX_TIMERS; ++i)
466  {
467  etl::callback_timer_data& timer = timer_array[i];
468 
469  if (timer.id == etl::timer::id::NO_TIMER)
470  {
471  // Create in-place.
472  new (&timer) callback_timer_data(i, callback_, period_, repeating_);
473  ++registered_timers;
474  id = i;
475  break;
476  }
477  }
478  }
479 
480  return id;
481  }
482 #endif
483 
484  //*******************************************
486  //*******************************************
487  bool unregister_timer(etl::timer::id::type id_)
488  {
489  bool result = false;
490 
491  if (id_ != etl::timer::id::NO_TIMER)
492  {
493  etl::callback_timer_data& timer = timer_array[id_];
494 
495  if (timer.id != etl::timer::id::NO_TIMER)
496  {
497  if (timer.is_active())
498  {
499  ETL_DISABLE_TIMER_UPDATES;
500  active_list.remove(timer.id, false);
501  ETL_ENABLE_TIMER_UPDATES;
502  }
503 
504  // Reset in-place.
505  new (&timer) callback_timer_data();
506  --registered_timers;
507 
508  result = true;
509  }
510  }
511 
512  return result;
513  }
514 
515  //*******************************************
517  //*******************************************
518  void enable(bool state_)
519  {
520  enabled = state_;
521  }
522 
523  //*******************************************
525  //*******************************************
526  bool is_running() const
527  {
528  return enabled;
529  }
530 
531  //*******************************************
533  //*******************************************
534  void clear()
535  {
536  ETL_DISABLE_TIMER_UPDATES;
537  active_list.clear();
538  ETL_ENABLE_TIMER_UPDATES;
539 
540  for (int i = 0; i < MAX_TIMERS; ++i)
541  {
542  ::new (&timer_array[i]) callback_timer_data();
543  }
544 
545  registered_timers = 0;
546  }
547 
548  //*******************************************
549  // Called by the timer service to indicate the
550  // amount of time that has elapsed since the last successful call to 'tick'.
551  // Returns true if the tick was processed,
552  // false if not.
553  //*******************************************
554  bool tick(uint32_t count)
555  {
556  if (enabled)
557  {
558  if (ETL_TIMER_UPDATES_ENABLED)
559  {
560  // We have something to do?
561  bool has_active = !active_list.empty();
562 
563  if (has_active)
564  {
565  while (has_active && (count >= active_list.front().delta))
566  {
567  etl::callback_timer_data& timer = active_list.front();
568 
569  count -= timer.delta;
570 
571  active_list.remove(timer.id, true);
572 
573  if (timer.repeating)
574  {
575  // Reinsert the timer.
576  timer.delta = timer.period;
577  active_list.insert(timer.id);
578  }
579 
580  if (timer.p_callback != ETL_NULLPTR)
581  {
582  if (timer.cbk_type == callback_timer_data::C_CALLBACK)
583  {
584  // Call the C callback.
585  reinterpret_cast<void(*)()>(timer.p_callback)();
586  }
587  else if(timer.cbk_type == callback_timer_data::IFUNCTION)
588  {
589  // Call the function wrapper callback.
590  (*reinterpret_cast<etl::ifunction<void>*>(timer.p_callback))();
591  }
592 #if ETL_CPP11_SUPPORTED
593  else if(timer.cbk_type == callback_timer_data::DELEGATE)
594  {
595  // Call the delegate callback.
596  (*reinterpret_cast<etl::delegate<void()>*>(timer.p_callback))();
597  }
598 #endif
599  }
600 
601  has_active = !active_list.empty();
602  }
603 
604  if (has_active)
605  {
606  // Subtract any remainder from the next due timeout.
607  active_list.front().delta -= count;
608  }
609  }
610 
611  return true;
612  }
613  }
614 
615  return false;
616  }
617 
618  //*******************************************
620  //*******************************************
621  bool start(etl::timer::id::type id_, bool immediate_ = false)
622  {
623  bool result = false;
624 
625  // Valid timer id?
626  if (id_ != etl::timer::id::NO_TIMER)
627  {
628  etl::callback_timer_data& timer = timer_array[id_];
629 
630  // Registered timer?
631  if (timer.id != etl::timer::id::NO_TIMER)
632  {
633  // Has a valid period.
634  if (timer.period != etl::timer::state::INACTIVE)
635  {
636  ETL_DISABLE_TIMER_UPDATES;
637  if (timer.is_active())
638  {
639  active_list.remove(timer.id, false);
640  }
641 
642  timer.delta = immediate_ ? 0 : timer.period;
643  active_list.insert(timer.id);
644  ETL_ENABLE_TIMER_UPDATES;
645 
646  result = true;
647  }
648  }
649  }
650 
651  return result;
652  }
653 
654  //*******************************************
656  //*******************************************
657  bool stop(etl::timer::id::type id_)
658  {
659  bool result = false;
660 
661  // Valid timer id?
662  if (id_ != etl::timer::id::NO_TIMER)
663  {
664  etl::callback_timer_data& timer = timer_array[id_];
665 
666  // Registered timer?
667  if (timer.id != etl::timer::id::NO_TIMER)
668  {
669  if (timer.is_active())
670  {
671  ETL_DISABLE_TIMER_UPDATES;
672  active_list.remove(timer.id, false);
673  ETL_ENABLE_TIMER_UPDATES;
674  }
675 
676  result = true;
677  }
678  }
679 
680  return result;
681  }
682 
683  //*******************************************
685  //*******************************************
686  bool set_period(etl::timer::id::type id_, uint32_t period_)
687  {
688  if (stop(id_))
689  {
690  timer_array[id_].period = period_;
691  return true;
692  }
693 
694  return false;
695  }
696 
697  //*******************************************
699  //*******************************************
700  bool set_mode(etl::timer::id::type id_, bool repeating_)
701  {
702  if (stop(id_))
703  {
704  timer_array[id_].repeating = repeating_;
705  return true;
706  }
707 
708  return false;
709  }
710 
711  protected:
712 
713  //*******************************************
715  //*******************************************
716  icallback_timer(callback_timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
717  : timer_array(timer_array_),
718  active_list(timer_array_),
719  enabled(false),
720 #if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK)
721  process_semaphore(0),
722 #endif
723  registered_timers(0),
724  MAX_TIMERS(MAX_TIMERS_)
725  {
726  }
727 
728  private:
729 
730  // The array of timer data structures.
731  callback_timer_data* const timer_array;
732 
733  // The list of active timers.
734  private_callback_timer::list active_list;
735 
736  volatile bool enabled;
737 #if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK)
738  volatile etl::timer_semaphore_t process_semaphore;
739 #endif
740  volatile uint_least8_t registered_timers;
741 
742  public:
743 
744  const uint_least8_t MAX_TIMERS;
745  };
746 
747  //***************************************************************************
749  //***************************************************************************
750  template <const uint_least8_t MAX_TIMERS_>
752  {
753  public:
754 
755  ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
756 
757  //*******************************************
759  //*******************************************
761  : icallback_timer(timer_array, MAX_TIMERS_)
762  {
763  }
764 
765  private:
766 
767  callback_timer_data timer_array[MAX_TIMERS_];
768  };
769 }
770 
771 #undef ETL_DISABLE_TIMER_UPDATES
772 #undef ETL_ENABLE_TIMER_UPDATES
773 #undef ETL_TIMER_UPDATES_ENABLED
774 
775 #undef ETL_FILE
776 
777 #endif
For all types except bool and pointers.
Definition: atomic_gcc_sync.h:69
The callback timer.
Definition: callback_timer.h:752
callback_timer()
Constructor.
Definition: callback_timer.h:760
Definition: delegate.h:91
Interface for callback timer.
Definition: callback_timer.h:383
bool unregister_timer(etl::timer::id::type id_)
Register a timer.
Definition: callback_timer.h:487
bool is_running() const
Get the enable/disable state.
Definition: callback_timer.h:526
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition: callback_timer.h:686
etl::timer::id::type register_timer(void(*p_callback_)(), uint32_t period_, bool repeating_)
Register a timer.
Definition: callback_timer.h:389
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition: callback_timer.h:621
icallback_timer(callback_timer_data *const timer_array_, const uint_least8_t MAX_TIMERS_)
Constructor.
Definition: callback_timer.h:716
bool set_mode(etl::timer::id::type id_, bool repeating_)
Sets a timer's mode.
Definition: callback_timer.h:700
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition: callback_timer.h:657
void enable(bool state_)
Enable/disable the timer.
Definition: callback_timer.h:518
etl::timer::id::type register_timer(etl::ifunction< void > &callback_, uint32_t period_, bool repeating_)
Register a timer.
Definition: callback_timer.h:421
void clear()
Clears the timer of data.
Definition: callback_timer.h:534
A specialised intrusive linked list for timer data.
Definition: callback_timer.h:204
Definition: function.h:73
Definition: absolute.h:37
The configuration of a timer.
Definition: callback_timer.h:89
callback_timer_data(etl::timer::id::type id_, void(*p_callback_)(), uint32_t period_, bool repeating_)
C function callback.
Definition: callback_timer.h:113
callback_timer_data(etl::timer::id::type id_, etl::ifunction< void > &callback_, uint32_t period_, bool repeating_)
ETL function callback.
Definition: callback_timer.h:131
bool is_active() const
Returns true if the timer is active.
Definition: callback_timer.h:169
void set_inactive()
Sets the timer to the inactive state.
Definition: callback_timer.h:177
Definition: timer.h:81
Common definitions for the timer framework.
Definition: timer.h:54