1 /**
2 * @file lv_task.c
3 * An 'lv_task' is a void (*fp) (void* param) type function which will be called periodically.
4 * A priority (5 levels + disable) can be assigned to lv_tasks.
5 */
6
7 /*********************
8 * INCLUDES
9 *********************/
10 #include <stddef.h>
11 #include "lv_task.h"
12 #include "../lv_hal/lv_hal_tick.h"
13 #include "lv_gc.h"
14
15 #if defined(LV_GC_INCLUDE)
16 #include LV_GC_INCLUDE
17 #endif /* LV_ENABLE_GC */
18
19 /*********************
20 * DEFINES
21 *********************/
22 #define IDLE_MEAS_PERIOD 500 /*[ms]*/
23 #define DEF_PRIO LV_TASK_PRIO_MID
24 #define DEF_PERIOD 500
25
26 /**********************
27 * TYPEDEFS
28 **********************/
29
30 /**********************
31 * STATIC PROTOTYPES
32 **********************/
33 static bool lv_task_exec(lv_task_t * task);
34
35 /**********************
36 * STATIC VARIABLES
37 **********************/
38 static bool lv_task_run = false;
39 static uint8_t idle_last = 0;
40 static bool task_deleted;
41 static bool task_created;
42
43 /**********************
44 * MACROS
45 **********************/
46
47 /**********************
48 * GLOBAL FUNCTIONS
49 **********************/
50
51 /**
52 * Init the lv_task module
53 */
lv_task_core_init(void)54 void lv_task_core_init(void)
55 {
56 lv_ll_init(&LV_GC_ROOT(_lv_task_ll), sizeof(lv_task_t));
57
58 /*Initially enable the lv_task handling*/
59 lv_task_enable(true);
60 }
61
62 /**
63 * Call it periodically to handle lv_tasks.
64 */
lv_task_handler(void)65 LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void)
66 {
67 LV_LOG_TRACE("lv_task_handler started");
68
69 /*Avoid concurrent running of the task handler*/
70 static bool task_handler_mutex = false;
71 if(task_handler_mutex) return;
72 task_handler_mutex = true;
73
74 static uint32_t idle_period_start = 0;
75 static uint32_t handler_start = 0;
76 static uint32_t busy_time = 0;
77
78 if(lv_task_run == false) {
79 task_handler_mutex = false; /*Release mutex*/
80 return;
81 }
82
83 handler_start = lv_tick_get();
84
85 /* Run all task from the highest to the lowest priority
86 * If a lower priority task is executed check task again from the highest priority
87 * but on the priority of executed tasks don't run tasks before the executed*/
88 lv_task_t * task_interrupter = NULL;
89 lv_task_t * next;
90 bool end_flag;
91 do {
92 end_flag = true;
93 task_deleted = false;
94 task_created = false;
95 LV_GC_ROOT(_lv_task_act) = lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll));
96 while(LV_GC_ROOT(_lv_task_act)) {
97 /* The task might be deleted if it runs only once ('once = 1')
98 * So get next element until the current is surely valid*/
99 next = lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), LV_GC_ROOT(_lv_task_act));
100
101 /*We reach priority of the turned off task. There is nothing more to do.*/
102 if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio == LV_TASK_PRIO_OFF) {
103 break;
104 }
105
106 /*Here is the interrupter task. Don't execute it again.*/
107 if(LV_GC_ROOT(_lv_task_act) == task_interrupter) {
108 task_interrupter = NULL; /*From this point only task after the interrupter comes, so
109 the interrupter is not interesting anymore*/
110 LV_GC_ROOT(_lv_task_act) = next;
111 continue; /*Load the next task*/
112 }
113
114 /*Just try to run the tasks with highest priority.*/
115 if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio == LV_TASK_PRIO_HIGHEST) {
116 lv_task_exec(LV_GC_ROOT(_lv_task_act));
117 }
118 /*Tasks with higher priority then the interrupted shall be run in every case*/
119 else if(task_interrupter) {
120 if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio > task_interrupter->prio) {
121 if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) {
122 task_interrupter =
123 LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */
124 end_flag = false;
125 break;
126 }
127 }
128 }
129 /* It is no interrupter task or we already reached it earlier.
130 * Just run the remaining tasks*/
131 else {
132 if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) {
133 task_interrupter = LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */
134 end_flag = false;
135 break;
136 }
137 }
138
139 if(task_deleted) break; /*If a task was deleted then this or the next item might be corrupted*/
140 if(task_created) break; /*If a task was created then this or the next item might be corrupted*/
141
142 LV_GC_ROOT(_lv_task_act) = next; /*Load the next task*/
143 }
144 } while(!end_flag);
145
146 busy_time += lv_tick_elaps(handler_start);
147 uint32_t idle_period_time = lv_tick_elaps(idle_period_start);
148 if(idle_period_time >= IDLE_MEAS_PERIOD) {
149
150 idle_last = (uint32_t)((uint32_t)busy_time * 100) / IDLE_MEAS_PERIOD; /*Calculate the busy percentage*/
151 idle_last = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/
152 busy_time = 0;
153 idle_period_start = lv_tick_get();
154 }
155
156 task_handler_mutex = false; /*Release the mutex*/
157
158 LV_LOG_TRACE("lv_task_handler ready");
159 }
160 /**
161 * Create an "empty" task. It needs to initialzed with at least
162 * `lv_task_set_cb` and `lv_task_set_period`
163 * @return pointer to the craeted task
164 */
lv_task_create_basic(void)165 lv_task_t * lv_task_create_basic(void)
166 {
167 lv_task_t * new_task = NULL;
168 lv_task_t * tmp;
169
170 /*Create task lists in order of priority from high to low*/
171 tmp = lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll));
172
173 /*It's the first task*/
174 if(NULL == tmp) {
175 new_task = lv_ll_ins_head(&LV_GC_ROOT(_lv_task_ll));
176 lv_mem_assert(new_task);
177 if(new_task == NULL) return NULL;
178 }
179 /*Insert the new task to proper place according to its priority*/
180 else {
181 do {
182 if(tmp->prio <= DEF_PRIO) {
183 new_task = lv_ll_ins_prev(&LV_GC_ROOT(_lv_task_ll), tmp);
184 lv_mem_assert(new_task);
185 if(new_task == NULL) return NULL;
186 break;
187 }
188 tmp = lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), tmp);
189 } while(tmp != NULL);
190
191 /*Only too high priority tasks were found. Add the task to the end*/
192 if(tmp == NULL) {
193 new_task = lv_ll_ins_tail(&LV_GC_ROOT(_lv_task_ll));
194 lv_mem_assert(new_task);
195 if(new_task == NULL) return NULL;
196 }
197 }
198
199 new_task->period = DEF_PERIOD;
200 new_task->task_cb = NULL;
201 new_task->prio = DEF_PRIO;
202
203 new_task->once = 0;
204 new_task->last_run = lv_tick_get();
205
206 new_task->user_data = NULL;
207
208 task_created = true;
209
210 return new_task;
211 }
212
213 /**
214 * Create a new lv_task
215 * @param task_xcb a callback which is the task itself. It will be called periodically.
216 * (the 'x' in the argument name indicates that its not a fully generic function because it not follows
217 * the `func_name(object, callback, ...)` convention)
218 * @param period call period in ms unit
219 * @param prio priority of the task (LV_TASK_PRIO_OFF means the task is stopped)
220 * @param user_data custom parameter
221 * @return pointer to the new task
222 */
lv_task_create(lv_task_cb_t task_cb,uint32_t period,lv_task_prio_t prio,void * user_data)223 lv_task_t * lv_task_create(lv_task_cb_t task_cb, uint32_t period, lv_task_prio_t prio, void * user_data)
224 {
225 lv_task_t * new_task = lv_task_create_basic();
226 lv_mem_assert(new_task);
227 if(new_task == NULL) return NULL;
228
229 lv_task_set_cb(new_task, task_cb);
230 lv_task_set_period(new_task, period);
231 lv_task_set_prio(new_task, prio);
232 new_task->user_data = user_data;
233
234 return new_task;
235 }
236
237 /**
238 * Set the callback the task (the function to call periodically)
239 * @param task pointer to a task
240 * @param task_cb teh function to call periodically
241 */
lv_task_set_cb(lv_task_t * task,lv_task_cb_t task_cb)242 void lv_task_set_cb(lv_task_t * task, lv_task_cb_t task_cb)
243 {
244 task->task_cb = task_cb;
245 }
246
247 /**
248 * Delete a lv_task
249 * @param task pointer to task created by task
250 */
lv_task_del(lv_task_t * task)251 void lv_task_del(lv_task_t * task)
252 {
253 lv_ll_rem(&LV_GC_ROOT(_lv_task_ll), task);
254
255 lv_mem_free(task);
256
257 if(LV_GC_ROOT(_lv_task_act) == task) task_deleted = true; /*The active task was deleted*/
258 }
259
260 /**
261 * Set new priority for a lv_task
262 * @param task pointer to a lv_task
263 * @param prio the new priority
264 */
lv_task_set_prio(lv_task_t * task,lv_task_prio_t prio)265 void lv_task_set_prio(lv_task_t * task, lv_task_prio_t prio)
266 {
267 if(task->prio == prio) return;
268
269 /*Find the tasks with new priority*/
270 lv_task_t * i;
271 LV_LL_READ(LV_GC_ROOT(_lv_task_ll), i)
272 {
273 if(i->prio <= prio) {
274 if(i != task) lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), task, i);
275 break;
276 }
277 }
278
279 /*There was no such a low priority so far then add the node to the tail*/
280 if(i == NULL) {
281 lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), task, NULL);
282 }
283
284 task->prio = prio;
285 }
286
287 /**
288 * Set new period for a lv_task
289 * @param task pointer to a lv_task
290 * @param period the new period
291 */
lv_task_set_period(lv_task_t * task,uint32_t period)292 void lv_task_set_period(lv_task_t * task, uint32_t period)
293 {
294 task->period = period;
295 }
296
297 /**
298 * Make a lv_task ready. It will not wait its period.
299 * @param task pointer to a lv_task.
300 */
lv_task_ready(lv_task_t * task)301 void lv_task_ready(lv_task_t * task)
302 {
303 task->last_run = lv_tick_get() - task->period - 1;
304 }
305
306 /**
307 * Delete the lv_task after one call
308 * @param task pointer to a lv_task.
309 */
lv_task_once(lv_task_t * task)310 void lv_task_once(lv_task_t * task)
311 {
312 task->once = 1;
313 }
314
315 /**
316 * Reset a lv_task.
317 * It will be called the previously set period milliseconds later.
318 * @param task pointer to a lv_task.
319 */
lv_task_reset(lv_task_t * task)320 void lv_task_reset(lv_task_t * task)
321 {
322 task->last_run = lv_tick_get();
323 }
324
325 /**
326 * Enable or disable the whole lv_task handling
327 * @param en: true: lv_task handling is running, false: lv_task handling is suspended
328 */
lv_task_enable(bool en)329 void lv_task_enable(bool en)
330 {
331 lv_task_run = en;
332 }
333
334 /**
335 * Get idle percentage
336 * @return the lv_task idle in percentage
337 */
lv_task_get_idle(void)338 uint8_t lv_task_get_idle(void)
339 {
340 return idle_last;
341 }
342
343 /**********************
344 * STATIC FUNCTIONS
345 **********************/
346
347 /**
348 * Execute task if its the priority is appropriate
349 * @param task pointer to lv_task
350 * @return true: execute, false: not executed
351 */
lv_task_exec(lv_task_t * task)352 static bool lv_task_exec(lv_task_t * task)
353 {
354 bool exec = false;
355
356 /*Execute if at least 'period' time elapsed*/
357 uint32_t elp = lv_tick_elaps(task->last_run);
358 if(elp >= task->period) {
359 task->last_run = lv_tick_get();
360 task_deleted = false;
361 task_created = false;
362 if(task->task_cb) task->task_cb(task);
363
364 /*Delete if it was a one shot lv_task*/
365 if(task_deleted == false) { /*The task might be deleted by itself as well*/
366 if(task->once != 0) {
367 lv_task_del(task);
368 }
369 }
370 exec = true;
371 }
372
373 return exec;
374 }
375