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
|