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