rt_timer实际就是RTT中实现的一个软定时器。MCU底层做的多的人一般对硬件定时器都很熟悉,软件定时器反而不太熟悉。这里稍微介绍下软件定时器。
回忆一下硬件定时器,设置一个通用的硬件定时器,步骤如下:
配置定时器时钟。
初始化定时寄存器,前两步就是为了确定超时时间。
写定时器中断函数,确定时间到了之后执行的代码。
再之后就是开启或者关闭定时器了。
软件定时器其实也一样,只是这些全部由程序来执行,软件定时器的时钟节拍由软件周期性的产生,在RTOS中一般由心跳提供。通过软定时器初始化函数来设置超时时间以及超时回调函数(对应硬件定时器的中断函数)。
在RTT中,软件定时器又被分为纯软定时器以及普通定时器。纯软定时器中,定时器是由一个专门的线程来执行超时回调,该线程默认优先级为0(既最高)。普通定时器的时钟节拍由心跳中断来提供,超时回调也在硬件ISR中执行,心跳中断一般是一个硬件定时器的中断。
开启
#define RT_USING_TIMER_SOF
这个宏可以开启纯软定时器功能。
为了知道RTT如何实现这两种模式的软件定时器,先来看看rt_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 */
};
typedef struct rt_timer *rt_timer_t;```
rt_timer的成员比较少,
1.parent,是object类的一个实例,说明rt_timer也继承自rt_object。
2.row[RT_TIMER_SKIP_LIST_LEVEL],一个双向链表结构,用来把rt_timer加入各种链表中。RT_TIMER_SKIP_LIST_LEVEL被定义为1,暂时不清楚RT_TIMER_SKIP_LIST_LEVEL这个变量的作用。可能是为日后可以将timer加入更多的链表。
3.timeout_func,超时回调函数指针,指向定时器超时后执行的回调函数。
4.parameter,回调函数的参数指针,指向回调函数执行时输入的参数。
5.init_tick,超时周期。超时时间=超时周期*每个周期的时间
6.timeout_tick,超时剩余周期。每个周期,该变量会被减1,直到到达0,就会触发timeout回调函数。
只有6个成员,看来成员是真的少啊。接下来,还是一样,继续看看与rt_timer相关的函数。可以看见,rt_timer相关函数有很多都与rt_thread相同,如init,creat,delete,detach,
control等函数。
```void rt_system_tick_init(void);
rt_tick_t rt_tick_get(void);
void rt_tick_set(rt_tick_t tick);
void rt_tick_increase(void);
int rt_tick_from_millisecond(rt_int32_t ms);
void rt_system_timer_init(void);
void rt_system_timer_thread_init(void);
rt_tick_t rt_timer_next_timeout_tick(void);
void rt_timer_check(void);
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_err_t rt_timer_detach(rt_timer_t timer);
rt_timer_t rt_timer_create(const char name,
void (timeout)(void parameter),
void parameter,
rt_tick_t time,
rt_uint8_t flag);
rt_err_t rt_timer_delete(rt_timer_t timer);
rt_err_t rt_timer_start(rt_timer_t timer);
rt_err_t rt_timer_stop(rt_timer_t timer);
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg);```
一、rt_timer_init()和rt_timer_create()
还是一个规律,rt_timer_init是针对静态的rt_timer进行初始化,rt_timer_create是动态创建rt_timer,并进行初始化。
```rt_timer_t rt_timer_create(const char name,
void (timeout)(void parameter),
void parameter,
rt_tick_t time,
rt_uint8_t flag)
{
struct rt_timer *timer;
/* allocate a object */
timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
if (timer == RT_NULL)
{
return RT_NULL;
}
_rt_timer_init(timer, timeout, parameter, time, flag);
return timer;
}```
两个函数最后都调用了_rt_timer_init()这个实际的初始化函数
这个函数做了以下一些操作:
1.设置flag,去掉RT_TIMER_FLAG_ACTIVATED的标志,rt_timer使用了基类object中的flag成员作为自己的标志位。主要有以下几种标志:
```#define RT_TIMER_FLAG_DEACTIVATED 0x0 /*< timer is deactive /
define RT_TIMER_FLAG_ACTIVATED 0x1 /*< timer is active /
define RT_TIMER_FLAG_ONE_SHOT 0x0 /*< one shot timer /
define RT_TIMER_FLAG_PERIODIC 0x2 /*< periodic timer /
define RT_TIMER_FLAG_HARD_TIMER 0x0 /*< hard timer,the timer’s callback function will be called in tick isr. /
define RT_TIMER_FLAG_SOFT_TIMER 0x4 /*< soft timer,the timer’s callback function will be called in timer thread. /```
可以看到有3个标志都是0x0,这点有点怪。
2.配置time_out回调函数,以及parameter参数
3.设置超时周期init_tick,并把timeout_tick置为0
4.初始化双向链表row
```static void _rt_timer_init(rt_timer_t timer,
void (timeout)(void parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
int i;
/* set flag */
timer->parent.flag = flag;
/* set deactivated */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
timer->timeout_func = timeout;
timer->parameter = parameter;
timer->timeout_tick = 0;
timer->init_tick = time;
/* initialize timer list */
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_init(&(timer->row*));
}
}```
二、rt_timer_detach()和rt_timer_delete()
同样的,这两个函数都是对rt_timer进行删除,rt_timer_detach()针对的是静态的。
主要执行以下操作:
1.参数检查
2.执行_rt_timer_remove()将rt_timer移除相应的timer队列。(ISR或软timer线程将不再执行它)
3.删除rt_timer(释放分配的内存,移出对象容器)
```rt_err_t rt_timer_delete(rt_timer_t timer)
{
register rt_base_t level;
/* timer check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE);
/* disable interrupt */
level = rt_hw_interrupt_disable();
_rt_timer_remove(timer);
/* enable interrupt */
rt_hw_interrupt_enable(level);
rt_object_delete((rt_object_t)timer);
return RT_EOK;
}```
三、rt_timer_start()和rt_timer_stop()
从名字就可以看出这两个函数一个是启动定时器一个是停止定时器。先来看启动的代码,代码挺长的主要完成以下一些步骤:
1.参数检查
2.重置rt_timer(将timer移出当前队列,RT_TIMER_FLAG_ACTIVATED标志移除。
3.调用rt_object_take_hook钩子函数
4.设置timeout_tick。
5.将rt_timer加入到对应的定时器列表中(纯软定时器加入纯软队列,普通定时器加入普通队列)
这里详细解析下下面这段代码。
rt_list_entry是一个神奇的宏定义,这个宏定义我也看了很久,写的很复杂。。关于宏定义提一下,RTT中具有很多很神奇的宏定义,写的十分精彩,大大减小了代码的复杂度,可以说是很优雅了。
#define rt_container_of(ptr, type, member) \ ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
总的来说rt_list_entry会通过成员地址返回包含该成员的实例的地址。一般使用方式是,ptr为类中rt_list_t成员的地址,type为类名,member为成员名
这段代码搜寻timer_list这个列表中的所有timer,当
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
时停止,后面在这个位置,把当前timer加入列表。这条语句看似是一个相减小与 RT_TICK_MAX / 2的条件,其实真实的含义是t->timeout_tick - timer->timeout_tick>0,这里是利用的32位整型的表示方式,RT_TICK_MAX 为0xffffffff,在MCU中对于有符号的整型,最高为都是符号位,当两个数相减的时候实际是把减数按位取反+1再相加,所以其实只有一种情况t->timeout_tick - timer->timeout_tick>RT_TICK_MAX /2会成立,那就是为负数的时候。
综上,这个for循环,其实是在rt_timer列表中搜寻到了刚好超时时间大于启动的rt_timer的那个索引,作为之后rt_timer插入列表的位置。所以整个rt_timer的列表(软timer一个列表,普通timer一个列表),都是按照timeout_tick 这个参数的值进行排序的,是一个有序列表。
``` row_head[0] = &timer_list[0];
for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
for (; row_head[row_lvl] != timer_list[row_lvl].prev;
row_head[row_lvl] = row_head[row_lvl]->next)
{
struct rt_timer t;
rt_list_t p = row_head[row_lvl]->next;
/* fix up the entry pointer */
t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
/* If we have two timers that timeout at the same time, it's
* preferred that the timer inserted early get called early.
* So insert the new timer to the end the the some-timeout timer
* list.
*/
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
{
break;
}
}
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
row_head[row_lvl + 1] = row_head[row_lvl] + 1;
}```
至于之后函数,功能其实是把一个完整的rt_timer列表拆分成几份,这样做的作用我也还没有看出来。。。
6.若启动的定时器为纯软的定时器,那么检测定时器线程是否启动,若未启动则重新启动线程。
```rt_err_t rt_timer_start(rt_timer_t timer)
{
unsigned int row_lvl;
rt_list_t timer_list;
register rt_base_t level;
rt_list_t row_head[RT_TIMER_SKIP_LIST_LEVEL];
unsigned int tst_nr;
static unsigned int random_nr;
/* timer check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
/* stop timer firstly */
level = rt_hw_interrupt_disable();
/* remove timer from list */
_rt_timer_remove(timer);
/* change status of timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_hw_interrupt_enable(level);
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
/*
* get timeout tick,
* the max timeout tick shall not great than RT_TICK_MAX/2
*/
RT_ASSERT(timer->init_tick < RT_TICK_MAX / 2);
timer->timeout_tick = rt_tick_get() + timer->init_tick;
/* disable interrupt */
level = rt_hw_interrupt_disable();
ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
/* insert timer to soft timer list */
timer_list = rt_soft_timer_list;
}
else
endif
{
/* insert timer to system timer list */
timer_list = rt_timer_list;
}
row_head[0] = &timer_list[0];
for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
for (; row_head[row_lvl] != timer_list[row_lvl].prev;
row_head[row_lvl] = row_head[row_lvl]->next)
{
struct rt_timer *t;
rt_list_t *p = row_head[row_lvl]->next;
/* fix up the entry pointer */
t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
/* If we have two timers that timeout at the same time, it's
* preferred that the timer inserted early get called early.
* So insert the new timer to the end the the some-timeout timer
* list.
*/
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
{
break;
}
}
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
row_head[row_lvl + 1] = row_head[row_lvl] + 1;
}
/* Interestingly, this super simple timer insert counter works very very
* well on distributing the list height uniformly. By means of "very very
* well", I mean it beats the randomness of timer->timeout_tick very easily
* (actually, the timeout_tick is not random and easy to be attacked). */
random_nr++;
tst_nr = random_nr;
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
else
break;
/* Shift over the bits we have tested. Works well with 1 bit and 2
* bits. */
tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
}
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
/* enable interrupt */
rt_hw_interrupt_enable(level);
ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
/* check whether timer thread is ready */
if ((timer_thread.stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY)
{
/* resume timer thread to check soft timer */
rt_thread_resume(&timer_thread);
rt_schedule();
}
}
endif
return RT_EOK;
}```
stop函数就相对来说简单了很多,主要就是:
1.参数检查
2.调用rt_object_put_hook钩子函数
3.将rt_timer移出列表。
```rt_err_t rt_timer_stop(rt_timer_t timer)
{
register rt_base_t level;
/* timer check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
return -RT_ERROR;
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
/* disable interrupt */
level = rt_hw_interrupt_disable();
_rt_timer_remove(timer);
/* enable interrupt */
rt_hw_interrupt_enable(level);
/* change stat */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
return RT_EOK;
}```
四、rt_timer_control()函数
改函数可以通过参数中的cmd来实现不同的功能,cmd总共有4中取值:
```#define RT_TIMER_CTRL_SET_TIME 0x0 /*< set timer control command /
define RT_TIMER_CTRL_GET_TIME 0x1 /*< get timer control command /
define RT_TIMER_CTRL_SET_ONESHOT 0x2 /*< change timer to one shot /
define RT_TIMER_CTRL_SET_PERIODIC 0x3 /*< change timer to periodic /```
分别对应:
设置定时器时间
获取定时器时间
设置定时器为单触发模式
设置定时器为周期性触发模式
当rt_timer被标记为周期性定时器后,当time_out回调触发后会执行start重新启动该定时器。
五、其他函数。
剩下的函数还有
(1)void rt_system_tick_init(void); (2)rt_tick_t rt_tick_get(void); (3)void rt_tick_set(rt_tick_t tick); (4)void rt_tick_increase(void); (5)int rt_tick_from_millisecond(rt_int32_t ms); (6)void rt_system_timer_init(void); (7)void rt_system_timer_thread_init(void); (8)rt_tick_t rt_timer_next_timeout_tick(void); (9)void rt_timer_check(void);
(1)是个空函数。
(2)返回系统tick数,这个数在系统启动后就随着心跳不断的增加。
(3)设置系统tick数
(4)
```void rt_tick_increase(void)
{
struct rt_thread *thread;
/* increase the global tick */
++ rt_tick;
/* check time slice */
thread = rt_thread_self();
-- thread->remaining_tick;
if (thread->remaining_tick == 0)
{
/* change to initialized tick */
thread->remaining_tick = thread->init_tick;
/* yield */
rt_thread_yield();
}
/* check timer */
rt_timer_check();
}```
函数会对rt_tick加1,并检查线程时间片时间是否过期,若过期,则调用rt_thread_yield()。这个函数在上一篇分析过。
之后会执行(9)rt_timer_check()函数
(5)是个工具函数,将ms数换算成tick数
(6)初始化普通定时器列表
(7)若开启了纯软定时器,启动定时器线程,纯软定时器的time_out回调将在这个线程中执行,该线程默认优先级为0
(8)返回下一个最近会到期的普通定时器的time_out值。
(9)
```void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
register rt_base_t level;
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter
"));
current_tick = rt_tick_get();
/* disable interrupt */
level = rt_hw_interrupt_disable();
while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
/*
* It supposes that the new tick shall less than the half duration of
* tick max.
*/
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));
/* remove timer from timer list firstly */
_rt_timer_remove(t);
/* call timeout function */
t->timeout_func(t->parameter);
/* re-get tick */
current_tick = rt_tick_get();
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d
", current_tick));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
else
{
/* stop timer */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
}
else
break;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave
"));
}```
该函数在ISR中执行,检测是否有普通定时器到期,对于到期的定时器先执行time_out回调,并判断是否是周期定时器,若是周期定时器,重新启动这个定时器。
那么对于rt_timer的解读就这么多,篇幅感觉越来越长呀。。
原作者:yushigengyu