1、时钟节拍

任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等。
RT-Thread 中,时钟节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整。 rtconfig.h配置文件中定义:

  1. /*
  2. *频率是1000HZ 周期是1/1000 s
  3. *所以节拍是1ms
  4. */
  5. #define RT_TICK_PER_SECOND 1000

2、获取系统节拍

2.1、获取系统计数函数

  1. /**
  2. * This function will return current tick from operating system startup
  3. *
  4. * @return current tick
  5. */
  6. rt_tick_t rt_tick_get(void)

2.2、实例

  1. /*
  2. *...
  3. *[D/main] tm:7018
  4. *[D/main] tm:8020
  5. *[D/main] tm:9022
  6. *[D/main] tm:10024
  7. *...
  8. */
  9. int main(void)
  10. {
  11. rt_tick_t tm = 0;
  12. LOG_D("Hello RT-Thread!");
  13. while (1)
  14. {
  15. tm = rt_tick_get();
  16. LOG_D("tm:%-d\n",tm);
  17. rt_thread_mdelay(1000);
  18. }
  19. return RT_EOK;
  20. }

3、定时器

定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,定时器有硬件定时器和软件定时器之分:
硬件定时器: 芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。
硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
软件定时器: 由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。
RT-Thread操作系统提供软件实现的定时器,以时钟节拍(OS Tick)的时间长度为单位,即定时数值必须是OS Tick的整数倍

3.1、RT_Thread定时器介绍

RT-Thread 的定时器提供两类定时器机制:
☐ 第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。
☐ 第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将永远持续执行下去

根据定时器超时函数执行时所处的上下文环境,RT-Thread的定时器可以分为HARD_TIMER模式和SOFT_TIMER模式。
HARD_TIMER模式:中断上下文
定时器超时函数的要求:执行时间应该尽量短,执行时不应导致当前上下文挂起、等待。例如在中断上下文中执行的超时函数它不应该试图去申请动态内存、释放动态内存等
SOFT_TIMER模式:线程上下文
该模式被启用后,系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER 模式的定时器超时函数在都会在timer线程的上下文环境中执行

3.2、定时器源码分析

(1) RT-Thread OS启动阶段,执行rtthread_startup函数,在该函数中调用了定时器初始化函数

  1. /* timer system initialization */
  2. rt_system_timer_init();
  3. /* timer thread initialization */
  4. rt_system_timer_thread_init();

(2)rt_system_timer_init (硬件定时器初始化)

  1. struct rt_list_node
  2. {
  3. struct rt_list_node *next; /**< point to next node. */
  4. struct rt_list_node *prev; /**< point to prev node. */
  5. };
  6. typedef struct rt_list_node rt_list_t;
  7. void rt_system_timer_init(void)
  8. {
  9. int i;
  10. /* 结构体数组,在初始化的时候只有一个元素,就是链表头,后期添加定时器,按定时器定时时间顺序进行排序插入*/ for (i = 0; i < sizeof(rt_timer_list) / sizeof(rt_timer_list[0]); i++)
  11. {
  12. rt_list_init(rt_timer_list + i);
  13. }
  14. }
  15. /**
  16. * @brief initialize a list t퍷
  17. *
  18. * @param l list to be initialized
  19. */
  20. rt_inline void rt_list_init(rt_list_t *l)
  21. {
  22. l->next = l->prev = l;
  23. }

(3)rt_system_timer_thread_init(软件定时器初始化)

  1. /**
  2. * @ingroup SystemInit
  3. *
  4. * This function will initialize system timer thread
  5. */
  6. void rt_system_timer_thread_init(void)
  7. {
  8. #ifdef RT_USING_TIMER_SOFT
  9. int i;
  10. /* 初始化链表头*/
  11. for (i = 0;i < sizeof(rt_soft_timer_list) / sizeof(rt_soft_timer_list[0]);i++)
  12. {
  13. rt_list_init(rt_soft_timer_list + i);
  14. }
  15. /* start software timer thread */
  16. rt_thread_init(&timer_thread, //启动一个定时器线程(软件定时器方式)
  17. "timer",
  18. rt_thread_timer_entry,
  19. RT_NULL,
  20. &timer_thread_stack[0],
  21. sizeof(timer_thread_stack),
  22. RT_TIMER_THREAD_PRIO,
  23. 10);
  24. /* startup */
  25. rt_thread_startup(&timer_thread);
  26. #endif
  27. }

