RT-Thread定时器

目录

定时器管理

 定时器超时函数

定时器管理接口

创建定时器

删除定时器

初始化定时器

脱离定时器

启动定时器

停止定时器

控制定时器

定时器执行上下文

定时器管理

定时器,是指从指定的时刻开始,经过一个指定的时间,然后触发一个事件,类似定个时间提醒第二天能够按时起床,定时器有软件定时器和硬件定时器之分;

  • 硬件定时器是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式。
  • 软件定时器是由操作系统提供的一类系统接口(函数),它在构建硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。

在操作系统中,通常软件定时器以系统节拍(tick)为单位。节拍长度指的是周期性硬件定时器两次中断间的间隔时间长度。这个周期性硬件定时器也称之为操作系统时钟。软件定时器以这个节拍时间长度为单位,数值必须是这个节拍的整数倍,例如节拍是10ms,那么上层软件定时器只能是10ms,20ms,100ms等,而不能取值为15ms。由于节拍定义了系统中定时器能够分辨的精度,系统可以根据实际系统CPU的处理能力和实时性需求设置合适的数值,tick值设置越小,精度越高,但是系统开销也将越大(在1S中系统用于处理时钟中断的次数也就越多)。RT-Thread的定时器也基于类似的系统节拍,提供了基于节拍整数倍的定时能力。

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

下面以一个实际代码来说明RT-Thread软件定时器的基本工作原理,在RT-Thread定时器模块中维护着这两个重要的全局变量:

  • 当前系统经过的tick时间rt_tick(当硬件定时器中断来临时,它将加1)
  • 定时器链表rt_time_list。系统新创建并激活的定时器都会按照以超时时间排序的方式插入到rt_time_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相关联的超时函数会被触发,接着将Timer2和Timer3定时器相关联的超时函数会被触发,接着将Time2和Timer3定时器从rt_timer_list链表中删除

如果系统当前定时器状态在10个tick以后(rt_tick=30)有一个任务创建了一个tick值为300的Timer4定时器,由于Timer4定时器的timeout=rt_tick+300=330,因此它将被插入到Timer2和Timer3定时器的中间,形成如图所示的链表结构

 定时器超时函数

软定时器最主要的目的是在经过设定的时间后,系统能够自动执行用户设定的动作。当定时器设定的时间到了,即超时时,执行的动作函数称之为定时器的超市函数。与线程不同的是,超市函数的执行上下文环境并未显式给出。

在RT-Thread实时操作系统中,定时器超时函数存在着两种情况:

  • 超时函数在(系统时钟)中断上下文环境中执行
  • 超时函数在线程的上下文环境中执行

如果超时函数是在中断上下文环境中执行,显然对于超时函数的要求与中断服务例程的要求相同,执行时间尽量短,执行时不应导致当前上下文挂起,等待。例如在中断上下文中执行的超时函数不应该试图去申请动态内存、释放动态内存等(其中一个就包括rt_timer_delete函数调用)。

而在超时函数在线程上下文中执行,则不会有这个限制,但是通常也要求超时函数执行时间应该尽量短,而不影响到其他定时器或本次定时器的下一次周期性超时,这两种情况在RT-Thread定时器控制块中分别使用参数:RT_TIMER_FLAG_HARD_TIMER和RT_TIMER_FLAG_SOFT_TIMER指定,HARD_TIMER代表的是定时器超时函数执行上下文是在中断上下文中执行,SOFT_TIMER代表的是定时器函数执行的上下文是timer线程(在rtconfig.h头文件中定义宏RT_USING_TIMER_SOFT使得timer线程能被使用)

定时器管理控制块’

struct rt_timer
{
    struct rt_object parent;                            /**< inherit from rt_object */

    rt_list_t        row[RT_TIMER_SKIP_LIST_LEVEL];

    void (*timeout_func)(void *parameter);              /**< timeout function */
    void            *parameter;                         /**< timeout function's parameter */

    rt_tick_t        init_tick;                         /**< timer timeout tick */
    rt_tick_t        timeout_tick;                      /**< timeout tick */
};

定时器控制块由struct rt_timer结构体定义,并形成定时器内核对象再链接到内核对象容器中进行管理。list成员则用户把一个激活的(已经启动的)定时器链接到rt_timer_list链表中。

