软件定时器是 uC/OS 操作系统的一个内核对象,软件定时器是基于时钟节拍和系统管理创建的软件性定时器,理论上可以创建无限多个,但精准度肯定比硬件定时稍逊一筹。使用硬件定时器往往需要查阅芯片的相关数据手册,比较繁琐,而使用 uC/OS 的软件定时非常方便。
软件定时器启动之后是由软件定时器任务 OS_TmrTask() 统一管理,在创建软件定时器之前必须先使能软件定时器和配置软件定时器的相关参数。
软件定时器的使能位于“os_cfg.h”:
/* ------------------------- TIMER MANAGEMENT -------------------------- */ #define OS_CFG_TMR_EN 1u //使能/禁用软件定时器
其有关参数的配置位于“os_cfg_app.h”:
/* ----------------------- TIMERS ----------------------- */ #define OS_CFG_TMR_TASK_PRIO 11u //定时器任务的优先级 #define OS_CFG_TMR_TASK_RATE_HZ 10u //定时器的时基 (一般不能大于 OS_CFG_TICK_RATE_HZ ) #define OS_CFG_TMR_TASK_STK_SIZE 128u //定时器任务的栈空间大小(单位:CPU_STK) #define OS_CFG_TMR_WHEEL_SIZE 17u // OSCfg_TmrWheel 数组的大小,推荐使用任务总数/4,且为质数
OSTmrCreate ()
要使用 uC/OS 的软件定时器必须先声明和创建软件定时器,调用 OSTmrCreate () 函数可以创建一个软件定时器。OSTmrCreate () 函数的信息如下表所示。
OSTmrCreate () 函数的定义位于“os_tmr.c”:
void OSTmrCreate (OS_TMR *p_tmr, //定时器控制块指针 CPU_CHAR *p_name, //命名定时器,有助于调试 OS_TICK dly, //初始定时节拍数 OS_TICK period, //周期定时重载节拍数 OS_OPT opt, //选项 OS_TMR_CALLBACK_PTR p_callback, //定时到期时的回调函数 void *p_callback_arg, //传给回调函数的参数 OS_ERR *p_err) //返回错误类型 { CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变 //量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR) //,开中断时将该值还原。 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return; //返回,不执行定时操作 } #endif #ifdef OS_SAFETY_CRITICAL_IEC61508 //如果使能(默认禁用)了安全关键 if (OSSafetyCriticalStartFlag == DEF_TRUE) { //如果是在调用 OSSafetyCriticalStart() 后创建该定时器 *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME; //错误类型为“非法创建内核对象” return; //返回,不执行定时操作 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能(默认使能)了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数是在中断中被调用 *p_err = OS_ERR_TMR_ISR; //错误类型为“在中断函数中定时” return; //返回,不执行定时操作 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能(默认使能)了参数检测 if (p_tmr == (OS_TMR *)0) { //如果参数 p_tmr 为空 *p_err = OS_ERR_OBJ_PTR_NULL; //错误类型为“定时器对象为空” return; //返回,不执行定时操作 } switch (opt) { //根据延时选项参数 opt 分类操作 case OS_OPT_TMR_PERIODIC: //如果选择周期性定时 if (period == (OS_TICK)0) { //如果周期重载实参为0 *p_err = OS_ERR_TMR_INVALID_PERIOD; //错误类型为“周期重载实参无效” return; //返回,不执行定时操作 } break; case OS_OPT_TMR_ONE_SHOT: //如果选择一次性定时 if (dly == (OS_TICK)0) { //如果定时初始实参为0 *p_err = OS_ERR_TMR_INVALID_DLY; //错误类型为“定时初始实参无效” return; //返回,不执行定时操作 } break; default: //如果选项超出预期 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项非法” return; //返回,不执行定时操作 } #endif OS_CRITICAL_ENTER(); //进入临界段 p_tmr->State = (OS_STATE )OS_TMR_STATE_STOPPED; //初始化定时器指标 p_tmr->Type = (OS_OBJ_TYPE )OS_OBJ_TYPE_TMR; p_tmr->NamePtr = (CPU_CHAR *)p_name; p_tmr->Dly = (OS_TICK )dly; p_tmr->Match = (OS_TICK )0; p_tmr->Remain = (OS_TICK )0; p_tmr->Period = (OS_TICK )period; p_tmr->Opt = (OS_OPT )opt; p_tmr->CallbackPtr = (OS_TMR_CALLBACK_PTR)p_callback; p_tmr->CallbackPtrArg = (void *)p_callback_arg; p_tmr->NextPtr = (OS_TMR *)0; p_tmr->PrevPtr = (OS_TMR *)0; #if OS_CFG_DBG_EN > 0u //如果使能(默认使能)了调试代码和变量 OS_TmrDbgListAdd(p_tmr); //将该定时添加到定时器双向调试链表 #endif OSTmrQty++; //定时器个数加1 OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段(无调度) *p_err = OS_ERR_NONE; //错误类型为“无错误” }
OSTmrStart ()
创建完软件定时器后,如果要使用该软件定时器,需要调用 OSTmrStart () 函数启动该软件定时器。OSTmrStart () 函数的信息如下表所示。
OSTmrCreate () 函数的定义也位于“os_tmr.c”。:
CPU_BOOLEAN OSTmrStart (OS_TMR *p_tmr, //定时器控制块指针 OS_ERR *p_err) //返回错误类型 { OS_ERR err; CPU_BOOLEAN success; //暂存函数执行结果 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能(默认使能)了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数是在中断中被调用 *p_err = OS_ERR_TMR_ISR; //错误类型为“在中断函数中定时” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能(默认使能)了参数检测 if (p_tmr == (OS_TMR *)0) { //如果使能 p_tmr 的实参为空 *p_err = OS_ERR_TMR_INVALID; //错误类型为“无效的定时器” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能(默认使能)了对象类型检测 if (p_tmr->Type != OS_OBJ_TYPE_TMR) { //如果该定时器的对象类型有误 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型错误” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif OSSchedLock(&err); //锁住调度器 switch (p_tmr->State) { //根据定时器的状态分类处理 case OS_TMR_STATE_RUNNING: //如果定时器正在运行,则重启 OS_TmrUnlink(p_tmr); //从定时器轮里移除该定时器 OS_TmrLink(p_tmr, OS_OPT_LINK_DLY); //将该定时器重新插入到定时器轮 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_NONE; //错误类型为“无错误” success = DEF_TRUE; //执行结果暂为 DEF_TRUE break; case OS_TMR_STATE_STOPPED: //如果定时器已被停止,则开启 case OS_TMR_STATE_COMPLETED: //如果定时器已完成了,则开启 OS_TmrLink(p_tmr, OS_OPT_LINK_DLY); //将该定时器重新插入到定时器轮 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_NONE; //错误类型为“无错误” success = DEF_TRUE; //执行结果暂为 DEF_TRUE break; case OS_TMR_STATE_UNUSED: //如果定时器未被创建 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_TMR_INACTIVE; //错误类型为“定时器未激活” success = DEF_FALSE; //执行结果暂为 DEF_FALSE break; default: //如果定时器的状态超出预期 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_TMR_INVALID_STATE; //错误类型为“定时器无效” success = DEF_FALSE; //执行结果暂为 DEF_FALSE break; } return (success); //返回执行结果 }
这里涉及到两个函数,OS_TmrLink() 和 OS_TmrUnlink()。所有的软件定时器是通过定时器轮来实现管理的,定时器轮与时钟节拍列表数组一样,就是有若干个定时器列表组成的数组。OS_TmrLink() 函数是将软件定时器插入到定时器轮的列表,相反 OS_TmrUnlink() 函数是将软件定时器从定时器轮的列表移除。该操作与时钟节拍插入和移除节拍列表类似。
OS_TmrLink() 函数的定义位于“os_tmr.c” :
void OS_TmrLink (OS_TMR *p_tmr, //定时器控制块指针 OS_OPT opt) //选项 { OS_TMR_SPOKE *p_spoke; OS_TMR *p_tmr0; OS_TMR *p_tmr1; OS_TMR_SPOKE_IX spoke; p_tmr->State = OS_TMR_STATE_RUNNING; //重置定时器为运行状态 if (opt == OS_OPT_LINK_PERIODIC) { //如果定时器是再次插入 p_tmr->Match = p_tmr->Period + OSTmrTickCtr; //匹配时间加个周期重载值 } else { //如果定时器是首次插入 if (p_tmr->Dly == (OS_TICK)0) { //如果定时器的 Dly = 0 p_tmr->Match = p_tmr->Period + OSTmrTickCtr; //匹配时间加个周期重载值 } else { //如果定时器的 Dly != 0 p_tmr->Match = p_tmr->Dly + OSTmrTickCtr; //匹配时间加个 Dly } } spoke = (OS_TMR_SPOKE_IX)(p_tmr->Match % OSCfg_TmrWheelSize); //通过哈希算法觉得将该定时器 p_spoke = &OSCfg_TmrWheel[spoke]; //插入到定时器轮的哪个列表。 if (p_spoke->FirstPtr == (OS_TMR *)0) { //如果列表为空, p_tmr->NextPtr = (OS_TMR *)0; //直接将该定时器作为列表的第一个元素。 p_tmr->PrevPtr = (OS_TMR *)0; p_spoke->FirstPtr = p_tmr; p_spoke->NbrEntries = 1u; } else { //如果列表非空 p_tmr->Remain = p_tmr->Match //算出定时器 p_tmr 的剩余时间 - OSTmrTickCtr; p_tmr1 = p_spoke->FirstPtr; //取列表的首个元素到 p_tmr1 while (p_tmr1 != (OS_TMR *)0) { //如果 p_tmr1 非空 p_tmr1->Remain = p_tmr1->Match //算出 p_tmr1 的剩余时间 - OSTmrTickCtr; if (p_tmr->Remain > p_tmr1->Remain) { //如果 p_tmr 的剩余时间大于 p_tmr1 的 if (p_tmr1->NextPtr != (OS_TMR *)0) { //如果 p_tmr1 后面非空 p_tmr1 = p_tmr1->NextPtr; //取p_tmr1后一个定时器为新的p_tmr1进行下一次循环 } else { //如果 p_tmr1 后面为空 p_tmr->NextPtr = (OS_TMR *)0; //将 p_tmr 插到 p_tmr1 的后面,结束循环 p_tmr->PrevPtr = p_tmr1; p_tmr1->NextPtr = p_tmr; p_tmr1 = (OS_TMR *)0; } } else { //如果 p_tmr 的剩余时间不大于 p_tmr1 的, if (p_tmr1->PrevPtr == (OS_TMR *)0) { //将 p_tmr 插入到 p_tmr1 的前一个,结束循环。 p_tmr->PrevPtr = (OS_TMR *)0; p_tmr->NextPtr = p_tmr1; p_tmr1->PrevPtr = p_tmr; p_spoke->FirstPtr = p_tmr; } else { p_tmr0 = p_tmr1->PrevPtr; p_tmr->PrevPtr = p_tmr0; p_tmr->NextPtr = p_tmr1; p_tmr0->NextPtr = p_tmr; p_tmr1->PrevPtr = p_tmr; } p_tmr1 = (OS_TMR *)0; } } p_spoke->NbrEntries++; //列表元素成员数加1 } if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) { //更新列表成员数最大值历史记录 p_spoke->NbrEntriesMax = p_spoke->NbrEntries; } }
OSTmrStop ()
OSTmrStop () 函数用于停止一个软件定时器。软件定时器被停掉之后可以调用OSTmrStart () 函数重启,但是重启之后定时器是从头计时,而不是接着上次停止的时刻继续计时。OSTmrStop () 函数的信息如下表所示。
OSTmrStop () 函数的定义也位于“os_tmr.c”。
CPU_BOOLEAN OSTmrStop (OS_TMR *p_tmr, //定时器控制块指针 OS_OPT opt, //选项 void *p_callback_arg, //传给回调函数的新参数 OS_ERR *p_err) //返回错误类型 { OS_TMR_CALLBACK_PTR p_fnct; OS_ERR err; CPU_BOOLEAN success; //暂存函数执行结果 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能(默认使能)了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数是在中断中被调用 *p_err = OS_ERR_TMR_ISR; //错误类型为“在中断函数中定时” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能(默认使能)了参数检测 if (p_tmr == (OS_TMR *)0) { //如果使能 p_tmr 的实参为空 *p_err = OS_ERR_TMR_INVALID; //错误类型为“无效的定时器” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能(默认使能)了对象类型检测 if (p_tmr->Type != OS_OBJ_TYPE_TMR) { //如果该定时器的对象类型有误 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型错误” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif OSSchedLock(&err); //锁住调度器 switch (p_tmr->State) { //根据定时器的状态分类处理 case OS_TMR_STATE_RUNNING: //如果定时器正在运行 OS_TmrUnlink(p_tmr); //从定时器轮列表里移除该定时器 *p_err = OS_ERR_NONE; //错误类型为“无错误” switch (opt) { //根据选项分类处理 case OS_OPT_TMR_CALLBACK: //执行回调函数,使用创建定时器时的实参 p_fnct = p_tmr->CallbackPtr; //取定时器的回调函数 if (p_fnct != (OS_TMR_CALLBACK_PTR)0) { //如果回调函数存在 (*p_fnct)((void *)p_tmr, p_tmr->CallbackPtrArg); //使用创建定时器时的实参执行回调函数 } else { //如果回调函数不存在 *p_err = OS_ERR_TMR_NO_CALLBACK; //错误类型为“定时器没有回调函数” } break; case OS_OPT_TMR_CALLBACK_ARG: //执行回调函数,使用 p_callback_arg 作为实参 p_fnct = p_tmr->CallbackPtr; //取定时器的回调函数 if (p_fnct != (OS_TMR_CALLBACK_PTR)0) { //如果回调函数存在 (*p_fnct)((void *)p_tmr, p_callback_arg); //使用 p_callback_arg 作为实参执行回调函数 } else { //如果回调函数不存在 *p_err = OS_ERR_TMR_NO_CALLBACK; //错误类型为“定时器没有回调函数” } break; case OS_OPT_TMR_NONE: //只需停掉定时器 break; default: //情况超出预期 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_OPT_INVALID; //错误类型为“选项无效” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } OSSchedUnlock(&err); success = DEF_TRUE; break; case OS_TMR_STATE_COMPLETED: //如果定时器已完成第一次定时 case OS_TMR_STATE_STOPPED: //如果定时器已被停止 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_TMR_STOPPED; //错误类型为“定时器已被停止” success = DEF_TRUE; //执行结果暂为 DEF_TRUE break; case OS_TMR_STATE_UNUSED: //如果该定时器未被创建过 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_TMR_INACTIVE; //错误类型为“定时器未激活” success = DEF_FALSE; //执行结果暂为 DEF_FALSE break; default: //如果定时器状态超出预期 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_TMR_INVALID_STATE;//错误类型为“定时器状态非法” success = DEF_FALSE; //执行结果暂为 DEF_FALSE break; } return (success); //返回执行结果 }
OSTmrDel ()
OSTmrDel () 函数用于删除一个软件定时器。OSTmrDel () 函数的信息如下表所示。
OSTmrDel () 函数的定义位于“os_tmr.c”:
#if OS_CFG_TMR_DEL_EN > 0u //如果使能(默认是嫩)了 OSTmrDel() 函数 CPU_BOOLEAN OSTmrDel (OS_TMR *p_tmr, //定时器控制块指针 OS_ERR *p_err) //返回错误类型 { OS_ERR err; CPU_BOOLEAN success; //暂存函数执行结果 #ifdef OS_SAFETY_CRITICAL //如果使能(默认禁用)了安全检测 if (p_err == (OS_ERR *)0) { //如果错误类型实参为空 OS_SAFETY_CRITICAL_EXCEPTION(); //执行安全检测异常函数 return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u //如果使能(默认使能)了中断中非法调用检测 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数是在中断中被调用 *p_err = OS_ERR_TMR_ISR; //错误类型为“在中断函数中定时” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_ARG_CHK_EN > 0u //如果使能(默认使能)了参数检测 if (p_tmr == (OS_TMR *)0) { //如果使能 p_tmr 的实参为空 *p_err = OS_ERR_TMR_INVALID; //错误类型为“无效的定时器” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif #if OS_CFG_OBJ_TYPE_CHK_EN > 0u //如果使能(默认使能)了对象类型检测 if (p_tmr->Type != OS_OBJ_TYPE_TMR) { //如果该定时器的对象类型有误 *p_err = OS_ERR_OBJ_TYPE; //错误类型为“对象类型错误” return (DEF_FALSE); //返回 DEF_FALSE,不继续执行 } #endif OSSchedLock(&err); //锁住调度器 #if OS_CFG_DBG_EN > 0u //如果使能(默认使能)了调试代码和变量 OS_TmrDbgListRemove(p_tmr); //将该定时从定时器双向调试链表移除 #endif OSTmrQty--; //定时器个数减1 switch (p_tmr->State) { //根据定时器的状态分类处理 case OS_TMR_STATE_RUNNING: //如果定时器正在运行 OS_TmrUnlink(p_tmr); //从当前定时器轮列表移除定时器 OS_TmrClr(p_tmr); //复位定时器的指标 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_NONE; //错误类型为“无错误” success = DEF_TRUE; //执行结果暂为 DEF_TRUE break; case OS_TMR_STATE_STOPPED: //如果定时器已被停止 case OS_TMR_STATE_COMPLETED: //如果定时器已完成第一次定时 OS_TmrClr(p_tmr); //复位定时器的指标 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_NONE; //错误类型为“无错误” success = DEF_TRUE; //执行结果暂为 DEF_TRUE break; case OS_TMR_STATE_UNUSED: //如果定时器已被删除 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_TMR_INACTIVE; //错误类型为“定时器未激活” success = DEF_FALSE; //执行结果暂为 DEF_FALSE break; default: //如果定时器的状态超出预期 OSSchedUnlock(&err); //解锁调度器 *p_err = OS_ERR_TMR_INVALID_STATE; //错误类型为“定时器无效” success = DEF_FALSE; //执行结果暂为 DEF_FALSE break; } return (success); //返回执行结果 } #endif