* LVGL library updated to v9.0.0, some small breaking changes in C, none in HASPmota * fix compilation * Move lvgl_berry to LVGL_assets
360 lines
9.5 KiB
C
360 lines
9.5 KiB
C
/**
|
|
* @file lv_timer.c
|
|
*/
|
|
|
|
/*********************
|
|
* INCLUDES
|
|
*********************/
|
|
#include "lv_timer.h"
|
|
#include "../core/lv_global.h"
|
|
#include "../tick/lv_tick.h"
|
|
#include "../stdlib/lv_mem.h"
|
|
#include "../stdlib/lv_sprintf.h"
|
|
#include "lv_assert.h"
|
|
#include "lv_ll.h"
|
|
#include "lv_profiler.h"
|
|
|
|
/*********************
|
|
* DEFINES
|
|
*********************/
|
|
|
|
#define IDLE_MEAS_PERIOD 500 /*[ms]*/
|
|
#define DEF_PERIOD 500
|
|
|
|
#define state LV_GLOBAL_DEFAULT()->timer_state
|
|
#define timer_ll_p &(state.timer_ll)
|
|
|
|
/**********************
|
|
* TYPEDEFS
|
|
**********************/
|
|
|
|
/**********************
|
|
* STATIC PROTOTYPES
|
|
**********************/
|
|
static bool lv_timer_exec(lv_timer_t * timer);
|
|
static uint32_t lv_timer_time_remaining(lv_timer_t * timer);
|
|
static void lv_timer_handler_resume(void);
|
|
|
|
/**********************
|
|
* STATIC VARIABLES
|
|
**********************/
|
|
|
|
/**********************
|
|
* MACROS
|
|
**********************/
|
|
#if LV_USE_LOG && LV_LOG_TRACE_TIMER
|
|
#define LV_TRACE_TIMER(...) LV_LOG_TRACE(__VA_ARGS__)
|
|
#else
|
|
#define LV_TRACE_TIMER(...)
|
|
#endif
|
|
|
|
/**********************
|
|
* GLOBAL FUNCTIONS
|
|
**********************/
|
|
|
|
void _lv_timer_core_init(void)
|
|
{
|
|
_lv_ll_init(timer_ll_p, sizeof(lv_timer_t));
|
|
|
|
/*Initially enable the lv_timer handling*/
|
|
lv_timer_enable(true);
|
|
}
|
|
|
|
LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void)
|
|
{
|
|
LV_TRACE_TIMER("begin");
|
|
|
|
lv_timer_state_t * state_p = &state;
|
|
/*Avoid concurrent running of the timer handler*/
|
|
if(state_p->already_running) {
|
|
LV_TRACE_TIMER("already running, concurrent calls are not allow, returning");
|
|
return 1;
|
|
}
|
|
state_p->already_running = true;
|
|
|
|
if(state_p->lv_timer_run == false) {
|
|
state_p->already_running = false; /*Release mutex*/
|
|
return 1;
|
|
}
|
|
|
|
LV_PROFILER_BEGIN;
|
|
uint32_t handler_start = lv_tick_get();
|
|
|
|
if(handler_start == 0) {
|
|
state.run_cnt++;
|
|
if(state.run_cnt > 100) {
|
|
state.run_cnt = 0;
|
|
LV_LOG_WARN("It seems lv_tick_inc() is not called.");
|
|
}
|
|
}
|
|
|
|
/*Run all timer from the list*/
|
|
lv_timer_t * next;
|
|
lv_timer_t * timer_active;
|
|
lv_ll_t * timer_head = timer_ll_p;
|
|
do {
|
|
state_p->timer_deleted = false;
|
|
state_p->timer_created = false;
|
|
|
|
timer_active = _lv_ll_get_head(timer_head);
|
|
while(timer_active) {
|
|
/*The timer might be deleted if it runs only once ('repeat_count = 1')
|
|
*So get next element until the current is surely valid*/
|
|
next = _lv_ll_get_next(timer_head, timer_active);
|
|
|
|
if(lv_timer_exec(timer_active)) {
|
|
/*If a timer was created or deleted then this or the next item might be corrupted*/
|
|
if(state_p->timer_created || state_p->timer_deleted) {
|
|
LV_TRACE_TIMER("Start from the first timer again because a timer was created or deleted");
|
|
break;
|
|
}
|
|
}
|
|
|
|
timer_active = next; /*Load the next timer*/
|
|
}
|
|
} while(timer_active);
|
|
|
|
uint32_t time_until_next = LV_NO_TIMER_READY;
|
|
next = _lv_ll_get_head(timer_head);
|
|
while(next) {
|
|
if(!next->paused) {
|
|
uint32_t delay = lv_timer_time_remaining(next);
|
|
if(delay < time_until_next)
|
|
time_until_next = delay;
|
|
}
|
|
|
|
next = _lv_ll_get_next(timer_head, next); /*Find the next timer*/
|
|
}
|
|
|
|
state_p->busy_time += lv_tick_elaps(handler_start);
|
|
uint32_t idle_period_time = lv_tick_elaps(state_p->idle_period_start);
|
|
if(idle_period_time >= IDLE_MEAS_PERIOD) {
|
|
state_p->idle_last = (state_p->busy_time * 100) / idle_period_time; /*Calculate the busy percentage*/
|
|
state_p->idle_last = state_p->idle_last > 100 ? 0 : 100 - state_p->idle_last; /*But we need idle time*/
|
|
state_p->busy_time = 0;
|
|
state_p->idle_period_start = lv_tick_get();
|
|
}
|
|
|
|
state_p->timer_time_until_next = time_until_next;
|
|
state_p->already_running = false; /*Release the mutex*/
|
|
|
|
LV_TRACE_TIMER("finished (%" LV_PRIu32 " ms until the next timer call)", time_until_next);
|
|
LV_PROFILER_END;
|
|
return time_until_next;
|
|
}
|
|
|
|
LV_ATTRIBUTE_TIMER_HANDLER void lv_timer_periodic_handler(void)
|
|
{
|
|
lv_timer_state_t * state_p = &state;
|
|
if(lv_tick_elaps(state_p->periodic_last_tick) >= state_p->timer_time_until_next) {
|
|
LV_TRACE_TIMER("calling lv_timer_handler()");
|
|
lv_timer_handler();
|
|
state_p->periodic_last_tick = lv_tick_get();
|
|
}
|
|
}
|
|
|
|
lv_timer_t * lv_timer_create_basic(void)
|
|
{
|
|
return lv_timer_create(NULL, DEF_PERIOD, NULL);
|
|
}
|
|
|
|
lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data)
|
|
{
|
|
lv_timer_t * new_timer = NULL;
|
|
|
|
new_timer = _lv_ll_ins_head(timer_ll_p);
|
|
LV_ASSERT_MALLOC(new_timer);
|
|
if(new_timer == NULL) return NULL;
|
|
|
|
new_timer->period = period;
|
|
new_timer->timer_cb = timer_xcb;
|
|
new_timer->repeat_count = -1;
|
|
new_timer->paused = 0;
|
|
new_timer->last_run = lv_tick_get();
|
|
new_timer->user_data = user_data;
|
|
new_timer->auto_delete = true;
|
|
|
|
state.timer_created = true;
|
|
|
|
lv_timer_handler_resume();
|
|
|
|
return new_timer;
|
|
}
|
|
|
|
void lv_timer_set_cb(lv_timer_t * timer, lv_timer_cb_t timer_cb)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->timer_cb = timer_cb;
|
|
}
|
|
|
|
void lv_timer_delete(lv_timer_t * timer)
|
|
{
|
|
_lv_ll_remove(timer_ll_p, timer);
|
|
state.timer_deleted = true;
|
|
|
|
lv_free(timer);
|
|
}
|
|
|
|
void lv_timer_pause(lv_timer_t * timer)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->paused = true;
|
|
}
|
|
|
|
void lv_timer_resume(lv_timer_t * timer)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->paused = false;
|
|
lv_timer_handler_resume();
|
|
}
|
|
|
|
void lv_timer_set_period(lv_timer_t * timer, uint32_t period)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->period = period;
|
|
}
|
|
|
|
void lv_timer_ready(lv_timer_t * timer)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->last_run = lv_tick_get() - timer->period - 1;
|
|
}
|
|
|
|
void lv_timer_set_repeat_count(lv_timer_t * timer, int32_t repeat_count)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->repeat_count = repeat_count;
|
|
}
|
|
|
|
void lv_timer_set_auto_delete(lv_timer_t * timer, bool auto_delete)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->auto_delete = auto_delete;
|
|
}
|
|
|
|
void lv_timer_set_user_data(lv_timer_t * timer, void * user_data)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->user_data = user_data;
|
|
}
|
|
|
|
void lv_timer_reset(lv_timer_t * timer)
|
|
{
|
|
LV_ASSERT_NULL(timer);
|
|
timer->last_run = lv_tick_get();
|
|
lv_timer_handler_resume();
|
|
}
|
|
|
|
void lv_timer_enable(bool en)
|
|
{
|
|
state.lv_timer_run = en;
|
|
if(en) lv_timer_handler_resume();
|
|
}
|
|
|
|
void _lv_timer_core_deinit(void)
|
|
{
|
|
lv_timer_enable(false);
|
|
|
|
_lv_ll_clear(timer_ll_p);
|
|
}
|
|
|
|
uint32_t lv_timer_get_idle(void)
|
|
{
|
|
return state.idle_last;
|
|
}
|
|
|
|
uint32_t lv_timer_get_time_until_next(void)
|
|
{
|
|
return state.timer_time_until_next;
|
|
}
|
|
|
|
lv_timer_t * lv_timer_get_next(lv_timer_t * timer)
|
|
{
|
|
if(timer == NULL) return _lv_ll_get_head(timer_ll_p);
|
|
else return _lv_ll_get_next(timer_ll_p, timer);
|
|
}
|
|
|
|
/**********************
|
|
* STATIC FUNCTIONS
|
|
**********************/
|
|
|
|
/**
|
|
* Execute timer if its remaining time is zero
|
|
* @param timer pointer to lv_timer
|
|
* @return true: execute, false: not executed
|
|
*/
|
|
static bool lv_timer_exec(lv_timer_t * timer)
|
|
{
|
|
if(timer->paused) return false;
|
|
|
|
bool exec = false;
|
|
if(lv_timer_time_remaining(timer) == 0) {
|
|
/* Decrement the repeat count before executing the timer_cb.
|
|
* If any timer is deleted `if(timer->repeat_count == 0)` is not executed below
|
|
* but at least the repeat count is zero and the timer can be deleted in the next round*/
|
|
int32_t original_repeat_count = timer->repeat_count;
|
|
if(timer->repeat_count > 0) timer->repeat_count--;
|
|
timer->last_run = lv_tick_get();
|
|
LV_TRACE_TIMER("calling timer callback: %p", *((void **)&timer->timer_cb));
|
|
|
|
if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer);
|
|
|
|
if(!state.timer_deleted) {
|
|
LV_TRACE_TIMER("timer callback %p finished", *((void **)&timer->timer_cb));
|
|
}
|
|
else {
|
|
LV_TRACE_TIMER("timer callback finished");
|
|
}
|
|
|
|
LV_ASSERT_MEM_INTEGRITY();
|
|
exec = true;
|
|
}
|
|
|
|
if(state.timer_deleted == false) { /*The timer might be deleted by itself as well*/
|
|
if(timer->repeat_count == 0) { /*The repeat count is over, delete the timer*/
|
|
if(timer->auto_delete) {
|
|
LV_TRACE_TIMER("deleting timer with %p callback because the repeat count is over", *((void **)&timer->timer_cb));
|
|
lv_timer_delete(timer);
|
|
}
|
|
else {
|
|
LV_TRACE_TIMER("pausing timer with %p callback because the repeat count is over", *((void **)&timer->timer_cb));
|
|
lv_timer_pause(timer);
|
|
}
|
|
}
|
|
}
|
|
|
|
return exec;
|
|
}
|
|
|
|
/**
|
|
* Find out how much time remains before a timer must be run.
|
|
* @param timer pointer to lv_timer
|
|
* @return the time remaining, or 0 if it needs to be run again
|
|
*/
|
|
static uint32_t lv_timer_time_remaining(lv_timer_t * timer)
|
|
{
|
|
/*Check if at least 'period' time elapsed*/
|
|
uint32_t elp = lv_tick_elaps(timer->last_run);
|
|
if(elp >= timer->period)
|
|
return 0;
|
|
return timer->period - elp;
|
|
}
|
|
|
|
/**
|
|
* Call the ready lv_timer
|
|
*/
|
|
static void lv_timer_handler_resume(void)
|
|
{
|
|
/*If there is a timer which is ready to run then resume the timer loop*/
|
|
state.timer_time_until_next = 0;
|
|
if(state.resume_cb) {
|
|
state.resume_cb(state.resume_data);
|
|
}
|
|
}
|
|
|
|
void lv_timer_handler_set_resume_cb(lv_timer_handler_resume_cb_t cb, void * data)
|
|
{
|
|
state.resume_cb = cb;
|
|
state.resume_data = data;
|
|
}
|