/**1* @file lv_task.c2* An 'lv_task' is a void (*fp) (void* param) type function which will be called periodically.3* A priority (5 levels + disable) can be assigned to lv_tasks.4*/56/*********************7* INCLUDES8*********************/9#include <stddef.h>10#include "lv_task.h"11#include "../lv_hal/lv_hal_tick.h"12#include "lv_gc.h"1314#if defined(LV_GC_INCLUDE)15# include LV_GC_INCLUDE16#endif /* LV_ENABLE_GC */1718/*********************19* DEFINES20*********************/21#define IDLE_MEAS_PERIOD 500 /*[ms]*/2223/**********************24* TYPEDEFS25**********************/2627/**********************28* STATIC PROTOTYPES29**********************/30static bool lv_task_exec(lv_task_t * lv_task_p);3132/**********************33* STATIC VARIABLES34**********************/35static bool lv_task_run = false;36static uint8_t idle_last = 0;37static bool task_deleted;38static bool task_created;3940/**********************41* MACROS42**********************/4344/**********************45* GLOBAL FUNCTIONS46**********************/4748/**49* Init the lv_task module50*/51void lv_task_init(void)52{53lv_ll_init(&LV_GC_ROOT(_lv_task_ll), sizeof(lv_task_t));5455/*Initially enable the lv_task handling*/56lv_task_enable(true);57}5859/**60* Call it periodically to handle lv_tasks.61*/62LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void)63{64LV_LOG_TRACE("lv_task_handler started");6566/*Avoid concurrent running of the task handler*/67static bool task_handler_mutex = false;68if(task_handler_mutex) return;69task_handler_mutex = true;7071static uint32_t idle_period_start = 0;72static uint32_t handler_start = 0;73static uint32_t busy_time = 0;7475if(lv_task_run == false) return;7677handler_start = lv_tick_get();7879/* Run all task from the highest to the lowest priority80* If a lower priority task is executed check task again from the highest priority81* but on the priority of executed tasks don't run tasks before the executed*/82lv_task_t * task_interrupter = NULL;83lv_task_t * next;84bool end_flag;85do {86end_flag = true;87task_deleted = false;88task_created = false;89LV_GC_ROOT(_lv_task_act) = lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll));90while(LV_GC_ROOT(_lv_task_act)) {91/* The task might be deleted if it runs only once ('once = 1')92* So get next element until the current is surely valid*/93next = lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), LV_GC_ROOT(_lv_task_act));9495/*We reach priority of the turned off task. There is nothing more to do.*/96if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio == LV_TASK_PRIO_OFF) {97break;98}99100/*Here is the interrupter task. Don't execute it again.*/101if(LV_GC_ROOT(_lv_task_act) == task_interrupter) {102task_interrupter = NULL; /*From this point only task after the interrupter comes, so the interrupter is not interesting anymore*/103LV_GC_ROOT(_lv_task_act) = next;104continue; /*Load the next task*/105}106107/*Just try to run the tasks with highest priority.*/108if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio == LV_TASK_PRIO_HIGHEST) {109lv_task_exec(LV_GC_ROOT(_lv_task_act));110}111/*Tasks with higher priority then the interrupted shall be run in every case*/112else if(task_interrupter) {113if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio > task_interrupter->prio) {114if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) {115task_interrupter = LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */116end_flag = false;117break;118}119}120}121/* It is no interrupter task or we already reached it earlier.122* Just run the remaining tasks*/123else {124if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) {125task_interrupter = LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */126end_flag = false;127break;128}129}130131if(task_deleted) break; /*If a task was deleted then this or the next item might be corrupted*/132if(task_created) break; /*If a task was deleted then this or the next item might be corrupted*/133134LV_GC_ROOT(_lv_task_act) = next; /*Load the next task*/135}136} while(!end_flag);137138busy_time += lv_tick_elaps(handler_start);139uint32_t idle_period_time = lv_tick_elaps(idle_period_start);140if(idle_period_time >= IDLE_MEAS_PERIOD) {141142idle_last = (uint32_t)((uint32_t)busy_time * 100) / IDLE_MEAS_PERIOD; /*Calculate the busy percentage*/143idle_last = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/144busy_time = 0;145idle_period_start = lv_tick_get();146147148}149150task_handler_mutex = false; /*Release the mutex*/151152LV_LOG_TRACE("lv_task_handler ready");153}154155/**156* Create a new lv_task157* @param task a function which is the task itself158* @param period call period in ms unit159* @param prio priority of the task (LV_TASK_PRIO_OFF means the task is stopped)160* @param param free parameter161* @return pointer to the new task162*/163lv_task_t * lv_task_create(void (*task)(void *), uint32_t period, lv_task_prio_t prio, void * param)164{165lv_task_t * new_lv_task = NULL;166lv_task_t * tmp;167168/*Create task lists in order of priority from high to low*/169tmp = lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll));170if(NULL == tmp) { /*First task*/171new_lv_task = lv_ll_ins_head(&LV_GC_ROOT(_lv_task_ll));172lv_mem_assert(new_lv_task);173if(new_lv_task == NULL) return NULL;174} else {175do {176if(tmp->prio <= prio) {177new_lv_task = lv_ll_ins_prev(&LV_GC_ROOT(_lv_task_ll), tmp);178lv_mem_assert(new_lv_task);179if(new_lv_task == NULL) return NULL;180break;181}182tmp = lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), tmp);183} while(tmp != NULL);184185if(tmp == NULL) { /*Only too high priority tasks were found*/186new_lv_task = lv_ll_ins_tail(&LV_GC_ROOT(_lv_task_ll));187lv_mem_assert(new_lv_task);188if(new_lv_task == NULL) return NULL;189}190}191192new_lv_task->period = period;193new_lv_task->task = task;194new_lv_task->prio = prio;195new_lv_task->param = param;196new_lv_task->once = 0;197new_lv_task->last_run = lv_tick_get();198199task_created = true;200201return new_lv_task;202}203204/**205* Delete a lv_task206* @param lv_task_p pointer to task created by lv_task_p207*/208void lv_task_del(lv_task_t * lv_task_p)209{210lv_ll_rem(&LV_GC_ROOT(_lv_task_ll), lv_task_p);211212lv_mem_free(lv_task_p);213214if(LV_GC_ROOT(_lv_task_act) == lv_task_p) task_deleted = true; /*The active task was deleted*/215}216217/**218* Set new priority for a lv_task219* @param lv_task_p pointer to a lv_task220* @param prio the new priority221*/222void lv_task_set_prio(lv_task_t * lv_task_p, lv_task_prio_t prio)223{224/*Find the tasks with new priority*/225lv_task_t * i;226LL_READ(LV_GC_ROOT(_lv_task_ll), i) {227if(i->prio <= prio) {228if(i != lv_task_p) lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), lv_task_p, i);229break;230}231}232233/*There was no such a low priority so far then add the node to the tail*/234if(i == NULL) {235lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), lv_task_p, NULL);236}237238239lv_task_p->prio = prio;240}241242/**243* Set new period for a lv_task244* @param lv_task_p pointer to a lv_task245* @param period the new period246*/247void lv_task_set_period(lv_task_t * lv_task_p, uint32_t period)248{249lv_task_p->period = period;250}251252/**253* Make a lv_task ready. It will not wait its period.254* @param lv_task_p pointer to a lv_task.255*/256void lv_task_ready(lv_task_t * lv_task_p)257{258lv_task_p->last_run = lv_tick_get() - lv_task_p->period - 1;259}260261/**262* Delete the lv_task after one call263* @param lv_task_p pointer to a lv_task.264*/265void lv_task_once(lv_task_t * lv_task_p)266{267lv_task_p->once = 1;268}269270/**271* Reset a lv_task.272* It will be called the previously set period milliseconds later.273* @param lv_task_p pointer to a lv_task.274*/275void lv_task_reset(lv_task_t * lv_task_p)276{277lv_task_p->last_run = lv_tick_get();278}279280/**281* Enable or disable the whole lv_task handling282* @param en: true: lv_task handling is running, false: lv_task handling is suspended283*/284void lv_task_enable(bool en)285{286lv_task_run = en;287}288289/**290* Get idle percentage291* @return the lv_task idle in percentage292*/293uint8_t lv_task_get_idle(void)294{295return idle_last;296}297298299/**********************300* STATIC FUNCTIONS301**********************/302303/**304* Execute task if its the priority is appropriate305* @param lv_task_p pointer to lv_task306* @return true: execute, false: not executed307*/308static bool lv_task_exec(lv_task_t * lv_task_p)309{310bool exec = false;311312/*Execute if at least 'period' time elapsed*/313uint32_t elp = lv_tick_elaps(lv_task_p->last_run);314if(elp >= lv_task_p->period) {315lv_task_p->last_run = lv_tick_get();316task_deleted = false;317task_created = false;318lv_task_p->task(lv_task_p->param);319320/*Delete if it was a one shot lv_task*/321if(task_deleted == false) { /*The task might be deleted by itself as well*/322if(lv_task_p->once != 0) {323lv_task_del(lv_task_p);324}325}326exec = true;327}328329return exec;330}331332333334