任务创建、删除、挂起、恢复
OS_TCB Task1_TaskTCB; //OS_TCB任务控制块 void task1_task(void *p_arg); //入口函数 #define TASK1_TASK_PRIO 5 //优先级 #define TASK1_STK_SIZE 64 //堆栈长度 CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE]; //堆栈数组
OSTaskCreate //创建 OSTaskDel((OS_TCB*)0,&err); //删除 start_task任务自身 OSTaskSuspend((OS_TCB*)&Task2_TaskTCB,&err); //挂起 OSTaskResume((OS_TCB*)&Task2_TaskTCB,&err); //恢复
一、UCOSIII启动与初始化:三步
1.初始化UCOSIII
OSInit(&err); //1初始化UCOSIII
2.创建开始任务
只创建一个start_task任务,其余在start_task这个任务里面创建
①进入临界区
②创建任务
③退出临界区
OS_CRITICAL_ENTER();//2进入临界区
OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块 (CPU_CHAR * )"start task", //任务名字 (OS_TASK_PTR )start_task, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )START_TASK_PRIO, //任务优先级 (CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err); //存放该函数错误时的返回值
OS_CRITICAL_EXIT(); //4退出临界区
3.开启UCOSIII
OSStart(&err); //5开启UCOSIII
OSInit(&err); //1初始化UCOSIII OS_CRITICAL_ENTER();//2进入临界区 //3创建开始任务 只创建一个start_task任务,其余在start_task这个任务里面创建 OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块 (CPU_CHAR * )"start task", //任务名字 (OS_TASK_PTR )start_task, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )START_TASK_PRIO, //任务优先级 (CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err); //存放该函数错误时的返回值 OS_CRITICAL_EXIT(); //4退出临界区 OSStart(&err); //5开启UCOSIII
二、创建任务:
任务控制块OS_TCB 用来保存任务的信息
任务堆栈CPU_STK 用来在切换和调用其它函数的时候保存现场
其实 CPU_STK就是 CPU_INT32,因此 任务的 实际堆栈大小应该为我们定义任务的 4倍
任务优先级 优先级位 映射表OSPrioTbl[] 管理优先级位
一个优先级可以有多个任务,由就绪任务列表管理 OSRdyList[]
//UCOSIII中以下优先级用户程序不能使用
//将这些优先级分配给了UCOSIII的5个系统内部任务
//优先级0:中断服务服务管理任务 OS_IntQTask()
//优先级1:时钟节拍任务 OS_TickTask()
//优先级2:定时任务 OS_TmrTask()
//优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask()
//优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()
总优先级数定义:OS_CFG_PRIO_MAX
//开始任务函数 void start_task(void *p_arg) { OS_ERR err; CPU_SR_ALLOC(); p_arg = p_arg; CPU_Init(); OS_CRITICAL_ENTER(); //进入临界区 //TCB 名字 函数入口 //优先级 堆栈 OSTaskCreate((OS_TCB * )&Task1_TaskTCB, (CPU_CHAR * )"task1 task", (OS_TASK_PTR )task1_task, (void * )0, (OS_PRIO )TASK1_TASK_PRIO, (CPU_STK * )&TASK1_TASK_STK[0], (CPU_STK_SIZE)TASK1_STK_SIZE/10, (CPU_STK_SIZE)TASK1_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err); OS_CRITICAL_EXIT(); //进入临界区 OSTaskDel((OS_TCB*)0,&err);//删除start_task任务自身 printf("创建完任务1、任务2, 并删除start_task任务自身\n"); }
三、任务调度与切换
UCOSIII可剥夺、可抢占式的
任务调度器:
1、任务级调度器:不同优先级的任务
2、中断级调度器:
3、时间片轮转调度:相同优先级的任务
/*任务级切换函数*/ void OSSched (void) { CPU_SR_ALLOC(); //(1)检查本函数是否在中断函数中被调用,因为任务级调度器不能用于中断级任务调度 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* ISRs still nested? */ return; /* Yes ... only schedule when no nested ISRs */ } //(2)检查调度器是否加锁,加锁了就不能做任务调度和切换 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Scheduler locked? */ return; /* Yes */ } //(3)关中断 CPU_INT_DIS(); //(4)获取 任务就续表中 已就绪的最高优先级任务 OSPrioHighRdy = OS_PrioGetHighest(); /* Find the highest priority ready */ //(5)获取该优先级下的任务列表中的第一个任务的OS_TCB OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; //(6)判断该任务是否已经是正在运行的任务,是就不用再任务切换了 if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task is still highest priority task? */ CPU_INT_EN(); /* Yes ... no need to context switch */ return; } //(7)真正执行 任务切换 :宏定义函数 OS_TASK_SW(); /* Perform a task level context switch */ //(8)开中断 CPU_INT_EN(); }
/*中断级切换函数*/ void OSIntExit (void) { CPU_SR_ALLOC(); //(1)判断UCOSIII是否运行,否,则退出 if (OSRunning != OS_STATE_OS_RUNNING) { /* Has the OS started? */ return; /* No */ } //关中断 CPU_INT_DIS(); //OSIntNestingCtr中断嵌套计数器,记录中断嵌套次数 //进入中断服务函数,调用OSIntEnter()函数,OSIntNestingCtr++; //退出中断服务函数时 调用OSIntExit(),OSIntNestingCtr-- //检查OSIntNestingCtr是否为0,确保退出中断服务函数时调用OSIntExit() 不会等于负数 if (OSIntNestingCtr == (OS_NESTING_CTR)0) { /* Prevent OSIntNestingCtr from wrapping */ CPU_INT_EN(); return; } //退出中断服务函数时 调用OSIntExit(),OSIntNestingCtr-- OSIntNestingCtr--; //如果OSIntNestingCtr还大于0,说明还有其他中断发生,就跳回中断服务程序,不需要做任务切换 if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* ISRs still nested? */ CPU_INT_EN(); /* Yes */ return; } //检查调度器是否加锁,加锁就直接跳出,不需要做任务切换 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Scheduler still locked? */ CPU_INT_EN(); /* Yes */ return; } //获取 任务就续表中 已就绪的最高优先级任务 OSPrioHighRdy = OS_PrioGetHighest(); /* Find highest priority */ OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; /* Get highest priority task ready-to-run */ if (OSTCBHighRdyPtr == OSTCBCurPtr) { /* Current task still the highest priority? */ CPU_INT_EN(); /* Yes */ return; } //调用中断级任务切换函数 OSIntCtxSw(); /* Perform interrupt level ctx switch */ CPU_INT_EN(); }
/*时间片轮转调度器*/ #if OS_CFG_SCHED_ROUND_ROBIN_EN > 0u void OS_SchedRoundRobin (OS_RDY_LIST *p_rdy_list) { OS_TCB *p_tcb; CPU_SR_ALLOC(); //时间片轮转调度en 是否允许 if (OSSchedRoundRobinEn != DEF_TRUE) { /* Make sure round-robin has been enabled */ return; } CPU_CRITICAL_ENTER(); //获取某一优先级下就绪任务列表中的第一个任务 p_tcb = p_rdy_list->HeadPtr; /* Decrement time quanta counter */ //如果p_tcb为空,即没有任务就绪,就直接退出,返回了 if (p_tcb == (OS_TCB *)0) { CPU_CRITICAL_EXIT(); return; } //如果p_tcb里只有系统设定的空闲任务,那么也可以退出返回了 if (p_tcb == &OSIdleTaskTCB) { CPU_CRITICAL_EXIT(); return; } //TimeQuantaCtr 当前任务时间片的剩余时间 if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { p_tcb->TimeQuantaCtr--; } //当前任务时间片还没用完,就不能任务切换,直接返回 if (p_tcb->TimeQuantaCtr > (OS_TICK)0) { /* Task not done with its time quanta */ CPU_CRITICAL_EXIT(); return; } //当前优先级下的任务数量,任务太少,小于2,即1个就不需要做任务切换,直接返回 if (p_rdy_list->NbrEntries < (OS_OBJ_QTY)2) { /* See if it's time to time slice current task */ CPU_CRITICAL_EXIT(); /* ... only if multiple tasks at same priority */ return; } //调度器是否上锁,上锁就无法任务切换,直接返回 if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can't round-robin if the scheduler is locked */ CPU_CRITICAL_EXIT(); return; } //双向链表 //改变p_rdy_list->头指针 指向 该优先级下的 下一个OS_TCB任务, //现在已经执行完的OS_TCB任务指针移到队尾 OS_RdyListMoveHeadToTail(p_rdy_list); /* Move current OS_TCB to the end of the list */ //指向了下一个OS_TCB p_tcb = p_rdy_list->HeadPtr; /* Point to new OS_TCB at head of the list */ //如果TimeQuanta=0,就TimeQuantaCtr = 默认时间片值 if (p_tcb->TimeQuanta == (OS_TICK)0) { /* See if we need to use the default time slice */ p_tcb->TimeQuantaCtr = OSSchedRoundRobinDfltTimeQuanta; //默认值是20 } else { p_tcb->TimeQuantaCtr = p_tcb->TimeQuanta; /* Load time slice counter with new time */ } CPU_CRITICAL_EXIT(); }
四、任务删除
OSTaskDel((OS_TCB*)&Task2_TaskTCB,&err);
五、任务挂起与恢复
OSTaskSuspend((OS_TCB*)&Task2_TaskTCB,&err);
OSTaskResume((OS_TCB*)&Task2_TaskTCB,&err);