1 /*
2 * Copyright (c) 2006-2024, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2006-03-12 Bernard first version
9 * 2006-04-29 Bernard implement thread timer
10 * 2006-06-04 Bernard implement rt_timer_control
11 * 2006-08-10 Bernard fix the periodic timer bug
12 * 2006-09-03 Bernard implement rt_timer_detach
13 * 2009-11-11 LiJin add soft timer
14 * 2010-05-12 Bernard fix the timer check bug.
15 * 2010-11-02 Charlie re-implement tick overflow issue
16 * 2012-12-15 Bernard fix the next timeout issue in soft timer
17 * 2014-07-12 Bernard does not lock scheduler when invoking soft-timer
18 * timeout function.
19 * 2021-08-15 supperthomas add the comment
20 * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to timer.c
21 * 2022-04-19 Stanley Correct descriptions
22 * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
23 * 2024-01-25 Shell add RT_TIMER_FLAG_THREAD_TIMER for timer to sync with sched
24 * 2024-05-01 wdfk-prog The rt_timer_check and _soft_timer_check functions are merged
25 */
26
27 #include <rtthread.h>
28 #include <rthw.h>
29
30 #define DBG_TAG "kernel.timer"
31 #define DBG_LVL DBG_INFO
32 #include <rtdbg.h>
33
34 #ifndef RT_USING_TIMER_ALL_SOFT
35 /* hard timer list */
36 static rt_list_t _timer_list[RT_TIMER_SKIP_LIST_LEVEL];
37 static struct rt_spinlock _htimer_lock;
38 #endif
39
40 #ifdef RT_USING_TIMER_SOFT
41
42 #ifndef RT_TIMER_THREAD_STACK_SIZE
43 #define RT_TIMER_THREAD_STACK_SIZE 512
44 #endif /* RT_TIMER_THREAD_STACK_SIZE */
45
46 #ifndef RT_TIMER_THREAD_PRIO
47 #define RT_TIMER_THREAD_PRIO 0
48 #endif /* RT_TIMER_THREAD_PRIO */
49
50 /* soft timer list */
51 static rt_list_t _soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
52 static struct rt_spinlock _stimer_lock;
53 static struct rt_thread _timer_thread;
54 static struct rt_semaphore _soft_timer_sem;
55 rt_align(RT_ALIGN_SIZE)
56 static rt_uint8_t _timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
57 #endif /* RT_USING_TIMER_SOFT */
58
59 #if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
60 extern void (*rt_object_take_hook)(struct rt_object *object);
61 extern void (*rt_object_put_hook)(struct rt_object *object);
62 static void (*rt_timer_enter_hook)(struct rt_timer *timer);
63 static void (*rt_timer_exit_hook)(struct rt_timer *timer);
64
65 /**
66 * @addtogroup group_hook
67 */
68
69 /**@{*/
70
71 /**
72 * @brief This function will set a hook function on timer,
73 * which will be invoked when enter timer timeout callback function.
74 *
75 * @param hook is the function point of timer
76 */
rt_timer_enter_sethook(void (* hook)(struct rt_timer * timer))77 void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer))
78 {
79 rt_timer_enter_hook = hook;
80 }
81
82 /**
83 * @brief This function will set a hook function, which will be
84 * invoked when exit timer timeout callback function.
85 *
86 * @param hook is the function point of timer
87 */
rt_timer_exit_sethook(void (* hook)(struct rt_timer * timer))88 void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer))
89 {
90 rt_timer_exit_hook = hook;
91 }
92
93 /**@}*/
94 #endif /* RT_USING_HOOK */
95
_timerlock_idx(struct rt_timer * timer)96 rt_inline struct rt_spinlock* _timerlock_idx(struct rt_timer *timer)
97 {
98 #ifdef RT_USING_TIMER_ALL_SOFT
99 return &_stimer_lock;
100 #else
101 #ifdef RT_USING_TIMER_SOFT
102 if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
103 {
104 return &_stimer_lock;
105 }
106 else
107 #endif /* RT_USING_TIMER_SOFT */
108 {
109 return &_htimer_lock;
110 }
111 #endif
112 }
113
114 /**
115 * @brief [internal] The init funtion of timer
116 *
117 * The internal called function of rt_timer_init
118 *
119 * @see rt_timer_init
120 *
121 * @param timer is timer object
122 *
123 * @param timeout is the timeout function
124 *
125 * @param parameter is the parameter of timeout function
126 *
127 * @param time is the tick of timer
128 *
129 * @param flag the flag of timer
130 */
_timer_init(rt_timer_t timer,void (* timeout)(void * parameter),void * parameter,rt_tick_t time,rt_uint8_t flag)131 static void _timer_init(rt_timer_t timer,
132 void (*timeout)(void *parameter),
133 void *parameter,
134 rt_tick_t time,
135 rt_uint8_t flag)
136 {
137 int i;
138
139 #ifdef RT_USING_TIMER_ALL_SOFT
140 flag |= RT_TIMER_FLAG_SOFT_TIMER;
141 #endif
142
143 /* set flag */
144 timer->parent.flag = flag;
145
146 /* set deactivated */
147 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
148
149 timer->timeout_func = timeout;
150 timer->parameter = parameter;
151
152 timer->timeout_tick = 0;
153 timer->init_tick = time;
154
155 /* initialize timer list */
156 for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
157 {
158 rt_list_init(&(timer->row[i]));
159 }
160 }
161
162 /**
163 * @brief Find the next emtpy timer ticks
164 *
165 * @param timer_list is the array of time list
166 *
167 * @param timeout_tick is the next timer's ticks
168 *
169 * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed.
170 * If the return value is any other values, it means this operation failed.
171 */
_timer_list_next_timeout(rt_list_t timer_list[],rt_tick_t * timeout_tick)172 static rt_err_t _timer_list_next_timeout(rt_list_t timer_list[], rt_tick_t *timeout_tick)
173 {
174 struct rt_timer *timer;
175
176 if (!rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
177 {
178 timer = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
179 struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
180 *timeout_tick = timer->timeout_tick;
181 return RT_EOK;
182 }
183 return -RT_ERROR;
184 }
185
186 /**
187 * @brief Remove the timer
188 *
189 * @param timer the point of the timer
190 */
_timer_remove(rt_timer_t timer)191 rt_inline void _timer_remove(rt_timer_t timer)
192 {
193 int i;
194
195 for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
196 {
197 rt_list_remove(&timer->row[i]);
198 }
199 }
200
201 #if (DBG_LVL == DBG_LOG)
202 /**
203 * @brief The number of timer
204 *
205 * @param timer the head of timer
206 *
207 * @return count of timer
208 */
_timer_count_height(struct rt_timer * timer)209 static int _timer_count_height(struct rt_timer *timer)
210 {
211 int i, cnt = 0;
212
213 for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
214 {
215 if (!rt_list_isempty(&timer->row[i]))
216 cnt++;
217 }
218 return cnt;
219 }
220 /**
221 * @brief dump the all timer information
222 *
223 * @param timer_heads the head of timer
224 */
rt_timer_dump(rt_list_t timer_heads[])225 void rt_timer_dump(rt_list_t timer_heads[])
226 {
227 rt_list_t *list;
228
229 for (list = timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1].next;
230 list != &timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1];
231 list = list->next)
232 {
233 struct rt_timer *timer = rt_list_entry(list,
234 struct rt_timer,
235 row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
236 rt_kprintf("%d", _timer_count_height(timer));
237 }
238 rt_kprintf("\n");
239 }
240 #endif /* (DBG_LVL == DBG_LOG) */
241
242 /**
243 * @addtogroup group_clock_management
244 */
245
246 /**@{*/
247
248 /**
249 * @brief This function will initialize a timer
250 * normally this function is used to initialize a static timer object.
251 *
252 * @param timer is the point of timer
253 *
254 * @param name is a pointer to the name of the timer
255 *
256 * @param timeout is the callback of timer
257 *
258 * @param parameter is the param of the callback
259 *
260 * @param time is timeout ticks of timer
261 *
262 * NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1).
263 *
264 * @param flag is the flag of timer
265 *
266 */
rt_timer_init(rt_timer_t timer,const char * name,void (* timeout)(void * parameter),void * parameter,rt_tick_t time,rt_uint8_t flag)267 void rt_timer_init(rt_timer_t timer,
268 const char *name,
269 void (*timeout)(void *parameter),
270 void *parameter,
271 rt_tick_t time,
272 rt_uint8_t flag)
273 {
274 /* parameter check */
275 RT_ASSERT(timer != RT_NULL);
276 RT_ASSERT(timeout != RT_NULL);
277 RT_ASSERT(time < RT_TICK_MAX / 2);
278
279 /* timer object initialization */
280 rt_object_init(&(timer->parent), RT_Object_Class_Timer, name);
281
282 _timer_init(timer, timeout, parameter, time, flag);
283 }
284 RTM_EXPORT(rt_timer_init);
285
286 /**
287 * @brief This function will detach a timer from timer management.
288 *
289 * @param timer is the timer to be detached
290 *
291 * @return the status of detach
292 */
rt_timer_detach(rt_timer_t timer)293 rt_err_t rt_timer_detach(rt_timer_t timer)
294 {
295 rt_base_t level;
296 struct rt_spinlock *spinlock;
297
298 /* parameter check */
299 RT_ASSERT(timer != RT_NULL);
300 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
301 RT_ASSERT(rt_object_is_systemobject(&timer->parent));
302
303 spinlock = _timerlock_idx(timer);
304 level = rt_spin_lock_irqsave(spinlock);
305
306 _timer_remove(timer);
307 /* stop timer */
308 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
309
310 rt_spin_unlock_irqrestore(spinlock, level);
311 rt_object_detach(&(timer->parent));
312
313 return RT_EOK;
314 }
315 RTM_EXPORT(rt_timer_detach);
316
317 #ifdef RT_USING_HEAP
318 /**
319 * @brief This function will create a timer
320 *
321 * @param name is the name of timer
322 *
323 * @param timeout is the timeout function
324 *
325 * @param parameter is the parameter of timeout function
326 *
327 * @param time is timeout ticks of the timer
328 *
329 * NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1).
330 *
331 * @param flag is the flag of timer. Timer will invoke the timeout function according to the selected values of flag, if one or more of the following flags is set.
332 *
333 * RT_TIMER_FLAG_ONE_SHOT One shot timing
334 * RT_TIMER_FLAG_PERIODIC Periodic timing
335 *
336 * RT_TIMER_FLAG_HARD_TIMER Hardware timer
337 * RT_TIMER_FLAG_SOFT_TIMER Software timer
338 * RT_TIMER_FLAG_THREAD_TIMER Thread timer
339 *
340 * NOTE:
341 * You can use multiple values with "|" logical operator. By default, system will use the RT_TIME_FLAG_HARD_TIMER.
342 *
343 * @return the created timer object
344 */
rt_timer_create(const char * name,void (* timeout)(void * parameter),void * parameter,rt_tick_t time,rt_uint8_t flag)345 rt_timer_t rt_timer_create(const char *name,
346 void (*timeout)(void *parameter),
347 void *parameter,
348 rt_tick_t time,
349 rt_uint8_t flag)
350 {
351 struct rt_timer *timer;
352
353 /* parameter check */
354 RT_ASSERT(timeout != RT_NULL);
355 RT_ASSERT(time < RT_TICK_MAX / 2);
356
357 /* allocate a object */
358 timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
359 if (timer == RT_NULL)
360 {
361 return RT_NULL;
362 }
363
364 _timer_init(timer, timeout, parameter, time, flag);
365
366 return timer;
367 }
368 RTM_EXPORT(rt_timer_create);
369
370 /**
371 * @brief This function will delete a timer and release timer memory
372 *
373 * @param timer the timer to be deleted
374 *
375 * @return the operation status, RT_EOK on OK; -RT_ERROR on error
376 */
rt_timer_delete(rt_timer_t timer)377 rt_err_t rt_timer_delete(rt_timer_t timer)
378 {
379 rt_base_t level;
380 struct rt_spinlock *spinlock;
381
382 /* parameter check */
383 RT_ASSERT(timer != RT_NULL);
384 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
385 RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE);
386
387 spinlock = _timerlock_idx(timer);
388
389 level = rt_spin_lock_irqsave(spinlock);
390
391 _timer_remove(timer);
392 /* stop timer */
393 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
394 rt_spin_unlock_irqrestore(spinlock, level);
395 rt_object_delete(&(timer->parent));
396
397 return RT_EOK;
398 }
399 RTM_EXPORT(rt_timer_delete);
400 #endif /* RT_USING_HEAP */
401
402 /**
403 * @brief This function will start the timer
404 *
405 * @param timer the timer to be started
406 *
407 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
408 */
_timer_start(rt_list_t * timer_list,rt_timer_t timer)409 static rt_err_t _timer_start(rt_list_t *timer_list, rt_timer_t timer)
410 {
411 unsigned int row_lvl;
412 rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
413 unsigned int tst_nr;
414 static unsigned int random_nr;
415
416 /* remove timer from list */
417 _timer_remove(timer);
418 /* change status of timer */
419 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
420
421 RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
422
423 timer->timeout_tick = rt_tick_get() + timer->init_tick;
424
425 row_head[0] = &timer_list[0];
426 for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
427 {
428 for (; row_head[row_lvl] != timer_list[row_lvl].prev;
429 row_head[row_lvl] = row_head[row_lvl]->next)
430 {
431 struct rt_timer *t;
432 rt_list_t *p = row_head[row_lvl]->next;
433
434 /* fix up the entry pointer */
435 t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
436
437 /* If we have two timers that timeout at the same time, it's
438 * preferred that the timer inserted early get called early.
439 * So insert the new timer to the end the the some-timeout timer
440 * list.
441 */
442 if ((t->timeout_tick - timer->timeout_tick) == 0)
443 {
444 continue;
445 }
446 else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
447 {
448 break;
449 }
450 }
451 if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
452 row_head[row_lvl + 1] = row_head[row_lvl] + 1;
453 }
454
455 /* Interestingly, this super simple timer insert counter works very very
456 * well on distributing the list height uniformly. By means of "very very
457 * well", I mean it beats the randomness of timer->timeout_tick very easily
458 * (actually, the timeout_tick is not random and easy to be attacked). */
459 random_nr++;
460 tst_nr = random_nr;
461
462 rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1],
463 &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
464 for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
465 {
466 if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
467 rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
468 &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
469 else
470 break;
471 /* Shift over the bits we have tested. Works well with 1 bit and 2
472 * bits. */
473 tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
474 }
475
476 timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
477
478 return RT_EOK;
479 }
480
481 /**
482 * @brief This function will check timer list, if a timeout event happens,
483 * the corresponding timeout function will be invoked.
484 *
485 * @param timer_list The timer list to check.
486 * @param lock The lock for the timer list.
487 */
_timer_check(rt_list_t * timer_list,struct rt_spinlock * lock)488 static void _timer_check(rt_list_t *timer_list, struct rt_spinlock *lock)
489 {
490 struct rt_timer *t;
491 rt_tick_t current_tick;
492 rt_base_t level;
493 rt_list_t list;
494
495 level = rt_spin_lock_irqsave(lock);
496
497 current_tick = rt_tick_get();
498
499 rt_list_init(&list);
500
501 while (!rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
502 {
503 t = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
504 struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
505
506 /* re-get tick */
507 current_tick = rt_tick_get();
508
509 /*
510 * It supposes that the new tick shall less than the half duration of
511 * tick max.
512 */
513 if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
514 {
515 RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));
516
517 /* remove timer from timer list firstly */
518 _timer_remove(t);
519 if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
520 {
521 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
522 }
523
524 /* add timer to temporary list */
525 rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
526
527 rt_spin_unlock_irqrestore(lock, level);
528
529 /* call timeout function */
530 t->timeout_func(t->parameter);
531
532 RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
533
534 level = rt_spin_lock_irqsave(lock);
535
536 /* Check whether the timer object is detached or started again */
537 if (rt_list_isempty(&list))
538 {
539 continue;
540 }
541 rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
542 if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
543 (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
544 {
545 /* start it */
546 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
547 _timer_start(timer_list, t);
548 }
549 }
550 else break;
551 }
552 rt_spin_unlock_irqrestore(lock, level);
553 }
554
555 /**
556 * @brief This function will start the timer
557 *
558 * @param timer the timer to be started
559 *
560 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
561 */
rt_timer_start(rt_timer_t timer)562 rt_err_t rt_timer_start(rt_timer_t timer)
563 {
564 rt_sched_lock_level_t slvl;
565 int is_thread_timer = 0;
566 struct rt_spinlock *spinlock;
567 rt_list_t *timer_list;
568 rt_base_t level;
569 rt_err_t err;
570
571 /* parameter check */
572 RT_ASSERT(timer != RT_NULL);
573 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
574
575 #ifdef RT_USING_TIMER_ALL_SOFT
576 timer_list = _soft_timer_list;
577 spinlock = &_stimer_lock;
578 #else
579 #ifdef RT_USING_TIMER_SOFT
580 if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
581 {
582 timer_list = _soft_timer_list;
583 spinlock = &_stimer_lock;
584 }
585 else
586 #endif /* RT_USING_TIMER_SOFT */
587 {
588 timer_list = _timer_list;
589 spinlock = &_htimer_lock;
590 }
591 #endif
592
593 if (timer->parent.flag & RT_TIMER_FLAG_THREAD_TIMER)
594 {
595 rt_thread_t thread;
596 is_thread_timer = 1;
597 rt_sched_lock(&slvl);
598
599 thread = rt_container_of(timer, struct rt_thread, thread_timer);
600 RT_ASSERT(rt_object_get_type(&thread->parent) == RT_Object_Class_Thread);
601 rt_sched_thread_timer_start(thread);
602 }
603
604 level = rt_spin_lock_irqsave(spinlock);
605
606 err = _timer_start(timer_list, timer);
607
608 rt_spin_unlock_irqrestore(spinlock, level);
609
610 if (is_thread_timer)
611 {
612 rt_sched_unlock(slvl);
613 }
614
615 return err;
616 }
617 RTM_EXPORT(rt_timer_start);
618
619 /**
620 * @brief This function will stop the timer
621 *
622 * @param timer the timer to be stopped
623 *
624 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
625 */
rt_timer_stop(rt_timer_t timer)626 rt_err_t rt_timer_stop(rt_timer_t timer)
627 {
628 rt_base_t level;
629 struct rt_spinlock *spinlock;
630
631 /* timer check */
632 RT_ASSERT(timer != RT_NULL);
633 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
634
635 spinlock = _timerlock_idx(timer);
636
637 level = rt_spin_lock_irqsave(spinlock);
638
639 if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
640 {
641 rt_spin_unlock_irqrestore(spinlock, level);
642 return -RT_ERROR;
643 }
644 RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
645
646 _timer_remove(timer);
647 /* change status */
648 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
649
650 rt_spin_unlock_irqrestore(spinlock, level);
651
652 return RT_EOK;
653 }
654 RTM_EXPORT(rt_timer_stop);
655
656 /**
657 * @brief This function will get or set some options of the timer
658 *
659 * @param timer the timer to be get or set
660 * @param cmd the control command
661 * @param arg the argument
662 *
663 * @return the statu of control
664 */
rt_timer_control(rt_timer_t timer,int cmd,void * arg)665 rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
666 {
667 struct rt_spinlock *spinlock;
668 rt_base_t level;
669
670 /* parameter check */
671 RT_ASSERT(timer != RT_NULL);
672 RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
673
674 spinlock = _timerlock_idx(timer);
675
676 level = rt_spin_lock_irqsave(spinlock);
677 switch (cmd)
678 {
679 case RT_TIMER_CTRL_GET_TIME:
680 *(rt_tick_t *)arg = timer->init_tick;
681 break;
682
683 case RT_TIMER_CTRL_SET_TIME:
684 RT_ASSERT((*(rt_tick_t *)arg) < RT_TICK_MAX / 2);
685 if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
686 {
687 _timer_remove(timer);
688 timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
689 }
690 timer->init_tick = *(rt_tick_t *)arg;
691 break;
692
693 case RT_TIMER_CTRL_SET_ONESHOT:
694 timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
695 break;
696
697 case RT_TIMER_CTRL_SET_PERIODIC:
698 timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
699 break;
700
701 case RT_TIMER_CTRL_GET_STATE:
702 if(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
703 {
704 /*timer is start and run*/
705 *(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED;
706 }
707 else
708 {
709 /*timer is stop*/
710 *(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
711 }
712 break;
713
714 case RT_TIMER_CTRL_GET_REMAIN_TIME:
715 *(rt_tick_t *)arg = timer->timeout_tick;
716 break;
717 case RT_TIMER_CTRL_GET_FUNC:
718 *(void **)arg = (void *)timer->timeout_func;
719 break;
720
721 case RT_TIMER_CTRL_SET_FUNC:
722 timer->timeout_func = (void (*)(void*))arg;
723 break;
724
725 case RT_TIMER_CTRL_GET_PARM:
726 *(void **)arg = timer->parameter;
727 break;
728
729 case RT_TIMER_CTRL_SET_PARM:
730 timer->parameter = arg;
731 break;
732
733 default:
734 break;
735 }
736 rt_spin_unlock_irqrestore(spinlock, level);
737
738 return RT_EOK;
739 }
740 RTM_EXPORT(rt_timer_control);
741
742 /**
743 * @brief This function will check timer list, if a timeout event happens,
744 * the corresponding timeout function will be invoked.
745 *
746 * @note This function shall be invoked in operating system timer interrupt.
747 */
rt_timer_check(void)748 void rt_timer_check(void)
749 {
750 RT_ASSERT(rt_interrupt_get_nest() > 0);
751
752 #ifdef RT_USING_SMP
753 /* Running on core 0 only */
754 if (rt_cpu_get_id() != 0)
755 {
756 return;
757 }
758 #endif
759
760 #ifdef RT_USING_TIMER_SOFT
761 rt_err_t ret = RT_ERROR;
762 rt_tick_t next_timeout;
763
764 ret = _timer_list_next_timeout(_soft_timer_list, &next_timeout);
765 if ((ret == RT_EOK) && (next_timeout <= rt_tick_get()))
766 {
767 rt_sem_release(&_soft_timer_sem);
768 }
769 #endif
770 #ifndef RT_USING_TIMER_ALL_SOFT
771 _timer_check(_timer_list, &_htimer_lock);
772 #endif
773 }
774
775 /**
776 * @brief This function will return the next timeout tick in the system.
777 *
778 * @return the next timeout tick in the system
779 */
rt_timer_next_timeout_tick(void)780 rt_tick_t rt_timer_next_timeout_tick(void)
781 {
782 rt_base_t level;
783 rt_tick_t htimer_next_timeout = RT_TICK_MAX, stimer_next_timeout = RT_TICK_MAX;
784
785 #ifndef RT_USING_TIMER_ALL_SOFT
786 level = rt_spin_lock_irqsave(&_htimer_lock);
787 _timer_list_next_timeout(_timer_list, &htimer_next_timeout);
788 rt_spin_unlock_irqrestore(&_htimer_lock, level);
789 #endif
790
791 #ifdef RT_USING_TIMER_SOFT
792 level = rt_spin_lock_irqsave(&_stimer_lock);
793 _timer_list_next_timeout(_soft_timer_list, &stimer_next_timeout);
794 rt_spin_unlock_irqrestore(&_stimer_lock, level);
795 #endif
796
797 return htimer_next_timeout < stimer_next_timeout ? htimer_next_timeout : stimer_next_timeout;
798 }
799
800 #ifdef RT_USING_TIMER_SOFT
801 /**
802 * @brief System timer thread entry
803 *
804 * @param parameter is the arg of the thread
805 */
_timer_thread_entry(void * parameter)806 static void _timer_thread_entry(void *parameter)
807 {
808 RT_UNUSED(parameter);
809
810 while (1)
811 {
812 _timer_check(_soft_timer_list, &_stimer_lock); /* check software timer */
813 rt_sem_take(&_soft_timer_sem, RT_WAITING_FOREVER);
814 }
815 }
816 #endif /* RT_USING_TIMER_SOFT */
817
818 /**
819 * @ingroup group_system_init
820 *
821 * @brief This function will initialize system timer
822 */
rt_system_timer_init(void)823 void rt_system_timer_init(void)
824 {
825 #ifndef RT_USING_TIMER_ALL_SOFT
826 rt_size_t i;
827
828 for (i = 0; i < sizeof(_timer_list) / sizeof(_timer_list[0]); i++)
829 {
830 rt_list_init(_timer_list + i);
831 }
832
833 rt_spin_lock_init(&_htimer_lock);
834 #endif
835 }
836
837 /**
838 * @ingroup group_system_init
839 *
840 * @brief This function will initialize system timer thread
841 */
rt_system_timer_thread_init(void)842 void rt_system_timer_thread_init(void)
843 {
844 #ifdef RT_USING_TIMER_SOFT
845 int i;
846
847 for (i = 0;
848 i < sizeof(_soft_timer_list) / sizeof(_soft_timer_list[0]);
849 i++)
850 {
851 rt_list_init(_soft_timer_list + i);
852 }
853 rt_spin_lock_init(&_stimer_lock);
854 rt_sem_init(&_soft_timer_sem, "stimer", 0, RT_IPC_FLAG_PRIO);
855 rt_sem_control(&_soft_timer_sem, RT_IPC_CMD_SET_VLIMIT, (void*)1);
856 /* start software timer thread */
857 rt_thread_init(&_timer_thread,
858 "timer",
859 _timer_thread_entry,
860 RT_NULL,
861 &_timer_thread_stack[0],
862 sizeof(_timer_thread_stack),
863 RT_TIMER_THREAD_PRIO,
864 10);
865
866 /* startup */
867 rt_thread_startup(&_timer_thread);
868 #endif /* RT_USING_TIMER_SOFT */
869 }
870
871 /**@}*/
872