1 /**
2 * @file lv_timer.c
3 */
4
5 /*********************
6 * INCLUDES
7 *********************/
8 #include "lv_timer.h"
9 #include "../hal/lv_hal_tick.h"
10 #include "lv_assert.h"
11 #include "lv_mem.h"
12 #include "lv_ll.h"
13 #include "lv_gc.h"
14
15 /*********************
16 * DEFINES
17 *********************/
18 #define IDLE_MEAS_PERIOD 500 /*[ms]*/
19 #define DEF_PERIOD 500
20
21 /**********************
22 * TYPEDEFS
23 **********************/
24
25 /**********************
26 * STATIC PROTOTYPES
27 **********************/
28 static bool lv_timer_exec(lv_timer_t * timer);
29 static uint32_t lv_timer_time_remaining(lv_timer_t * timer);
30
31 /**********************
32 * STATIC VARIABLES
33 **********************/
34 static bool lv_timer_run = false;
35 static uint8_t idle_last = 0;
36 static bool timer_deleted;
37 static bool timer_created;
38
39 /**********************
40 * MACROS
41 **********************/
42 #if LV_LOG_TRACE_TIMER
43 #define TIMER_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
44 #else
45 #define TIMER_TRACE(...)
46 #endif
47
48 /**********************
49 * GLOBAL FUNCTIONS
50 **********************/
51
52 /**
53 * Init the lv_timer module
54 */
_lv_timer_core_init(void)55 void _lv_timer_core_init(void)
56 {
57 _lv_ll_init(&LV_GC_ROOT(_lv_timer_ll), sizeof(lv_timer_t));
58
59 /*Initially enable the lv_timer handling*/
60 lv_timer_enable(true);
61 }
62
63 /**
64 * Call it periodically to handle lv_timers.
65 * @return the time after which it must be called again
66 */
lv_timer_handler(void)67 uint32_t LV_ATTRIBUTE_TIMER_HANDLER lv_timer_handler(void)
68 {
69 TIMER_TRACE("begin");
70
71 /*Avoid concurrent running of the timer handler*/
72 static bool already_running = false;
73 if(already_running) {
74 TIMER_TRACE("already running, concurrent calls are not allow, returning");
75 return 1;
76 }
77 already_running = true;
78
79 if(lv_timer_run == false) {
80 already_running = false; /*Release mutex*/
81 return 1;
82 }
83
84 static uint32_t idle_period_start = 0;
85 static uint32_t busy_time = 0;
86
87 uint32_t handler_start = lv_tick_get();
88
89 if(handler_start == 0) {
90 static uint32_t run_cnt = 0;
91 run_cnt++;
92 if(run_cnt > 100) {
93 run_cnt = 0;
94 LV_LOG_WARN("It seems lv_tick_inc() is not called.");
95 }
96 }
97
98 /*Run all timer from the list*/
99 lv_timer_t * next;
100 do {
101 timer_deleted = false;
102 timer_created = false;
103 LV_GC_ROOT(_lv_timer_act) = _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
104 while(LV_GC_ROOT(_lv_timer_act)) {
105 /*The timer might be deleted if it runs only once ('repeat_count = 1')
106 *So get next element until the current is surely valid*/
107 next = _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), LV_GC_ROOT(_lv_timer_act));
108
109 if(lv_timer_exec(LV_GC_ROOT(_lv_timer_act))) {
110 /*If a timer was created or deleted then this or the next item might be corrupted*/
111 if(timer_created || timer_deleted) {
112 TIMER_TRACE("Start from the first timer again because a timer was created or deleted");
113 break;
114 }
115 }
116
117 LV_GC_ROOT(_lv_timer_act) = next; /*Load the next timer*/
118 }
119 } while(LV_GC_ROOT(_lv_timer_act));
120
121 uint32_t time_till_next = LV_NO_TIMER_READY;
122 next = _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
123 while(next) {
124 if(!next->paused) {
125 uint32_t delay = lv_timer_time_remaining(next);
126 if(delay < time_till_next)
127 time_till_next = delay;
128 }
129
130 next = _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), next); /*Find the next timer*/
131 }
132
133 busy_time += lv_tick_elaps(handler_start);
134 uint32_t idle_period_time = lv_tick_elaps(idle_period_start);
135 if(idle_period_time >= IDLE_MEAS_PERIOD) {
136 idle_last = (busy_time * 100) / idle_period_time; /*Calculate the busy percentage*/
137 idle_last = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/
138 busy_time = 0;
139 idle_period_start = lv_tick_get();
140 }
141
142 already_running = false; /*Release the mutex*/
143
144 TIMER_TRACE("finished (%d ms until the next timer call)", time_till_next);
145 return time_till_next;
146 }
147
148 /**
149 * Create an "empty" timer. It needs to initialized with at least
150 * `lv_timer_set_cb` and `lv_timer_set_period`
151 * @return pointer to the created timer
152 */
lv_timer_create_basic(void)153 lv_timer_t * lv_timer_create_basic(void)
154 {
155 return lv_timer_create(NULL, DEF_PERIOD, NULL);
156 }
157
158 /**
159 * Create a new lv_timer
160 * @param timer_xcb a callback which is the timer itself. It will be called periodically.
161 * (the 'x' in the argument name indicates that it's not a fully generic function because it not follows
162 * the `func_name(object, callback, ...)` convention)
163 * @param period call period in ms unit
164 * @param user_data custom parameter
165 * @return pointer to the new timer
166 */
lv_timer_create(lv_timer_cb_t timer_xcb,uint32_t period,void * user_data)167 lv_timer_t * lv_timer_create(lv_timer_cb_t timer_xcb, uint32_t period, void * user_data)
168 {
169 lv_timer_t * new_timer = NULL;
170
171 new_timer = _lv_ll_ins_head(&LV_GC_ROOT(_lv_timer_ll));
172 LV_ASSERT_MALLOC(new_timer);
173 if(new_timer == NULL) return NULL;
174
175 new_timer->period = period;
176 new_timer->timer_cb = timer_xcb;
177 new_timer->repeat_count = -1;
178 new_timer->paused = 0;
179 new_timer->last_run = lv_tick_get();
180 new_timer->user_data = user_data;
181
182 timer_created = true;
183
184 return new_timer;
185 }
186
187 /**
188 * Set the callback the timer (the function to call periodically)
189 * @param timer pointer to a timer
190 * @param timer_cb the function to call periodically
191 */
lv_timer_set_cb(lv_timer_t * timer,lv_timer_cb_t timer_cb)192 void lv_timer_set_cb(lv_timer_t * timer, lv_timer_cb_t timer_cb)
193 {
194 timer->timer_cb = timer_cb;
195 }
196
197 /**
198 * Delete a lv_timer
199 * @param timer pointer to timer created by timer
200 */
lv_timer_del(lv_timer_t * timer)201 void lv_timer_del(lv_timer_t * timer)
202 {
203 _lv_ll_remove(&LV_GC_ROOT(_lv_timer_ll), timer);
204 timer_deleted = true;
205
206 lv_mem_free(timer);
207 }
208
209 /**
210 * Pause/resume a timer.
211 * @param timer pointer to an lv_timer
212 */
lv_timer_pause(lv_timer_t * timer)213 void lv_timer_pause(lv_timer_t * timer)
214 {
215 timer->paused = true;
216 }
217
lv_timer_resume(lv_timer_t * timer)218 void lv_timer_resume(lv_timer_t * timer)
219 {
220 timer->paused = false;
221 }
222
223 /**
224 * Set new period for a lv_timer
225 * @param timer pointer to a lv_timer
226 * @param period the new period
227 */
lv_timer_set_period(lv_timer_t * timer,uint32_t period)228 void lv_timer_set_period(lv_timer_t * timer, uint32_t period)
229 {
230 timer->period = period;
231 }
232
233 /**
234 * Make a lv_timer ready. It will not wait its period.
235 * @param timer pointer to a lv_timer.
236 */
lv_timer_ready(lv_timer_t * timer)237 void lv_timer_ready(lv_timer_t * timer)
238 {
239 timer->last_run = lv_tick_get() - timer->period - 1;
240 }
241
242 /**
243 * Set the number of times a timer will repeat.
244 * @param timer pointer to a lv_timer.
245 * @param repeat_count -1 : infinity; 0 : stop ; n >0: residual times
246 */
lv_timer_set_repeat_count(lv_timer_t * timer,int32_t repeat_count)247 void lv_timer_set_repeat_count(lv_timer_t * timer, int32_t repeat_count)
248 {
249 timer->repeat_count = repeat_count;
250 }
251
252 /**
253 * Reset a lv_timer.
254 * It will be called the previously set period milliseconds later.
255 * @param timer pointer to a lv_timer.
256 */
lv_timer_reset(lv_timer_t * timer)257 void lv_timer_reset(lv_timer_t * timer)
258 {
259 timer->last_run = lv_tick_get();
260 }
261
262 /**
263 * Enable or disable the whole lv_timer handling
264 * @param en true: lv_timer handling is running, false: lv_timer handling is suspended
265 */
lv_timer_enable(bool en)266 void lv_timer_enable(bool en)
267 {
268 lv_timer_run = en;
269 }
270
271 /**
272 * Get idle percentage
273 * @return the lv_timer idle in percentage
274 */
lv_timer_get_idle(void)275 uint8_t lv_timer_get_idle(void)
276 {
277 return idle_last;
278 }
279
280 /**
281 * Iterate through the timers
282 * @param timer NULL to start iteration or the previous return value to get the next timer
283 * @return the next timer or NULL if there is no more timer
284 */
lv_timer_get_next(lv_timer_t * timer)285 lv_timer_t * lv_timer_get_next(lv_timer_t * timer)
286 {
287 if(timer == NULL) return _lv_ll_get_head(&LV_GC_ROOT(_lv_timer_ll));
288 else return _lv_ll_get_next(&LV_GC_ROOT(_lv_timer_ll), timer);
289 }
290
291 /**********************
292 * STATIC FUNCTIONS
293 **********************/
294
295 /**
296 * Execute timer if its remaining time is zero
297 * @param timer pointer to lv_timer
298 * @return true: execute, false: not executed
299 */
lv_timer_exec(lv_timer_t * timer)300 static bool lv_timer_exec(lv_timer_t * timer)
301 {
302 if(timer->paused) return false;
303
304 bool exec = false;
305 if(lv_timer_time_remaining(timer) == 0) {
306 /* Decrement the repeat count before executing the timer_cb.
307 * If any timer is deleted `if(timer->repeat_count == 0)` is not executed below
308 * but at least the repeat count is zero and the timer can be deleted in the next round*/
309 int32_t original_repeat_count = timer->repeat_count;
310 if(timer->repeat_count > 0) timer->repeat_count--;
311 timer->last_run = lv_tick_get();
312 TIMER_TRACE("calling timer callback: %p", *((void **)&timer->timer_cb));
313 if(timer->timer_cb && original_repeat_count != 0) timer->timer_cb(timer);
314 TIMER_TRACE("timer callback %p finished", *((void **)&timer->timer_cb));
315 LV_ASSERT_MEM_INTEGRITY();
316 exec = true;
317 }
318
319 if(timer_deleted == false) { /*The timer might be deleted by itself as well*/
320 if(timer->repeat_count == 0) { /*The repeat count is over, delete the timer*/
321 TIMER_TRACE("deleting timer with %p callback because the repeat count is over", *((void **)&timer->timer_cb));
322 lv_timer_del(timer);
323 }
324 }
325
326 return exec;
327 }
328
329 /**
330 * Find out how much time remains before a timer must be run.
331 * @param timer pointer to lv_timer
332 * @return the time remaining, or 0 if it needs to be run again
333 */
lv_timer_time_remaining(lv_timer_t * timer)334 static uint32_t lv_timer_time_remaining(lv_timer_t * timer)
335 {
336 /*Check if at least 'period' time elapsed*/
337 uint32_t elp = lv_tick_elaps(timer->last_run);
338 if(elp >= timer->period)
339 return 0;
340 return timer->period - elp;
341 }
342