1.所谓定时器本质上是递减计数器,当计数器减到零时可以出发某种动作的执行。这种动作可以通过回调函数(callback funtion,简称回调)来实现。需要注意的是,一定要避免在回调函数中使用阻塞调用。
注:定时器在一些协议栈的实现中很有用,也可以用来定期轮训IO设备。
2.定时器任务的频率通过宏定义OS_CFG_TMR_TASK_RATE_HZ来实现,单位是Hz。
该宏定义位于文件os_cfg_app.h中。
注:定时器任务频率的常用推荐值是10Hz。
3.定时器是一个内核对象,其数据类型为OS_TMR。它的定义位于文件os.h中。
typedef struct os_tmr OS_TMR; // 643行
struct os_tmr { // 1001行 - 1018行
OS_OBJ_TYPE Type; // 类型,这里说明一下,内核对象结构体的定义都是以Type作为第一个成员变量,这样uC/OS-III就可以判断结构体是那种内核对象。
CPU_CHAR *NamePtr; // 定时器的名字
OS_TMR_CALLBACK_PTR CallbackPtr; // 回调函数指针
void *CallbackPtrArg; // 回调函数的参数
OS_TMR *NextPtr; // 这两个指针构成双向链表,连接定时器
OS_TMR *PrevPtr;
OS_TICK Match; // 当计数器的值到达该值时,定时时间到
OS_TICK Remain; // 存储距离定时器定时时间到的剩余时间
OS_TICK Dly; // 开始之前的初始值
OS_TICK Period; // 定时周期
OS_OPT Opt; // 选项
OS_STATE State; // 定时器的状态
#if OS_CFG_DBG_EN > 0u // 允许调试
OS_TMR *DbgPrevPtr;
OS_TMR *DbgNextPtr;
#endif
};
4.定时器的状态有4种:未使用、停止、运行、完成。
(1)“未使用”状态代表定时器未被创建或者已经删除
(2)定时器已创建但未调用OSTmrStart()函数之前或调用OSTmrStop()函数之后,定时器处于“停止”状态
(3)调用OSTmrStart()之后,定时器处于“运行”状态
(4)“完成”状态是指单次定时器过期后所处的状态
5.定时器列表包含一个表(OSCfg_TmrWheel[],在os_cfg_app.c中声明)和一个计数器(OSTmrTickCtr,在os.h中声明)。
6.OSCfg_TmrWheel[]表称为定时器轮。定时器轮中的每个条目的数据类型是OS_TMR_SPOKE(该结构体定义在os.h中),它的大小由宏定义OS_CFG_TMR_WHEEL_SIZE(定义在os_cfg_app.h中)设定。
建议不要将OS_CFG_TMR_WHEEL_SIZE设为定时器任务率的偶数倍,最好使用素数。
7.OS_TMR_SPOKE结构体的定义位于os.h中
typedef struct os_tmr_spoke OS_TMR_SPOKE; // 644行
struct os_tmr_spoke { // 1022行 - 1026行
OS_TMR *FirstPtr; // 在双向链表中指向第一个定时器
OS_OBJ_QTY NbrEntries; // 链接到这个条目的定时器的数量
OS_OBJ_QTY NbrEntriesMax; // 用来跟踪表中最大定时器数目
};
8.定时任务OS_TmrTask()的定位位于os_tmr.c中,1041行 - 1101行
void OS_TmrTask (void *p_arg)
{
CPU_BOOLEAN done;
OS_ERR err;
OS_TMR_CALLBACK_PTR p_fnct;
OS_TMR_SPOKE *p_spoke;
OS_TMR *p_tmr;
OS_TMR *p_tmr_next;
OS_TMR_SPOKE_IX spoke;
CPU_TS ts;
CPU_TS ts_start;
CPU_TS ts_end;
p_arg = p_arg; // 参数
while (DEF_ON) {
(void)OSTaskSemPend((OS_TICK )0, // 等待信号量。若用直接发布模式,该信号量来自OSTimeTick();若用延迟发布模式,该信号量来自OS_IntQTask()
(OS_OPT )OS_OPT_PEND_BLOCKING,
(CPU_TS *)&ts,
(OS_ERR *)&err);
OSSchedLock(&err); // 锁定调度器
ts_start = OS_TS_GET(); // 记录定时任务的开始执行时间
OSTmrTickCtr++; // 计数器加1
spoke = (OS_TMR_SPOKE_IX)(OSTmrTickCtr % OSCfg_TmrWheelSize); // 获取定时器轮的条目位置
p_spoke = &OSCfg_TmrWheel[spoke]; // 根据条目位置获取条目
p_tmr = p_spoke->FirstPtr; // 获取该条目下的第一个定时器
done = DEF_FALSE;
while (done == DEF_FALSE) {
if (p_tmr != (OS_TMR *)0) {
p_tmr_next = (OS_TMR *)p_tmr->NextPtr; // 获取下一个定时器
if (OSTmrTickCtr == p_tmr->Match) { // 定时时间到
OS_TmrUnlink(p_tmr); // 从当前条目中删除定时器
if (p_tmr->Opt == OS_OPT_TMR_PERIODIC) { // 周期执行
OS_TmrLink(p_tmr, OS_OPT_LINK_PERIODIC); // 在定时器轮中重新分配一个位置给定时器
} else { // 单次执行
p_tmr->State = OS_TMR_STATE_COMPLETED; // 定时器的状态改为“完成”
}
p_fnct = p_tmr->CallbackPtr; // 执行回调函数
if (p_fnct != (OS_TMR_CALLBACK_PTR)0) {
(*p_fnct)((void *)p_tmr,
p_tmr->CallbackPtrArg);
}
p_tmr = p_tmr_next; // 定时器指针向后移
} else { // 定时时间未到
done = DEF_TRUE;
}
} else {
done = DEF_TRUE;
}
}
ts_end = OS_TS_GET() - ts_start; // 测量定时任务的执行时间
OSSchedUnlock(&err); // 解锁定时器
if (ts_end > OSTmrTaskTimeMax) { // 记录定时任务执行的最长时间
OSTmrTaskTimeMax = ts_end;
}
}
}