(学习日记)2024.03.28:UCOSIII第二十五节:常见任务管理函数

本文主要是介绍(学习日记)2024.03.28:UCOSIII第二十五节:常见任务管理函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。


标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。


点击此处进入学习日记的总目录

2024.03.28:UCOSIII第二十五节:常见任务管理函数

  • 三十九、UCOSIII:常见任务管理函数
    • 1、任务挂起函数OS_TaskSuspend()
    • 2、任务恢复函数OSTaskResume()
    • 3、删除任务函数OSTaskDel()
    • 4、任务延时函数
      • 1. OSTimeDly()
      • 2. OSTimeDlyHMSM()

三十九、UCOSIII:常见任务管理函数

通过之前章节的学习,我们对任务创建以及任务调度的实现已然掌握了。
下面就补充一些ΜC/OS提供给我们对任务操作的一些常用函数。

1、任务挂起函数OS_TaskSuspend()

挂起指定任务。被挂起的任务绝不会得到CPU的使用权,不管该任务具有什么优先级。

任务可以通过调用vTaskSuspend()函数都可以将处于任何状态的任务挂起。
被挂起的任务得不到CPU的使用权,也不会参与调度, 它相对于调度器而言是不可见的,除非它从挂起态中解除。
任务挂起是我们经常使用的一个函数, 想要使用的就必须将宏定义OS_CFG_TASK_SUSPEND_EN启用,这样在编译的时候才会包含OS_TaskSuspend()这个函数, 源码如下:

