1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-14 06:42:58 +08:00
lvgl/lv_misc/lv_task.c

312 lines
8.4 KiB
C
Raw Normal View History

2017-11-23 20:42:14 +01:00
/**
2017-11-24 17:48:47 +01:00
* @file lv_task.c
* An 'lv_task' is a void (*fp) (void* param) type function which will be called periodically.
2018-06-19 09:49:58 +02:00
* A priority (5 levels + disable) can be assigned to lv_tasks.
2017-11-23 20:42:14 +01:00
*/
/*********************
* INCLUDES
*********************/
2017-11-26 11:38:28 +01:00
#include <stddef.h>
2017-11-23 20:42:14 +01:00
#include "lv_task.h"
#include "../lv_hal/lv_hal_tick.h"
/*********************
* DEFINES
*********************/
2017-12-20 00:47:37 +01:00
#define IDLE_MEAS_PERIOD 500 /*[ms]*/
2017-11-23 20:42:14 +01:00
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
2018-06-19 09:49:58 +02:00
static bool lv_task_exec(lv_task_t * lv_task_p);
2017-11-23 20:42:14 +01:00
/**********************
* STATIC VARIABLES
**********************/
2017-11-24 17:48:47 +01:00
static lv_ll_t lv_task_ll; /*Linked list to store the lv_tasks*/
static bool lv_task_run = false;
2017-11-23 20:42:14 +01:00
static uint8_t idle_last = 0;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
/**
2017-11-24 17:48:47 +01:00
* Init the lv_task module
2017-11-23 20:42:14 +01:00
*/
2017-11-24 17:48:47 +01:00
void lv_task_init(void)
2017-11-23 20:42:14 +01:00
{
2017-11-24 17:48:47 +01:00
lv_ll_init(&lv_task_ll, sizeof(lv_task_t));
2018-06-19 09:49:58 +02:00
2017-11-24 17:48:47 +01:00
/*Initially enable the lv_task handling*/
lv_task_enable(true);
2017-11-23 20:42:14 +01:00
}
/**
2017-11-24 17:48:47 +01:00
* Call it periodically to handle lv_tasks.
2017-11-23 20:42:14 +01:00
*/
2018-03-07 09:22:37 +01:00
LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void)
2017-11-23 20:42:14 +01:00
{
2018-10-05 17:22:49 +02:00
LV_LOG_TRACE("lv_task_handler started");
2018-07-25 17:57:08 +02:00
2018-10-05 17:22:49 +02:00
/*Avoid concurrent running of the task handler*/
static bool task_handler_mutex = false;
if(task_handler_mutex) return;
task_handler_mutex = true;
2017-12-20 00:47:37 +01:00
static uint32_t idle_period_start = 0;
static uint32_t handler_start = 0;
static uint32_t busy_time = 0;
2018-06-19 09:49:58 +02:00
if(lv_task_run == false) return;
2017-11-23 20:42:14 +01:00
2018-06-19 09:49:58 +02:00
handler_start = lv_tick_get();
2017-12-20 00:47:37 +01:00
2018-06-19 09:49:58 +02:00
/* 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*/
2018-07-22 22:03:22 +02:00
lv_task_t * task_interrupter = NULL;
2018-04-18 13:23:19 +02:00
lv_task_t * next;
2018-06-19 09:49:58 +02:00
bool end_flag;
do {
end_flag = true;
lv_task_t * act = lv_ll_get_head(&lv_task_ll);
while(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_task_ll, act);
/*We reach priority of the turned off task. There is nothing more to do.*/
if(act->prio == LV_TASK_PRIO_OFF) {
break;
}
/*Here is the interrupter task. Don't execute it again.*/
2018-07-22 22:03:22 +02:00
if(act == task_interrupter) {
task_interrupter = NULL; /*From this point only task after the interrupter comes, so the interrupter is not interesting anymore*/
2018-06-19 09:49:58 +02:00
act = next;
continue; /*Load the next task*/
}
/*Just try to run the tasks with highest priority.*/
if(act->prio == LV_TASK_PRIO_HIGHEST) {
lv_task_exec(act);
}
/*Tasks with higher priority then the interrupted shall be run in every case*/
2018-07-22 22:03:22 +02:00
else if(task_interrupter) {
if(act->prio > task_interrupter->prio) {
2018-06-19 09:49:58 +02:00
if(lv_task_exec(act)) {
2018-07-22 22:03:22 +02:00
task_interrupter = act; /*Check all tasks again from the highest priority */
2018-06-19 09:49:58 +02:00
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(act)) {
2018-07-22 22:03:22 +02:00
task_interrupter = act; /*Check all tasks again from the highest priority */
2018-06-19 09:49:58 +02:00
end_flag = false;
break;
}
}
act = next; /*Load the next task*/
}
} while(!end_flag);
2017-12-20 00:47:37 +01:00
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 = (uint32_t)((uint32_t)busy_time * 100) / IDLE_MEAS_PERIOD; /*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();
}
2018-07-25 17:57:08 +02:00
2018-10-05 17:22:49 +02:00
task_handler_mutex = false; /*Release the mutex*/
2018-10-05 17:22:49 +02:00
LV_LOG_TRACE("lv_task_handler ready");
2017-11-23 20:42:14 +01:00
}
/**
2017-11-24 17:48:47 +01:00
* Create a new lv_task
2017-11-23 20:42:14 +01:00
* @param task a function which is the task itself
* @param period call period in ms unit
2017-11-24 17:48:47 +01:00
* @param prio priority of the task (LV_TASK_PRIO_OFF means the task is stopped)
2017-11-23 20:42:14 +01:00
* @param param free parameter
* @return pointer to the new task
*/
2018-06-19 09:49:58 +02:00
lv_task_t * lv_task_create(void (*task)(void *), uint32_t period, lv_task_prio_t prio, void * param)
2017-11-23 20:42:14 +01:00
{
2018-06-19 09:49:58 +02:00
lv_task_t * new_lv_task = NULL;
lv_task_t * tmp;
2018-02-15 13:24:56 +01:00
/*Create task lists in order of priority from high to low*/
tmp = lv_ll_get_head(&lv_task_ll);
if(NULL == tmp) { /*First task*/
new_lv_task = lv_ll_ins_head(&lv_task_ll);
lv_mem_assert(new_lv_task);
if(new_lv_task == NULL) return NULL;
2018-06-19 09:49:58 +02:00
} else {
do {
if(tmp->prio <= prio) {
2018-02-15 13:24:56 +01:00
new_lv_task = lv_ll_ins_prev(&lv_task_ll, tmp);
lv_mem_assert(new_lv_task);
if(new_lv_task == NULL) return NULL;
2018-02-15 13:24:56 +01:00
break;
}
2018-06-19 09:49:58 +02:00
tmp = lv_ll_get_next(&lv_task_ll, tmp);
} while(tmp != NULL);
2018-02-15 13:24:56 +01:00
if(tmp == NULL) { /*Only too high priority tasks were found*/
new_lv_task = lv_ll_ins_tail(&lv_task_ll);
lv_mem_assert(new_lv_task);
if(new_lv_task == NULL) return NULL;
2018-02-15 13:24:56 +01:00
}
}
2017-11-24 17:48:47 +01:00
new_lv_task->period = period;
new_lv_task->task = task;
new_lv_task->prio = prio;
new_lv_task->param = param;
new_lv_task->once = 0;
2017-11-26 11:38:28 +01:00
new_lv_task->last_run = lv_tick_get();
2017-11-24 17:48:47 +01:00
return new_lv_task;
2017-11-23 20:42:14 +01:00
}
/**
2017-11-24 17:48:47 +01:00
* Delete a lv_task
* @param lv_task_p pointer to task created by lv_task_p
2017-11-23 20:42:14 +01:00
*/
2018-06-19 09:49:58 +02:00
void lv_task_del(lv_task_t * lv_task_p)
2017-11-23 20:42:14 +01:00
{
2017-11-24 17:48:47 +01:00
lv_ll_rem(&lv_task_ll, lv_task_p);
2018-06-19 09:49:58 +02:00
2017-11-24 17:48:47 +01:00
lv_mem_free(lv_task_p);
2017-11-23 20:42:14 +01:00
}
/**
2017-11-24 17:48:47 +01:00
* Set new priority for a lv_task
* @param lv_task_p pointer to a lv_task
2017-11-23 20:42:14 +01:00
* @param prio the new priority
*/
2018-06-19 09:49:58 +02:00
void lv_task_set_prio(lv_task_t * lv_task_p, lv_task_prio_t prio)
2017-11-23 20:42:14 +01:00
{
2018-04-18 13:23:19 +02:00
/*Find the tasks with new priority*/
lv_task_t * i;
LL_READ(lv_task_ll, i) {
if(i->prio <= prio) {
if(i != lv_task_p) lv_ll_move_before(&lv_task_ll, lv_task_p, i);
break;
}
}
2018-02-15 13:24:56 +01:00
2018-04-18 13:23:19 +02:00
/*There was no such a low priority so far then add the node to the tail*/
if(i == NULL) {
lv_ll_move_before(&lv_task_ll, lv_task_p, NULL);
}
lv_task_p->prio = prio;
2017-11-23 20:42:14 +01:00
}
/**
2017-11-24 17:48:47 +01:00
* Set new period for a lv_task
* @param lv_task_p pointer to a lv_task
2017-11-23 20:42:14 +01:00
* @param period the new period
*/
2018-06-19 09:49:58 +02:00
void lv_task_set_period(lv_task_t * lv_task_p, uint32_t period)
2017-11-23 20:42:14 +01:00
{
2017-11-24 17:48:47 +01:00
lv_task_p->period = period;
2017-11-23 20:42:14 +01:00
}
/**
2017-11-24 17:48:47 +01:00
* Make a lv_task ready. It will not wait its period.
* @param lv_task_p pointer to a lv_task.
2017-11-23 20:42:14 +01:00
*/
2018-06-19 09:49:58 +02:00
void lv_task_ready(lv_task_t * lv_task_p)
2017-11-23 20:42:14 +01:00
{
2017-11-26 11:38:28 +01:00
lv_task_p->last_run = lv_tick_get() - lv_task_p->period - 1;
2017-11-23 20:42:14 +01:00
}
/**
2017-11-24 17:48:47 +01:00
* Delete the lv_task after one call
* @param lv_task_p pointer to a lv_task.
2017-11-23 20:42:14 +01:00
*/
2017-11-24 17:48:47 +01:00
void lv_task_once(lv_task_t * lv_task_p)
2017-11-23 20:42:14 +01:00
{
2017-11-24 17:48:47 +01:00
lv_task_p->once = 1;
2017-11-23 20:42:14 +01:00
}
/**
2018-06-19 09:49:58 +02:00
* Reset a lv_task.
2017-11-23 20:42:14 +01:00
* It will be called the previously set period milliseconds later.
2017-11-24 17:48:47 +01:00
* @param lv_task_p pointer to a lv_task.
2017-11-23 20:42:14 +01:00
*/
2018-06-19 09:49:58 +02:00
void lv_task_reset(lv_task_t * lv_task_p)
2017-11-23 20:42:14 +01:00
{
2017-11-26 11:38:28 +01:00
lv_task_p->last_run = lv_tick_get();
2017-11-23 20:42:14 +01:00
}
/**
2017-11-24 17:48:47 +01:00
* Enable or disable the whole lv_task handling
* @param en: true: lv_task handling is running, false: lv_task handling is suspended
2017-11-23 20:42:14 +01:00
*/
2017-11-24 17:48:47 +01:00
void lv_task_enable(bool en)
2017-11-23 20:42:14 +01:00
{
2018-06-19 09:49:58 +02:00
lv_task_run = en;
2017-11-23 20:42:14 +01:00
}
/**
* Get idle percentage
2017-11-24 17:48:47 +01:00
* @return the lv_task idle in percentage
2017-11-23 20:42:14 +01:00
*/
2017-11-24 17:48:47 +01:00
uint8_t lv_task_get_idle(void)
2017-11-23 20:42:14 +01:00
{
return idle_last;
}
/**********************
* STATIC FUNCTIONS
**********************/
/**
2018-06-19 09:49:58 +02:00
* Execute task if its the priority is appropriate
2017-11-24 17:48:47 +01:00
* @param lv_task_p pointer to lv_task
2017-11-23 20:42:14 +01:00
* @return true: execute, false: not executed
*/
2018-06-19 09:49:58 +02:00
static bool lv_task_exec(lv_task_t * lv_task_p)
2017-11-23 20:42:14 +01:00
{
bool exec = false;
2018-06-19 09:49:58 +02:00
2018-02-15 13:24:56 +01:00
/*Execute if at least 'period' time elapsed*/
uint32_t elp = lv_tick_elaps(lv_task_p->last_run);
if(elp >= lv_task_p->period) {
lv_task_p->last_run = lv_tick_get();
lv_task_p->task(lv_task_p->param);
/*Delete if it was a one shot lv_task*/
if(lv_task_p->once != 0) lv_task_del(lv_task_p);
exec = true;
2017-11-23 20:42:14 +01:00
}
2018-06-19 09:49:58 +02:00
2017-11-23 20:42:14 +01:00
return exec;
}