定时器管理接口

定时器管理系统初始化

初始化定时器管理系统,可以通过下面的接口函数实现:

void rt_system_timer_init(void)
{
    int i;

    for (i = 0; i < sizeof(rt_timer_list) / sizeof(rt_timer_list[0]); i++)
    {
        rt_list_init(rt_timer_list + i);
    }
}

上面的使用的是硬件定时器初始化,如果要使用SOFT_TIMER,则系统初始化时,应该调用下面的接口:

void rt_system_timer_thread_init(void)
{
#ifdef RT_USING_TIMER_SOFT
    int i;

    for (i = 0;
         i < sizeof(rt_soft_timer_list) / sizeof(rt_soft_timer_list[0]);
         i++)
    {
        rt_list_init(rt_soft_timer_list + i);
    }

    /* start software timer thread */
    rt_thread_init(&timer_thread,
                   "timer",
                   rt_thread_timer_entry,
                   RT_NULL,
                   &timer_thread_stack[0],
                   sizeof(timer_thread_stack),
                   RT_TIMER_THREAD_PRIO,
                   10);

    /* startup */
    rt_thread_startup(&timer_thread);
#endif
}

创建定时器

动态创建一个定时器:

rt_timer_t rt_timer_create(const char *name,
                           void (*timeout)(void *parameter),
                           void       *parameter,
                           rt_tick_t   time,
                           rt_uint8_t  flag)

参数

const char*name : 定时器的名称

void (timeout)(void parameter):定时器超时函数指针(定时器超时时,系统会调用这个函数)

