diff options
Diffstat (limited to 'src/libs/lvgl/src/lv_misc/lv_task.c')
| m--------- | src/libs/lvgl | 0 | ||||
| -rw-r--r-- | src/libs/lvgl/src/lv_misc/lv_task.c | 415 |
2 files changed, 415 insertions, 0 deletions
diff --git a/src/libs/lvgl b/src/libs/lvgl deleted file mode 160000 -Subproject 23430cf20e32294549fff9b2879a9466dacc19b diff --git a/src/libs/lvgl/src/lv_misc/lv_task.c b/src/libs/lvgl/src/lv_misc/lv_task.c new file mode 100644 index 0000000..147ada6 --- /dev/null +++ b/src/libs/lvgl/src/lv_misc/lv_task.c @@ -0,0 +1,415 @@ +/** + * @file lv_task.c + * An 'lv_task' is a void (*fp) (struct _lv_task_t* param) type function which will be called periodically. + * A priority (5 levels + disable) can be assigned to lv_tasks. + */ + +/********************* + * INCLUDES + *********************/ +#include <stddef.h> +#include "lv_task.h" +#include "../lv_misc/lv_debug.h" +#include "../lv_hal/lv_hal_tick.h" +#include "lv_gc.h" + +/********************* + * DEFINES + *********************/ +#define IDLE_MEAS_PERIOD 500 /*[ms]*/ +#define DEF_PRIO LV_TASK_PRIO_MID +#define DEF_PERIOD 500 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_task_exec(lv_task_t * task); +static uint32_t lv_task_time_remaining(lv_task_t * task); + +/********************** + * STATIC VARIABLES + **********************/ +static bool lv_task_run = false; +static uint8_t idle_last = 0; +static bool task_deleted; +static bool task_list_changed; +static bool task_created; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Init the lv_task module + */ +void _lv_task_core_init(void) +{ + _lv_ll_init(&LV_GC_ROOT(_lv_task_ll), sizeof(lv_task_t)); + + /*Initially enable the lv_task handling*/ + lv_task_enable(true); +} + +/** + * Call it periodically to handle lv_tasks. + * @return the time after which it must be called again + */ +LV_ATTRIBUTE_TASK_HANDLER uint32_t lv_task_handler(void) +{ + LV_LOG_TRACE("lv_task_handler started"); + + /*Avoid concurrent running of the task handler*/ + static bool already_running = false; + if(already_running) return 1; + already_running = true; + + if(lv_task_run == false) { + already_running = false; /*Release mutex*/ + return 1; + } + + static uint32_t idle_period_start = 0; + static uint32_t busy_time = 0; + + uint32_t handler_start = lv_tick_get(); + + /* Run all task from the highest to the lowest priority + * If a lower priority task is executed check task again from the highest priority + * but on the priority of executed tasks don't run tasks before the executed*/ + lv_task_t * task_interrupter = NULL; + lv_task_t * next; + bool end_flag; + do { + end_flag = true; + task_deleted = false; + task_created = false; + task_list_changed = false; + LV_GC_ROOT(_lv_task_act) = _lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll)); + while(LV_GC_ROOT(_lv_task_act)) { + /* The task might be deleted if it runs only once ('once = 1') + * So get next element until the current is surely valid*/ + next = _lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), LV_GC_ROOT(_lv_task_act)); + + /*We reach priority of the turned off task. There is nothing more to do.*/ + if(LV_GC_ROOT(_lv_task_act)->prio == LV_TASK_PRIO_OFF) { + break; + } + + /*Here is the interrupter task. Don't execute it again.*/ + if(LV_GC_ROOT(_lv_task_act) == task_interrupter) { + task_interrupter = NULL; /*From this point only task after the interrupter comes, so + the interrupter is not interesting anymore*/ + LV_GC_ROOT(_lv_task_act) = next; + continue; /*Load the next task*/ + } + + /*Just try to run the tasks with highest priority.*/ + if(LV_GC_ROOT(_lv_task_act)->prio == LV_TASK_PRIO_HIGHEST) { + lv_task_exec(LV_GC_ROOT(_lv_task_act)); + } + /*Tasks with higher priority than the interrupted shall be run in every case*/ + else if(task_interrupter) { + if(LV_GC_ROOT(_lv_task_act)->prio > task_interrupter->prio) { + if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) { + if(!task_created && !task_deleted) { + /*Check all tasks again from the highest priority */ + task_interrupter = LV_GC_ROOT(_lv_task_act); + end_flag = false; + break; + } + } + } + } + /* It is no interrupter task or we already reached it earlier. + * Just run the remaining tasks*/ + else { + if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) { + if(!task_created && !task_deleted) { + task_interrupter = LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */ + end_flag = false; + break; + } + } + } + + /*If a task was created or deleted then this or the next item might be corrupted*/ + if(task_created || task_deleted) { + task_interrupter = NULL; + break; + } + + if(task_list_changed) { + task_interrupter = NULL; + end_flag = false; + break; + } + + LV_GC_ROOT(_lv_task_act) = next; /*Load the next task*/ + } + } while(!end_flag); + + uint32_t time_till_next = LV_NO_TASK_READY; + next = _lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll)); + while(next && next->prio != LV_TASK_PRIO_OFF) { + uint32_t delay = lv_task_time_remaining(next); + if(delay < time_till_next) + time_till_next = delay; + + next = _lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), next); /*Find the next task*/ + } + + busy_time += lv_tick_elaps(handler_start); + uint32_t idle_period_time = lv_tick_elaps(idle_period_start); + if(idle_period_time >= IDLE_MEAS_PERIOD) { + idle_last = (busy_time * 100) / idle_period_time; /*Calculate the busy percentage*/ + idle_last = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/ + busy_time = 0; + idle_period_start = lv_tick_get(); + } + + already_running = false; /*Release the mutex*/ + + LV_LOG_TRACE("lv_task_handler ready"); + return time_till_next; +} +/** + * Create an "empty" task. It needs to initialized with at least + * `lv_task_set_cb` and `lv_task_set_period` + * @return pointer to the created task + */ +lv_task_t * lv_task_create_basic(void) +{ + return lv_task_create(NULL, DEF_PERIOD, DEF_PRIO, NULL); +} + +/** + * Create a new lv_task + * @param task_xcb a callback which is the task itself. It will be called periodically. + * (the 'x' in the argument name indicates that its not a fully generic function because it not follows + * the `func_name(object, callback, ...)` convention) + * @param period call period in ms unit + * @param prio priority of the task (LV_TASK_PRIO_OFF means the task is stopped) + * @param user_data custom parameter + * @return pointer to the new task + */ +lv_task_t * lv_task_create(lv_task_cb_t task_xcb, uint32_t period, lv_task_prio_t prio, void * user_data) +{ + lv_task_t * new_task = NULL; + lv_task_t * tmp; + + /*Create task lists in order of priority from high to low*/ + tmp = _lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll)); + + /*It's the first task*/ + if(NULL == tmp) { + new_task = _lv_ll_ins_head(&LV_GC_ROOT(_lv_task_ll)); + LV_ASSERT_MEM(new_task); + if(new_task == NULL) return NULL; + } + /*Insert the new task to proper place according to its priority*/ + else { + do { + if(tmp->prio <= prio) { + new_task = _lv_ll_ins_prev(&LV_GC_ROOT(_lv_task_ll), tmp); + LV_ASSERT_MEM(new_task); + if(new_task == NULL) return NULL; + break; + } + tmp = _lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), tmp); + } while(tmp != NULL); + + /*Only too high priority tasks were found. Add the task to the end*/ + if(tmp == NULL) { + new_task = _lv_ll_ins_tail(&LV_GC_ROOT(_lv_task_ll)); + LV_ASSERT_MEM(new_task); + if(new_task == NULL) return NULL; + } + } + task_list_changed = true; + + new_task->period = period; + new_task->task_cb = task_xcb; + new_task->prio = prio; + + new_task->repeat_count = -1; + new_task->last_run = lv_tick_get(); + + new_task->user_data = user_data; + + task_created = true; + + return new_task; +} + +/** + * Set the callback the task (the function to call periodically) + * @param task pointer to a task + * @param task_cb the function to call periodically + */ +void lv_task_set_cb(lv_task_t * task, lv_task_cb_t task_cb) +{ + task->task_cb = task_cb; +} + +/** + * Delete a lv_task + * @param task pointer to task created by task + */ +void lv_task_del(lv_task_t * task) +{ + _lv_ll_remove(&LV_GC_ROOT(_lv_task_ll), task); + task_list_changed = true; + + lv_mem_free(task); + + if(LV_GC_ROOT(_lv_task_act) == task) task_deleted = true; /*The active task was deleted*/ +} + +/** + * Set new priority for a lv_task + * @param task pointer to a lv_task + * @param prio the new priority + */ +void lv_task_set_prio(lv_task_t * task, lv_task_prio_t prio) +{ + if(task->prio == prio) return; + + /*Find the tasks with new priority*/ + lv_task_t * i; + _LV_LL_READ(LV_GC_ROOT(_lv_task_ll), i) { + if(i->prio <= prio) { + if(i != task) _lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), task, i); + break; + } + } + + /*There was no such a low priority so far then add the node to the tail*/ + if(i == NULL) { + _lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), task, NULL); + } + task_list_changed = true; + + task->prio = prio; +} + +/** + * Set new period for a lv_task + * @param task pointer to a lv_task + * @param period the new period + */ +void lv_task_set_period(lv_task_t * task, uint32_t period) +{ + task->period = period; +} + +/** + * Make a lv_task ready. It will not wait its period. + * @param task pointer to a lv_task. + */ +void lv_task_ready(lv_task_t * task) +{ + task->last_run = lv_tick_get() - task->period - 1; +} + +/** + * Set the number of times a task will repeat. + * @param task pointer to a lv_task. + * @param repeat_count -1 : infinity; 0 : stop ; n>0: residual times + */ +void lv_task_set_repeat_count(lv_task_t * task, int32_t repeat_count) +{ + task->repeat_count = repeat_count; +} + +/** + * Reset a lv_task. + * It will be called the previously set period milliseconds later. + * @param task pointer to a lv_task. + */ +void lv_task_reset(lv_task_t * task) +{ + task->last_run = lv_tick_get(); +} + +/** + * Enable or disable the whole lv_task handling + * @param en: true: lv_task handling is running, false: lv_task handling is suspended + */ +void lv_task_enable(bool en) +{ + lv_task_run = en; +} + +/** + * Get idle percentage + * @return the lv_task idle in percentage + */ +uint8_t lv_task_get_idle(void) +{ + return idle_last; +} + +/** + * Iterate through the tasks + * @param task NULL to start iteration or the previous return value to get the next task + * @return the next task or NULL if there is no more task + */ +lv_task_t * lv_task_get_next(lv_task_t * task) +{ + if(task == NULL) return _lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll)); + else return _lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), task); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Execute task if its the priority is appropriate + * @param task pointer to lv_task + * @return true: execute, false: not executed + */ +static bool lv_task_exec(lv_task_t * task) +{ + bool exec = false; + + if(lv_task_time_remaining(task) == 0) { + task->last_run = lv_tick_get(); + if(task->task_cb) task->task_cb(task); + + /*Delete if it was a one shot lv_task*/ + if(task_deleted == false) { /*The task might be deleted by itself as well*/ + if(task->repeat_count > 0) { + task->repeat_count--; + } + if(task->repeat_count == 0) { + lv_task_del(task); + } + } + exec = true; + } + + return exec; +} + +/** + * Find out how much time remains before a task must be run. + * @param task pointer to lv_task + * @return the time remaining, or 0 if it needs to be run again + */ +static uint32_t lv_task_time_remaining(lv_task_t * task) +{ + /*Check if at least 'period' time elapsed*/ + uint32_t elp = lv_tick_elaps(task->last_run); + if(elp >= task->period) + return 0; + return task->period - elp; +} |
