Stefan Scholz / ETL
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers callback_timer.h Source File

callback_timer.h

00001 /******************************************************************************
00002 The MIT License(MIT)
00003 
00004 Embedded Template Library.
00005 https://github.com/ETLCPP/etl
00006 https://www.etlcpp.com
00007 
00008 Copyright(c) 2017 jwellbelove
00009 
00010 Permission is hereby granted, free of charge, to any person obtaining a copy
00011 of this software and associated documentation files(the "Software"), to deal
00012 in the Software without restriction, including without limitation the rights
00013 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
00014 copies of the Software, and to permit persons to whom the Software is
00015 furnished to do so, subject to the following conditions :
00016 
00017 The above copyright notice and this permission notice shall be included in all
00018 copies or substantial portions of the Software.
00019 
00020 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00021 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00022 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
00023 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00024 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00025 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00026 SOFTWARE.
00027 ******************************************************************************/
00028 
00029 #ifndef __ETL_CALLBACK_TIMER__
00030 #define __ETL_CALLBACK_TIMER__
00031 
00032 #include <stdint.h>
00033 #include <algorithm>
00034 
00035 #include "platform.h "
00036 #include "nullptr.h "
00037 #include "function.h "
00038 #include "static_assert.h"
00039 #include "timer.h"
00040 #include "atomic.h"
00041 
00042 #undef ETL_FILE
00043 #define ETL_FILE "42"
00044 
00045 namespace etl
00046 {
00047   //*************************************************************************
00048   /// The configuration of a timer.
00049   struct callback_timer_data
00050   {
00051     //*******************************************
00052     callback_timer_data()
00053       : p_callback(std::nullptr),
00054         period(0),
00055         delta(etl::timer::state::INACTIVE),
00056         id(etl::timer::id::NO_TIMER),
00057         previous(etl::timer::id::NO_TIMER),
00058         next(etl::timer::id::NO_TIMER),
00059         repeating(true),
00060         has_c_callback(true)
00061     {
00062     }
00063 
00064     //*******************************************
00065     /// C function callback
00066     //*******************************************
00067     callback_timer_data(etl::timer::id::type id_,
00068                         void                 (*p_callback_)(),
00069                         uint32_t             period_,
00070                         bool                 repeating_)
00071       : p_callback(reinterpret_cast<void*>(p_callback_)),
00072         period(period_),
00073         delta(etl::timer::state::INACTIVE),
00074         id(id_),
00075         previous(etl::timer::id::NO_TIMER),
00076         next(etl::timer::id::NO_TIMER),
00077         repeating(repeating_),
00078         has_c_callback(true)
00079     {
00080     }
00081 
00082     //*******************************************
00083     /// ETL function callback
00084     //*******************************************
00085     callback_timer_data(etl::timer::id::type  id_,
00086                         etl::ifunction<void>& callback_,
00087                         uint32_t              period_,
00088                         bool                  repeating_)
00089       : p_callback(reinterpret_cast<void*>(&callback_)),
00090         period(period_),
00091         delta(etl::timer::state::INACTIVE),
00092         id(id_),
00093         previous(etl::timer::id::NO_TIMER),
00094         next(etl::timer::id::NO_TIMER),
00095         repeating(repeating_),
00096         has_c_callback(false)
00097     {
00098     }
00099 
00100     //*******************************************
00101     /// Returns true if the timer is active.
00102     //*******************************************
00103     bool is_active() const
00104     {
00105       return delta != etl::timer::state::INACTIVE;
00106     }
00107 
00108     //*******************************************
00109     /// Sets the timer to the inactive state.
00110     //*******************************************
00111     void set_inactive()
00112     {
00113       delta = etl::timer::state::INACTIVE;
00114     }
00115 
00116     void*                 p_callback;
00117     uint32_t              period;
00118     uint32_t              delta;
00119     etl::timer::id::type  id;
00120     uint_least8_t         previous;
00121     uint_least8_t         next;
00122     bool                  repeating;
00123     bool                  has_c_callback;
00124 
00125   private:
00126 
00127     // Disabled.
00128     callback_timer_data(const callback_timer_data& other);
00129     callback_timer_data& operator =(const callback_timer_data& other);
00130   };
00131 
00132   namespace __private_callback_timer__
00133   {
00134     //*************************************************************************
00135     /// A specialised intrusive linked list for timer data.
00136     //*************************************************************************
00137     class list
00138     {
00139     public:
00140 
00141       //*******************************
00142       list(etl::callback_timer_data* ptimers_)
00143         : head(etl::timer::id::NO_TIMER),
00144           tail(etl::timer::id::NO_TIMER),
00145           current(etl::timer::id::NO_TIMER),
00146           ptimers(ptimers_)
00147       {
00148       }
00149 
00150       //*******************************
00151       bool empty() const
00152       {
00153         return head == etl::timer::id::NO_TIMER;
00154       }
00155 
00156       //*******************************
00157       // Inserts the timer at the correct delta position
00158       //*******************************
00159       void insert(etl::timer::id::type id_)
00160       {
00161         etl::callback_timer_data& timer = ptimers[id_];
00162 
00163         if (head == etl::timer::id::NO_TIMER)
00164         {
00165           // No entries yet.
00166           head = id_;
00167           tail = id_;
00168           timer.previous = etl::timer::id::NO_TIMER;
00169           timer.next     = etl::timer::id::NO_TIMER;
00170         }
00171         else
00172         {
00173           // We already have entries.
00174           etl::timer::id::type test_id = begin();
00175 
00176           while (test_id != etl::timer::id::NO_TIMER)
00177           {
00178             etl::callback_timer_data& test = ptimers[test_id];
00179 
00180             // Find the correct place to insert.
00181             if (timer.delta <= test.delta)
00182             {
00183               if (test.id == head)
00184               {
00185                 head = timer.id;
00186               }
00187 
00188               // Insert before test.
00189               timer.previous = test.previous;
00190               test.previous  = timer.id;
00191               timer.next     = test.id;
00192 
00193               // Adjust the next delta to compensate.
00194               test.delta -= timer.delta;
00195 
00196               if (timer.previous != etl::timer::id::NO_TIMER)
00197               {
00198                 ptimers[timer.previous].next = timer.id;
00199               }
00200               break;
00201             }
00202             else
00203             {
00204               timer.delta -= test.delta;
00205             }
00206 
00207             test_id = next(test_id);
00208           }
00209 
00210           // Reached the end?
00211           if (test_id == etl::timer::id::NO_TIMER)
00212           {
00213             // Tag on to the tail.
00214             ptimers[tail].next = timer.id;
00215             timer.previous     = tail;
00216             timer.next         = etl::timer::id::NO_TIMER;
00217             tail               = timer.id;
00218           }
00219         }
00220       }
00221 
00222       //*******************************
00223       void remove(etl::timer::id::type id_, bool has_expired)
00224       {
00225         etl::callback_timer_data& timer = ptimers[id_];
00226 
00227         if (head == id_)
00228         {
00229           head = timer.next;
00230         }
00231         else
00232         {
00233           ptimers[timer.previous].next = timer.next;
00234         }
00235 
00236         if (tail == id_)
00237         {
00238           tail = timer.previous;
00239         }
00240         else
00241         {
00242           ptimers[timer.next].previous = timer.previous;
00243         }
00244 
00245         if (!has_expired)
00246         {
00247           // Adjust the next delta.
00248           if (timer.next != etl::timer::id::NO_TIMER)
00249           {
00250             ptimers[timer.next].delta += timer.delta;
00251           }
00252         }
00253 
00254         timer.previous = etl::timer::id::NO_TIMER;
00255         timer.next     = etl::timer::id::NO_TIMER;
00256         timer.delta    = etl::timer::state::INACTIVE;
00257       }
00258 
00259       //*******************************
00260       etl::callback_timer_data& front()
00261       {
00262         return ptimers[head];
00263       }
00264 
00265       //*******************************
00266       etl::timer::id::type begin()
00267       {
00268         current = head;
00269         return current;
00270       }
00271 
00272       //*******************************
00273       etl::timer::id::type previous(etl::timer::id::type last)
00274       {
00275         current = ptimers[last].previous;
00276         return current;
00277       }
00278 
00279       //*******************************
00280       etl::timer::id::type next(etl::timer::id::type last)
00281       {
00282         current = ptimers[last].next;
00283         return current;
00284       }
00285 
00286       //*******************************
00287       void clear()
00288       {
00289         etl::timer::id::type id = begin();
00290 
00291         while (id != etl::timer::id::NO_TIMER)
00292         {
00293           etl::callback_timer_data& timer = ptimers[id];
00294           id = next(id);
00295           timer.next = etl::timer::id::NO_TIMER;
00296         }
00297 
00298         head    = etl::timer::id::NO_TIMER;
00299         tail    = etl::timer::id::NO_TIMER;
00300         current = etl::timer::id::NO_TIMER;
00301       }
00302 
00303     private:
00304 
00305       etl::timer::id::type head;
00306       etl::timer::id::type tail;
00307       etl::timer::id::type current;
00308 
00309       etl::callback_timer_data* const ptimers;
00310     };
00311   }
00312 
00313   //***************************************************************************
00314   /// Interface for callback timer
00315   //***************************************************************************
00316   class icallback_timer
00317   {
00318   public:
00319 
00320     //*******************************************
00321     /// Register a timer.
00322     //*******************************************
00323     etl::timer::id::type register_timer(void     (*p_callback_)(),
00324                                         uint32_t period_,
00325                                         bool     repeating_)
00326     {
00327       etl::timer::id::type id = etl::timer::id::NO_TIMER;
00328 
00329       disable_timer_updates();
00330 
00331       bool is_space = (registered_timers < MAX_TIMERS);
00332 
00333       if (is_space)
00334       {
00335         // Search for the free space.
00336         for (uint_least8_t i = 0; i < MAX_TIMERS; ++i)
00337         {
00338           etl::callback_timer_data& timer = timer_array[i];
00339 
00340           if (timer.id == etl::timer::id::NO_TIMER)
00341           {
00342             // Create in-place.
00343             new (&timer) callback_timer_data(i, p_callback_, period_, repeating_);
00344             ++registered_timers;
00345             id = i;
00346             break;
00347           }
00348         }
00349       }
00350 
00351       enable_timer_updates();
00352 
00353       return id;
00354     }
00355 
00356     //*******************************************
00357     /// Register a timer.
00358     //*******************************************
00359     etl::timer::id::type register_timer(etl::ifunction<void>& callback_,
00360                                         uint32_t              period_,
00361                                         bool                  repeating_)
00362     {
00363       etl::timer::id::type id = etl::timer::id::NO_TIMER;
00364 
00365       disable_timer_updates();
00366 
00367       bool is_space = (registered_timers < MAX_TIMERS);
00368 
00369       if (is_space)
00370       {
00371         // Search for the free space.
00372         for (uint_least8_t i = 0; i < MAX_TIMERS; ++i)
00373         {
00374           etl::callback_timer_data& timer = timer_array[i];
00375 
00376           if (timer.id == etl::timer::id::NO_TIMER)
00377           {
00378             // Create in-place.
00379             new (&timer) callback_timer_data(i, callback_, period_, repeating_);
00380             ++registered_timers;
00381             id = i;
00382             break;
00383           }
00384         }
00385       }
00386 
00387       enable_timer_updates();
00388 
00389       return id;
00390     }
00391 
00392     //*******************************************
00393     /// Unregister a timer.
00394     //*******************************************
00395     bool unregister_timer(etl::timer::id::type id_)
00396     {
00397       bool result = false;
00398 
00399       if (id_ != etl::timer::id::NO_TIMER)
00400       {
00401         disable_timer_updates();
00402 
00403         etl::callback_timer_data& timer = timer_array[id_];
00404 
00405         if (timer.id != etl::timer::id::NO_TIMER)
00406         {
00407           if (timer.is_active())
00408           {
00409             active_list.remove(timer.id, false);
00410 
00411             // Reset in-place.
00412             new (&timer) callback_timer_data();
00413             --registered_timers;
00414 
00415             result = true;
00416           }
00417         }
00418 
00419         enable_timer_updates();
00420       }
00421 
00422       return result;
00423     }
00424 
00425     //*******************************************
00426     /// Enable/disable the timer.
00427     //*******************************************
00428     void enable(bool state_)
00429     {
00430       enabled = state_;
00431     }
00432 
00433     //*******************************************
00434     /// Get the enable/disable state.
00435     //*******************************************
00436     bool is_running() const
00437     {
00438       return enabled;
00439     }
00440 
00441     //*******************************************
00442     /// Clears the timer of data.
00443     //*******************************************
00444     void clear()
00445     {
00446       disable_timer_updates();
00447 
00448       active_list.clear();
00449 
00450       for (int i = 0; i < MAX_TIMERS; ++i)
00451       {
00452         new (&timer_array[i]) callback_timer_data();
00453       }
00454 
00455       registered_timers = 0;
00456 
00457       enable_timer_updates();
00458     }
00459 
00460     //*******************************************
00461     // Called by the timer service to indicate the
00462     // amount of time that has elapsed since the last successful call to 'tick'.
00463     // Returns true if the tick was processed,
00464     // false if not.
00465     //*******************************************
00466     bool tick(uint32_t count)
00467     {
00468       if (enabled)
00469       {
00470         if (process_semaphore.load() == 0)
00471         {
00472           // We have something to do?
00473           bool has_active = !active_list.empty();
00474 
00475           if (has_active)
00476           {
00477             while (has_active && (count >= active_list.front().delta))
00478             {
00479               etl::callback_timer_data& timer = active_list.front();
00480 
00481               count -= timer.delta;
00482 
00483               active_list.remove(timer.id, true);
00484 
00485               if (timer.repeating)
00486               {
00487                 // Reinsert the timer.
00488                 timer.delta = timer.period;
00489                 active_list.insert(timer.id);
00490               }
00491 
00492               if (timer.p_callback != std::nullptr)
00493               {
00494                 if (timer.has_c_callback)
00495                 {
00496                   // Call the C callback.
00497                   reinterpret_cast<void(*)()>(timer.p_callback)();
00498                 }
00499                 else
00500                 {
00501                   // Call the function wrapper callback.
00502                   (*reinterpret_cast<etl::ifunction<void>*>(timer.p_callback))();
00503                 }
00504               }
00505 
00506               has_active = !active_list.empty();
00507             }
00508 
00509             if (has_active)
00510             {
00511               // Subtract any remainder from the next due timeout.
00512               active_list.front().delta -= count;
00513             }
00514           }
00515 
00516           return true;
00517         }
00518       }
00519 
00520       return false;
00521     }
00522 
00523     //*******************************************
00524     /// Starts a timer.
00525     //*******************************************
00526     bool start(etl::timer::id::type id_, bool immediate_ = false)
00527     {
00528       disable_timer_updates();
00529 
00530       bool result = false;
00531 
00532       // Valid timer id?
00533       if (id_ != etl::timer::id::NO_TIMER)
00534       {
00535         etl::callback_timer_data& timer = timer_array[id_];
00536 
00537         // Registered timer?
00538         if (timer.id != etl::timer::id::NO_TIMER)
00539         {         
00540           // Has a valid period.
00541           if (timer.period != etl::timer::state::INACTIVE)
00542           {
00543             if (timer.is_active())
00544             {
00545               active_list.remove(timer.id, false);
00546             }
00547 
00548             timer.delta = immediate_ ? 0 : timer.period;
00549             active_list.insert(timer.id);
00550 
00551             result = true;
00552           }                
00553         }
00554       }
00555 
00556       enable_timer_updates();
00557 
00558       return result;
00559     }
00560 
00561     //*******************************************
00562     /// Stops a timer.
00563     //*******************************************
00564     bool stop(etl::timer::id::type id_)
00565     {
00566       disable_timer_updates();
00567 
00568       bool result = false;
00569 
00570       // Valid timer id?
00571       if (id_ != etl::timer::id::NO_TIMER)
00572       {
00573         etl::callback_timer_data& timer = timer_array[id_];
00574 
00575         // Registered timer?
00576         if (timer.id != etl::timer::id::NO_TIMER)
00577         {
00578           if (timer.is_active())
00579           {
00580             active_list.remove(timer.id, false);
00581             result = true;
00582           }
00583         }
00584       }
00585 
00586       enable_timer_updates();
00587 
00588       return result;
00589     }
00590 
00591     //*******************************************
00592     /// Sets a timer's period.
00593     //*******************************************
00594     bool set_period(etl::timer::id::type id_, uint32_t period_)
00595     {
00596       if (stop(id_))
00597       {
00598         timer_array[id_].period = period_;
00599         return start(id_);
00600       }
00601 
00602       return false;
00603     }
00604 
00605     //*******************************************
00606     /// Sets a timer's mode.
00607     //*******************************************
00608     bool set_mode(etl::timer::id::type id_, bool repeating_)
00609     {
00610       if (stop(id_))
00611       {
00612         timer_array[id_].repeating = repeating_;
00613         return start(id_);
00614       }
00615 
00616       return false;
00617     }
00618 
00619   protected:
00620 
00621     //*******************************************
00622     /// Constructor.
00623     //*******************************************
00624     icallback_timer(callback_timer_data* const timer_array_, const uint_least8_t  MAX_TIMERS_)
00625       : timer_array(timer_array_),
00626         active_list(timer_array_),
00627         enabled(false),
00628         process_semaphore(0),
00629         registered_timers(0),
00630         MAX_TIMERS(MAX_TIMERS_)
00631     {
00632     }
00633 
00634   private:
00635 
00636     //*******************************************
00637     /// Enable timer callback events.
00638     //*******************************************
00639     void enable_timer_updates()
00640     {
00641       --process_semaphore;
00642     }
00643 
00644     //*******************************************
00645     /// Disable timer callback events.
00646     //*******************************************
00647     void disable_timer_updates()
00648     {
00649       ++process_semaphore;
00650     }
00651 
00652     // The array of timer data structures.
00653     callback_timer_data* const timer_array;
00654 
00655     // The list of active timers.
00656     __private_callback_timer__::list active_list;
00657 
00658     volatile bool enabled;
00659     volatile etl::timer_semaphore_t process_semaphore;
00660     volatile uint_least8_t registered_timers;
00661 
00662   public:
00663 
00664     const uint_least8_t MAX_TIMERS;
00665   };
00666 
00667   //***************************************************************************
00668   /// The callback timer
00669   //***************************************************************************
00670   template <const uint_least8_t MAX_TIMERS_>
00671   class callback_timer : public etl::icallback_timer
00672   {
00673   public:
00674 
00675     STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
00676 
00677     //*******************************************
00678     /// Constructor.
00679     //*******************************************
00680     callback_timer()
00681       : icallback_timer(timer_array, MAX_TIMERS_)
00682     {
00683     }
00684 
00685   private:
00686 
00687     callback_timer_data timer_array[MAX_TIMERS_];
00688   };
00689 }
00690 
00691 #undef ETL_FILE
00692 
00693 #endif
00694