void *parameter:定时器超时函数的入口参数(当定时器超时时,调用超时回调函数会把这个参数做为入口参数传递给超时函数

rt_tick_t time:定时器的超时函数,单位是系统节拍

rt_uint8_t flag:定时器创建时的参数,支持的值包括(可以用或关系取多个值)

rt_def.h中定义了一些定时器相关的宏:

#define RT_TIMER_FLAG_DEACTIVATED 0x0 /* 定时器为非激活态 */
#define RT_TIMER_FLAG_ACTIVATED 0x1 /* 定时器为激活状态 */
#define RT_TIMER_FLAG_ONE_SHOT 0x0 /* 单次定时 */
#define RT_TIMER_FLAG_PERIODIC 0x2 /* 周期定时 */
#define RT_TIMER_FLAG_HARD_TIMER 0x0 /* 硬件定时器 */
#define RT_TIMER_FLAG_SOFT_TIMER 0x4 /* 软件定时器 */

当指定的flag为RT_TIMER_FLAG_HARD_TIMER时,如果定时器超时,定时器的回调函数将在时钟中断的服务例程上下文调用,当指定的flag为RT_TIMER_FLAG_SOFT_TIMER,如果定时器超时,定时器的回调函数将在系统时钟timer线程的上下文中被调用。

函数返回:如果定时器创建成功,返回定时器句柄,否则返回失败RT_NULL。

删除定时器

系统不在使用特定定时器,可以使用下面的函数接口

rt_err_t rt_timer_delete(rt_timer_t timer)

 调用后,系统会把这个定时器从rt_timer_list链表中删除,然后释放相应的定时器控制块占有的内存。

初始化定时器

void rt_timer_init(rt_timer_t  timer,
                   const char *name,
                   void (*timeout)(void *parameter),
                   void       *parameter,
                   rt_tick_t   time,
                   rt_uint8_t  flag)

当选择静态创建定时器时,可利用rt_timer_init()来初始化该定时器,函数会初始化相应的定时器控制块和定时器名称,超时函数等。 

当指定的flag为RT_IMER_FLAG_HARD_TIMER时,如果定时器超时,定时器的回调函数
将在时钟中断的服务例程上下文中被调用;当指定的flag为RT_TIMER_FLAG_SOFT_TIMER
时,如果定时器超时,定时器的回调函数将在系统时钟timer线程的上下文中被调用。

脱离定时器

rt_err_t rt_timer_detach(rt_timer_t timer)

 脱离定时器,系统会把定时器对象从系统容器链表中删除,但是定时器对象所占有的内存不被释放。

启动定时器

rt_err_t rt_timer_start(rt_timer_t timer)

当定时器创建之后或者初始化之后,并不会被立即启动,必须在调用启动定时器函数接口后,才开始工作。调用定时器启动函数之后,定时器的状态将更改为激活状态(RT_TIMER_FLAG_ACTIVATED),并按照超时顺序插入到rt_timer_list链表中。

停止定时器

rt_err_t rt_timer_stop(rt_timer_t timer)
调用定时器停止函数接口后,定时器状态将更改为停止状态,并从rt_timer_list链表中 脱离出来不参与定时器超时检查。当一个(周期性)定时器超时时,也可以调用这个函数接 口停止这个(周期性)定时器本身。

控制定时器

/**
 * This function will get or set some options of the timer
 *
 * @param timer the timer to be get or set
 * @param cmd the control command
 * @param arg the argument
 *
 * @return RT_EOK
 */
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
{

定时器执行上下文

RT-Thread里定时器默认的方式是HARD_TIMER定时器,即定时器超时后,超时函数是在系统时钟中断上下文中运行的,在中断上下文中执行的方式决定了定时器的超时函数不应调用任何会让当前上下文挂起的系统函数,也不能执行非常长的时间,否则会导致其他中断的响应时间加长或抢占其他线程执行的时间。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我会尽力回答你的问题。首先,RT-Thread是一个实时操作系统,它提供了一些基本的实时操作系统功能,例如线程、信号量、消息队列、定时器等。其中,定时器是一种非常重要的实时操作系统功能,它可以用来在指定的时间间隔内执行一些操作。 在RT-Thread中,定时器的实现是基于软件定时器,即在内核中维护一个定时器列表,每个定时器都有一个超时时间。当定时器到期时,内核会调用定时器回调函数来执行相应的操作。 下面是RT-Thread定时器的源码分析: 1. 定时器结构体 在RT-Thread中,定时器的结构体定义如下: ```c struct rt_timer { char name[RT_NAME_MAX]; /* 定时器名称 */ rt_list_t list; /* 定时器列表 */ rt_tick_t timeout_tick; /* 定时器超时时间 */ rt_tick_t init_tick; /* 定时器初始时间 */ rt_uint8_t flag; /* 定时器标志 */ rt_uint8_t state; /* 定时器状态 */ void (*timeout_func)(void* parameter); /* 定时器回调函数 */ void* parameter; /* 回调函数参数 */ }; ``` 可以看到,定时器结构体包含了定时器的名称、超时时间、标志、状态、回调函数等信息。 2. 定时器创建 在RT-Thread中,定时器的创建函数是rt_timer_create(),它的函数原型如下: ```c rt_err_t rt_timer_create(rt_timer_t *timer, const char *name, void (*timeout_func)(void* parameter), void* parameter, rt_tick_t time, rt_uint8_t flag); ``` 其中,timer表示定时器指针,name表示定时器名称,timeout_func表示定时器回调函数,parameter表示回调函数参数,time表示定时器超时时间,flag表示定时器标志。 rt_timer_create()函数会在内核中创建一个定时器,并将定时器添加到定时器列表中。如果创建成功,函数返回RT_EOK,否则返回错误码。 3. 定时器启动 在RT-Thread中,定时器的启动函数是rt_timer_start(),它的函数原型如下: ```c rt_err_t rt_timer_start(rt_timer_t timer); ``` rt_timer_start()函数会启动指定的定时器,并将其状态设置为RT_TIMER_FLAG_ACTIVATED。如果启动成功,函数返回RT_EOK,否则返回错误码。 4. 定时器停止 在RT-Thread中,定时器的停止函数是rt_timer_stop(),它的函数原型如下: ```c rt_err_t rt_timer_stop(rt_timer_t timer); ``` rt_timer_stop()函数会停止指定的定时器,并将其状态设置为RT_TIMER_FLAG_DEACTIVATED。如果停止成功,函数返回RT_EOK,否则返回错误码。 5. 定时器删除 在RT-Thread中,定时器的删除函数是rt_timer_delete(),它的函数原型如下: ```c rt_err_t rt_timer_delete(rt_timer_t timer); ``` rt_timer_delete()函数会删除指定的定时器,并释放相应的资源。如果删除成功,函数返回RT_EOK,否则返回错误码。 以上就是RT-Thread定时器的源码分析,希望能对你有所帮助。如果你有其他问题,可以继续问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值