嵌入式实时操作系统,可以充分的利用单片机的资源,提高CPU使用效率。操作系统最主要就是实现任务的调度、管理。同时对于实时操作系统来说响应时间很重要。
操作系统编写最主要就是用到了堆栈SP于RET指令。这两个东西怎么用呢?其实在我们每次调用函数的时候都会自动将函数的断点地址(执行函数调用时的PC)压入到SP中,而从函数中返回时其实是利用RET指令将断点弹回到PC(程序指针)中。所以利用堆栈和RET指令就可以实现简单的任务的切换。这么说肯定挺模糊的,接下来一步一步解释。
首先,要知道任务是一个死循环。如下面所示,可以看出两个任务都是死循环,按照以往的情况,程序是跳不出来的,只能在while(1)中无限执行。那怎么才可以实现从task0到task1的切换呢?
其实如果我们能够改变PC的值是不是就可以改变程序执行顺序了。任务的调度切换就是利用改变PC的值来改变程序执行顺序的。
其次,就是要解决如何实现PC值的正确变换问题,如何让PC指向我们需要执行的地方。这就是通过堆栈来实现的。我们可以为每个任务建立一个堆栈用于保存任务PC的值,以及任务寄存器的值。这样每次进行任务切换时只要从相应的堆栈中取出PC和寄存器的值就可以实现任务的调度了。
一个简单的操作系统如下所示,只能实现简单的任务调度,延时。必须注意,空闲任务(Idle)必须建立,否则会出错。
#include<reg52.h> #define OSEnterCritical() EA=0 #define OSExitCritical() EA=1 #define EnterInt() EA=0; #define uint unsigned short int #define uchar unsigned char #define MAX_Tasks 3 #define False 0 #define Ture 1 #define MaxPrio 2 #define IdlePrio MaxPrio #define OS_Task_Create_Error 1 #define OS_Delet_Task_Error 2 #define OS_Delet_Task_Not_Exit 3 #define OS_Resume_Idle_Error 4 #define OS_Resume_Task_Error 5 typedef struct { uchar OSStackTop; //SP uchar OSSuspend; uchar OSTCBDly; //delay time }OSTCB; uchar code OSMapTbl[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; OSTCB OSTCBTbl[MAX_Tasks]; volatile uchar OSRdyTbl; volatile uchar OSIntNesting; //用于中断锁死 volatile uchar OSSchNesting; //任务切换上锁 volatile uchar OSRuning=False; volatile uchar OSStartStack[MAX_Tasks][20]; volatile uchar OSPoint[MAX_Tasks][2]; volatile uchar OSPrioCur; //volatile uchar OSTaskPend; OSInit() { // uchar i; EA=0; ET0=1; TMOD=0x01; TH0=0xB1; TL0=0xE0; OSRdyTbl=0; OSIntNesting=0; OSSchNesting=0; } //PCL,PCH,ACC ,B,DPL,DPH,PSW,R0-R7 uchar *OSStackInit(uint task,uchar *ptr,uchar OSPrio) { uchar* stk; stk=ptr; OSPoint[OSPrio][0]=task; OSPoint[OSPrio][1]=task>>8; *(stk++)= OSPoint[OSPrio][0]; *(stk++)= OSPoint[OSPrio][1]; *(stk++)= 0x00; //ACC *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk++)= 0x00; *(stk) = 0x00; return stk; } uchar OSTaskCreate(uint task,uchar *ptr,uchar OSPrio) { uchar* psp; OSEnterCritical(); if(OSPrio<=MaxPrio) //创建的任务优先级有效 { psp=OSStackInit(task,ptr,OSPrio); //初始化堆栈 OSRdyTbl|=OSMapTbl[OSPrio]; OSTCBTbl[OSPrio].OSStackTop=psp; OSTCBTbl[OSPrio].OSSuspend=0; OSTCBTbl[OSPrio].OSTCBDly=0; } else { OSExitCritical(); return OS_Task_Create_Error; } OSExitCritical(); } /*===================================================== 任务调度函数 入口参数:无 函数说明:进入函数后,先进行堆栈保护,然后查找最高优先 级任务运行 ======================================================*/ void OSSchedule() { uchar i; OSEnterCritical(); #pragma asm PUSH ACC PUSH B PUSH DPH PUSH DPL PUSH PSW PUSH 0 PUSH 7 PUSH 1 PUSH 2 PUSH 3 PUSH 4 PUSH 5 PUSH 6 #pragma endasm OSTCBTbl[OSPrioCur].OSStackTop=SP; if(OSRdyTbl) //如果就续表中有任务 { for(i=0; i<MAX_Tasks;i++) { if((OSRdyTbl & OSMapTbl)&&(!OSTCBTbl.OSSuspend)) //任务优先级最高且未被挂起 { OSPrioCur=i; break; } } } SP=OSTCBTbl[OSPrioCur].OSStackTop; #pragma asm POP 6; POP 5; POP 4; POP 3; POP 2; POP 1; POP 7; POP 0; POP PSW; POP DPL; POP DPH; POP B; POP ACC; #pragma endasm OSExitCritical(); } void OSStart() { TR0=1; EA=1; while(1); } /*========================================================= 延时若干个系统时钟 入口参数:延时系统时间个数 ===========================================================*/ void OSDelay(uchar time) { if(time==0)//延时为0,返回 return; OSEnterCritical(); OSTCBTbl[OSPrioCur].OSTCBDly=time; OSTCBTbl[OSPrioCur].OSSuspend=1; OSExitCritical(); OSSchedule(); } /*========================================================= 任务删除函数 入口参数:为被删除任务优先级 函数说明:将任务从就绪表中删除 ===========================================================*/ uchar OSTaskDelet(uchar priority) { OSEnterCritical(); if(priority>=IdlePrio) { OSExitCritical(); return OS_Delet_Task_Error; } if(!(OSRdyTbl & OSMapTbl[priority])) { OSExitCritical(); return OS_Delet_Task_Not_Exit; } OSRdyTbl &= ~(OSMapTbl[priority]); OSExitCritical(); if(priority<OSPrioCur) { OSSchedule(); } } /*========================================================= 任务恢复函数 入口参数:恢的任务优先级 函数说明:恢复被OSTaskDelet()删除的任务 ===========================================================*/ uchar OSTaskResume(uchar priority) { OSEnterCritical(); if(priority==IdlePrio)//恢复的任务不能为空闲任务,为空闲任务返回错误标志 { OSExitCritical(); return OS_Resume_Idle_Error; } if((!(OSRdyTbl & OSMapTbl[priority])) && (priority>=0)) { OSRdyTbl |= (OSMapTbl[priority]); } else //返回的任务不存在,返回错误标志 { OSExitCritical(); return OS_Resume_Task_Error; } OSExitCritical(); if(priority<OSPrioCur) { OSSchedule(); } } /*======================================================================== 定时器0用于产生系统时钟,这里每过20ms中断一次。 ==========================================================================*/ void timer0() interrupt 1 { uchar i; EA=0; TR0=0; TH0=0xB1; TL0=0xE0; if(OSRuning==Ture) //如果不是第一次调度则保护任务堆栈 ,有部分堆栈进入中断时自动完成 { //定时器自动保护的堆栈可通过反汇编查看,以此确定堆栈的安排顺序 __asm PUSH 1 __asm PUSH 2 __asm PUSH 3 __asm PUSH 4 __asm PUSH 5 __asm PUSH 6 OSTCBTbl[OSPrioCur].OSStackTop=SP; } else //第一次进入则将OSRuning置位 { OSRuning=Ture; } if(OSRuning==Ture)//如在运行, { for(i=0;i<MAX_Tasks;i++) { if((OSRdyTbl & OSMapTbl) && OSTCBTbl.OSTCBDly)//如果任务需要延时,将任务延时参数减一 { OSTCBTbl.OSTCBDly--; if(OSTCBTbl.OSTCBDly==0) { OSTCBTbl.OSSuspend=0; } } } for(i=0;i<MAX_Tasks;i++) { if((OSRdyTbl & OSMapTbl) && (!OSTCBTbl.OSSuspend))//查找优先级最高的且未挂起的任务 { OSPrioCur=i; break; } } SP=OSTCBTbl[OSPrioCur].OSStackTop; #pragma asm POP 6; POP 5; POP 4; POP 3; POP 2; POP 1; /* POP 7; POP 0; POP PSW; POP DPH; POP DPL; POP ACC; */ #pragma endasm } TR0=1; EA=1; } void Idle() { while(1); } void task0() { uchar temp=0x01,i; while(1) { for(i=0;i<8;i++) { P1=~temp; temp<<=1; OSDelay(10); } temp=0x01; } } void task1() { uchar i=0,j=0; while(1) { for(i=0;i<16;i++) { P3=j; OSDelay(20); j++; } j=0; } } int main() { OSInit(); OSTaskCreate(&Idle,&OSStartStack[2],IdlePrio); OSTaskCreate(&task1,&OSStartStack[1],1); OSTaskCreate(&task0,&OSStartStack[0],0); OSStart(); } |