3.3、定时器工作机制

下面以一个例子来说明 RT-Thread 定时器的工作机制。在 RT-Thread 定时器模块中维护着两个重要的全局变量:
☐ 当前系统经过的 tick 时间 rt_tick(当硬件定时器中断来临时,它将加 1) ;
☐ 定时器链表 rt_timer_list。系统新创建并激活的定时器都会按照以超时时间排序的方式插入到rt_timer_list 链表中。
如下图所示,系统当前tick值为20,在当前系统中已经创建并启动了三个定时器,分别是定时时间为50个tick的Timer1、100个tick的Timer2和500个tick的Timer3,这三个定时器分别加上系统
当前时间 rt_tick=20,从小到大排序链接在 rt_timer_list 链表中,形成如图所示的定时器链表结构。而 rt_tick 随着硬件定时器的触发一直在增长(每一次硬件定时器中断来临,rt_tick 变量会加 1) ,50个tick以后,rt_tick从20增长到70,与Timer1的timeout值相等,这时会触发与Timer1定时器相关联的超时函数,同时将Timer1从rt_timer_list链表上删除。同理,100个tick和500个tick过去后,与Timer2 和 Timer3 定时器相关联的超时函数会被触发,接着将 Time2 和 Timer3 定时器从 rt_timer_list链表中删除。
如果系统当前定时器状态在 10 个 tick 以后(rt_tick=30)有一个任务新创建了一个 tick 值为 300 的Timer4定时器,由于Timer4定时器的timeout=rt_tick+300=330,因此它将被插入到Timer2和Timer3定时器中间,形成如下图所示链表结构:
image.png

3.4、定时器相关接口

☐ 动态创建一个定时器和删除定时器

  1. /**
  2. * This function will create a timer
  3. *
  4. * @param name the name of timer
  5. * @param timeout the timeout function
  6. * @param parameter the parameter of timeout function
  7. * @param time the tick of timer
  8. * @param flag the flag of timer
  9. * #define RT_TIMER_FLAG_ONE_SHOT 0x0 /**< one shot timer */
  10. * #define RT_TIMER_FLAG_PERIODIC 0x2 /**< periodic timer */
  11. * #define RT_TIMER_FLAG_HARD_TIMER 0x0 /**< hard timer,the timer's callback function will be called in tick isr. */
  12. * #define RT_TIMER_FLAG_SOFT_TIMER 0x4 /**< soft timer,the timer's callback function will be called in timer thread. */
  13. * @return the created timer object
  14. */
  15. rt_timer_t rt_timer_create(const char *name,
  16. void (*timeout)(void *parameter),
  17. void *parameter,
  18. rt_tick_t time,
  19. rt_uint8_t flag)
  20. /**
  21. * This function will delete a timer and release timer memory
  22. *
  23. * @param timer the timer to be deleted
  24. *
  25. * @return the operation status, RT_EOK on OK; RT_ERROR on error
  26. */
  27. rt_err_t rt_timer_delete(rt_timer_t timer)

☐ 初始化和脱离定时器

  1. /**
  2. * This function will initialize a timer, normally this function is used to
  3. * initialize a static timer object.
  4. *
  5. * @param timer the static timer object (typedef struct rt_timer *rt_timer_t;)
  6. * @param name the name of timer
  7. * @param timeout the timeout function
  8. * @param parameter the parameter of timeout function
  9. * @param time the tick of timer
  10. * @param flag the flag of timer
  11. */
  12. void rt_timer_init(rt_timer_t timer,
  13. const char *name,
  14. void (*timeout)(void *parameter),
  15. void *parameter,
  16. rt_tick_t time,
  17. rt_uint8_t flag)

