第二十二章 软件定时器2
五、定时器超时函数
定时器最主要目的:是在经过指定的定时时间后,系统能自动执行用户设定的动作————超时函数。该函数在创建定时器的时候需要用户自己定义,并且编写对应的执行代码。
定时器超时函数存在着两种情况:
- 超时函数在(系统时钟)中断上下文环境中执行(硬件定时器)
- 超时函数在线程的上下文环境中执行(软件定时器)。
硬件定时器超时在 systick 的 isr 中的实现
1 void rt_tick_increase(void)
2 {
3 struct rt_thread *thread;
4
5 /* 系统时间全局变量自加 */
6 ++ rt_tick;
7
8 /* 检查时间片 */
9 thread = rt_thread_self();
10
11 -- thread->remaining_tick;
12 if (thread->remaining_tick == 0) {
13 /* 更改为初始化的时间 */
14 thread->remaining_tick = thread->init_tick;
15
16 /* 强制切换 */
17 rt_thread_yield();
18 }
19
20 /* 检查定时器时间 */
21 rt_timer_check(); (1)
22 }
(1):rt_timer_check()是具体的检查定时器是否超时的函数
1 /* system timer thread entry */
2 static void rt_thread_timer_entry(void *parameter)
3 {
4 rt_tick_t next_timeout;
5
6 while (1) {
7 /* 获取软件定时器列表中下一个定时器的到达时间 */
8 next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list);
(1)/* **rt_thread_timer_entry 是一个线程,所以也是需要死循环的,线程
在运行的时候扫描软件定时器列表获取下一个定时器定时到达的时间。 ***/
9 if (next_timeout == RT_TICK_MAX) {
10 /* 如果没有软件定时器,则挂起线程自身 */
11 rt_thread_suspend(rt_thread_self()); (2)
**如果此时软件定时器列表中没有软件定时器,就把线程自身挂起。
因为软件定时器线程的运行是会占用 CPU 的,当没有开启软件定时器的时候就不要经常进
入线程扫描,直接挂起线程即可。挂起自身之后要发起一次线程调度,让出 CPU。 **
12 rt_schedule();
13 } else {
14 rt_tick_t current_tick;
15
16 /* 获取当前系统时间 */
17 current_tick = rt_tick_get(); (3)
**如果启动了软件定时器,那么就获取当前系统时间 current_tick。**
18
19 if ((next_timeout - current_tick) < RT_TICK_MAX / 2) {(4)
**下一个定时器溢出时间与系统当前时间比较,如果时间还没到,执行 (5) (6) 。**
20 /* 计算下一个定时器溢出时间与当前时间的间隔 */
21 next_timeout = next_timeout - current_tick; (5) **计算还有多长时间到达下一个定时器溢出的时间,记录在
next_timeout 中。**
22 rt_thread_delay(next_timeout); (6) **将定时器线程延时 next_timeout,这样子做就不需要经常进入定时
器线程查找定时器,直到下一个定时器需要唤醒的时候才进来处理,这样子大大提高 CPU
的利用率,**
23 }
24 }
25
26 /* 检查软件定时器列表 */
27 rt_soft_timer_check(); (7) **软件定时器扫描函数 **
28 }
29 }
30 #endif
软件定时器扫描函数 rt_soft_timer_check()
1 void rt_soft_timer_check(void)
2 {
3 rt_tick_t current_tick;
4 rt_list_t *n;
5 struct rt_timer *t;
6
7 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n"));
8
9 current_tick = rt_tick_get();
10
11 /*锁定调度程序*/
12 rt_enter_critical();
13
14 for (n = rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next;
15 n != &(rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]);) {
16 t = rt_list_entry(n, struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
17
18 /*
19 * 判断是超时
20 *
21 */
22 if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2) {(1)
**判断是系统时间是否到达定时器溢出时间。 **
23 RT_OBJECT_HOOK_CALL(rt_timer_timeout_hook, (t));
24
25 /* 移动节点到下一个 */
26 n = n->next;
27
28 /* 首先从定时器列表中删除定时器 */
29 _rt_timer_remove(t); (2)**如果到达了定时器溢出时间,首先移动软件定时器列表的表头指
针,指向下一个定时器,然后从软件定时器列表中删除当前时间溢出的定时器。 **
30
31 /* 执行超时功能时不锁定调度程序 */
32 rt_exit_critical();
33 /* 调用超时函数 */
34 t->timeout_func(t->parameter); (3) **执行定时器的超时函数**
35
36 /* 重新获取当前系统时间 tick */
37 current_tick = rt_tick_get(); (4) **重新获取当前系统时间 current_tick**
38
39 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
40
41 /* 锁定调度程序 */
42 rt_enter_critical();
43
44 if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) && 在这里插入代码片
45 (t->parent.flag & RT_TIMER_FLAG_ACTIVATED)) { (5)
**如果这个定时器是周期定时器的话,那么需要根据初始设置的定
时时间重新加入定时器链表中,设置定时器状态为可用,然后调用启动定时器函数
rt_timer_start 将定时器重新添加到软件定时器列表中去,插入定时器列表会按定时器溢出
时间 timeout 进行排序。**
46 /* 开始,设置定时器状态为可用 */
47 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
48 rt_timer_start(t);
49 } else {
50 /* 停止,设置定时器状态为不可用 */
51 t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; (6) **如果软件定时器是单次模式的话,则将软件定时器设置为不可用
状态。 **
52 }
53 } else break; /* 不再检查了 */ (7) **退出。**
54 }
55
56 /* 解锁调度程序 */
57 rt_exit_critical();
58
59 RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n"));
60 }
六、软件定时器的使用
1、软件定时器的创建函数
1 rt_timer_t rt_timer_create(const char *name, (1) **定时器的名称**
2 void (*timeout)(void *parameter), (2)**定时器超时函数指针(当定时器超时时,系统会调用这个指针指
向的函数),函数主体由用户自己实现。 **
3 void *parameter, (3) **定时器超时函数的入口参数(当定时器超时时,调用超时函数会
把这个参数做为入口参数传递给超时函数)。 **
4 rt_tick_t time, (4) **:定时器的超时时间,单位是 tick。 **
5 rt_uint8_t flag) (5) **定时器创建时的参数**
6 {
7 struct rt_timer *timer;
8
9 /* 分配定时器对象 */
10 timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
11 if (timer == RT_NULL) { (6) **分配软件定时器对象**
12 return RT_NULL;
13 }
14
15 _rt_timer_init(timer, timeout, parameter, time, flag); (7) **调用_rt_timer_init 初始化函数进行定时器的初始化**
16
17 return timer; (8) **如果定时器创建成功,则返回定时器的句柄,
如果创建失败,会返回 RT_NULL(**
18 }
(5):定时器创建时的参数
1 #define RT_TIMER_FLAG_DEACTIVATED 0x0 /**< 计时器是无效的 */
2 #define RT_TIMER_FLAG_ACTIVATED 0x1 /**< 定时器是可用的 */
3 #define RT_TIMER_FLAG_ONE_SHOT 0x0 /**< 单次定时器 */
4 #define RT_TIMER_FLAG_PERIODIC 0x2 /**< 周期定时器 */
5
6 #define RT_TIMER_FLAG_HARD_TIMER 0x0 /**<硬定时器,定时器的超时函数将
**如果定时器超时,定时器的超时函数将在中断中被调用**
7 在 tick isr 中调用。*/
8 #define RT_TIMER_FLAG_SOFT_TIMER 0x4 /**<软定时器,定时器的超时函数将
**如果定时器超时,定时器的超时函数将在线程中被调用**
9 在定时器线程中调用。*/
例子:
1 /* 创建一个软件定时器 */
2 swtmr1 = rt_timer_create("swtmr1_callback", /* 软件定时器的名称 */
3 swtmr1_callback,/* 软件定时器的超时函数 */
4 0, /* 定时器超时函数的入口参数 */
5 5000, /* 软件定时器的超时时间(周期超时时间) */
6 RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER);
7 /*一次模式 软件定时器模式*/
8 /* 启动定时器 */
9 if (swtmr1 != RT_NULL)
10 rt_timer_start(swtmr1);
11
12 /* 创建一个软件定时器 */
13 swtmr2 = rt_timer_create("swtmr2_callback", /* 软件定时器的名称 */
14 swtmr2_callback, /* 软件定时器的超时函数 */
15 0, /* 定时器超时函数的入口参数 */
16 1000, /* 软件定时器的超时时间(周期超时时间) */
17 RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
18 /* 软件定时器模式 周期模式 */
19 /* 启动定时器 */
20 if (swtmr2 != RT_NULL)
21 rt_timer_start(swtmr2);