acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_timer.c (9593B)

      1 /**
      2  * @file lv_timer.c
      3  */
      4 
      5 /*********************
      6  *      INCLUDES
      7  *********************/
      8 #include "lv_timer.h"
      9 #include "../hal/lv_hal_tick.h"
     10 #include "lv_assert.h"
     11 #include "lv_mem.h"
     12 #include "lv_ll.h"
     13 #include "lv_gc.h"
     14 
     15 /*********************
     16  *      DEFINES
     17  *********************/
     18 #define IDLE_MEAS_PERIOD 500 /*[ms]*/
     19 #define DEF_PERIOD 500
     20 
     21 /**********************
     22  *      TYPEDEFS
     23  **********************/
     24 
     25 /**********************
     26  *  STATIC PROTOTYPES
     27  **********************/
     28 static bool lv_timer_exec(lv_timer_t * timer);
     29 static uint32_t lv_timer_time_remaining(lv_timer_t * timer);
     30 
     31 /**********************
     32  *  STATIC VARIABLES
     33  **********************/
     34 static bool lv_timer_run = false;
     35 static uint8_t idle_last = 0;
     36 static bool timer_deleted;
     37 static bool timer_created;
     38 
     39 /**********************
     40  *      MACROS
     41  **********************/
     42 #if LV_LOG_TRACE_TIMER
     43     #define TIMER_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
     44 #else
     45     #define TIMER_TRACE(...)
     46 #endif
     47 
     48 /**********************
     49  *   GLOBAL FUNCTIONS
     50  **********************/
     51 
     52 /**
     53  * Init the lv_timer module
     54  */
     55 void _lv_timer_core_init(void)
     56 {
     57     _lv_ll_init(&LV_GC_ROOT(_lv_timer_ll), sizeof(lv_timer_t));
     58 
     59     /*Initially enable the lv_timer handling*/
     60     lv_timer_enable(true);
     61 }
     62 
     63 /**
     64  * Call it periodically to handle lv_timers.
     65  * @return the time after which it must be called again
     66  */
     67 LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void)
     68 {
     69     TIMER_TRACE("begin");
     70 
     71     /*Avoid concurrent running of the timer handler*/
     72     static bool already_running = false;
     73     if(already_running) {
     74         TIMER_TRACE("already running, concurrent calls are not allow, returning");
     75         return 1;
     76     }
     77     already_running = true;
     78 
     79     if(lv_timer_run == false) {
     80         already_running = false; /*Release mutex*/
     81         return 1;
     82     }
     83 
     84     static uint32_t idle_period_start = 0;
     85     static uint32_t busy_time         = 0;
     86 
     87     uint32_t handler_start = lv_tick_get();
     88 
     89     if(handler_start == 0) {
     90         static uint32_t run_cnt = 0;
     91         run_cnt++;
     92         if(run_cnt > 100) {
     93             run_cnt = 0;
     94             LV_LOG_WARN("It seems lv_tick_inc() is not called.");
     95         }
     96     }
     97 
     98     /*Run all timer from the list*/
     99     lv_timer_t * next;
    100     do {
    101         timer_deleted             = false;
    102         timer_created             = false;
    103         LV_GC_ROOT(_lv_timer_act) = _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
    104         while(LV_GC_ROOT(_lv_timer_act)) {
    105             /*The timer might be deleted if it runs only once ('repeat_count = 1')
    106              *So get next element until the current is surely valid*/
    107             next = _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), LV_GC_ROOT(_lv_timer_act));
    108 
    109             if(lv_timer_exec(LV_GC_ROOT(_lv_timer_act))) {
    110                 /*If a timer was created or deleted then this or the next item might be corrupted*/
    111                 if(timer_created || timer_deleted) {
    112                     TIMER_TRACE("Start from the first timer again because a timer was created or deleted");
    113                     break;
    114                 }
    115             }
    116 
    117             LV_GC_ROOT(_lv_timer_act) = next; /*Load the next timer*/
    118         }
    119     } while(LV_GC_ROOT(_lv_timer_act));
    120 
    121     uint32_t time_till_next = LV_NO_TIMER_READY;
    122     next = _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
    123     while(next) {
    124         if(!next->paused) {
    125             uint32_t delay = lv_timer_time_remaining(next);
    126             if(delay < time_till_next)
    127                 time_till_next = delay;
    128         }
    129 
    130         next = _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), next); /*Find the next timer*/
    131     }
    132 
    133     busy_time += lv_tick_elaps(handler_start);
    134     uint32_t idle_period_time = lv_tick_elaps(idle_period_start);
    135     if(idle_period_time >= IDLE_MEAS_PERIOD) {
    136         idle_last         = (busy_time * 100) / idle_period_time;  /*Calculate the busy percentage*/
    137         idle_last         = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/
    138         busy_time         = 0;
    139         idle_period_start = lv_tick_get();
    140     }
    141 
    142     already_running = false; /*Release the mutex*/
    143 
    144     TIMER_TRACE("finished (%d ms until the next timer call)", time_till_next);
    145     return time_till_next;
    146 }
    147 
    148 /**
    149  * Create an "empty" timer. It needs to initialized with at least
    150  * `lv_timer_set_cb` and `lv_timer_set_period`
    151  * @return pointer to the created timer
    152  */
    153 lv_timer_t * lv_timer_create_basic(void)
    154 {
    155     return lv_timer_create(NULL, DEF_PERIOD, NULL);
    156 }
    157 
    158 /**
    159  * Create a new lv_timer
    160  * @param timer_xcb a callback which is the timer itself. It will be called periodically.
    161  *                 (the 'x' in the argument name indicates that it's not a fully generic function because it not follows
    162  *                  the `func_name(object, callback, ...)` convention)
    163  * @param period call period in ms unit
    164  * @param user_data custom parameter
    165  * @return pointer to the new timer
    166  */
    167 lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data)
    168 {
    169     lv_timer_t * new_timer = NULL;
    170 
    171     new_timer = _lv_ll_ins_head(&LV_GC_ROOT(_lv_timer_ll));
    172     LV_ASSERT_MALLOC(new_timer);
    173     if(new_timer == NULL) return NULL;
    174 
    175     new_timer->period = period;
    176     new_timer->timer_cb = timer_xcb;
    177     new_timer->repeat_count = -1;
    178     new_timer->paused = 0;
    179     new_timer->last_run = lv_tick_get();
    180     new_timer->user_data = user_data;
    181 
    182     timer_created = true;
    183 
    184     return new_timer;
    185 }
    186 
    187 /**
    188  * Set the callback the timer (the function to call periodically)
    189  * @param timer pointer to a timer
    190  * @param timer_cb the function to call periodically
    191  */
    192 void lv_timer_set_cb(lv_timer_t * timer, lv_timer_cb_t timer_cb)
    193 {
    194     timer->timer_cb = timer_cb;
    195 }
    196 
    197 /**
    198  * Delete a lv_timer
    199  * @param timer pointer to timer created by timer
    200  */
    201 void lv_timer_del(lv_timer_t * timer)
    202 {
    203     _lv_ll_remove(&LV_GC_ROOT(_lv_timer_ll), timer);
    204     timer_deleted = true;
    205 
    206     lv_mem_free(timer);
    207 }
    208 
    209 /**
    210  * Pause/resume a timer.
    211  * @param timer pointer to an lv_timer
    212  */
    213 void lv_timer_pause(lv_timer_t * timer)
    214 {
    215     timer->paused = true;
    216 }
    217 
    218 void lv_timer_resume(lv_timer_t * timer)
    219 {
    220     timer->paused = false;
    221 }
    222 
    223 /**
    224  * Set new period for a lv_timer
    225  * @param timer pointer to a lv_timer
    226  * @param period the new period
    227  */
    228 void lv_timer_set_period(lv_timer_t * timer, uint32_t period)
    229 {
    230     timer->period = period;
    231 }
    232 
    233 /**
    234  * Make a lv_timer ready. It will not wait its period.
    235  * @param timer pointer to a lv_timer.
    236  */
    237 void lv_timer_ready(lv_timer_t * timer)
    238 {
    239     timer->last_run = lv_tick_get() - timer->period - 1;
    240 }
    241 
    242 /**
    243  * Set the number of times a timer will repeat.
    244  * @param timer pointer to a lv_timer.
    245  * @param repeat_count -1 : infinity;  0 : stop ;  n >0: residual times
    246  */
    247 void lv_timer_set_repeat_count(lv_timer_t * timer, int32_t repeat_count)
    248 {
    249     timer->repeat_count = repeat_count;
    250 }
    251 
    252 /**
    253  * Reset a lv_timer.
    254  * It will be called the previously set period milliseconds later.
    255  * @param timer pointer to a lv_timer.
    256  */
    257 void lv_timer_reset(lv_timer_t * timer)
    258 {
    259     timer->last_run = lv_tick_get();
    260 }
    261 
    262 /**
    263  * Enable or disable the whole lv_timer handling
    264  * @param en true: lv_timer handling is running, false: lv_timer handling is suspended
    265  */
    266 void lv_timer_enable(bool en)
    267 {
    268     lv_timer_run = en;
    269 }
    270 
    271 /**
    272  * Get idle percentage
    273  * @return the lv_timer idle in percentage
    274  */
    275 uint8_t lv_timer_get_idle(void)
    276 {
    277     return idle_last;
    278 }
    279 
    280 /**
    281  * Iterate through the timers
    282  * @param timer NULL to start iteration or the previous return value to get the next timer
    283  * @return the next timer or NULL if there is no more timer
    284  */
    285 lv_timer_t * lv_timer_get_next(lv_timer_t * timer)
    286 {
    287     if(timer == NULL) return _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
    288     else return _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), timer);
    289 }
    290 
    291 /**********************
    292  *   STATIC FUNCTIONS
    293  **********************/
    294 
    295 /**
    296  * Execute timer if its remaining time is zero
    297  * @param timer pointer to lv_timer
    298  * @return true: execute, false: not executed
    299  */
    300 static bool lv_timer_exec(lv_timer_t * timer)
    301 {
    302     if(timer->paused) return false;
    303 
    304     bool exec = false;
    305     if(lv_timer_time_remaining(timer) == 0) {
    306         /* Decrement the repeat count before executing the timer_cb.
    307          * If any timer is deleted `if(timer->repeat_count == 0)` is not executed below
    308          * but at least the repeat count is zero and the timer can be deleted in the next round*/
    309         int32_t original_repeat_count = timer->repeat_count;
    310         if(timer->repeat_count > 0) timer->repeat_count--;
    311         timer->last_run = lv_tick_get();
    312         TIMER_TRACE("calling timer callback: %p", *((void **)&timer->timer_cb));
    313         if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer);
    314         TIMER_TRACE("timer callback %p finished", *((void **)&timer->timer_cb));
    315         LV_ASSERT_MEM_INTEGRITY();
    316         exec = true;
    317     }
    318 
    319     if(timer_deleted == false) { /*The timer might be deleted by itself as well*/
    320         if(timer->repeat_count == 0) { /*The repeat count is over, delete the timer*/
    321             TIMER_TRACE("deleting timer with %p callback because the repeat count is over", *((void **)&timer->timer_cb));
    322             lv_timer_del(timer);
    323         }
    324     }
    325 
    326     return exec;
    327 }
    328 
    329 /**
    330  * Find out how much time remains before a timer must be run.
    331  * @param timer pointer to lv_timer
    332  * @return the time remaining, or 0 if it needs to be run again
    333  */
    334 static uint32_t lv_timer_time_remaining(lv_timer_t * timer)
    335 {
    336     /*Check if at least 'period' time elapsed*/
    337     uint32_t elp = lv_tick_elaps(timer->last_run);
    338     if(elp >= timer->period)
    339         return 0;
    340     return timer->period - elp;
    341 }