静态定时器不需要再使用时,可以使用下面的函数接口:

  1. /**
  2. * This function will detach a timer from timer management.
  3. *
  4. * @param timer the static timer object
  5. *
  6. * @return the operation status, RT_EOK on OK; RT_ERROR on error
  7. */
  8. rt_err_t rt_timer_detach(rt_timer_t timer)

☐ 启动和停止定时器

  1. /**
  2. * This function will start the timer
  3. *
  4. * @param timer the timer to be started
  5. *
  6. * @return the operation status, RT_EOK on OK, -RT_ERROR on error
  7. */
  8. rt_err_t rt_timer_start(rt_timer_t timer)

若想使它停止,可以使用下面的函数接口:

  1. /**
  2. * This function will stop the timer
  3. *
  4. * @param timer the timer to be stopped
  5. *
  6. * @return the operation status, RT_EOK on OK, -RT_ERROR on error
  7. */
  8. rt_err_t rt_timer_stop(rt_timer_t timer)

☐ 控制定时器

  1. /**
  2. * This function will get or set some options of the timer
  3. *
  4. * @param timer the timer to be get or set
  5. * @param cmd the control command
  6. * @param arg the argument
  7. * #define RT_TIMER_CTRL_SET_TIME 0x0 /**< set timer control command */
  8. * #define RT_TIMER_CTRL_GET_TIME 0x1 /**< get timer control command */
  9. * #define RT_TIMER_CTRL_SET_ONESHOT 0x2 /**< change timer to one shot */
  10. * #define RT_TIMER_CTRL_SET_PERIODIC 0x3 /**< change timer to periodic */
  11. * @return RT_EOK
  12. */
  13. rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)

4、高精度延时

注意:这个函数只支持低于1个OS Tick的延时, 否则SysTick会出现溢出而不能够获得指定的延时时间

  1. /**
  2. * This function will delay for some us.
  3. *
  4. * @param us the delay time of us
  5. */
  6. void rt_hw_us_delay(rt_uint32_t us)

5、示例

5.1 动态创建定时器

  1. #include <rtthread.h>
  2. #define DBG_TAG "main"
  3. #define DBG_LVL DBG_LOG
  4. #include <rtdbg.h>
  5. static rt_timer_t timer1;
  6. static rt_timer_t timer2;
  7. static void timeout1(void* parameter)
  8. {
  9. rt_kprintf("periodic timer is timeout\n");
  10. }
  11. static void timeout2(void* parameter)
  12. {
  13. rt_kprintf("one shot timer is timeout\n");
  14. }
  15. int main(void)
  16. {
  17. timer1 = rt_timer_create(
  18. "timer1", //名字
  19. timeout1, //函数
  20. RT_NULL, //参数
  21. 1000, //1000ms后触发
  22. RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER //周期性触发 默认硬件 这里选择软件
  23. );
  24. if (timer1 != RT_NULL)
  25. rt_timer_start(timer1); //开启定时器
  26. timer2 = rt_timer_create(
  27. "timer2",
  28. timeout2,
  29. RT_NULL,
  30. 3000,
  31. RT_TIMER_FLAG_ONE_SHOT //单次触发
  32. );
  33. if (timer2 != RT_NULL)
  34. rt_timer_start(timer2); //开启定时器
  35. return 0;
  36. }

5.2 静态创建定时器

  1. struct rt_timer tm2;
  2. int flags = 0;
  3. void tm2_callback(void *parameter)
  4. {
  5. flags++;
  6. if(flags == 10){
  7. rt_timer_control(&tm2, RT_TIMER_CTRL_SET_ONESHOT,NULL);
  8. flags = 0;
  9. }
  10. rt_tick_t timeout = 1000;
  11. rt_timer_control(&tm2, RT_TIMER_CTRL_SET_TIME , (void *)&timeout);
  12. rt_kprintf("[%u]tm2_callback running...\n",rt_tick_get());
  13. }
  14. int main(void)
  15. {
  16. //静态创建定时器
  17. rt_timer_init(&tm2, "tm2_demo", tm2_callback, NULL, 3000, \
  18. RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
  19. rt_timer_start(&tm2);
  20. rt_timer_detach(&tm2);
  21. }