#if OS_CFG_TASK_SUSPEND_EN > 0u//如果启用了函数 OSTaskSuspend()
void   OS_TaskSuspend (OS_TCB  *p_tcb,              //(1)//任务控制块指针OS_ERR  *p_err)                 //(2)//返回错误类型
{CPU_SR_ALLOC();  //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和//定义一个局部变量,用于保存关中断前的 CPU 状态寄存器// SR(临界段关中断只需保存SR),开中断时将该值还原。CPU_CRITICAL_ENTER();                         //关中断if (p_tcb == (OS_TCB *)0) {         //(3)//如果 p_tcb 为空p_tcb = OSTCBCurPtr;    //挂起自身}if (p_tcb == OSTCBCurPtr) {         //(4)//如果是挂起自身if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    //如果调度器被锁CPU_CRITICAL_EXIT();                            //开中断*p_err = OS_ERR_SCHED_LOCKED;          //错误类型为“调度器被锁”return;                                         //返回,停止执行}}*p_err = OS_ERR_NONE;                             //错误类型为“无错误”switch (p_tcb->TaskState) {      //(5)//根据 p_tcb 的任务状态分类处理case OS_TASK_STATE_RDY:       //(6)//如果是就绪状态OS_CRITICAL_ENTER_CPU_EXIT();                 //锁调度器,重开中断p_tcb->TaskState  =  OS_TASK_STATE_SUSPENDED; //任务状态改为“挂起状态”p_tcb->SuspendCtr = (OS_NESTING_CTR)1;           //挂起前套数为1OS_RdyListRemove(p_tcb);                  //将任务从就绪列表移除OS_CRITICAL_EXIT_NO_SCHED();              //开调度器,不进行调度break;                                           //跳出case OS_TASK_STATE_DLY:         //(7)//如果是延时状态将改为“延时中被挂起”p_tcb->TaskState  = OS_TASK_STATE_DLY_SUSPENDED;p_tcb->SuspendCtr = (OS_NESTING_CTR)1;           //挂起前套数为1CPU_CRITICAL_EXIT();                             //开中断break;                                           //跳出case OS_TASK_STATE_PEND: //(8)//如果是无期限等待状态将改为“无期限等待中被挂起”p_tcb->TaskState  = OS_TASK_STATE_PEND_SUSPENDED;p_tcb->SuspendCtr = (OS_NESTING_CTR)1;           //挂起前套数为1CPU_CRITICAL_EXIT();                             //开中断break;                                           //跳出case OS_TASK_STATE_PEND_TIMEOUT://(9)//如果是有期限等待将改为“有期限等待中被挂起”p_tcb->TaskState  = OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED;p_tcb->SuspendCtr = (OS_NESTING_CTR)1;           //挂起前套数为1CPU_CRITICAL_EXIT();                             //开中断break;                                           //跳出case OS_TASK_STATE_SUSPENDED:     //(10)              //如果状态中有挂起状态case OS_TASK_STATE_DLY_SUSPENDED:case OS_TASK_STATE_PEND_SUSPENDED:case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:p_tcb->SuspendCtr++;                             //挂起嵌套数加1CPU_CRITICAL_EXIT();                             //开中断break;                                           //跳出default:                         //(11)//如果任务状态超出预期CPU_CRITICAL_EXIT();                             //开中断*p_err = OS_ERR_STATE_INVALID;          //错误类型为“状态非法”return;                                          //返回,停止执行}OSSched();                     //(12)//调度任务
}
#endif
  • (1):任务控制块指针,该指针指向要挂起的任务,也可以是任务自身,但是不能是空闲任务,空闲任务永远不允许挂起。
  • (2):用于存放返回错误代码,如果挂起任务失败,则返回对应的错误代码。
  • (3):如果传递进来的任务控制块指针是NULL或者是0, 则表明要挂起的任务是任务自身,将任务控制块的指针指向当前任务。
  • (4):如果的任务是当前任务,也就是挂起任务自身,那么需要判断一下调度器有没有被锁定, 因为挂起任务自身之后,就肯定需要切换任务,而如果调度器被锁定的话,就无法切换任务了,所以会返回错误类型“调度器被锁”,然后退出。
  • (5):根据要挂起的任务状态分类处理,这样处理逻辑简单,更加方便快捷。
  • (6):如果任务处于就绪状态,那么该任务能直接挂起,但是接下来我们要操作进行列表, 时间是不确定的,我们不能将中断关闭太久,这样子会影响系统对中断的响应,此时系统就会打开中断, 但是系统又不想其他任务来影响我们操作就绪列表,所以系统还会锁定调度器,不进行任务切换,这样子就不会有任务打扰我们的操作了, 然后将任务状态变为挂起态,挂起次数为1次,然后调用OS_RdyListRemove()函数将任务从就绪列表移除,再打开调度器, 然后跳出,最后才进行任务的调度。
  • (7):如果任务当前处于延时状态,那么也能被挂起, 任务状态将改为“延时中被挂起”状态,挂起次数为1次,然后打开中断,退出。
  • (8):如果任务当前处于无期限等待状态, 那么也能被挂起,任务状态将改为“无期限等待中被挂起”状态,挂起次数为1次,然后打开中断,退出。
  • (9):如果任务当前处于有期限等待状态, 那么也能被挂起,任务状态将改为“有期限等待中被挂起”状态,挂起次数为1次,然后打开中断,退出。
  • (10):如果要挂起的任务本身就处于挂起态, 那么再次挂起就要记录挂起的次数,将挂起的次数加一,然后打开中断,退出。
  • (11):对于其他的任务状态,返回状态非法错误,然后退出。
  • (12):进行一次任务调度。

注:任务可以调用OS_TaskSuspend()这个函数来挂起任务自身,但是在挂起自身的时候会进行一次任务上下文切换, 需要挂起自身就将任务控制块指针设置为NULL或0传递进来即可。无论任务是什么状态都可以被挂起, 只要调用了OS_TaskSuspend()这个函数就会挂起成功,不论是挂起其他任务还是挂起任务自身。

任务的挂起与恢复函数在很多时候都是很有用的,比如我们想暂停某个任务运行一段时间,但是我们又需要在其恢复的时候继续工作, 那么删除任务是不可能的。
因为删除了任务的话,任务的所有的信息都是不可能恢复的,删除是完完全全删除了, 里面的资源都被系统释放掉。

挂起任务就不会这样子,调用挂起任务函数,仅仅是将任务进入挂起态, 其内部的资源都会保留下来,同时也不会参与系统中任务的调度,当调用恢复函数的时候,整个任务立即从挂起态进入就绪态, 并且参与任务的调度,如果该任务的优先级是当前就绪态优先级最高的任务,那么立即会按照挂起前的任务状态继续执行该任务, 从而达到我们需要的效果。
注意,是继续执行,也就是说,挂起任务之前是什么状态,都会被系统保留下来,在恢复的瞬间, 继续执行。

这个任务函数的使用方法是很简单的,只需把任务句柄传递进来即可,OS_TaskSuspend()会根据任务句柄的信息将对应的任务挂起

/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
staticOS_TCB   AppTaskLed1TCB;/* LED任务句柄 */
static void KEY_Task(void* parameter)
{OS_ERR      err;while (1) {if ( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) {/* KEY1 被按下 */printf("挂起LED任务!\n");OSTaskSuspend (AppTaskLed1TCB, & err );   /* 挂起LED1任务 */}OSTimeDly ( 20, OS_OPT_TIME_DLY, & err );   /* 延时20个tick */}
}

2、任务恢复函数OSTaskResume()

既然有任务的挂起,那么当然一样有恢复,不然任务怎么恢复呢。
任务恢复就是让挂起的任务重新进入就绪状态,恢复的任务会保留挂起前的状态信息, 在恢复的时候根据挂起时的状态继续运行。
如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。
下面一起看看任务恢复函数OSTaskResume()的源码:

#if OS_CFG_TASK_SUSPEND_EN > 0u//如果启用了函数 OSTaskResume()
void  OSTaskResume (OS_TCB  *p_tcb,     //(1)//任务控制块指针OS_ERR  *p_err)     //(2)//返回错误类型
{CPU_SR_ALLOC();  //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和//定义一个局部变量,用于保存关中断前的 CPU 状态寄存器// SR(临界段关中断只需保存SR),开中断时将该值还原。#ifdef OS_SAFETY_CRITICAL//如果启用了安全检测if (p_err == (OS_ERR *)0) {      //(3)//如果 p_err 为空OS_SAFETY_CRITICAL_EXCEPTION();   //执行安全检测异常函数return;                           //返回,停止执行}
#endif
//如果禁用了中断延迟发布和中断中非法调用检测
#if (OS_CFG_ISR_POST_DEFERRED_EN   == 0u) && \(OS_CFG_CALLED_FROM_ISR_CHK_EN >  0u)           //(4)if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果在中断中调用该函数*p_err = OS_ERR_TASK_RESUME_ISR;        //错误类型为“在中断中恢复任务”return;                                //返回,停止执行}
#endifCPU_CRITICAL_ENTER();                     //关中断
#if OS_CFG_ARG_CHK_EN > 0u//如果启用了参数检测if ((p_tcb == (OS_TCB *)0) ||             //如果被恢复任务为空或是自身(p_tcb == OSTCBCurPtr)) {   //(5)CPU_CRITICAL_EXIT();                  //开中断*p_err  = OS_ERR_TASK_RESUME_SELF;     //错误类型为“恢复自身”return;                               //返回,停止执行}
#endifCPU_CRITICAL_EXIT();                      //关中断#if OS_CFG_ISR_POST_DEFERRED_EN > 0u	//(6)//如果启用了中断延迟发布if (OSIntNestingCtr > (OS_NESTING_CTR)0) {  //如果该函数在中断中被调用OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_TASK_RESUME,(void      *)p_tcb,(void      *)0,(OS_MSG_SIZE)0,(OS_FLAGS   )0,(OS_OPT     )0,(CPU_TS     )0,(OS_ERR    *)p_err);//把恢复任务命令发布到中断消息队列return;       //返回,停止执行}
#endif/* 如果禁用了中断延迟发布或不是在中断中调用该函数 */OS_TaskResume(p_tcb, p_err);         //直接将任务 p_tcb 恢复//(7)
}
#endif
  • (1):任务控制块指针,该指针指向要恢复的任务,与挂起任务不同的是,该指针不允许指向任务自身。
  • (2):用于存放返回错误代码,如果恢复任务失败,则返回对应的错误代码。
  • (3):如果启用了安全检测(OS_SAFETY_CRITICAL)这个宏定义, 那么在编译代码的时候会包含安全检测, 如果p_err指针为空,系统会执行安全检测异常函数OS_SAFETY_CRITICAL_EXCEPTION(),然后退出。
  • (4):如果禁用了中断延迟发布和中断中非法调用检测,那么在中断中恢复任务则是非法的, 会直接返回错误类型为“在中断中恢复任务”,并且退出。而如果启用了中断延迟发布的话呢,就可以在中断中恢复任务, 因为中断延迟发布的真正操作是在中断发布任务中。
  • (5):如果启用了参数检测(OS_CFG_ARG_CHK_EN)这个宏定义,如果被恢复任务为空或是自身, 也是不允许的,会返回错误类型为“恢复自身”,并且退出操作。
  • (6):如果启用了中断延迟发布,并且如果该函数在中断中被调用, 系统就会把恢复任务命令发布到中断消息队列中,唤醒中断发布任务,在任务中恢复指定任务,并且退出。
  • (7):如果禁用了中断延迟发布或不是在中断中调用该函数, 直接调用OS_TaskResume()函数恢复任务

OS_TaskResume()函数源码如下:

#if OS_CFG_TASK_SUSPEND_EN > 0u//如果启用了函数 OSTaskResume()
void  OS_TaskResume (OS_TCB  *p_tcb,      //任务控制块指针OS_ERR  *p_err)      //返回错误类型
{CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和//定义一个局部变量,用于保存关中断前的 CPU 状态寄存器// SR(临界段关中断只需保存SR),开中断时将该值还原。CPU_CRITICAL_ENTER();                 //关中断*p_err  = OS_ERR_NONE;                 //错误类型为“无错误”switch (p_tcb->TaskState) {      //(1)//根据 p_tcb 的任务状态分类处理case OS_TASK_STATE_RDY:               //如果状态中没有挂起状态case OS_TASK_STATE_DLY:case OS_TASK_STATE_PEND:case OS_TASK_STATE_PEND_TIMEOUT:CPU_CRITICAL_EXIT();                              //开中断*p_err = OS_ERR_TASK_NOT_SUSPENDED;  //(2)//错误类型为“任务未被挂起”break;                                            //跳出case OS_TASK_STATE_SUSPENDED:           //(3)//如果是“挂起状态”OS_CRITICAL_ENTER_CPU_EXIT();                 //锁调度器,重开中断p_tcb->SuspendCtr--;              //(4)//任务的挂起嵌套数减1if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) {  //如果挂起前套数为0p_tcb->TaskState = OS_TASK_STATE_RDY;    //修改状态为“就绪状态”OS_TaskRdy(p_tcb);                  //把 p_tcb 插入就绪列表}OS_CRITICAL_EXIT_NO_SCHED();              //开调度器,不调度任务break;                                            //跳出case OS_TASK_STATE_DLY_SUSPENDED:      //(5)//如果是“延时中被挂起”p_tcb->SuspendCtr--;                       //任务的挂起嵌套数减1if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) { //如果挂起前套数为0p_tcb->TaskState = OS_TASK_STATE_DLY;    //修改状态为“延时状态”}CPU_CRITICAL_EXIT();                              //开中断break;                                            //跳出case OS_TASK_STATE_PEND_SUSPENDED:    //(6)//如果是“无期限等待中被挂起”p_tcb->SuspendCtr--;                      //任务的挂起嵌套数减1if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) {  //如果挂起前套数为0p_tcb->TaskState = OS_TASK_STATE_PEND; //修改状态为“无期限等待状态”}CPU_CRITICAL_EXIT();                              //开中断break;                                            //跳出case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:	//(7)//如果是“有期限等待中被挂起”p_tcb->SuspendCtr--;                //任务的挂起嵌套数减1if (p_tcb->SuspendCtr == (OS_NESTING_CTR)0) { //如果挂起前套数为0p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT;}CPU_CRITICAL_EXIT();                              //开中断break;                                            //跳出default:                        //(8) //如果 p_tcb 任务状态超出预期CPU_CRITICAL_EXIT();                              //开中断*p_err = OS_ERR_STATE_INVALID;        //错误类型为“状态非法”return;//跳出}OSSched();                //(9)//调度任务
}
#endif
  • (1):根据要挂起的任务状态分类处理,这样处理逻辑简单,更加方便快捷。
  • (2):如果要恢复的任务状态中没有挂起状态, 那表示任务没有被挂起,根本不需要恢复任务,返回错误类型为“任务未被挂起”,并且退出操作。
  • (3):如果要恢复的任务是单纯的挂起状态,那么可以恢复任务。
  • (4):任务的挂起记录次数减1,如果挂起前次数为0,表示任务已经完全恢复了,那么就可以参与系统的调度, 此时就要把任务添加到就绪列表中,并且将任务的状态变为就绪状态,操作完成之后就跳出switch语句,打开中断但是不进行任务调度, 因为在最后面才会进行任务调度。
  • (5):如果任务在延时的时候被挂起了,也可以进行恢复任务操作,任务的挂起记录次数减1,如果挂起前次数为0, 表示任务已经完全恢复了,那就会恢复挂起前的状态——延时状态,然后退出。
  • (6):同理,如果任务在无期限等待的时候被挂起了,也可以进行恢复任务操作,任务的挂起记录次数减1, 如果挂起前次数为0,表示任务已经完全恢复了,那就会恢复挂起前的状态——无期限等待状态,然后退出。
  • (7):如果任务在有期限等待的时候被挂起了,也可以进行恢复任务操作,任务的挂起记录次数减1, 如果挂起前次数为0,表示任务已经完全恢复了,那就会恢复挂起前的状态——有期限等待状态,然后退出。
  • (8):对于其他的任务状态,返回状态非法错误,然后退出。
  • (9):进行一次任务调度。

OSTaskResume()函数用于恢复挂起的任务。任务在挂起时候调用过多少次的OS_TaskSuspend()函数,那么就需要调用多少次OSTaskResume() 函数才能将任务恢复运行,下面来看看任务恢复函数OSTaskResume()的使用实例

/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
staticOS_TCB   AppTaskLed1TCB;/* LED任务句柄 */static void KEY_Task(void* parameter)
{OS_ERR      err;while (1) {if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) {/* KEY2 被按下 */printf("恢复LED任务!\n");OSTaskResume ( &AppTaskLed1TCB, & err );  /* 恢复LED任务! */}OSTimeDly ( 20, OS_OPT_TIME_DLY, & err );   /* 延时20个tick */}
}

3、删除任务函数OSTaskDel()

OSTaskDel()用于删除一个任务。
当一个任务删除另外一个任务时,形参为要删除任务创建时返回的任务句柄,如果是删除自身,则形参为 NULL。
要想使用该函数必须在os_cfg.h中把OS_CFG_TASK_DEL_EN宏定义配置为1,删除的任务将从所有就绪,阻塞,挂起和事件列表中删除, 任务删除函数OSTaskDel()源码具体如下

#if OS_CFG_TASK_DEL_EN > 0u//如果启用了函数 OSTaskDel()
void  OSTaskDel (OS_TCB  *p_tcb,                 //目标任务控制块指针OS_ERR  *p_err)                 //返回错误类型
{CPU_SR_ALLOC(); //使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和//定义一个局部变量,用于保存关中断前的 CPU 状态寄存器// SR(临界段关中断只需保存SR),开中断时将该值还原。#ifdef OS_SAFETY_CRITICAL//如果启用(默认禁用)了安全检测if (p_err == (OS_ERR *)0) {                //如果 p_err 为空OS_SAFETY_CRITICAL_EXCEPTION();        //执行安全检测异常函数return;                                //返回,停止执行}
#endif#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u  //(1)//如果启用了中断中非法调用检测if (OSIntNestingCtr > (OS_NESTING_CTR)0) { //如果该函数在中断中被调用*p_err = OS_ERR_TASK_DEL_ISR;           //错误类型为“在中断中删除任务”return;                                //返回,停止执行}
#endifif (p_tcb == &OSIdleTaskTCB) {      //(2)//如果目标任务是空闲任务*p_err = OS_ERR_TASK_DEL_IDLE;          //错误类型为“删除空闲任务”return;                                //返回,停止执行}#if OS_CFG_ISR_POST_DEFERRED_EN > 0u    //(3)//如果启用了中断延迟发布if (p_tcb == &OSIntQTaskTCB) {          //如果目标任务是中断延迟提交任务*p_err = OS_ERR_TASK_DEL_INVALID;       //错误类型为“非法删除任务”return;                                //返回,停止执行}
#endifif (p_tcb == (OS_TCB *)0) {        //(4)//如果 p_tcb 为空CPU_CRITICAL_ENTER();                  //关中断p_tcb  = OSTCBCurPtr;         //目标任务设为自身CPU_CRITICAL_EXIT();                   //开中断}OS_CRITICAL_ENTER();                       //进入临界段switch (p_tcb->TaskState) {      //(5)//根据目标任务的任务状态分类处理case OS_TASK_STATE_RDY:                //如果是就绪状态OS_RdyListRemove(p_tcb);     //(6)//将任务从就绪列表移除break;                            //跳出case OS_TASK_STATE_SUSPENDED:    //(7)//如果是挂起状态break;                            //直接跳出case OS_TASK_STATE_DLY:        //(8)//如果包含延时状态case OS_TASK_STATE_DLY_SUSPENDED:OS_TickListRemove(p_tcb);         //将任务从节拍列表移除break;                            //跳出case OS_TASK_STATE_PEND:       //(9)//如果包含等待状态case OS_TASK_STATE_PEND_SUSPENDED:case OS_TASK_STATE_PEND_TIMEOUT:case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:OS_TickListRemove(p_tcb);    //(10)//将任务从节拍列表移除switch (p_tcb->PendOn) {   //(11)//根据任务的等待对象分类处理case OS_TASK_PEND_ON_NOTHING: //如果没在等待内核对象case OS_TASK_PEND_ON_TASK_Q:  //如果等待的是任务消息队列case OS_TASK_PEND_ON_TASK_SEM://如果等待的是任务信号量break;                   //直接跳出case OS_TASK_PEND_ON_FLAG:    //如果等待的是事件case OS_TASK_PEND_ON_MULTI:   //如果等待多个内核对象case OS_TASK_PEND_ON_MUTEX:   //如果等待的是互斥量case OS_TASK_PEND_ON_Q:       //如果等待的是消息队列case OS_TASK_PEND_ON_SEM:     //如果等待的是信号量OS_PendListRemove(p_tcb);//(12)//将任务从等待列表移除break;                   //跳出default:                      //如果等待对象超出预期break;                   //直接跳出}break;                            //跳出default:                        //(13)//如果目标任务状态超出预期OS_CRITICAL_EXIT();                //退出临界段*p_err = OS_ERR_STATE_INVALID;      //错误类型为“状态非法”return;                            //返回,停止执行}#if OS_CFG_TASK_Q_EN > 0u   //(14)//如果启用了任务消息队列(void)OS_MsgQFreeAll(&p_tcb->MsgQ);        //释放任务的所有任务消息
#endifOSTaskDelHook(p_tcb);              //(15)//调用用户自定义的钩子函数#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)OS_TLS_TaskDel(p_tcb);                                  /* Call TLSk     */
#endif#if OS_CFG_DBG_EN > 0u  //(16)//如果启用了调试代码和变量OS_TaskDbgListRemove(p_tcb);               //将任务从任务调试双向列表移除
#endifOSTaskQty--;                      //(17)//任务数目减1OS_TaskInitTCB(p_tcb);             //(18)//初始化任务控制块p_tcb->TaskState = (OS_STATE)OS_TASK_STATE_DEL;//标定任务已被删除OS_CRITICAL_EXIT_NO_SCHED();               //退出临界段(无调度)*p_err = OS_ERR_NONE;                       //错误类型为“无错误”OSSched();                          //(19)//调度任务
}
#endif
  • (1):如果启用了中断中非法调用检测, 那么在中断中删除任务则是非法的,会直接返回错误类型为“在中断中删除任务”,并且退出。
  • (2):如果要删除的目标任务是空闲任务,这是绝对不允许的,系统中空闲任务的存在是必然的, 绝对不允许删除空闲任务,会返回错误类型为“删除空闲任务”的错误代码,并且退出。
  • (3):如果启用了中断延迟发布,但是要删除的目标任务是中断延迟发布任务,这也是绝对不允许的, 因为启用了中断延迟发布,则代表着系统中必须有一个中断延迟发布任务处理在中断中的发布的事情,所以会返回错误类型为“非法删除任务”的错误代码,并且退出。
  • (4):如果传递进来的任务控制块指针为0, 表示要删除的任务是任务自身,则将任务控制块指针指向当前任务,目标任务设为任务自身。
  • (5):根据目标任务的任务状态分类处理。
  • (6):如果任务是处于就绪态的,就将任务从就绪列表移除。
  • (7):如果任务是处于挂起状态就直接跳出switch语句。
  • (8):如果任务包含延时状态,那么将任务从节拍列表移除。
  • (9):如果任务包含等待状态。
  • (10):系统首先会将任务从节拍列表移除。
  • (11):然后再根据任务的等待对象分类处理, 如果没在等待内核对象或者等待的是任务消息队列或者等待的是任务信号量,那么直接跳出switch语句。
  • (12):而任务如果是在等待内核资源这些,如事件、消息队列、 信号量等,系统会直接将任务从等待列表移除,然后跳出switch语句。
  • (13):如果目标任务状态超出预期,直接返回错误类型为“状态非法”的错误,并且退出删除操作。
  • (14):如果启用了任务消息队列,将释放任务的所有任务消息。
  • (15):在删除任务的时候,系统还会调用用户自定义的钩子函数,用户可以通过该钩子函数进行自定义的操作。
  • (16):如果启用了调试代码和变量,将任务从任务调试双向列表移除。
  • (17):到这里就进行任务的删除操,系统的任务数目减1。
  • (18):初始化对应的任务控制块,将任务状态变为删除态, 退出临界段但不进行调度,返回错误类型为“无错误”的错误代码。
  • (19):进行一次任务调度。

删除任务是说任务将返回并处以删除(休眠)状态,任务的代码不再被μC/OS调用。
删除任务不是删除代码,删除任务和挂起任务有些相似,其实有着本质的区别。
根本来说,最大的不同就是删除任务队任务控制块的操作,我们知道在任务创建的时候,需要给每个任务分配一个任务控制块,这个任务控制块存储有关这个任务重要的信息,对任务间有至关重要的作用,挂起任务根本不会动任务控制块,但删除任务就会把任务控制块进行初始化,这样关于任务的任何信息都被抹去。

注意,删除任务并不会释放任务的栈空间。

删除任务函数的使用实例如下:

/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为NULL。
*/
staticOS_TCB   AppTaskLed1TCB;/* LED任务句柄 */static void KEY_Task(void* parameter)
{OS_ERR      err;
while (1) {if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) {/* KEY2 被按下 */printf("删除LED任务!\n");OSTaskDel( &AppTaskLed1TCB, & err );  /* 删除LED任务! */}OSTimeDly ( 20, OS_OPT_TIME_DLY, & err );   /* 延时20个tick */}
}

4、任务延时函数

1. OSTimeDly()

OSTimeDly()在我们任务中用得非常之多,每个任务都必须是死循环,并且是必须要有阻塞的情况,否则低优先级的任务就无法被运行了。
OSTimeDly()函数常用于停止当前任务进行的运行,延时一段时间后再运行。
OSTimeDly()函数源码具体如下:

void  OSTimeDly (OS_TICK   dly,              //(1)//延时的时钟节拍数OS_OPT    opt,                          //(2)//选项OS_ERR   *p_err)            //(3)//返回错误类型
{CPU_SR_ALLOC();//使用到临界段(在关/开中断时)时必须用到该宏,该宏声明和定义一个局部变//量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR)//,开中断时将该值还原。#ifdef OS_SAFETY_CRITICAL   //(4)//如果启用(默认禁用)了安全检测if (p_err == (OS_ERR *)0) {                        //如果错误类型实参为空OS_SAFETY_CRITICAL_EXCEPTION();                //执行安全检测异常函数return;                                        //返回,不执行延时操作}
#endif//(5)
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u//如果启用(默认启用)了中断中非法调用检测if (OSIntNestingCtr > (OS_NESTING_CTR)0u){//如果该延时函数是在中断中被调用*p_err = OS_ERR_TIME_DLY_ISR;       //错误类型为“在中断函数中延时”return;                             //返回,不执行延时操作}
#endif
/* 当调度器被锁时任务不能延时 */         //(6)if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0u) {  //如果调度器被锁*p_err = OS_ERR_SCHED_LOCKED;             //错误类型为“调度器被锁”return;                                        //返回,不执行延时操作}switch (opt) {             //(7)//根据延时选项参数 opt 分类操作case OS_OPT_TIME_DLY:               //如果选择相对时间(从现在起延时多长时间)case OS_OPT_TIME_TIMEOUT:                      //如果选择超时(实际同上)case OS_OPT_TIME_PERIODIC:                     //如果选择周期性延时if (dly == (OS_TICK)0u) {    //(8)//如果参数 dly 为0(0意味不延时)*p_err = OS_ERR_TIME_ZERO_DLY;         //错误类型为“0延时”return;                               //返回,不执行延时操作}break;case OS_OPT_TIME_MATCH:         //(9)//如果选择绝对时间(匹配系统开始运行(OSStart())后的时钟节拍数)break;default:                            //(10)//如果选项超出范围*p_err = OS_ERR_OPT_INVALID;               //错误类型为“选项非法”return;                                   //返回,不执行延时操作}OS_CRITICAL_ENTER();                             //进入临界段OSTCBCurPtr->TaskState = OS_TASK_STATE_DLY;  //(11)//修改当前任务的任务状态为延时状态OS_TickListInsert(OSTCBCurPtr,             //将当前任务插入节拍列表dly,opt,p_err);         //(12)if (*p_err != OS_ERR_NONE) {          //如果当前任务插入节拍列表时出现错误OS_CRITICAL_EXIT_NO_SCHED();                  //退出临界段(无调度)return;                                       //返回,不执行延时操作}OS_RdyListRemove(OSTCBCurPtr);          //(13)//从就绪列表移除当前任务OS_CRITICAL_EXIT_NO_SCHED();                       //退出临界段(无调度)OSSched();                              //(14)//任务切换*p_err = OS_ERR_NONE;                               //错误类型为“无错误”
}
  • (1):任务延时的时钟节拍数,也就是延时的时间。
  • (2):任务延时的可选选项,在os.h中有定义
#define  OS_OPT_TIME_DLY                     DEF_BIT_NONE   
//OS_OPT_TIME_DLY:dly 为相对时间,就是从现在起延时多长时间, 到时钟节拍总计数OSTickCtr = OSTickCtr当前 + dly 时延时结束。
#define  OS_OPT_TIME_TIMEOUT                ((OS_OPT)DEF_BIT_01) 
//OS_OPT_TIME_TIMEOUT:跟 OS_OPT_TIME_DLY 的作用情况一样。
#define  OS_OPT_TIME_MATCH                  ((OS_OPT)DEF_BIT_02)
//OS_OPT_TIME_MATCH:dly为绝对时间, 就是从系统开始运行(调用 OSStart())时到节拍总计数OSTickCtr = dly 时延时结束。
#define  OS_OPT_TIME_PERIODIC               ((OS_OPT)DEF_BIT_03)
//OS_OPT_TIME_PERIODIC:周期性延时, 跟 OS_OPT_TIME_DLY的作用差不多,如果是长时间延时,该选项更精准一些。
  • (3):用于存放返回错误代码,如果挂起任务失败,则返回对应的错误代码。
  • (4):如果启用(默认禁用)了安全检测, 系统就会执行安全检测的代码,如果错误类型实参为空,就执行安全检测异常函数,然后返回,不执行延时操作。
  • (5):如果启用(默认启用)了中断中非法调用检测 ,如果该延时函数是在中断中被调用,将返回错误类型为“在中断函数中延时”的错误代码,退出,不执行延时操作。
  • (6):如果调度器被锁,则不允许进行延时操作, 返回错误类型为“调度器被锁”的错误代码,并且退出延时操作。因为延时就必须进行任务的切换,所以在延时的时候不能锁定调度器,
  • (7):根据延时选项参数 opt 分类操作。
  • (8):如果选择相对时间(从现在起延时多长时间)或者选择超时时间或者选择周期性延时, 那么这表示延时时间,如果参数 dly 为0(0意味不延时),就会返回错误类型为“0延时”的错误代码,并且退出不执行延时操作。
  • (9):如果选择绝对时间(匹配系统开始运行(OSStart())后的时钟节拍数。
  • (10):如果选项超出范围,则视为非法,返回错误类型为“选项非法”的错误代码,并且退出不执行延时操作。
  • (11):程序能执行到这里,说明能正常进行延时操作,那么系统就会修改当前任务的任务状态为延时状态。
  • (12):调用OS_TickListInsert()函数将当前任务插入节拍列表, 加入节拍列表的任务会按照延时时间进行升序排列
  • (13):调用OS_RdyListRemove()函数从就绪列表移除当前任务,进行延时操作。
  • (14):进行一次任务切换。

OS_TickListInsert()源码具体如下:

void  OS_TickListInsert (OS_TCB   *p_tcb, //任务控制块OS_TICK   time,  //时间OS_OPT    opt,   //选项OS_ERR   *p_err) //返回错误类型
{OS_TICK            tick_delta;OS_TICK            tick_next;OS_TICK_SPOKE     *p_spoke;OS_TCB            *p_tcb0;OS_TCB            *p_tcb1;OS_TICK_SPOKE_IX   spoke;if (opt == OS_OPT_TIME_MATCH){      //如果 time 是个绝对时间tick_delta = time - OSTickCtr - 1u;  //计算离到期还有多长时间if (tick_delta > OS_TICK_TH_RDY){    //如果延时时间超过了门限p_tcb->TickCtrMatch = (OS_TICK        )0u;  //将任务的时钟节拍的匹配变量置0p_tcb->TickRemain   = (OS_TICK        )0u; //将任务的延时还需时钟节拍数置0p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0; //该任务不插入节拍列表*p_err      =  OS_ERR_TIME_ZERO_DLY; //错误类型相当于“0延时”return;                         //返回,不将任务插入节拍列表}p_tcb->TickCtrMatch = time; //任务等待的匹配点为 OSTickCtr = timep_tcb->TickRemain   = tick_delta + 1u; //计算任务离到期还有多长时间}else if (time > (OS_TICK)0u){          //如果 time > 0if (opt == OS_OPT_TIME_PERIODIC){    //如果 time 是周期性时间tick_next  = p_tcb->TickCtrPrev + time;//计算任务接下来要匹配的时钟节拍总计数tick_delta = tick_next - OSTickCtr - 1u;  //计算任务离匹配还有个多长时间if (tick_delta < time){        //如果 p_tcb->TickCtrPrev<OSTickCtr+1p_tcb->TickCtrMatch = tick_next; //将 p_tcb->TickCtrPrev + time设为时钟节拍匹配点}else{         //如果 p_tcb->TickCtrPrev >= OSTickCtr + 1p_tcb->TickCtrMatch = OSTickCtr + time; //将 OSTickCtr + time 设为时钟节拍匹配点}p_tcb->TickRemain   = p_tcb->TickCtrMatch - OSTickCtr; //计算任务离到期还有多长时间p_tcb->TickCtrPrev  = p_tcb->TickCtrMatch; //保存当前匹配值为下一周期延时用}else{                            //如果 time 是相对时间p_tcb->TickCtrMatch = OSTickCtr + time; //任务等待的匹配点为 OSTickCtr + timep_tcb->TickRemain   = time; //计算任务离到期的时间就是 time}}else{                           //如果 time = 0p_tcb->TickCtrMatch = (OS_TICK        )0u; //将任务的时钟节拍的匹配变量置0p_tcb->TickRemain   = (OS_TICK        )0u; //将任务的延时还需时钟节拍数置0p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0; //该任务不插入节拍列表*p_err               =  OS_ERR_TIME_ZERO_DLY; //错误类型为“0延时”return;                           //返回,不将任务插入节拍列表}spoke   = (OS_TICK_SPOKE_IX)(p_tcb->TickCtrMatch % OSCfg_TickWheelSize);//使用哈希算法(取余)来决定任务存于数组p_spoke = &OSCfg_TickWheel[spoke];//OSCfg_TickWheel的哪个元素(组织一个节拍列表),//与更新节拍列表相对应,可方便查找到期任务。if (p_spoke->NbrEntries == (OS_OBJ_QTY)0u){     //如果当前节拍列表为空p_tcb->TickNextPtr   = (OS_TCB   *)0;//任务中指向节拍列表中下一个任务的指针置空p_tcb->TickPrevPtr   = (OS_TCB   *)0;//任务中指向节拍列表中前一个任务的指针置空p_spoke->FirstPtr    =  p_tcb;//当前任务被列为该节拍列表的第一个任务p_spoke->NbrEntries  = (OS_OBJ_QTY)1u;   //节拍列表中的元素数目为1}else{                                     //如果当前节拍列表非空p_tcb1     = p_spoke->FirstPtr;          //获取列表中的第一个任务while (p_tcb1 != (OS_TCB *)0){          //如果该任务存在p_tcb1->TickRemain = p_tcb1->TickCtrMatch   //计算该任务的剩余等待时间- OSTickCtr;if (p_tcb->TickRemain > p_tcb1->TickRemain){//如果当前任务的剩余等待时间大于该任务的if (p_tcb1->TickNextPtr != (OS_TCB *)0){//如果该任务不是列表的最后一个元素p_tcb1               =  p_tcb1->TickNextPtr;//让当前任务继续与该任务的下一个任务作比较}else{         //如果该任务是列表的最后一个元素p_tcb->TickNextPtr   = (OS_TCB *)0; //当前任务为列表的最后一个元素p_tcb->TickPrevPtr   =  p_tcb1;  //该任务是当前任务的前一个元素p_tcb1->TickNextPtr  =  p_tcb; //当前任务是该任务的后一个元素p_tcb1             = (OS_TCB *)0; //插入完成,退出 while 循环}}else{                //如果当前任务的剩余等待时间不大于该任务的if (p_tcb1->TickPrevPtr == (OS_TCB *)0){//如果该任务是列表的第一个元素p_tcb->TickPrevPtr   = (OS_TCB *)0; //当前任务就作为列表的第一个元素p_tcb->TickNextPtr   =  p_tcb1; //该任务是当前任务的后一个元素p_tcb1->TickPrevPtr  =  p_tcb;  //当前任务是该任务的前一个元素p_spoke->FirstPtr    =  p_tcb;  //当前任务是列表的第一个元素}else{                          //如果该任务也不是是列表的第一个元素p_tcb0  =  p_tcb1->TickPrevPtr; // p_tcb0 暂存该任务的前一个任务p_tcb->TickPrevPtr   =  p_tcb0;//该任务的前一个任务作为当前任务的前一个任务p_tcb->TickNextPtr   =  p_tcb1; //该任务作为当前任务的后一个任务p_tcb0->TickNextPtr  =  p_tcb;  // p_tcb0暂存的任务的下一个任务改为当前任务p_tcb1->TickPrevPtr  =  p_tcb; // 该任务的前一个任务也改为当前任务}p_tcb1 = (OS_TCB *)0;     //插入完成,退出 while 循环}}p_spoke->NbrEntries++;             //节拍列表中的元素数目加1}       //更新节拍列表的元素数目的最大记录if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) {p_spoke->NbrEntriesMax = p_spoke->NbrEntries;}p_tcb->TickSpokePtr = p_spoke;       //记录当前任务存放于哪个节拍列表*p_err               = OS_ERR_NONE;//错误类型为“无错误”
}

任务的延时在实际中运用特别多,因为需要暂停一个任务,让任务放弃CPU,延时结束后再继续运行该任务。
如果任务中没有阻塞的话,比该任务优先级低的任务则无法得到CPU的使用权,就无法运行。

延时函数的使用实例具体如下:

void vTaskA( void * pvParameters )
{while (1) {//  ...//  这里为任务主体代码//  .../* 调用相对延时函数,阻塞1000个tick */OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err );}
}

2. OSTimeDlyHMSM()

OSTimeDlyHMSM() 函数与 OSTimeDly() 函数的功能类似,也是用于停止当前任务进行的运行,延时一段时间后再运行。
但是OSTimeDlyHMSM()函数会更加直观,延时多少个小时、分钟、秒、毫秒。

用户若要使用 OSTimeDlyHMSM()函数, 必须将宏OS_CFG_TIME_DLY_HMSM_EN 设为1,该宏定义位于os_cfg.h中。

OSTimeDlyHMSM()函数源码具体如下:

#if OS_CFG_TIME_DLY_HMSM_EN > 0u//如果启用(默认启用)了 OSTimeDlyHMSM() 函数
void  OSTimeDlyHMSM (CPU_INT16U   hours,    //(1)     //延时小时数CPU_INT16U   minutes,  //(2)      //分钟数CPU_INT16U   seconds,  //(3)      //秒数CPU_INT32U   milli,   //(4)       //毫秒数OS_OPT       opt,      //(5)      //选项OS_ERR      *p_err)    //(6)      //返回错误类型
{
#if OS_CFG_ARG_CHK_EN > 0u  //(7)//如果启用(默认启用)了参数检测功能CPU_BOOLEAN  opt_invalid;      //声明变量用于参数检测CPU_BOOLEAN  opt_non_strict;
#endifOS_OPT       opt_time;OS_RATE_HZ   tick_rate;OS_TICK      ticks;CPU_SR_ALLOC();#ifdef OS_SAFETY_CRITICAL   //(8)//如果启用(默认禁用)了安全检测if (p_err == (OS_ERR *)0) {          //如果错误类型实参为空OS_SAFETY_CRITICAL_EXCEPTION();  //执行安全检测异常函数return;                          //返回,不执行延时操作}
#endif#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u    //(9)//如果启用(默认启用)了中断中非法调用检测if (OSIntNestingCtr > (OS_NESTING_CTR)0u){//如果该延时函数是在中断中被调用*p_err = OS_ERR_TIME_DLY_ISR;     //错误类型为“在中断函数中延时”return;                    //返回,不执行延时操作}
#endif/* 当调度器被锁时任务不能延时 */if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0u) { //(10)//如果调度器被锁*p_err = OS_ERR_SCHED_LOCKED;         //错误类型为“调度器被锁”return;                         //返回,不执行延时操作}opt_time = opt & OS_OPT_TIME_MASK; //(11)//检测除选项中与延时时间性质有关的位switch (opt_time) {                    //根据延时选项参数 opt 分类操作caseOS_OPT_TIME_DLY:               //如果选择相对时间(从现在起延时多长时间)case OS_OPT_TIME_TIMEOUT:                //如果选择超时(实际同上)case OS_OPT_TIME_PERIODIC:                         //如果选择周期性延时if (milli == (CPU_INT32U)0u) {                //如果毫秒数为0if (seconds == (CPU_INT16U)0u) {          //如果秒数为0if (minutes == (CPU_INT16U)0u) {      //如果分钟数为0if (hours == (CPU_INT16U)0u) {    //如果小时数为0*p_err = OS_ERR_TIME_ZERO_DLY; //错误类型为“0延时”return;             //(12)//返回,不执行延时操作}}}}
break;case OS_OPT_TIME_MATCH:                 //(13)//如果选择绝对时间(把系统开始运行(OSStart()时做为起点)break;default:                               //(14)//如果选项超出范围*p_err = OS_ERR_OPT_INVALID;                   //错误类型为“选项非法”return;                                       //返回,不执行延时操作}#if OS_CFG_ARG_CHK_EN > 0u                  //(15)
//如果启用(默认启用)了参数检测功能opt_invalid = DEF_BIT_IS_SET_ANY(opt, ~OS_OPT_TIME_OPTS_MASK);//检测除选项位以后其他位是否被置位if (opt_invalid == DEF_YES) {           //(16)//如果除选项位以后其他位有被置位的*p_err = OS_ERR_OPT_INVALID;             //错误类型为“选项非法”return;                            //返回,不执行延时操作}opt_non_strict = DEF_BIT_IS_SET(opt, OS_OPT_TIME_HMSM_NON_STRICT);//(17)//检测有关时间参数取值范围的选项位if (opt_non_strict != DEF_YES) {//如果选项选择了OS_OPT_TIME_HMSM_STRICTif (milli   > (CPU_INT32U)999u) {  //(18)     //如果毫秒数>999*p_err = OS_ERR_TIME_INVALID_MILLISECONDS; //错误类型为“毫秒数不可用”return;             //返回,不执行延时操作}if (seconds > (CPU_INT16U)59u) {    //(19)//如果秒数>59*p_err = OS_ERR_TIME_INVALID_SECONDS;  //错误类型为“秒数不可用”return;                          //返回,不执行延时操作}if (minutes > (CPU_INT16U)59u) {  //(20)//如果分钟数>59*p_err = OS_ERR_TIME_INVALID_MINUTES; //错误类型为“分钟数不可用”return;                            //返回,不执行延时操作}if (hours   > (CPU_INT16U)99u) {    //(21)//如果小时数>99*p_err = OS_ERR_TIME_INVALID_HOURS;   //错误类型为“小时数不可用”return;                            //返回,不执行延时操作}} else {          //如果选项选择了 OS_OPT_TIME_HMSM_NON_STRICTif (minutes > (CPU_INT16U)9999u) {   //(22)//如果分钟数>9999*p_err = OS_ERR_TIME_INVALID_MINUTES; //错误类型为“分钟数不可用”return;                                 //返回,不执行延时操作}if (hours   > (CPU_INT16U)999u) {  //(23)     //如果小时数>999*p_err = OS_ERR_TIME_INVALID_HOURS; //错误类型为“小时数不可用”return;                         //返回,不执行延时操作}}
#endif/*将延时时间转换成时钟节拍数*/tick_rate = OSCfg_TickRate_Hz;      //(24)//获取时钟节拍的频率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;   //(25)//将延时时间转换成时钟节拍数if (ticks > (OS_TICK)0u) {             //(26)//如果延时节拍数>0OS_CRITICAL_ENTER();                         //进入临界段OSTCBCurPtr->TaskState = OS_TASK_STATE_DLY;  //修改当前任务的任务状态为延时状态OS_TickListInsert(OSTCBCurPtr,     //将当前任务插入节拍列表ticks,opt_time,p_err);             //(27)if(*p_err != OS_ERR_NONE) {  //如果当前任务插入节拍列表时出现错误OS_CRITICAL_EXIT_NO_SCHED();            //退出临界段(无调度)return;                                 //返回,不执行延时操作}OS_RdyListRemove(OSTCBCurPtr);     //(28)//从就绪列表移除当前任务OS_CRITICAL_EXIT_NO_SCHED();                 //退出临界段(无调度)OSSched();                         //(29)//任务切换*p_err = OS_ERR_NONE;                         //错误类型为“无错误”} else {                                         //如果延时节拍数=0*p_err = OS_ERR_TIME_ZERO_DLY;       //错误类型为“0延时”}
}
#endif
  • (1):延时时间——小时数。
  • (2):延时时间——分钟数
  • (3):延时时间——秒数
  • (4):延时时间——毫秒数
  • (5):任务延时的可选选项,在os.h中有定义,具体如下:
#define  OS_OPT_TIME_DLY                      DEF_BIT_NONE  
//OS_OPT_TIME_DLY:dly 为相对时间,就是从现在起延时多长时间, 到时钟节拍总计数OSTickCtr = OSTickCtr 当前 + dly 时延时结束。
#define  OS_OPT_TIME_TIMEOUT                ((OS_OPT)DEF_BIT_01)
//OS_OPT_TIME_TIMEOUT:跟 OS_OPT_TIME_DLY 的作用情况一样。
#define  OS_OPT_TIME_MATCH                  ((OS_OPT)DEF_BIT_02)
//OS_OPT_TIME_MATCH:dly为绝对时间, 就是从系统开始运行(调用 OSStart())时到节拍总计数OSTickCtr = dly 时延时结束。
#define  OS_OPT_TIME_PERIODIC               ((OS_OPT)DEF_BIT_03)
//OS_OPT_TIME_PERIODIC:周期性延时, 跟 OS_OPT_TIME_DLY的作用差不多,如果是长时间延时,该选项更精准一些。#define  OS_OPT_TIME_HMSM_STRICT            ((OS_OPT)DEF_BIT_NONE)
/*延时时间取值比较严格:
小时数hours: (0-99)
分钟数minutes: (0-59)
秒数seconds: (0-59)
毫秒数milliseconds: (0-999)
*/
#define  OS_OPT_TIME_HMSM_NON_STRICT        ((OS_OPT)DEF_BIT_04)
/*延时时间取值比较宽松。
小时数hours: (0-999)
分钟数minutes: (0-9999)
秒数seconds: (0-65535)
毫秒数milliseconds: (0-4294967295)
*/
  • (6):用于存放返回错误代码,如果挂起任务失败,则返回对应的错误代码。
  • (7):如果启用(默认启用)了参数检测功能,则定义一些变量用于参数检测。
  • (8):如果启用(默认禁用)了安全检测,就会包含安全检测的代码, 如果错误类型实参为空,执行安全检测异常函数,然后返回,不执行延时操作。
  • (9):如果启用(默认启用)了中断中非法调用检测, 并且如果该延时函数是在中断中被调用,则被视为非法,返回错误类型为“在中断函数中延时”的错误,然后返回,不执行延时操作。
  • (10):当调度器被锁时任务不能延时,任务延时后会进行任务调度,如果调度器被锁, 就会返回错误类型为“调度器被锁”的错误,然后返回,不执行延时操作。
  • (11):检测除选项中与延时时间性质有关的位,并且根据延时选项参数 opt 分类操作。
  • (12):如果选择相对延时(从现在起延时多长时间)、 超时延时、周期性延时等延时类型,就会检测一下延时的时间是多 少,如果是0,则是不允许的,返回错误类型为“0延时”的错误,不进行延时操作。
  • (13):如果选择绝对时间,会把系统开始运行OSStart()时做为起点。
  • (14):如果选项超出范围,返回错误类型为“选项非法”的错误,然后退出,不进行延时操作。
  • (15):如果启用(默认启用)了参数检测功能,就会检测除选项位以外其他位是否被置位。
  • (16):如果除选项位外其他位有被置位的,则返回错误类型为“选项非法”的错误,然后退出,不执行延时操作。
  • (17):检测有关时间参数取值范围的选项位, 如果选项选择了 OS_OPT_TIME_HMSM_STRICT,就是比较严格的参数范围。
  • (18):如果毫秒数大于999,返回错误类型为“毫秒数不可用”的错误,然后退出,不执行延时操作。
  • (19):如果如果秒数大于59,返回错误类型为“秒数不可用”的错误,然后退出,不执行延时操作。
  • (20):如果分钟数大于59,返回错误类型为“分钟数不可用”的错误,然后退出,不执行延时操作。
  • (21):如果小时数大于99,返回错误类型为“小时数不可用”的错误,然后退出,不执行延时操作。
  • (22):而如果选项选择了 OS_OPT_TIME_HMSM_ NON_STRICT, 就是比较宽松的延时操作, 如果分钟数大于9999,返回错误类型为“分钟数不可用”的错误,然后退出,不执行延时操作。
  • (23):如果小时数大于999,返回错误类型为“小时数不可用”的错误,然后退出,不执行延时操作。
  • (24):因为我们延时的时间是时、分、秒、毫秒,但是系统的时间单位是时钟节拍, 所以需要将延时时间转换成时钟节拍数,首先获取时钟节拍的频率。
  • (25):然后根据我们延时的时间进行计算转换,将延时时间转换成时钟节拍数tick。
  • (26):如果延时节拍数大于0,表示可以延时,修改当前任务的任务状态为延时状态。
  • (27):调用OS_TickListInsert()函数将当前任务插入节拍列表。
  • (28):调用OS_RdyListRemove()函数从就绪列表移除当前任务。
  • (29):进行一次任务切换。

任务延时函数OSTimeDlyHMSM()的使用实例具体如下:

void vTaskA( void * pvParameters )
{while (1) {//  ...//  这里为任务主体代码//  .../* 调用延时函数,延时1s */OSTimeDlyHMSM(0,0,1,0, OS_OPT_TIME_DLY, & err );}
}

这篇关于(学习日记)2024.03.28:UCOSIII第二十五节:常见任务管理函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/870323

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学