- 简述:
- 本章之前OS还没有用到优先级,只支持两个任务相互切换;
- 本章之后加入优先级功能,在uC/OS-III中,数字优先级越小,逻辑优先级越高;
- 修改之前如下几个函数:
- OSInit();
- 任务控制块TCB;
- 任务创建OSTaskCreate();
- 空闲函数OS_Idle_TaskInit();
- 系统启动函数OSStart();
- 任务切换PendSV_Handler
- 系统延时OSTimeDly();
- 任务调度OSSched();
- 系统时基OSTimeTick();
- 例程:
- OSInit();
/********************************************************* * 函数名:void OSInit(OS_ERR *p_err); * 描述 :初始化全局变量,就绪列表 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void OSInit(OS_ERR *p_err) { //配置OS初始态为停止态 OSRunning = OS_STATE_OS_STOPPED; //初始化两个全局TCB,这两个TCB用于任务切换; OSTCBCurPtr = (OS_TCB *)0; OSTCBHighRdyPtr = (OS_TCB *)0; //初始化优先级变量 OSPrioCur = (OS_PRIO)0; OSPrioHighRdy = (OS_PRIO)0; //任务就绪列表初始化 OS_RdyListInit(); //初始化空闲任务 OS_IdleTaskInit(p_err); if(*p_err != OS_ERR_NONE) { return; } }
- 任务控制块TCB;
/* ************************************************************************************************************************ * OS_TCB 任务控制块 ************************************************************************************************************************ */ struct os_tcb { CPU_STK *StkPtr; CPU_STK_SIZE StkSize; //任务延时周期个数 OS_TICK TaskDelayTicks; //任务优先级 OS_PRIO Prio; //就绪列表的下一个指针 OS_TCB *NextPtr; //就绪列表的上一个指针 OS_TCB *PrevPtr; };
- 任务创建OSTaskCreate();
/********************************************************* * 函数名: void OSTaskCreate ( OS_TCB *p_tcb, //任务控制块指针 OS_TASK_PTR p_task, //任务函数名 void *parg, //形参,用于传递参数 OS_PRIO prio, //任务优先级 CPU_STK *p_stk_base, //指向任务堆栈的起始地址 CPU_STK_SIZE stk_size, //任务堆栈的大小 OS_ERR *p_err //错误码 ); * 描述 :创建任务 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void OSTaskCreate ( OS_TCB *p_tcb, //任务控制块指针 OS_TASK_PTR p_task, //任务函数名 void *p_arg, //形参,用于传递参数 OS_PRIO prio, //任务优先级 CPU_STK *p_stk_base, //指向任务堆栈的起始地址 CPU_STK_SIZE stk_size, //任务堆栈的大小 OS_ERR *p_err //错误码 ) { CPU_STK *p_sp; //保存CPU中断前的状态 CPU_SR_ALLOC(); //初始化TCB为默认值 OS_TaskInitTCB(p_tcb); //初始化堆栈 p_sp = OSTaskStkInit( p_task, //任务堆栈初始化 p_arg, p_stk_base, stk_size ); p_tcb->Prio = prio; p_tcb ->StkPtr = p_sp; //将剩余栈的栈顶指针p_sp保存到任务控制块TCB的第一个成员StkPtr中; p_tcb ->StkSize = stk_size; //任务堆栈大小保存到任务控制块TCB成员的StkSize中; //进入临界段 OS_CRITICAL_ENTER(); //将任务添加到就绪列表 OS_PrioInsert(p_tcb->Prio); OS_RdyListInsertTail(p_tcb); //退出临界段 OS_CRITICAL_EXIT(); *p_err = OS_ERR_NONE; //函数执行到此表示无错误,返回OS_ERR_NONE; }
- 空闲函数OS_Idle_TaskInit();
/********************************************************* * 函数名:OS_IdleTaskInit(OS_ERR *p_err); * 描述 :空闲任务初始化,放在系统初始化中 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void OS_IdleTaskInit(OS_ERR *p_err) { //初始化空闲任务计数器,设置为0 OSIdleTaskCtr = (OS_IDLE_CTR)0; //创建空闲任务 OSTaskCreate( (OS_TCB *)&OSIdleTaskTCB, (OS_TASK_PTR )OS_IdleTask, (void *)0, (OS_PRIO )(OS_CFG_PRIO_MAX - 1u), (CPU_STK *)OSCfg_IdleTaskStkBasePtr, (CPU_STK_SIZE)OSCfg_IdleTaskStkSize, (OS_ERR *)p_err ); }
- 系统启动函数OSStart();
/********************************************************* * 函数名: void OSStrart(OS_ERR *p_err) * 描述 :系列启动 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void OSStart(OS_ERR *p_err) { if(OSRunning == OS_STATE_OS_STOPPED) { //寻找最高优先级 OSPrioHighRdy = OS_PrioGetHighest(); OSPrioCur = OSPrioHighRdy; //找到最高优先级的TCB OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; OSTCBCurPtr = OSTCBHighRdyPtr; //标记OS 开始运行 OSRunning = OS_STATE_OS_RUNNING; //启动任务切换,不会返回 OSStartHighRdy(); //不会运行到这里,如果运行到这里表示发生了致命错误 *p_err = OS_ERR_FATAL_RETURN; } else { *p_err = OS_STATE_OS_RUNNING; } }
- 任务切换PendSV_Handler
;************************************************************************************************************************ ; PendSVHandler异常 ;************************************************************************************************************************ PendSV_Handler ;任务的保存,即把CPU寄存器的值存储到任务的堆栈中 CPSID I ;关中断,NMI和HardFault除外,防止上、下文切换被中断 MRS R0, PSP ;将PSP的值加载到R0 CBZ R0, OS_CPU_PendSVHandle_nosave ;判断R0,如果值为0则跳转到OS_CPU_PendSVHandle_nosave ;进行第一次任务切换的时候,r0肯定为0 ;在进入PendSV异常的时候,当前CPU的xPSR,PC(任务入口地址),R14\R12\R3\R2\R1\R0会自动存储到当前任务堆栈,同时递减PSP的值 STMDB R0!, {R4-R11} ;手动存储CPU寄存器R4-R11的值 到当前任务的堆栈 LDR R1, = OSTCBCurPtr ;加载OSTCBCurPtr指针的地址到R1,这里LDR属于伪指令 LDR R1, [R1] ;加载OSTCBCurPtr指针到R1,这里LDR属于ARM指令 STR R0, [R1] ;存储R0的值到 OSTCBCurPtr->OSTCBStkPtr,这个时候R0存的是任务空闲栈的栈顶 ;任务的切换,即把下一个要运行任务的堆栈内容加载到CPU寄存器中 OS_CPU_PendSVHandle_nosave ; OSPrioCur = OSPrioHighRdy; LDR R0, =OSPrioCur LDR R1, =OSPrioHighRdy LDRB R2, [R1] STRB R2, [R0] ;OSTCBCurPtr = OSTCBHighRdyPtr; LDR R0, = OSTCBCurPtr ;加载OSTCBCurPtr指针的地址到R0,这里LDR属于伪指令 LDR R1, = OSTCBHighRdyPtr ;加载OSTCBHighRdyPtr指针的地址到R1,这里LDR属于伪指令 LDR R2, [R1] ;加载OSTCBHighRdyPtr指针到R2, STR R2, [R0] ;存储OSTCBHighRdyPtr到OSTCBCurPtr LDR R0, [R2] ;加载OSTCBHighRdyPtr到R0 LDMIA R0!, {R4-R11} ;加载需要手动保存的信息到CPU寄存器R4-R11 ;更新PSP的值,这时PSP指向下一个要执行的任务的堆栈的栈底(这个栈底已经加上刚刚手动加载到CPU寄存器R4-R11的偏移) MSR PSP, R0 ORR LR, LR, #0x04 ;确保异常返回使用的堆栈指针是PSP,即LR寄存器的bit2为1 CPSIE I ;开中断 ;异常返回,这个时候任务堆栈中的剩下内容将会自动加载到xPSR,PC(任务入口地址)R14\R12\R3\R2\R1\R0(任务的形参) ;同时PSP的值也将更新,即指向任务堆栈的栈顶,在STM32中,堆栈是由高地址向低地址生长 BX LR NOP ;汇编指令对齐,不然会有警告
- 系统延时OSTimeDly();
/********************************************************* * 函数名:void OSTimeDly(OS_TICK dly); * 描述 :阻塞延时 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void OSTimeDly(OS_TICK dly) { //保存CPU中断状态 CPU_SR_ALLOC(); //进入临界区 CPU_CRITICAL_ENTER(); //设置延时时间 OSTCBCurPtr->TaskDelayTicks = dly; //从就绪列表中移除 OS_PrioRemove(OSTCBCurPtr->Prio); //退出临界区 CPU_CRITICAL_EXIT(); //任务调度 OSSched(); }
- 任务调度OSSched();
/********************************************************* * 函数名: void OSSched(void); * 描述 :任务调度 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void OSSched(void) { //保存CPU中断状态 CPU_SR_ALLOC(); //进入临界区 CPU_CRITICAL_ENTER(); //查找最高优先级的任务 OSPrioHighRdy = OS_PrioGetHighest(); OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr; //如果最高优先级的任务是当前任务,则直接返回,不进行任务切换 if(OSTCBHighRdyPtr == OSTCBCurPtr) { //退出临界区 OS_CRITICAL_EXIT(); return; } //退出临界区 CPU_CRITICAL_EXIT(); OS_TASK_SW(); //触发PendSV异常 }
- 系统时基OSTimeTick();
/********************************************************* * 函数名:OSTimeTick(void) * 描述 :调用任务调度函数 * 输入 :无 * 输出 :无 * 返回 :无 * 调用 :内部调用 **********************************************************/ void OSTimeTick(void) { CPU_INT32U i; //保存中断CPU状态 CPU_SR_ALLOC(); //进入临界区 OS_CRITICAL_ENTER(); //扫描就绪列表中所有任务的TaskDelayTicks,如果不为0,则减1 for(i = 0; i < OS_CFG_PRIO_MAX; i++) { if(OSRdyList[i].HeadPtr->TaskDelayTicks > 0) { OSRdyList[i].HeadPtr->TaskDelayTicks --; if(OSRdyList[i].HeadPtr->TaskDelayTicks == 0) { //为0表示延时时间到,让任务就绪 OS_PrioInsert(i); } } } //退出临界区 OS_CRITICAL_EXIT(); OSSched(); //任务调度 }
- OSInit();
- 总结:本节是对之前功能的修改完善;
【17】从0到1教你写uC/OS-III————>支持多优先级
最新推荐文章于 2020-04-17 23:50:28 发布