RT-Thread的定时器有硬件定时器和软件定时器之分:
通过是否定义宏RT_USING_TIMER_SOFT
来决定启用软件定时器,默认不启用
static rt_timer_t timer1;
/**
* @ "timer1" 是定时器名称
* @ test_timer_callback 是定时器回调函数
* @ RT_NULL 是定时器回调函数参数
* @ 100 是定时器周期,单位是tick,这里可以为0,start后不会马上触发,会在下一个tick才触发
* @ RT_TIMER_FLAG_PERIODIC 是定时器类型,周期性定时器,RT_TIMER_FLAG_ONE_SHOT是单次定时器
* @ 如果调用软件定时器还需要与上T_TIMER_FLAG_SOFT_TIMER,否则创建的还是硬件定时器
*/
timer1 = rt_timer_create("timer1", test_timer_callback, RT_NULL, 100, RT_TIMER_FLAG_PERIODIC);
rt_timer_start(timer1);
关于rt_object_allocate
函数可参考内核对象管理章节
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;
// 值不能超过 0xffffffff的一半
RT_ASSERT(time < RT_TICK_MAX / 2);
// 参考内核对象管理 章节
timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
_timer_init(timer, timeout, parameter, time, flag);
return timer;
}
static void _timer_init(rt_timer_t timer, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag)
{
int i;
// 设置周期性定时器还是单次定时器
timer->parent.flag = flag;
// 刚开始不激活定时器
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
// 简而言之这里的flag bit0表示是否激活,bit1表示是否周期性
// 设置回调
timer->timeout_func = timeout;
timer->parameter = parameter;
timer->timeout_tick = 0;
// 设置定时时长
timer->init_tick = time;
// 跳表算法后续讲解,这里RT_TIMER_SKIP_LIST_LEVEL默认为1
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_init(&(timer->row[i]));
}
}
定时器的创建比较简单,定时器的启动可能稍微复杂点
rt_err_t rt_timer_start(rt_timer_t timer)
{
unsigned int row_lvl;
rt_list_t *timer_list;
rt_base_t level;
rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
unsigned int tst_nr;
static unsigned int random_nr;
level = rt_hw_interrupt_disable();
// 将自己从定时器链表中删除
_timer_remove(timer);
// 停止定时器 已经关中断了,这里有必要吗?
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
// 设置定时器的超时时间
timer->timeout_tick = rt_tick_get() + timer->init_tick;
// 软硬件定时器有各自的链表
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
// rt_timer_create的时候必须带上RT_TIMER_FLAG_SOFT_TIMER
// 不然创建的就是硬件定时器
timer_list = _soft_timer_list;
}
else
#endif /* RT_USING_TIMER_SOFT */
{
timer_list = _timer_list;
}
row_head[0] = &timer_list[0];
// RT_TIMER_SKIP_LIST_LEVEL 是跳表的层数 这里默认为1
for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
// 遍历row_head[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;
// 获取到rt_timer结构体的地址
t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
// 定时器链表是以绝对超时时间为依据的顺序表
// 如果当前定时器的超时时间和链表中一样,应该插入到后面
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
// 首先思考一个问题,任意两个timeout_tick最大相差多少?
// 那当然是timeout_tick1是0,timeout_tick2是 (RT_TICK_MAX / 2 - 1)
// 所以这里需要插入的timer在链表中t的前面(即timeout_tick更小),此判断才成立
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
{
// 此时需要需要把timer插入到t的前面
// 因为row_head[row_lvl]->next指向的就是t,所以这里直接break
// 后面将timer插入到row_head[row_lvl]之后就行,timer也顺利成章变到了t之前
break;
}
}
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
row_head[row_lvl + 1] = row_head[row_lvl] + 1;
}
// 跳表相关的,这个版本跳表就一层,可以不予理会
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]));
// 调表的层数大于等于2时这里才会执行,这个版本跳表就一层,可以不予理会
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;
tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
}
// 将定时器激活
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
if ((_soft_timer_status == RT_SOFT_TIMER_IDLE) &&
((_timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND))
{
// [插个眼1]:如果定时器线程休眠了,将其唤醒
rt_thread_resume(&_timer_thread);
need_schedule = RT_TRUE;
}
}
#endif /* RT_USING_TIMER_SOFT */
rt_hw_interrupt_enable(level);
return RT_EOK;
}
硬件定时器的唤醒一般在systick中断SysTick_Handler
中
void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
rt_base_t level;
rt_list_t list;
rt_list_init(&list);
current_tick = rt_tick_get();
level = rt_hw_interrupt_disable();
// 定时器链表不为空
while (!rt_list_isempty(&_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
// 获取当前rt_timer结构体指针
t = rt_list_entry(_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, struct rt_timer,
row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
// current_tick >= t->timeout_tick,即定时已到
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
// 准备调用,先从链表中删除
_timer_remove(t);
if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
{
// 如非周期性定时器,就关闭
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
// 加入到临时链表中
rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
// 调用回调函数
t->timeout_func(t->parameter);
current_tick = rt_tick_get();
/* Check whether the timer object is detached or started again */
// 刚加入list又对list判空,为什么呢?
// 因为回调函数中可能会对定时器操作,比如重新start或者卸载定时器
if (rt_list_isempty(&list))
{
continue;
}
// 又从list中移出来
rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
// 如是周期性定时器,则重新启动
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && (t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
}
else
break;
}
rt_hw_interrupt_enable(level);
}
软件定时器线程优先级默认最高
#define RT_TIMER_THREAD_PRIO 0
static void _timer_thread_entry(void *parameter)
{
rt_tick_t next_timeout;
while (1)
{
if (_timer_list_next_timeout(_soft_timer_list, &next_timeout) != RT_EOK)
{
// 如果链表为空则主动休眠,否则返回最近的超时时间
rt_thread_suspend(rt_thread_self());
rt_schedule();
}
else
{
rt_tick_t current_tick;
current_tick = rt_tick_get();
if ((next_timeout - current_tick) < RT_TICK_MAX / 2)
{
// 如果没超时,还需要多久直接delay
next_timeout = next_timeout - current_tick;
// 思考: 如果在delay过程中,创建了一个更早触发的定时器,能响应吗?
// 答案: 请看[插个眼1],创建定时器时会唤醒线程
rt_thread_delay(next_timeout);
}
}
// 和硬件定时器的rt_timer_check差不多
rt_soft_timer_check();
}
}