系列文章
文章目录
前言
在前面的两篇文章中,我重点分析了内核部分的实现以及原理,现在开始看一看外部的一些实现。
本章所讲的内容包括如何在用户的应用程序中建立任务、删除任务、改变任务的优先级、挂起和恢复任务,以及获得有关任务的信息。
µC/OS-Ⅱ可以管理多达64个任务,并从中保留了四个最高优先级和四个最低优先级的任务供自己使用,所以用户可以使用的只有56个任务。任务的优先级越高,反映优先级的值则越低。在最新的µC/OS-Ⅱ版本中,任务的优先级数也可作为任务的标识符使用。
(一)重提任务
任务可以是一个无限的循环,也可以在一次执行完毕后被删除。
这里需要注意的是,任务的代码并不是真正的删除了,而是UCOSII不再理会该任务代码,所以该任务代码不会再执行。
(二)核心API源码分析
通过源码分析我们可以发现UCOSii是将优先级作为任务的主要标志,操作主要以优先级作为参数,在不支持时间片的RTOS中一般可以这么干,但是如果,需要时间片轮训的话就不能这样了。所以UCOSIII一项重要的改变就是不在以优先级作为主要识别参数。
1.建立任务,OSTaskCreate()
想让µC/OS-Ⅱ管理用户的任务,用户必须要先建立任务。用户可以通过传递任务地址和其它参数到以下两个函数之一来建立任务:OSTaskCreate() 或 OSTaskCreateExt()。OSTaskCreate()与µC/OS是向下兼容的,OSTaskCreateExt()是OSTaskCreate()的扩展版本,提供了一些附加的功能。用两个函数中的任何一个都可以建立任务。任务可以在多任务调度开始前建立,也可以在其它任务的执行过程中被建立。在开始多任务调度(即调用OSStart())前,用户必须建立至少一个任务。任务不能由中断服务程序(ISR)来建立。
OSTaskCreate()需要四个参数:
task是任务代码的指针,
pdata是当任务开始执行时传递给任务的参数的指针,
ptos是分配给任务的堆栈的栈顶指针(参看4.02,任务堆栈),
prio是分配给任务的优先级
124 *************************************************************************************************
125 * 建立一个新任务(CREATE A TASK)
126 *
127 * 描述: 建立一个新任务。任务的建立可以在多任务环境启动之前,也可以在正在运行的任务中建立.中断
128 * 处理程序中不能建立任务.一个任务必须为无限循环结构(如下所示),且不能有返回点。
129 * OSTaskCreate()是为与先前的μC/OS版本保持兼容,新增的特性在OSTaskCreateExt()函数中.
130 * 无论用户程序中是否产生中断,在初始化任务堆栈时,堆栈的结构必须与CPU中断后寄存器入栈的
131 * 顺序结构相同.详细说明请参考所用处理器的手册。
132 *
133 * 参数: task 是指向任务代码的指针。
134 *
135 * pdata 指向一个数据结构,该结构用来在建立任务时向任务传递参数。下例中说明uC/OS中的任
136 * 务结构以及如何传递参数pdata:
137 * void Task (void *pdata)
138 * {
139 * ... // 对参数'pdata'进行操作
140 * for (;;) { // 任务函数体.
141 * ...
142 * ...
143 * // 在任务体中必须调用如下函数之一:
144 * // OSMboxPend() 用于任务等待消息,消息通过中断或另外的任务发送给需要的任务
145 * // OSFlgPend() 用于任务等待事件标志中的事件标志
146 * // OSMutexPend() 任务需要独占资源
147 * // OSQPend() 用于任务等待消息
148 * // OSSemPend() 用于任务试图取得共享资源的使用权,任务需要与其它任务或中断
149 * 同步及任务需要等待特定事件的发生场合
150 * // OSTimeDly() 任务延时若干时钟节拍
151 * // OSTimeDlyHMSM() 任务延时若干时间
152 * // OSTaskSuspend() 挂起任务本身
153 * // OSTaskDel() 删除任务本身
154 * ...如果一个都没有用的话那么如果这个优先级最高的话,或者其他任务没有主动抢占的话
155 * ...那么CPU就会一直在这里运行
156 * }
157 * ptos 为指向任务堆栈栈顶的指针。任务堆栈用来保存局部变量,函数参数,返回地址以及任务被
158 * 中断时的CPU寄存器内容.任务堆栈的大小决定于任务的需要及预计的中断嵌套层数。计算
159 * 堆栈的大小,需要知道任务的局部变量所占的空间,可能产生嵌套调用的函数,及中断嵌套
160 * 所需空间。如果初始化常量OS_STK_GROWTH设为1,堆栈被设为从内存高地址向低地址增长,
161 * 此时ptos应该指向任务堆栈空间的最高地址。反之,如果OS_STK_GROWTH设为0,堆栈将从内
162 * 存的低地址向高地址增长。
163 *
164 * prio 为任务的优先级。每个任务必须有一个唯一的优先级作为标识。数字越小,优先级越高。
165 *
166 * 返回: OS_NO_ERR 函数调用成功;
167 * OS_PRIO_EXIT 具有该优先级的任务已经存在;
168 * OS_PRIO_INVALID 参数指定的优先级大于OS_LOWEST_PRIO; (i.e. >= OS_LOWEST_PRIO)
169 * OS_NO_MORE_TCB 系统中没有OS_TCB可以分配给任务了。
170 *
171 * 注意: 1、任务堆栈必须声明为OS_STK类型。
172 * 2、在任务中必须调用uC/OS提供的下述过程之一:延时等待、任务挂起、等待事件发生(等待信
173 * 号量,消息邮箱、消息队列),以使其他任务得到CPU。
174 * 3、用户程序中不能使用优先级0,1,2,3,以及OS_LOWEST_PRIO-3, OS_LOWEST_PRIO-2,
175 * OS_LOWEST_PRIO-1, OS_LOWEST_PRIO。这些优先级μC/OS系统保留,其余的56个优先级提供给
176 * 应用程序。
177 *************************************************************************************************
178 */
179
180 #if OS_TASK_CREATE_EN > 0 //允许生成OSTaskCreate()函数
181 INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)
182 { //建立任务(任务代码指针、传递参数指针、分配任务堆栈栈顶指针、任务优先级)
183 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
184 OS_CPU_SR cpu_sr;
185 #endif
186 OS_STK *psp; //初始化任务堆栈指针变量,返回新的栈顶指针
187 INT8U err; //定义(获得并定义初始化任务控制块)是否成功
188
189 #if OS_ARG_CHK_EN > 0 //所有参数必须在指定的参数内
190 if (prio > OS_LOWEST_PRIO) { //检查任务优先级是否合法
191 return (OS_PRIO_INVALID); //参数指定的优先级大于OS_LOWEST_PRIO
192 }
193 #endif
194 OS_ENTER_CRITICAL(); //关闭中断
195 if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { //确认优先级未被使用,即就绪态为0
196 OSTCBPrioTbl[prio] = (OS_TCB *)1; //保留这个优先级,将就绪态设为0
197
198 OS_EXIT_CRITICAL(); //打开中断
199 psp = (OS_STK *)OSTaskStkInit(task, pdata, ptos, 0); //初始化任务堆栈
200 err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0); //获得并初始化任务控制块
201 if (err == OS_NO_ERR) { //任务控制初始化成功
202 OS_ENTER_CRITICAL(); //关闭中断
203 OSTaskCtr++; //任务计数器加1
204 OS_EXIT_CRITICAL(); //打开中断
205 if (OSRunning == TRUE) { //检查是否有(某个)任务在运行
206 OS_Sched(); //任务调度,最高任务优先级运行
207 }
208 } else { //否则,任务初始化失败
209 OS_ENTER_CRITICAL(); //关闭中断
210 OSTCBPrioTbl[prio] = (OS_TCB *)0; //放弃任务,设此任务就绪态为0
211 OS_EXIT_CRITICAL(); //打开中断
212 }
213 return (err); //返回(获得并定义初始化任务控制块是否成功)
214 }
215 OS_EXIT_CRITICAL(); //打开中断
216 return (OS_PRIO_EXIST); //返回(具有该优先级的任务已经存在)
217 }
218 #endif
OSTaskCreate()一开始先检测分配给任务的优先级是否有效。任务的优先级必须在0到OS_LOWEST_PRIO之间。接着,OSTaskCreate()要确保在规定的优先级上还没有建立任务。在使用µC/OS-Ⅱ时,每个任务都有特定的优先级。如果某个优先级是空闲的,µC/OS-Ⅱ通过放置一个非空指针在OSTCBPrioTbl[]中来保留该优先级。这就使得OSTaskCreate()在设置任务数据结构的其他部分时能重新允许中断。
然后,OSTaskCreate()调用OSTaskStkInit()[L4.1(5)],它负责建立任务的堆栈。该函数是与处理器的硬件体系相关的函数,可以在OS_CPU_C.C文件中找到。有关实现OSTaskStkInit()的细节可参看第8章——移植µC/OS-Ⅱ。如果已经有人在你用的处理器上成功地移植了µC/OS-Ⅱ,而你又得到了他的代码,就不必考虑该函数的实现细节了。OSTaskStkInit()函数返回新的堆栈栈顶(psp),并被保存在任务的0S_TCB中。注意用户得将传递给OSTaskStkInit()函数的第四个参数opt置0,因为OSTaskCreate()与OSTaskCreateExt()不同,它不支持用户为任务的创建过程设置不同的选项,所以没有任何选项可以通过opt参数传递给OSTaskStkInit()。
µC/OS-Ⅱ支持的处理器的堆栈既可以从上(高地址)往下(低地址)递减也可以从下往上递增。用户在调用OSTaskCreate()的时候必须知道堆栈是递增的还是递减的(参看所用处理器的OS_CPU.H中的OS_STACK_GROWTH),因为用户必须得把堆栈的栈顶传递给OSTaskCreate(),而栈顶可能是堆栈的最高地址(堆栈从上往下递减),也可能是最低地址(堆栈从下往上长)。
一旦OSTaskStkInit()函数完成了建立堆栈的任务,OSTaskCreate()就调用OSTCBInit()[L4.1(6)],从空闲的OS_TCB池中获得并初始化一个OS_TCB。OSTCBInit()的代码如程序清单 L4.2所示,它存在于0S_CORE.C文件中而不是OS_TASK.C文件中。OSTCBInit()函数首先从OS_TCB缓冲池中获得一个OS_TCB,如果OS_TCB池中有空闲的OS_TCB[L4.2(2)],它就被初始化。注意一旦OS_TCB被分配,该任务的创建者就已经完全拥有它了,即使这时内核又创建了其它的任务,这些新任务也不可能对已分配的OS_TCB作任何操作,所以OSTCBInit()在这时就可以允许中断,并继续初始化OS_TCB的数据单元。
OSTCBInit()
INT8U OS_TCBInit (INT8U prio,
OS_STK *ptos,
OS_STK *pbos,
INT16U id,
INT32U stk_size,
void *pext,
INT16U opt)
{
OS_TCB *ptcb;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_TASK_REG_TBL_SIZE > 0u
INT8U i;
#endif
OS_ENTER_CRITICAL();
ptcb = OSTCBFreeList; /* Get a free TCB from the free TCB list */
if (ptcb != (OS_TCB *)0) {
OSTCBFreeList = ptcb->OSTCBNext; /* Update pointer to free TCB list */
OS_EXIT_CRITICAL();
ptcb->OSTCBStkPtr = ptos; /* Load Stack pointer in TCB */
ptcb->OSTCBPrio = prio; /* Load task priority into TCB */
ptcb->OSTCBStat = OS_STAT_RDY; /* Task is ready to run */
ptcb->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */
ptcb->OSTCBDly = 0u; /* Task is not delayed */
#if OS_TASK_CREATE_EXT_EN > 0u
ptcb->OSTCBExtPtr = pext; /* Store pointer to TCB extension */
ptcb->OSTCBStkSize = stk_size; /* Store stack size */
ptcb->OSTCBStkBottom = pbos; /* Store pointer to bottom of stack */
ptcb->OSTCBOpt = opt; /* Store task options */
ptcb->OSTCBId = id; /* Store task ID */
#else
pext = pext; /* Prevent compiler warning if not used */
stk_size = stk_size;
pbos = pbos;
opt = opt;
id = id;
#endif
#if OS_TASK_DEL_EN > 0u
ptcb->OSTCBDelReq = OS_ERR_NONE;
#endif
#if OS_LOWEST_PRIO <= 63u /* Pre-compute X, Y */
ptcb->OSTCBY = (INT8U)(prio >> 3u);
ptcb->OSTCBX = (INT8U)(prio & 0x07u);
#else /* Pre-compute X, Y */
ptcb->OSTCBY = (INT8U)((INT8U)(prio >> 4u) & 0xFFu);
ptcb->OSTCBX = (INT8U) (prio & 0x0Fu);
#endif
/* Pre-compute BitX and BitY */
ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);
ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);
#if (OS_EVENT_EN)
ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Task is not pending on an event */
#if (OS_EVENT_MULTI_EN > 0u)
ptcb->OSTCBEventMultiPtr = (OS_EVENT **)0; /* Task is not pending on any events */
#endif
#endif
#if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u) && (OS_TASK_DEL_EN > 0u)
ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0; /* Task is not pending on an event flag */
#endif
#if (OS_MBOX_EN > 0u) || ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u))
ptcb->OSTCBMsg = (void *)0; /* No message received */
#endif
#if OS_TASK_PROFILE_EN > 0u
ptcb->OSTCBCtxSwCtr = 0uL; /* Initialize profiling variables */
ptcb->OSTCBCyclesStart = 0uL;
ptcb->OSTCBCyclesTot = 0uL;
ptcb->OSTCBStkBase = (OS_STK *)0;
ptcb->OSTCBStkUsed = 0uL;
#endif
#if OS_TASK_NAME_EN > 0u
ptcb->OSTCBTaskName = (INT8U *)(void *)"?";
#endif
#if OS_TASK_REG_TBL_SIZE > 0u /* Initialize the task variables */
for (i = 0u; i < OS_TASK_REG_TBL_SIZE; i++) {
ptcb->OSTCBRegTbl[i] = 0u;
}
#endif
OSTCBInitHook(ptcb);
OSTaskCreateHook(ptcb); /* Call user defined hook */
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = ptcb;
ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */
ptcb->OSTCBPrev = (OS_TCB *)0;
if (OSTCBList != (OS_TCB *)0) {
OSTCBList->OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OSTaskCtr++; /* Increment the #tasks counter */
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
OS_EXIT_CRITICAL();
return (OS_ERR_TASK_NO_MORE_TCB);
}
重点关注:
OSTCBPrioTbl[prio] = ptcb;
ptcb->OSTCBNext = OSTCBList;
ptcb->OSTCBPrev = (OS_TCB *)0;
if (OSTCBList != (OS_TCB *)0) {
OSTCBList->OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
这是一个典型的双向链表的头插法。
重点在第一句,该记得之前OS_Sched()函数在确定优先级后就是通过这个数组定位到TCB块的吗。这里用一个数组将每一个TCB块填入对应的优先级,使用的时候就会方便很多。
2.创建任务堆栈的两种方式
任务的堆栈可以使用静态的堆栈生成,也可以使用动态的堆栈生成。
(1)静态堆栈:
static OS_STK MyTaskStack[stack_size];
(2)动态堆栈:
OS_STK *pstk;
pstk = (OS_STK *)malloc(stack_size);
if(pstk != (OS_STK *)0)
{
//确保malloc能够得到足够的内存空间
}
(3)UCOSII支持的堆栈可以是递减的,也可以是递增的。
在调用函数OS_TaskCreate(),必须知道堆栈是递减的,还是递增的。
因为必须把堆栈的栈顶地址传递给上面的两个函数。
PS:这里面就有OS_CPU.h文件中的OS_STK_GROWTH为0,需要将堆栈的最低地址传递给任务创建的函数。
这个是堆栈从下往上增长的:
OS_STK TaskStack[TASK_STACK_SIZE];
OSTaskCreate(task, pdata, &TaskStack[0], prio);
这个是堆栈从上往下增长的:
OS_STK TaskStack[TASK_STACK_SIZE];
OSTaskCreate(task, pdata, &TaskStack[TASK_STACK_SIZE-1], prio);
3.删除任务 OSTaskDel()
有时候删除任务是很有必要的。删除任务,是说任务将返回并处于休眠状态(参看3.02,任务状态),并不是说任务的代码被删除了,只是任务的代码不再被µC/OS-Ⅱ调用。通过调用OSTaskDel()就可以完成删除任务的功能
INT8U OSTaskDel (INT8U prio)
359 *************************************************************************************************
360 * 删除一个指定优先级的任务(DELETE A TASK)
361 *
362 * 描述: 删除一个指定优先级的任务。任务可以传递自己的优先级给OSTaskDel(),从而删除自身.如果任
363 * 务不知道自己的优先级,还可以传递参数OS_PRIO_SELF.被删除的任务将回到休眠状态.任务被删
364 * 除后可以用函数OSTaskCreate()或OSTaskCreateExt()重新建立.
365 *
366 * 参数: prio 为指定要删除任务的优先级,也可以用参数OS_PRIO_SELF代替,此时,下一个优先级最高
367 * 的就绪任务将开始运行。
368 *
369 * 返回: OS_NO_ERR:函数调用成功;
370 *? OS_TASK_DEL_IDLE:错误操作,试图删除空闲任务(Idle task);
371 *? OS_TASK_DEL_ ERR:错误操作,指定要删除的任务不存在;
372 *? OS_PRIO_INVALID:参数指定的优先级大于OS_LOWEST_PRIO;
373 *? OS_TASK_DEL_ISR:错误操作,试图在中断处理程序中删除任务.
374 *
375 *
376 * 注意: 1、OSTaskDel()将判断用户是否试图删除uC/OS中的空闲任务(Idle task);
377 * 2、在删除占用系统资源的任务时要小心,此时,为安全起见可以用另一个函数OSTaskDelReq().
378
379 *************************************************************************************************
380 */
381 /*$PAGE*/?
382 #if OS_TASK_DEL_EN > 0 //允许生成 OSTaskDel()函数
383 INT8U OSTaskDel (INT8U prio) //删除任务(任务的优先级)
384 {
385 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
386 OS_CPU_SR cpu_sr;
387 #endif
388
389 #if OS_EVENT_EN > 0 //消息事件是否 > 0
390 OS_EVENT *pevent; //定义事件指针
391 #endif
392 //版本是否 > 2.51 并且 允许产生事件标志相关代码 并且应用中最多事件标志组的数目 > 0
393 #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
394 OS_FLAG_NODE *pnode; //定义标志节点
395 #endif
396 OS_TCB *ptcb; //定义消息事件的任务控制块指针
397
398
399
400 if (OSIntNesting > 0) { //当前中断嵌套 > 0时,表示还有中断程序运行
401 return (OS_TASK_DEL_ISR); //错误操作,试图在中断处理程序中删除任务
402 }
403 #if OS_ARG_CHK_EN > 0 //所有参数必须在指定的参数内
404 if (prio == OS_IDLE_PRIO) { //检查任务优先级是否是空闲任务
405 return (OS_TASK_DEL_IDLE); //错误操作,试图删除空闲任务(Idle task)
406 } //当任务 >= 最低优先级 并且 任务不是本身
407 if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { //检查任务是否是合法的
408 return (OS_PRIO_INVALID); //参数指定的优先级大于OS_LOWEST_PRIO
409 }
410 #endif
411 OS_ENTER_CRITICAL(); //关闭中断
412 if (prio == OS_PRIO_SELF) { //检查的删除者是否是任务(优先级)本身
413 prio = OSTCBCur->OSTCBPrio; //正在运行的优先级(任务本身的优先级)
414 } //删除的任务必须存在
415 if ((ptcb = OSTCBPrioTbl[prio]) != (OS_TCB *)0) { //调用这个任务的优先级的就绪值
416 if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0x00) { //当就绪=1(即就绪状态)
417 OSRdyGrp &= ~ptcb->OSTCBBitY; //该任务处于就绪值,从就绪表中去除
418 }
419 #if OS_EVENT_EN > 0 //消息事件是否 > 0
420 pevent = ptcb->OSTCBEventPtr; //拾取该任务的事件控制指针
421 if (pevent != (OS_EVENT *)0) {
422 //指向事件控制块的指针是否为Null
423 if ((pevent->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
424 //事件(消息)在等待表中将自己所处的表中删除
425 pevent->OSEventGrp &= ~ptcb->OSTCBBitY;
426 }
427 }
428 #endif
429 //版本是否 > 2.51 并且 允许产生事件标志相关代码 并且应用中最多事件标志组的数目 > 0
430 #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0)
431 pnode = ptcb->OSTCBFlagNode; //标志节点 = 指向事件标志节点的指针
432 if (pnode != (OS_FLAG_NODE *)0) { //如果任务处于事件标志的等待表中
433 OS_FlagUnlink(pnode); //就从此表中删除
434 }
435 #endif
436 ptcb->OSTCBDly = 0; //将任务时钟节拍清0,确保重新开中断,中断程序不使该任务就绪
437 ptcb->OSTCBStat = OS_STAT_RDY; //将任务的状态字处于完毕状态
438 if (OSLockNesting < 255) { //如果锁定嵌套计数器 < 255,
439 OSLockNesting++; //锁定嵌套计数器加1,使这个任务处于休眠状态
440 }
441 OS_EXIT_CRITICAL(); //打开中断
442 OS_Dummy(); //增加一个空指令
443 OS_ENTER_CRITICAL(); //关闭中断
444 if (OSLockNesting > 0) { //(可以继续执行删除任务的操作了)
445 OSLockNesting--; //重新关闭中断后,可以通过锁定嵌套计数器减1,重新允许任务调度
446 }
447 OSTaskDelHook(ptcb); //可在钩子程序中加入自定程序
448 OSTaskCtr--; //任务计数器减1,ucos管理的任务减少一个
449 OSTCBPrioTbl[prio] = (OS_TCB *)0; //将被删除的任务控制块OS_TCB指针=Null,
450 //从任务优先级中把OS_TCB删除
451 if (ptcb->OSTCBPrev == (OS_TCB *)0) { //任务块双向链接表的前链接是否Null
452 ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0; //删除该任务的任务控制块OS_TCB
453 OSTCBList = ptcb->OSTCBNext; //链接表指向后一个链接
454 } else { //否则
455 ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext; // ?
456 ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev; // ?
457 } //OSTCBPrev:用于任务块双向链接表的前链接
458 //OSTCBNext:用于任务块双向链接表的后链接
459 //OSTCBFreeList:空任务控制块指针
460 ptcb->OSTCBNext = OSTCBFreeList; //被删除的任务控制块OS_TCB被退回到空闲OS_TCB表中
461 OSTCBFreeList = ptcb; //以供建立其它任务使用
462 OS_EXIT_CRITICAL(); //打开中断
463 OS_Sched(); //任务调度,最高任务优先级运行
464 return (OS_NO_ERR); //函数调用成功
465 }
466 OS_EXIT_CRITICAL(); //打开中断
467 return (OS_TASK_DEL_ERR); //错误操作,指定要删除的任务不存在
468 }
469 #endif
4.任务的挂起与恢复OS_TaskSuspend(),OSTaskResume()
有时候将任务挂起是很有用的。挂起任务可通过调用OSTaskSuspend()函数来完成。被挂起的任务只能通过调用OSTaskResume()函数来恢复。任务挂起是一个附加功能。也就是说,如果任务在被挂起的同时也在等待延时的期满,那么,挂起操作需要被取消,而任务继续等待延时期满,并转入就绪状态。任务可以挂起自己或者其它任务。
OSTaskResume()是通过清除OSTCBStat域中的OS_STAT_SUSPEND位来取消挂起的。要使任务处于就绪状态,OS_TCBDly域必须为0,这是因为在OSTCBStat中没有任何标志表明任务正在等待延时的期满。只有当以上两个条件都满足的时候,任务才处于就绪状态。最后,任务调度程序会检查被恢复的任务拥有的优先级是否比调用本函数的任务的优先级高
这两个函数本质上就是在就绪表上写1或0,让任务切换机制检测不到这个任务的存在。但是这个任务没有消失。
OS_TaskSuspend()
688 /*
689 *************************************************************************************************
690 * 无条件挂起一个任务(SUSPEND A TASK)
691 *
692 * 描述: 无条件挂起一个任务. 调用此函数的任务也可以传递参数OS_PRIO_SELF,挂起调用任务本身.
693 * 当前任务挂起后,只有其他任务才能唤醒.任务挂起后,系统会重新进行任务调度,运行下一个
694 * 优先级最高的就绪任务.唤醒挂起任务需要调用函数OSTaskResume().
695 * //任务的挂起是可以叠加到其他操作上的。例如,任务被挂起时正在进行延时操作,那么任务的
696 * 唤醒就需要两个条件:延时的结束以及其他任务的唤醒操作.又如,任务被挂起时正在等待信
697 * 号量,当任务从信号量的等待对列中清除后也不能立即运行,而必须等到唤醒操作后。
698 *
699 * 参数: prio 为指定要获取挂起的任务优先级,也可以指定参数OS_PRIO_SELF,挂起任务本身.此时,
700 * 下一个优先级最高的就绪任务将运行.
701 *
702 * 返回: OS_NO_ERR: 函数调用成功;
703 *? OS_TASK_ SUSPEND_IDLE: 试图挂起uC/OS-II中的空闲任务(Idle task)。此为非法操作;
704 *? OS_PRIO_INVALID: 参数指定的优先级大于OS_LOWEST_PRIO或没有设定OS_PRIO_SELF的值;
705 *? OS_TASK_ SUSPEND _PRIO:要挂起的任务不存在。
706 *
707 * 注意: 1、在程序中OSTaskSuspend()和OSTaskResume()应该成对使用;
708 * 2、用OSTaskSuspend()挂起的任务只能用OSTaskResume()唤醒.
709 *************************************************************************************************
710 */
711
712 #if OS_TASK_SUSPEND_EN > 0 //允许生成 OSTaskSuspend()函数
713 INT8U OSTaskSuspend (INT8U prio) //无条件挂起一个任务(任务优先级)
714 {
715 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
716 OS_CPU_SR cpu_sr;
717 #endif
718 BOOLEAN self; //定义布尔(=1为任务本身)
719 OS_TCB *ptcb; //定义消息事件的任务控制块指针
720
721
722 #if OS_ARG_CHK_EN > 0 //所有参数必须在指定的参数内
723 if (prio == OS_IDLE_PRIO) { //检查任务的优先级是否是空闲任务
724 return (OS_TASK_SUSPEND_IDLE); //试图挂起uC/OS-II中的空闲任务(Idle task)。此为非法操作
725 } //当任务 >= 最低优先级 并且 任务不是本身
726 if (prio >= OS_LOWEST_PRIO && prio != OS_PRIO_SELF) { //检查任务是否是合法的
727 return (OS_PRIO_INVALID); //参数指定的优先级大于OS_LOWEST_PRIO或没有设定OS_PRIO_SELF的值
728 }
729 #endif
730 OS_ENTER_CRITICAL(); //关闭中断
731 if (prio == OS_PRIO_SELF) { //如果删除者是任务本身
732 prio = OSTCBCur->OSTCBPrio; //从当前任务的任务控制块OS_TCB中获取当前任务的优先级
733 self = TRUE; //是任务本身(为1)
734 } else if (prio == OSTCBCur->OSTCBPrio) { //也可以通过指定优先级,挂起调用本函数的任务
735 self = TRUE; //是任务本身(为1)
736 } else { //否则
737 self = FALSE; //要删除的不是任务本身
738 }
739 if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { //检验要挂起的任务是否存在(为1任务存在)
740 OS_EXIT_CRITICAL(); //打开中断
741 return (OS_TASK_SUSPEND_PRIO); //返回(要挂起的任务不存在)
742 }
743 if ((OSRdyTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0x00) { //如果该任务存在
744 OSRdyGrp &= ~ptcb->OSTCBBitY; //从就绪表中清除它(等待事件的话也删除)
745 }
746 ptcb->OSTCBStat |= OS_STAT_SUSPEND; //在任务的状态字中标明'SUSPENDED',表示任务被挂起
747 OS_EXIT_CRITICAL(); //打开中断
748 if (self == TRUE) { //如果是任务本身(为1)
749 OS_Sched(); //任务调度,最高任务优先级运行
750 //说明:self == TRUE,删除本身,需要进入任务调度执行新的任务
751 //self = FALSE,删除的是其它任务,无需进入调度,可继续执行本任务
752 }
753 return (OS_NO_ERR); //返回(函数调用成功)
754 }
755 #endif
OSTaskResume()
549 /*
550 *************************************************************************************************
551 * 唤醒一个用OSTaskSuspend()函数挂起的任务(RESUME A SUSPENDED TASK)
552 *
553 * 描述: 唤醒一个用OSTaskSuspend()函数挂起的任务。OSTaskResume()也是唯一能"解挂"挂起任务的函数。
554 *
555 * 参数: prio 指定要唤醒任务的优先级。
556 *
557 * 返回: OS_NO_ERR: 函数调用成功;
558 *? OS_TASK_RESUME_PRIO: 要唤醒的任务不存在;
559 *? OS_TASK_NOT_SUSPENDED:要唤醒的任务不在挂起状态;
560 *? OS_PRIO_INVALID: 参数指定的优先级大于或等于OS_LOWEST_PRIO。
561 *************************************************************************************************
562 */
563
564 #if OS_TASK_SUSPEND_EN > 0 //允许生成 OSTaskDel()函数
565 INT8U OSTaskResume (INT8U prio) //唤醒一个用OSTaskSuspend()函数挂起的任务(任务的优先级)
566 {
567 #if OS_CRITICAL_METHOD == 3 //中断函数被设定为模式3
568 OS_CPU_SR cpu_sr;
569 #endif
570 OS_TCB *ptcb; //定义消息事件的任务控制块指针
571
572
573 #if OS_ARG_CHK_EN > 0 //所有参数必须在指定的参数内
574 if (prio >= OS_LOWEST_PRIO) { //当任务 >= 最低优先级
575 return (OS_PRIO_INVALID); //返回(要唤醒的任务不存在)
576 }
577 #endif
578 OS_ENTER_CRITICAL(); //关闭中断
579 if ((ptcb = OSTCBPrioTbl[prio]) == (OS_TCB *)0) { //调用这个任务的优先级的就绪值
580 OS_EXIT_CRITICAL(); //打开中断
581 return (OS_TASK_RESUME_PRIO); //返回(要唤醒的任务不存在)
582 } //任务控制块状态字相与OS_STAT_SUSPEND为1,任务存在
583 if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) != 0x00) { //要恢复的任务必须是存在的
584 //通过清除OSTCBStat域中的OS_STAT_SUSPEND未而取消挂起
585 //要使任务处于就绪状态,OSTCBDly必须是0,这是因为在OSTCBStat中,没有任何标志表明任务正在
586 //等待延时事件到
587 if (((ptcb->OSTCBStat &= ~OS_STAT_SUSPEND) == OS_STAT_RDY) &&
588 (ptcb->OSTCBDly == 0)) {
589 //只有以上两个条件满足,任务才能处于就绪态
590 OSRdyGrp |= ptcb->OSTCBBitY; //保存任务就绪标准0-7到OSRdyGrp
591 OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; //保存任务优先级别0-7到OSRdyTbl[]
592 OS_EXIT_CRITICAL(); //打开中断
593 OS_Sched(); //任务调度,最高任务优先级运行
594 } else { //否则
595 OS_EXIT_CRITICAL(); //打开中断
596 }
597 return (OS_NO_ERR); //返回(函数调用成功)
598 }
599 OS_EXIT_CRITICAL(); //打开中断
600 return (OS_TASK_NOT_SUSPENDED); //返回(要唤醒的任务不在挂起状态)
601 }
602 #endif
(三)基于STM32的任务管理实验
实现任务管理的基本操作,包括任务的创建,挂起恢复与删除
/*
* @Descripttion:
* @version:
* @Author: Yueyang
* @email: 1700695611@qq.com
* @Date: 2020-09-10 13:20:54
* @LastEditors: Yueyang
* @LastEditTime: 2020-09-30 02:10:26
*/
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
#include "includes.h"
#include "usb_lib.h"
#include "hw_config.h"
#include "usb_pwr.h"
#include "24cxx.h"
#include "exti.h"
#include "timer.h"
#include "string.h"
/UCOSII任务设置///
//START 任务
//设置任务优先级
#define START_TASK_PRIO 10 //开始任务的优先级设置为最低
//设置任务堆栈大小
#define START_STK_SIZE 64
//任务堆栈
OS_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *pdata);
#define LED_TASK_PRIO 6
#define LED_STK_SIZE 64
OS_STK LED_TASK_STK[LED_STK_SIZE];
void led_task();
//USB任务
//设置任务优先级
#define USB_TASK_PRIO 5
//设置任务堆栈大小
#define USB_STK_SIZE 128
//任务堆栈
OS_STK USB_TASK_STK[USB_STK_SIZE];
//任务函数
void usb_task(void *pdata);
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
delay_init(); //延时函数初始化
uart_init(115200);
OSInit();
OSTaskCreate(start_task,(void *)0,(OS_STK *)&START_TASK_STK[START_STK_SIZE-1],START_TASK_PRIO );//创建起始任务
OSStart();
}
//开始任务
void start_task(void *pdata)
{
OS_CPU_SR cpu_sr=0;
pdata = pdata;
OS_ENTER_CRITICAL(); //进入临界区(无法被中断打断)
OSTaskCreate(usb_task,(void *)0,(OS_STK*)&USB_TASK_STK[USB_STK_SIZE-1],USB_TASK_PRIO);
OSTaskCreate(led_task,(void *)0,(OS_STK*)&LED_TASK_STK[LED_STK_SIZE-1],LED_TASK_PRIO);
//OSTaskCreate(I2C_task,(void *)0,(OS_STK*)&IIC_TASK_STK[IIC_STK_SIZE-1],IIC_TASK_PRIO);
delay_ms(1800);
USB_Port_Set(0); //USB 先断开
delay_ms(700);
USB_Port_Set(1); //USB 再次连接
Set_USBClock();
USB_Interrupts_Config();
USB_Init();
AT24CXX_Init();
EXTIX_Init();
LED_Init();
TIM3_Int_Init(799,999);
OSTaskDel(START_TASK_PRIO); //挂起起始任务.
OS_EXIT_CRITICAL(); //退出临界区(可以被中断打断)
}
void usb_task(void *pdatac)
{
u8 usbstatus=0;
u16 times=0;
u16 len,t;
u8 tbuf[20];
u8 usb_str[]="usb_task";
INT8U err;
usbstatus=bDeviceState;
while(1){
if(USB_USART_RX_STA&0x8000)
{
len=USB_USART_RX_STA&0x3FFF;//得到此次接收到的数据长度
for(t=0;t<len;t++)
{
USB_USART_SendData(USB_USART_RX_BUF[t]);//以字节方式,发送给USB
}
usb_printf("\r\n\r\n");//插入换行
USB_USART_RX_STA=0;
}else
{
times++;
usb_printf("usb \n");
if(times%20==0)
{
OSTaskSuspend(LED_TASK_PRIO);
usb_printf("I suspend the LED TASK\n");
}
else if(times%30==0)
{
OSTaskResume(LED_TASK_PRIO);
usb_printf("I resume the LED TASK\n");
}
else if(times%50==0)
{
OSTaskDel(LED_TASK_PRIO);
usb_printf("I delete the LED TASK\n");
}
delay_ms(1000);
}
}
}
void led_task()
{
u8 led_str[]="led_task";
INT8U err;
while(1)
{
LED0=!LED0;
usb_printf("led \n");
delay_ms(1000);
}
}
首先创建了一个start任务
在start中分别创建了usb打印任务,和LED闪灯任务
USB任务没20次挂起LED,30次时恢复,50次时删除
运行效果