这一节和上一节有一些承接关系,上一节主要讲述时间节拍列表处理延时任务的原理和函数的启动逻辑,那么什么情况下一个任务会插入到时钟节拍列表,
很显然肯定是用到了延时函数或者是有限等待一个信号量、消息队列、事件标志组,剩下的基本就是延时的时候被挂起或有限等待的时候被挂起。
这节总结一下常用的延时函数
OSTimeDlyHMSM()
void OSTimeDlyHMSM (CPU_INT16U hours, //延时时间–时
CPU_INT16U minutes, //延时时间–分
CPU_INT16U seconds, /延时时间–秒
CPU_INT32U milli, //延时时间–毫秒
OS_OPT opt, //选项即延时的模式
OS_ERR *p_err) //返回的错误值
两个例子
OSTimeDlyHMSM(0,0,0,20,OS_OPT_TIME_PERIODIC,&err) 例如这里选择了相对延时,延时 =20毫秒
OSTimeDlyHMSM(0,0,1,20,OS_OPT_TIME_PERIODIC,&err) 例如这里选择了相对延时,延时 =1秒 + 20毫秒
函数功能 | 当使用该函数时任务有就绪态变成等待态,延时结束后该任务才能继续运行 |
---|---|
注意 | 不要在中断里面用这个函数,而且这个延时函数对任务才有效果,可是中断服务函数不是任务,为了避免出错它里面有参数检查,一定确认是在中断里面使用就会立即退出,所以你想在中断单纯的延时也不会有延时的效果,补充一点所有的延时函数都是这样的都不能在中断里面使用UCOSIII定义的几个函数如OSTimeDlyHMSM()、OSTimeDly () 具体看代码 |
选项
接下来解析一下代码
#if OS_CFG_TIME_DLY_HMSM_EN > 0u //这里注意这个宏定义要使能该函数才能用 OS_CFG_TIME_DLY_HMSM_EN
void OSTimeDlyHMSM (CPU_INT16U hours,
CPU_INT16U minutes,
CPU_INT16U seconds,
CPU_INT32U milli,
OS_OPT opt,
OS_ERR *p_err)
{
OS_OPT opt_time;
OS_RATE_HZ tick_rate;
OS_TICK ticks;
CPU_SR_ALLOC();
//上面基本是参数检查的代码省略
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u /* 中断服务函数参数检查 */
if (OSIntNestingCtr > (OS_NESTING_CTR)0u) { /*OSIntNestingCtr大于0,表示进入中断,该函数不支持在中断里面使用. 补充一点只要进入中断OSIntNestingCtr就会自增1*/
*p_err = OS_ERR_TIME_DLY_RESUME_ISR;
return;
}
#endif
tick_rate = OSCfg_TickRate_Hz;//(OSCfg_TickRate_Hz=200)
//计算延时时间相当于多少个节拍数,应为UCOSIII是以节拍数作为时间的单位 1节拍数=5ms
ticks = ((OS_TICK)hours * (OS_TICK)3600u + (OS_TICK)minutes * (OS_TICK)60u + (OS_TICK)seconds) * tick_rate + (tick_rate * ((OS_TICK)milli + (OS_TICK)500u / tick_rate)) / (OS_TICK)1000u;
if (ticks > (OS_TICK)0u) { //延时时间要大于0延时才叫延时
OS_CRITICAL_ENTER(); //进入临界段代码保护
OSTCBCurPtr->TaskState = OS_TASK_STATE_DLY; //将任务变成等待态(延时了嘛)该任务之前是就绪态
OS_TickListInsert(OSTCBCurPtr, //将该任务插入时钟节拍列表,该函数上一节解析了
ticks,
opt_time,
p_err);
if (*p_err != OS_ERR_NONE) {
OS_CRITICAL_EXIT_NO_SCHED();
return;
}
OS_RdyListRemove(OSTCBCurPtr); /* 将该任务从任务就绪列表里面删除 */
OS_CRITICAL_EXIT_NO_SCHED(); //退出临界段代码保护
OSSched(); /* 进行任务调度 */
//任务恢复为就绪态 代码从这里开始运行
*p_err = OS_ERR_NONE;
} else {
*p_err = OS_ERR_TIME_ZERO_DLY;
}
}
#endif
另一个常用的延时函数 OSTimeDly () 该函数和上面延时函数功能一样
void OSTimeDly (OS_TICK dly, //延时需要的节拍数
OS_OPT opt, //延时类型的选项
OS_ERR *p_err); //返回的错误值
两个例子
OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err ); //相对延时 延时1000个时钟节拍(5s)
OSTimeDly ( 200, OS_OPT_TIME_DLY, & err ); //相对延时 延时200个时钟节拍(1s)
opt 选项
代码和OSTimeDlyHMSM()基本差不多就不介绍了
延时函数用着两个基本就可以了,延时函数一旦使用就意味着任务要从就绪态变成等待态了,任务在延时的时间之内不运行,延时结束会重新从等待态变回就绪态才会继续运行
读到这里也许有人会想如果任务延时一段时间之后,我想停止任务的延时可以吗
必须可以
该函数是 OSTimeDlyResume ()
void OSTimeDlyResume ( OS_TCB *p_tcb, //任务的任务控制块
OS_ERR *p_err); //返回的错误值
首先有要注意几点:
1.你要停止一个任务的延时,该任务必须之前是进入延时,不然怎么停止
2. 肯定是在另一个任务上面用这个将你想停止延时的任务停掉,即 该函数的 p_tcb 参数不可以是当前任务
3. 该函数不能在中断服务函数里面使用
4. 有限等待某些消息或者信号量等等这种任务的延时是不可以停止的,还有就是没延时就被挂起来的任务是没有意义的,只有经过 OSTimeDly()、OSTimeDlyHMSM() 延时的任务才会执行延时停止才有效果
那么该函数如何停止延时的看代码
#if OS_CFG_TIME_DLY_RESUME_EN > 0u /* 该宏定义置1才能使用该函数 */
void OSTimeDlyResume (OS_TCB *p_tcb,
OS_ERR *p_err)
{
CPU_SR_ALLOC();
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u /* 中断参数检查,该函数不能在中断使用 */
if (OSIntNestingCtr > (OS_NESTING_CTR)0u) { /* Not allowed to call from an ISR */
*p_err = OS_ERR_TIME_DLY_RESUME_ISR;
return;
}
#endif
CPU_CRITICAL_ENTER(); /* 进入临界段 */
if (p_tcb == OSTCBCurPtr) { /* 停止延时的任务控制块不能是当前正在运行的任务控制块 */
*p_err = OS_ERR_TASK_NOT_DLY;
CPU_CRITICAL_EXIT(); /* 退出临界段 */
return;
}
switch (p_tcb->TaskState) {
case OS_TASK_STATE_RDY: /* 任务处于就绪态不需要停止延时 */
CPU_CRITICAL_EXIT(); /* 退出临界段 */
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_DLY: /* 该任务(不是当前任务)处于延时*/
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
p_tcb->TaskState = OS_TASK_STATE_RDY;
OS_TickListRemove(p_tcb); /* 从时钟节拍列表删除该任务*/
OS_RdyListInsert(p_tcb); /* 在任务就绪列表插入该任务,因为解除延时就是直接从等待态变成就绪态 */
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_NONE;
break;
case OS_TASK_STATE_PEND: /* 任务是有限等待信号量之类的任务不能停止延时 */
CPU_CRITICAL_EXIT(); /* 退出临界段 */
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_PEND_TIMEOUT:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_SUSPENDED: /* 任务被挂了不能停止延时 */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_DLY_SUSPENDED: /* 任务在延时期间被挂起来可以将延时时间直接除去,变成纯粹的挂起来,像前一个选项那样 */
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();
p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
OS_TickListRemove(p_tcb); /* Remove task from tick list */
OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_TASK_SUSPENDED;
break;
case OS_TASK_STATE_PEND_SUSPENDED:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_TASK_NOT_DLY;
break;
default:
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_STATE_INVALID;
break;
}
OSSched();
}
#endif