一步步写STM32 OS【四】OS基本框架
扫描二维码
随时随地手机看文章
一、上篇回顾
上一篇文章中,我们完成了两个任务使用PendSV实现了互相切换的功能,下面我们接着其思路往下做。这次我们完成OS基本框架,即实现一个非抢占式(已经调度的进程执行完成,然后根据优先级调度等待的进程)的任务调度系统,至于抢占式的,就留给大家思考了。上次代码中Task_Switch实现了两个任务的切换,代码如下:
voidTask_Switch(){if(g_OS_Tcb_CurP==&TCB_1)g_OS_Tcb_HighRdyP=&TCB_2;elseg_OS_Tcb_HighRdyP=&TCB_1;OSCtxSw();}
我们把要切换任务指针付给跟_OS_Tcb_HighRdyP,然后调用OSCtxSw触发PendSV异常,就实现了任务的切换。如果是多个任务,我们只需找出就绪任务中优先级最大的切换之即可。
二、添加任务调度功能
为了实现这一目标我们至少需要知道任务的状态和时间等数据。我们定义了一个任务状态枚举类型OS_TASK_STA,方便添加修改状态。在OS_TCB结构体中添加了两个成员TimeDly和State,TimeDly是为了实现OS_TimeDly,至于State与优先级一起是作为任务切换的依据。
typedefenumOS_TASK_STA{TASK_READY,TASK_DELAY,}OS_TASK_STA;typedefstructOS_TCB{OS_STK*StkAddr;OS_U32TimeDly;OS_TASK_STAState;}OS_TCB,*OS_TCBP;
说到任务切换,我们必须面对临界区的问题,在一些临界的代码两端不加临界区进去和退出代码,会出现许多意想不到的问题。以下地方需要特别注意,对关键的全局变量的写操作、对任务控制块的操作等。进入临界区和退出临界区需要关闭和开启中断,我们采用uCOS中的一部分代码:
PUBLICOS_CPU_SR_SavePUBLICOS_CPU_SR_RestoreOS_CPU_SR_SaveMRSR0,PRIMASKCPSIDIBXLROS_CPU_SR_RestoreMSRPRIMASK,R0BXLR
#defineOS_USE_CRITICALOS_U32cpu_sr;#defineOS_ENTER_CRITICAL(){cpu_sr=OS_CPU_SR_Save();}#defineOS_EXIT_CRITICAL(){OS_CPU_SR_Restore(cpu_sr);}#defineOS_PendSV_Trigger()OSCtxSw()
一个OS至少要有任务表,我们可以用数组,当然也可以用链表。为了简单,我们使用数组,使用数组下表作为优先级。当然,必要的地方一定要做数组越界检查。
#defineOS_TASK_MAX_NUM32OS_TCBPOS_TCB_TABLE[OS_TASK_MAX_NUM];
为了使OS更完整,我们定义几个全局变量,OS_TimeTick记录系统时间,g_Prio_Cur记录当前运行的任务优先级,g_Prio_HighRdy记录任务调度后就绪任务中的最高优先级。
OS_U32OS_TimeTick;OS_U8g_Prio_Cur;OS_U8g_Prio_HighRdy;
下面三个函数与PendSV一起实现了任务的调度功能。
OS_Task_Switch函数功能:找出已就绪最高优先级的任务,并将其TCB指针赋值给g_OS_Tcb_HighRdyP,将其优先级赋值g_Prio_HighRdy。注意其中使用了临界区。
voidOS_Task_Switch(void){OS_S32i;OS_TCBPtcb_p;OS_USE_CRITICALfor(i=0;iState==TASK_READY)break;}OS_ENTER_CRITICAL();g_OS_Tcb_HighRdyP=tcb_p;g_Prio_HighRdy=i;OS_EXIT_CRITICAL();}
OS_TimeDly至当前任务为延时状态,并将延时时间赋值给当前TCB的TimeDly成员,并调用OS_Task_Switch函数,然后触发PendSV进行上下文切换。OS_Task_Switch找到就绪状态中优先级最高的,并将其赋值相关全局变量,作为上下文切换的依据。
voidOS_TimeDly(OS_U32ticks){OS_USE_CRITICALOS_ENTER_CRITICAL();g_OS_Tcb_CurP->State=TASK_DELAY;g_OS_Tcb_CurP->TimeDly=ticks;OS_EXIT_CRITICAL();OS_Task_Switch();OS_PendSV_Trigger();}
SysTick_Handler实现系统计时,并遍历任务表,任务若是延时状态,就令其延时值减一,若减完后为零,就将其置为就绪状态。
voidSysTick_Handler(void){OS_TCBPtcb_p;OS_S32i;OS_USE_CRITICALOS_ENTER_CRITICAL();++OS_TimeTick;for(i=0;iState==TASK_DELAY){--tcb_p->TimeDly;if(tcb_p->TimeDly==0)tcb_p->State=TASK_READY;}}OS_EXIT_CRITICAL();}
当所有任务都没就绪怎么办?这时就需要空闲任务了,我们把它设为优先级最低的任务。WFE指令为休眠指令,当来中断时,退出休眠,然后看看有没有已就绪的任务,有则调度之,否则继续休眠,这样可以减小功耗哦。
voidOS_Task_Idle(void){while(1){asm("WFE");OS_Task_Switch();OS_PendSV_Trigger();}}
当一个任务只运行一次时(例如下面main.c的task1),结束时就会调用OS_Task_End函数,此函数会调用OS_Task_Delete函数从任务表中删除当前的任务,然后调度任务。
voidOS_Task_Delete(OS_U8prio){if(prio>=OS_TASK_MAX_NUM)return;OS_TCB_TABLE[prio]=0;}voidOS_Task_End(void){printf("TaskofPrio%dEndn",g_Prio_Cur);OS_Task_Delete(g_Prio_Cur);OS_Task_Switch();OS_PendSV_Trigger();}
三、OS实战
下是完整的main.c代码:
#include"stdio.h"#include"stm32f4xx.h"#defineOS_EXCEPT_STK_SIZE1024#defineTASK_1_STK_SIZE128#defineTASK_2_STK_SIZE128#defineTASK_3_STK_SIZE128#defineTASK_IDLE_STK_SIZE1024#defineOS_TASK_MAX_NUM32#defineOS_TICKS_PER_SECOND1000#defineOS_USE_CRITICALOS_U32cpu_sr;#defineOS_ENTER_CRITICAL(){cpu_sr=OS_CPU_SR_Save();}#defineOS_EXIT_CRITICAL(){OS_CPU_SR_Restore(cpu_sr);}#defineOS_PendSV_Trigger()OSCtxSw()typedefsignedcharOS_S8;typedefsignedshortOS_S16;typedefsignedintOS_S32;typedefunsignedcharOS_U8;typedefunsignedshortOS_U16;typedefunsignedintOS_U32;typedefunsignedintOS_STK;typedefvoid(*OS_TASK)(void);typedefenumOS_TASK_STA{TASK_READY,TASK_DELAY,}OS_TASK_STA;typedefstructOS_TCB{OS_STK*StkAddr;OS_U32TimeDly;OS_U8State;}OS_TCB,*OS_TCBP;OS_TCBPOS_TCB_TABLE[OS_TASK_MAX_NUM];OS_TCBPg_OS_Tcb_CurP;OS_TCBPg_OS_Tcb_HighRdyP;OS_U32OS_TimeTick;OS_U8g_Prio_Cur;OS_U8g_Prio_HighRdy;staticOS_STKOS_CPU_ExceptStk[OS_EXCEPT_STK_SIZE];OS_STK*g_OS_CPU_ExceptStkBase;staticOS_TCBTCB_1;staticOS_TCBTCB_2;staticOS_TCBTCB_3;staticOS_TCBTCB_IDLE;staticOS_STKTASK_1_STK[TASK_1_STK_SIZE];staticOS_STKTASK_2_STK[TASK_2_STK_SIZE];staticOS_STKTASK_3_STK[TASK_3_STK_SIZE];staticOS_STKTASK_IDLE_STK[TASK_IDLE_STK_SIZE];externOS_U32SystemCoreClock;externvoidOSStart_Asm(void);externvoidOSCtxSw(void);externOS_U32OS_CPU_SR_Save(void);externvoidOS_CPU_SR_Restore(OS_U32);voidtask_1(void);voidtask_2(void);voidtask_3(void);voidOS_Task_Idle(void);voidOS_TimeDly(OS_U32);voidOS_Task_Switch(void);voidOS_Task_Create(OS_TCB*,OS_TASK,OS_STK*,OS_U8);voidOS_Task_Delete(OS_U8);voidOS_Task_End(void);voidOS_Init(void);voidOS_Start(void);voidtask_1(void){printf("[%d]Task1Runing!!!n",OS_TimeTick);OS_Task_Create(&TCB_2,task_2,&TASK_2_STK[TASK_2_STK_SIZE-1],5);OS_Task_Create(&TCB_3,task_3,&TASK_3_STK[TASK_3_STK_SIZE-1],7);}voidtask_2(void){while(1){printf("[%d]Task2Runing!!!n",OS_TimeTick);OS_TimeDly(1000);}}voidtask_3(void){while(1){printf("[%d]Task3Runing!!!n",OS_TimeTick);OS_TimeDly(1500);}}voidOS_Task_Idle(void){while(1){asm("WFE");OS_Task_Switch();OS_PendSV_Trigger();}}voidOS_TimeDly(OS_U32ticks){OS_USE_CRITICALOS_ENTER_CRITICAL();g_OS_Tcb_CurP->State=TASK_DELAY;g_OS_Tcb_CurP->TimeDly=ticks;OS_EXIT_CRITICAL();OS_Task_Switch();OS_PendSV_Trigger();}voidOS_Task_Switch(void){OS_S32i;OS_TCBPtcb_p;OS_USE_CRITICALfor(i=0;iState==TASK_READY)break;}OS_ENTER_CRITICAL();g_OS_Tcb_HighRdyP=tcb_p;g_Prio_HighRdy=i;OS_EXIT_CRITICAL();}voidOS_Task_Delete(OS_U8prio){if(prio>=OS_TASK_MAX_NUM)return;OS_TCB_TABLE[prio]=0;}voidOS_Task_End(void){printf("TaskofPrio%dEndn",g_Prio_Cur);OS_Task_Delete(g_Prio_Cur);OS_Task_Switch();OS_PendSV_Trigger();}voidOS_Task_Create(OS_TCB*tcb,OS_TASKtask,OS_STK*stk,OS_U8prio){OS_USE_CRITICALOS_STK*p_stk;if(prio>=OS_TASK_MAX_NUM)return;OS_ENTER_CRITICAL();p_stk=stk;p_stk=(OS_STK*)((OS_STK)(p_stk)&0xFFFFFFF8u);*(--p_stk)=(OS_STK)0x01000000uL;//xPSR*(--p_stk)=(OS_STK)task;//EntryPoint*(--p_stk)=(OS_STK)OS_Task_End;//R14(LR)*(--p_stk)=(OS_STK)0x12121212uL;//R12*(--p_stk)=(OS_STK)0x03030303uL;//R3*(--p_stk)=(OS_STK)0x02020202uL;//R2*(--p_stk)=(OS_STK)0x01010101uL;//R1*(--p_stk)=(OS_STK)0x00000000u;//R0*(--p_stk)=(OS_STK)0x11111111uL;//R11*(--p_stk)=(OS_STK)0x10101010uL;//R10*(--p_stk)=(OS_STK)0x09090909uL;//R9*(--p_stk)=(OS_STK)0x08080808uL;//R8*(--p_stk)=(OS_STK)0x07070707uL;//R7*(--p_stk)=(OS_STK)0x06060606uL;//R6*(--p_stk)=(OS_STK)0x05050505uL;//R5*(--p_stk)=(OS_STK)0x04040404uL;//R4tcb->StkAddr=p_stk;tcb->TimeDly=0;tcb->State=TASK_READY;OS_TCB_TABLE[prio]=tcb;OS_EXIT_CRITICAL();}voidSysTick_Handler(void){OS_TCBPtcb_p;OS_S32i;OS_USE_CRITICALOS_ENTER_CRITICAL();++OS_TimeTick;for(i=0;i State==TASK_DELAY){--tcb_p->TimeDly;if(tcb_p->TimeDly==0)tcb_p->State=TASK_READY;}}OS_EXIT_CRITICAL();}voidOS_Init(void){inti;g_OS_CPU_ExceptStkBase=OS_CPU_ExceptStk+OS_EXCEPT_STK_SIZE-1;asm("CPSIDI");for(i=0;i