产品开发案例教学-实时操作系统uCOS.ppt
产品开发案例教学,计算机通信专业,主要内容,uC/OS-II概述uC/OS-II基本概念uC/OS-II内核结构uC/OS-II任务管理uC/OS-II时间管理uC/OS-II任务间通信uC/OS-II移植,C/OS-II概述,C/OS-II Micro C O S 2,Micro-Controller Operating System Version 2,微控制器操作系统;C/OS,美国人Jean Labrosse 1992年完成;1998年C/OS-II,目前的版本C/OS-II V2.7X网站(),C/OS-II的各种商业应用,全世界有数百种产品在应用医疗器械移动电话路由器工业控制GPS 导航系统智能仪器更多,C/OS-II的特点,UC/OS是一个非常小巧的实时操作系统;整个代码分为内核层以及移植层,这样使得它的植性很方便。采用抢占式调度策略,保证任务的实时性。能够管理多达64个任务。提供了邮箱、消息队列、信号量、内存管理、时间管理等系统服务。,如何学习C/OS-II,看C/OS-II书籍;阅读源代码,随书CD中包含了源代码;实际编译、运行和调试;在具体的开发板上进行移植。,为了实现资源共享,一个操作系统必须提供临界段擦作的功能。C/OS-为了处理临界段代码需要关中断,处理完毕后再开中断。这使得C/OS-能够避免同时有其它任务或中断服务进入临界段代码。C/OS-定义两个宏(macros)来开关中断。分别是:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。这两个宏的定义取决于所用的微处理器,每种微处理器都有自己的OS_CPU.H文件。,临界段(Critical Sections),共享资源,可以被一个以上任务使用的资源叫做共享资源。为了防止数据被破坏,每个任务在与共享资源打交道时,必须独占该资源。这叫做互斥(mutual exclusion)。,任务,一个任务,是一个简单的程序,该程序可以认为CPU完全只属该程序自己。实时应用程序的设计过程,包括如何把问题分割成多个任务,每个任务都是整个应用的某一部分,每个任务被赋予一定的优先级,有它自己的一套CPU寄存器和自己的栈空间。典型地、每个任务都是一个无限的循环。每个任务都处在以下5种状态之一的状态下,这5种状态是休眠态,就绪态、运行态、挂起态(等待某一事件发生)和被中断态。,任务(task),一个任务通常是一个无限的循环:void mytask(void*pdata)do init while(1)do something;waiting;do something;,任务状态,任务的状态休眠,休眠状态(Dormant):任务存在于内存空间中,但内核不可见;可以通过以下函数通知内核,使之变为就绪状态:OSTaskCreate()或OSTaskCreateExt()可以通过以下函数返回到休眠状态:OSTaskDel(),任务的状态就绪,就绪状态(Ready):万事具备,只欠CPU;在所有的就绪任务当中,具有最高优先级的任务被选中去运行;如果任务在运行的时候被抢占了CPU,则又回到就绪状态。,任务的状态运行,运行状态(Running):任务在CPU上运行;当一个任务在运行时,如果没有关闭中断,则有可能被中断所打断;当一个任务在运行时,可能因为各种原因进入阻塞状态。OSMBoxPend(),OSQPend(),OSSemPend()OSTaskSuspend(),OSTimeDly(),任务的状态ISR,中断服务状态(ISR):该任务原来在CPU上运行,后来被中断所打断,由中断服务程序ISR接管了CPU;当中断服务程序运行完毕后,内核要判断是否有新的、更高优先级的任务就绪,如果有,则原有的任务被抢占;如果没有,则原有的任务重新运行。,任务的状态阻塞,阻塞/等待状态(Waiting):任务由于正在等待某个事件(信号量、邮箱或队列)而被挂起;当任务等待的事件发生时,回到就绪状态。OSMBoxpost(),OSQPost(),OSSemPost(),OSTaskResume(),OSTimeDlyResume()或OSTimeTick(),状态的转换,删除任务,任务切换,Context Switch 在有的书中翻译成上下文切换,实际含义是任务切换,或CPU寄存器内容切换。当多任务内核决定运行另外的任务时,它保存正在运行任务的当前状态(Context),即CPU寄存器中的全部内容。这些内容保存在任务的当前状况保存区(Tasks Context Storage area),也就是任务自己的栈区之中。入栈工作完成以后,就是把下一个将要运行的任务的当前状况从该任务的栈中重新装入CPU的寄存器,并开始下一个任务的运行。这个过程叫做任务切换。,任务调度,调度(Scheduler),英文还有一词叫dispatcher,也是调度的意思。这是内核的主要职责之一,就是要决定该轮到哪个任务运行了。多数实时内核是基于优先级调度法的。每个任务根据其重要程度的不同被赋予一定的优先级。基于优先级的调度法指,CPU总是让处在就绪态的优先级最高的任务先运行。然而,究竟何时让高优先级任务掌握CPU的使用权,有两种不同的情况,这要看用的是什么类型的内核,是不可剥夺型的还是可剥夺型内核。,任务间的通讯,有时很需要任务间的或中断服务与任务间的通讯。这种信息传递称为任务间的通讯。任务间信息的传递有两个途径:通过全程变量或发消息给另一个任务。,任务(task)管理,C/OS-可以管理多达64个任务。保留了优先级为0、1、2、3、OS_LOWEST_PRIO-3、OS_LOWEST_PRI0-2,OS_LOWEST_PRI0-1以及OS_LOWEST_PRI0这8个任务以被将来使用。用户可以有多达56个应用任务。必须给每个任务赋以不同的优先级。优先级号越低,任务的优先级越高。,中断与时钟节拍,当发生中断时,首先应保护现场,将CPU寄存器入栈,再处理中断函数,然后恢复现场,将CPU寄存器出栈,最后执行中断返回。uC/OS中提供了OSIntEnter()和OSIntExit()告诉内核进入了中断状态。时钟节拍是一种特殊的中断,操作系统的心脏。对任务列表进行扫描,判断是否有延时任务应该处于准备就绪状态,最后进行上下文切换。,时钟节拍,C/OS需要用户提供周期性信号源,用于实现时间延时和确认超时。节拍率应在说10到100Hz。时钟节拍率越高,系统的额外负荷就越重。时钟节拍的实际频率取决于用户应用程序的精度。时钟节拍源可以是专门的硬件定时器,也可以是来自50/60Hz交流电源的信号。用户必须在多任务系统启动以后再开启时钟节拍器,也就是在调用OSStart()之后。,OSTickISR,void OSTickISR(void)保存处理器寄存器的值;调用OSIntEnter()或是将OSIntNesting加1;调用OSTimeTick();调用OSIntExit();恢复处理器寄存器的值;执行中断返回指令;,任务控制块(TCB),OS_TCB是一个任务的核心数据结构,保存该任务的相关参数,包括任务堆栈指针,状态,优先级,任务表位置,任务链表指针等。一旦任务建立了,任务控制块OS_TCB将被赋值。所有的任务控制块分为两条链表,空闲链表和使用链表。,任务控制块结构的主要成员,typedef struct os_tcb OS_STK*OSTCBStkPtr;/*当前任务栈顶的指针*/struct os_tcb*OSTCBNext;/*任务控制块的双重链接指针*/struct os_tcb*OSTCBPrev;/*任务控制块的双重链接指针*/OS_EVENT*OSTCBEventPtr;/*事件控制块的指针*/void*OSTCBMsg;/*消息的指针*/INT16U OSTCBDly;/*任务延时*/INT8U OSTCBStat;/*任务的状态字*/INT8U OSTCBPrio;/*任务优先级*/INT8U OSTCBX;/*用于加速进入就绪态的过程*/INT8U OSTCBY;/*用于加速进入就绪态的过程*/INT8U OSTCBBitX;/*用于加速进入就绪态的过程*/INT8U OSTCBBitY;/*用于加速进入就绪态的过*/OS_TCB;,空闲任务和统计任务,内核总是创建一个空闲任务OSTaskIdle();总是设置为最低优先级,OS_LOWEST_PRIOR;当所有其他任务都未在执行时,空闲任务开始执行;应用程序不能删除该任务;空闲任务的工作就是把32位计数器OSIdleCtr加1,该计数器被统计任务所使用;统计任务OSTaskStat(),提供运行时间统计。每秒钟运行一次,计算当前的CPU利用率。其优先级是OS_LOWEST_PRIOR-1,可选。,任务就绪表(Ready List),每个任务的就绪态标志都放入就绪表中的,就绪表中有两个变量OSRdyGrp和OSRdyTbl。在OSRdyGrp中,任务按优先级分组,8个任务为一组。OSRdyGrp中的每一位表示8组任务中每一组中是否有进入就绪态的任务。任务进入就绪态时,就绪表OSRdyTbl中的相应元素的相应位也置位。,任务就绪表,优先级最低任务,(空闲任务),优先级最高任务,任务优先级号,对于整数OSRdyTbli(0i7),若它的某一位为1,则OSRdyGrp的第i位为1。,任务就绪表,假设用户创建的任务优先级为6,使任务进入就绪态,如果prio是任务是优先级,也是任务的识别号,则将任务放入就绪表,即使任务进入就绪态的方法是:OSRdyGrp|=OSMapTblprio3;OSRdyTblprio3|=OSMapTblprio,使任务脱离就绪态,将任务就绪表OSRdyTblprio3相应元素的相应位清零,而且当OSRdyTblprio3中的所有位都为零时,即全组任务中没有一个进入就绪态时,OSRdyGrp的相应位才为零。if(OSRdyTblprio3,uC/OS-II流程图,uC/OS-II的启动,多任务的启动是用户通过调用OSStart()实现的。然而,启动C/OS-之前,用户至少要建立一个应用任务。,void main(void)OSInit();/*初始化uC/OS-II*/.通过调用OSTaskCreate()或OSTaskCreateExt()创建至少一个任务;.OSStart();/*开始多任务调度!永不返回*/,C/OS-初始化,在调用C/OS-的任何其它服务之前,C/OS-要求用户首先调用系统初始化函数OSInit()。OSInit()建立空闲任务idle task,这个任务总是处于就绪态的。空闲任务OSTaskIdle()的优先级总是设成最低,即OS_LOWEST_PRIO。C/OS-还初始化了4个空数据结构缓冲区。,C/OS-初始化后的一些数据结构内容,C/OS-初始化后的缓冲区,OSStart(),if(OSRunning=FALSE)y=OSUnMapTblOSRdyGrp;x=OSUnMapTblOSRdyTbly;OSPrioHighRdy=(INT8U)(y 3)+x);OSPrioCur=OSPrioHighRdy;OSTCBHighRdy=OSTCBPrioTblOSPrioHighRdy;OSTCBCur=OSTCBHighRdy;OSStartHighRdy();,系统启动后的状态,假设用户创建的任务优先级为6,任务调度(Task Scheduling),C/OS-总是运行进入就绪态任务中优先级最高的那一个。确定哪个任务优先级最高,下面该哪个任务运行了的工作是由调度器(Scheduler)完成的。任务级的调度是由函数OSSched()完成的。中断级的调度是由另一个函数OSIntExt()完成的,这个函数将在以后描述。,根据就绪表确定最高优先级,两个关键:将优先级数分解为高三位和低三位分别确定;高优先级有着小的优先级号;,根据就绪表确定最高优先级,通过OSRdyGrp值确定高3位,假设OSRdyGrp0 x08=0 x00001000,第3位为1,优先级的高3位为011;通过OSRdyTbl3的值来确定低3位,假设OSRdyTbl30 x3a,第1位为1,优先级的低3位为001,3*8+1=25,任务优先级,源代码中使用了查表法,查表法具有确定的时间,增加了系统的可预测性,uC/OS中所有的系统调用时间都是确定的Y=OSUnMapTblOSRdyGrp;X=OSUnMapTblOSRdyTblY;Prio=(Y3)+X;,参见OS_CORE.C,任务调度器,void OSSched(void)INT8U y;OS_ENTER_CRITICAL();if(OSLockNesting|OSIntNesting)=0)y=OSUnMapTblOSRdyGrp;OSPrioHighRdy=(INT8U)(y 3)+OSUnMapTblOSRdyTbly);if(OSPrioHighRdy!=OSPrioCur)OSTCBHighRdy=OSTCBPrioTblOSPrioHighRdy;OSCtxSwCtr+;OS_TASK_SW();OS_EXIT_CRITICAL();,检查是否中断调用和允许任务调用,找到优先级最高的任务,该任务是否正在运行,任务切换,将被挂起任务的寄存器内容入栈;将较高优先级任务的寄存器内容出栈,恢复到硬件寄存器中。,任务级的任务切换OS_TASK_SW(),保护当前任务的现场恢复新任务的现场执行中断返回指令开始执行新的任务,调用OS_TASK_SW()前的数据结构,低优先级任务 OS_TCB,OSTCBCur(1),存贮器低地址,存贮器高地址,堆栈方向,SP,R1,R2,R3,R4,PC,PSW,存贮器低地址,存贮器高地址,高优先级任务 OS_TCB,OSTCBHighRdy(3),(2),CPU,(4),(5),保存当前CPU寄存器的值,低优先级任务 OS_TCB,OSTCBCur,存贮器低地址,存贮器高地址,堆栈方向,SP,R1,R2,R3,R4,PC,PSW,存贮器低地址,存贮器高地址,高优先级任务 OS_TCB,OSTCBHighRdy(3),(2),CPU,(4),(5),(1),(3),重新装入要运行的任务,低优先级任务 OS_TCB,OSTCBCur,存贮器低地址,存贮器高地址,堆栈方向,SP,R1,R2,R3,R4,PC,PSW,存贮器低地址,存贮器高地址,高优先级任务 OS_TCB,OSTCBHighRdyOSTCBCur(1),(2),CPU,(4),(4),(1),(3),(3),(4),任务切换OS_TASK_SW()的代码,Void OSCtxSw(void)将R1,R2,R3及R4推入当前堆栈;OSTCBCurOSTCBStkPtr=SP;OSTCBCur=OSTCBHighRdy;SP=OSTCBHighRdy OSTCBSTKPtr;将R4,R3,R2及R1从新堆栈中弹出;执行中断返回指令;,创建任务,创建任务的函数OSTaskCreate();OSTaskCreateExt();OSTaskCreateExt()是OSTaskCreate()的扩展版本,提供了一些附加的功能;任务可以在多任务调度开始(即调用OSStart()之前创建,也可以在其它任务的执行过程中被创建。但在OSStart()被调用之前,用户必须创建至少一个任务;不能在中断服务程序(ISR)中创建新任务。,OSTaskCreate(),INT8U OSTaskCreate(void(*task)(void*pd),/任务代码指针 void*pdata,/任务参数指针OS_STK*ptos,/任务栈的栈顶指针 INT8U prio/任务的优先级);返回值OS_NO_ERR:函数调用成功;OS_PRIO_EXIT:任务优先级已经存在;OS_PRIO_INVALID:任务优先级无效。,OSTaskCreate()的实现过程,任务优先级检查该优先级是否在0到OS_LOWSEST_PRIO之间?该优先级是否空闲?调用OSTaskStkInit(),创建任务的栈帧;调用OSTCBInit(),从空闲的OS_TCB池(即OSTCBFreeList链表)中获得一个TCB并初始化其内容,然后把它加入到OSTCBList链表的开头,并把它设定为就绪状态;任务个数OSTaskCtr加1;调用用户自定义的函数OSTaskCreateHook();判断是否需要调度(调用者是正在执行的任务),任务的栈空间,每个任务都有自己的栈空间(Stack),栈必须声明为OS_STK类型,并且由连续的内存空间组成;栈空间的分配方法静态分配:在编译的时候分配,例如:static OS_STK MyTaskStackstack_size;OS_STK MyTaskStackstack_size;动态分配:在任务运行的时候使用malloc()函数来动态申请内存空间;,OS_STK*pstk;pstk=(OS_STK*)malloc(stack_size);/*确认malloc()能得到足够的内存空间*/if(pstk!=(OS_STK*)0)Create the task;,动态分配,栈的增长方向,栈的增长方向的设置从低地址到高地址:在OS_CPU.H中,将常量 OS_STK_GROWTH设定为 0;从高地址到低地址:在OS_CPU.H中,将常量 OS_STK_GROWTH设定为 1;OS_STK TaskStackTASK_STACK_SIZE;OSTaskCreate(task,pdata,中断处理,中断:由于某种事件的发生而导致程序流程的改变。产生中断的事件称为中断源。CPU响应中断的条件:至少有一个中断源向CPU发出中断信号;系统允许中断,且对此中断信号未予屏蔽。,中断服务程序ISR,中断一旦被识别,CPU会保存部分(或全部)运行上下文(context,即寄存器的值),然后跳转到专门的子程序去处理此次事件,称为中断服务子程序(ISR)。C/OS-中,中断服务子程序要用汇编语言来编写,然而,如果用户使用的C语言编译器支持在线汇编语言的话,用户可以直接将中断服务子程序代码放在C语言的程序文件中。,OSTickISR,void OSTickISR(void)保存处理器寄存器的值;调用OSIntEnter()或是将OSIntNesting加1;调用OSTimeTick();调用OSIntExit();恢复处理器寄存器的值;执行中断返回指令;,OSIntEnter(),/*在调用本函数之前必须先将中断关闭*/void OSIntEnter(void)if(OSRunning=TRUE)if(OSIntNesting 255)OSIntNesting+;,OSIntExit的意义,OSIntExit(),void OSIntExit(void)OS_ENTER_CRITICAL();/关中断 if(-OSIntNesting|OSLockNesting)=0)/判断嵌套是否为零/把高优先级任务装入 OSIntExitY=OSUnMapTblOSRdyGrp;OSPrioHighRdy=(INT8U)(OSIntExitY 3)+OSUnMapTblOSRdyTblOSIntExitY);if(OSPrioHighRdy!=OSPrioCur)OSTCBHighRdy=OSTCBPrioTblOSPrioHighRdy;OSCtxSwCtr+;OSIntCtxSw();OS_EXIT_CRITICAL();/开中断返回,OSIntCtxSw(),在任务切换时,为什么使用OSIntCtxSw()而不是调度函数中的OS_TASK_SW()?原因有二点一半的任务切换工作,即CPU寄存器入栈,已经在前面做完了;需要保证所有被挂起任务的栈结构是一样的。,时间管理,与时间管理相关的系统服务:OSTimeDLY()OSTimeDLYHMSM()OSTimeDlyResmue()OStimeGet()OSTimeSet(),OSTimeDLY(),OSTimeDLY():任务延时函数,申请该服务的任务可以延时一段时间;调用OSTimeDLY后,任务进入等待状态;使用方法void OSTimeDly(INT16U ticks);ticks表示需要延时的时间长度,用时钟节拍的个数来表示。,OSTimeDLY(),void OSTimeDly(INT16U ticks)if(ticks 0)OS_ENTER_CRITICAL();if(OSRdyTblOSTCBCur-OSTCBY,任务间通信与同步,任务间通信的管理:事件控制块ECB;同步与互斥临界区(Critical Sections);信号量(Semaphores);任务间通信邮箱(Message Mailboxes);消息队列(Message Queues)。,任务间通信,低级通信只能传递状态和整数值等控制信息,传送的信息量小;例如:信号量高级通信能够传送任意数量的数据;例如:共享内存、邮箱、消息队列,消息邮箱,邮箱(MailBox):一个任务或ISR可以通过邮箱向另一个任务发送一个指针型的变量,该指针指向一个包含了特定“消息”(message)的数据结构;必须在OS_CFG.H中将OS_MBOX_EN开关常量置为1,这样C/OS才能支持邮箱。,一个邮箱可能处于两种状态:满的状态:邮箱包含一个非空指针型变量;空的状态:邮箱的内容为空指针NULL;邮箱的系统服务OSMboxCreate()OSMboxPost()OSMboxPend()OSMboxAccept()OSMboxQuery(),消息邮箱,任务、ISR和消息邮箱的关系,邮箱的系统服务(1),OSMboxCreate():创建一个邮箱在创建邮箱时,须分配一个ECB,并使用其中的字段OSEventPtr指针来存放消息的地址;OS_EVENT*OSMboxCreate(void*msg);msg:指针的初始值,一般情形下为NULL。OSMboxPend():等待一个邮箱中的消息若邮箱为满,将其内容(某消息的地址)返回;若邮箱为空,当前任务将被阻塞,直到邮箱中有了消息或等待超时;OSMboxPend(OS_EVENT*pevent,INT16U timeout,INT8U*err);,邮箱的系统服务(2),OSMboxPost():发送一个消息到邮箱中如果有任务在等待该消息,将其中的最高优先级任务从等待列表中删除,变为就绪状态;OSMboxPost(OS_EVENT*pevent,void*msg);OSMboxAccept():无等待地请求邮箱消息若邮箱为满,返回它的当前内容;若邮箱为空,返回空指针;OSMboxAccept(OS_EVENT*pevent);OSMboxQuery():查询一个邮箱的状态OSMboxQuery(OS_EVENT*pevent,OS_MBOX_DATA*pdata);,OSMboxCreate()函数OS_EVENT*CommMbox;void main(void).OSInit();.CommMbox=OSMboxCreate(void*)0);.OSStart();,样例程序,样例程序,OSMboxPend()函数void CommTask(void*pdata)INT8U err;void*msg;pdata=pdata;for(;).msg=OSMboxPend(CommMbox,10,if(err=OS_NO_ERR)/*收到消息时的代码*/else/*未收到消息时的代码*/,样例程序,OSMboxPost()函数OS_EVENT*CommMbox;INT8U CommRxBuf100;void CommTaskRx(void*pdata)INT8U err;.for(;).err=OSMboxPost(CommMbox,(void*).,第三章、实时操作系统C/OS-II,1,3,2,4,C/OS-II概述,任务管理,中断和时间管理,任务之间的通信与同步,5,存储管理,6,C/OS-II的移植,所谓移植,就是使一个实时操作系统能够在某个微处理器或微控制器平台上运行;为了方便移植,COS-II的大部分代码是用标准的C语言编写的,无须改动。但仍需要用C和汇编语言写一些与处理器相关的代码,这是因为C/OS-在读写处理器的寄存器时只能通过汇编语言来实现。,移植概述,处理器的C编译器能产生可重入代码;在程序中可以打开或者关闭中断;处理器支持中断,并且能产生定时中断(通常在101000Hz之间);处理器支持能容纳一定量数据的硬件堆栈处理器有将栈指针和其他CPU寄存器内容保存到栈(或者内存)的指令,以及相应的出栈指令。,移植COS-II时须满足的条件,可重入代码举例,程序1:可重入型函数void swap(int*x,int*y)int temp;temp=*x;*x=*y;*y=temp;,非可重入代码举例,程序2:非可重入型函数int temp;void swap(int*x,int*y)temp=*x;*x=*y;*y=temp;,返回,不可重入函数被中断破坏,如何使函数具有可重入性,使Swap()函数具有可重入性:把Temp定义为局部变量;调用Swap()函数之前关中断,调动后再开中断;用信号量禁止该函数在使用过程中被再次调用;,设置OS_CPU.H中与处理器和编译器相关的代码;用汇编语言编写四个与处理器相关的函数(OS_CPU.ASM);用C语言编写十个操作系统相关的函数(OS_CPU_C.C)。,移植工作的主要内容,OS_CPU.H的修改包括三部分内容:一些符号常量、3个宏定义和10个数据类型的定义;,OS_CPU.H,设置OS_STK_GROWTH,绝大多数的微处理器和微控制器的堆栈是从上往下长的。但是某些处理器是用另外一种方式工作的。C/OS-被设计成两种情况都可以处理,只要在结构常量OS_STK_GROWTH中指定堆栈的生长方式就可以了。置OS_STK_GROWTH为0表示堆栈从下往上长。置OS_STK_GROWTH为1表示堆栈从上往下长。,宏定义OS_ENTER_CRITICAL():进入临界区(禁止中断);OS_EXIT_CRITICAL():退出临界区(允许中断);OS_TASK_SW():当需要任务切换时这个宏被调用。其功能是产生一个软中断,并跳转到中断处理程序OSCtxSw(),去完成此次任务切换。,开中断/关中断,开关中断的方式。推荐使用method3#if OS_CRITICAL_METHOD=3OS_CPU_SR cpu_sr;#endif OS_ENTER_CRITICAL();OS_EXIT_CRITICAL();,使用method3方式的开关中断,#define OS_ENTER_CRITICAL()cpu_sr=INTS_OFF();#define OS_EXIT_CRITICAL()if(cpu_sr=0)INTS_ON();,ARM的中断模式,设备的中断在ARM中被映射到了两个异常中断FIQ和IRQ通过控制CPSR中的对应数据位,可以开启或者关闭中断为了方便和统一uCOS-II系统中断的处理,只使用了IRQ模式的中断。,程序状态寄存器-CPRS,条件位:N=1-结果为负,0-结果为正或0Z=1-结果为0,0-结果不为0C=1-进位,0-借位V=1-结果溢出,0结果没溢出Q 位:仅ARM 5TE/J架构支持指示增强型DSP指令是否溢出J 位仅ARM 5TE/J架构支持J=1:处理器处于Jazelle状态,中断禁止位:I=1:禁止 IRQ.F=1:禁止 FIQ.T Bit仅ARM xT架构支持T=0:处于 ARM 状态T=1:处于 Thumb 状态Mode位(处理器模式位):0b10000User0b10001FIQ0b10010IRQ0b10011Supervisor0b10111Abort0b11011Undefined0b11111System,打开/关闭中断,#define OS_ENTER_CRITICAL()cpu_sr=INTS_OFF();#define OS_EXIT_CRITICAL()if(cpu_sr=0)INTS_ON();,打开/关闭中断,EXPORT INTS_OFFEXPORT INTS_ONINTS_OFF mrs r0,cpsr;current CSR mov r1,r0;make a copy for masking orr r1,r1,#0 xC0;mask off int bits msr CPSR_cxsf,r1;disable ints and r0,r0,#0 x80;return IRQ bit mov pc,lr;return,7,0,31,7,0,31,打开/关闭中断,INTS_ON mrs r0,cpsr;current CSR bic r0,r0,#0 x80;mask on ints msr CPSR_cxsf,r0;enable ints mov pc,lr;return,7,0,7,0,31,与编译器有关的数据类型,BOOLEAN:无符号8位整数;INT8U:无符号8位整数;INT8S:有符号8位整数;INT16U:无符号16位整数;INT16S:有符号16位整数;INT32U:无符号32位整数;INT32S:有符号32位整数;FP32:单精度浮点数;FP64:双精度浮点数;OS_STK:栈单元的数据类型;需要做的事情:查看编译器手册,为这些数据类型找到对应的标准C数据类型。,OS_CPU_A.ASM的移植需要编写四个汇编语言函数OSStartHighRdy();OSCtxSw();OSIntCtxSw();OSTickISR();,OS_CPU_A.ASM,OSStartHighRdy(),功能:在系统启动时(即OSStart()函数当中),通过调用本函数运行当前最高优先级的任务。前提条件:OSTCBHighRdy指向的是优先级最高的任务的TCB;处于就绪状态的任务的栈结构(栈底内容)看起来就像刚发生过中断并将所有寄存器的内容保存到栈中的情形一样。,OSStartHighRdy():运行优先级最高的就绪任务,OSStartHighRdyLDRr4,addr_OSTCBCur;得到当前任务TCB地址LDRr5,addr_OSTCBHighRdy;得到最高优先级任务TCB地址LDRr5,r5;获得堆栈指针LDRsp,r5;转移到新的堆栈中STRr5,r4;设置新的当前任务TCB地址LDMFDsp!,r4 MSR SPSR,r4LDMFDsp!,r4;从栈顶获得新的状态MSR CPSR,r4;CPSR 处于 SVC32Mode摸式LDMFDsp!,r0-r12,lr,pc;运行新的任务,OSCtxSw(),功能:实现两个任务的切换。调用方式:OSSched()OS_TASK_SW()产生中断中断处理程序OSCtxSw()说明:对于原任务,中断已经发生,中断返回地址已经在栈中;与OSStartHighRdy()相比,增加了一个步骤,即先要把原任务的寄存器入栈。,void OSCtxSw(void)保存CPU寄存器的内容;/*将当前任务的栈指针保存到它的OS_TCB中*/OSTCBCur-OSTCBStkPtr=Stack pointer;调用用户自定义的OSTaskSwHook();OSTCBCur=OSTCBHighRdy;OSPrioCur=OSPrioHighRdy;/*恢复即将执行的新任务的栈指针*/Stack pointer=OSTCBHighRdy-OSTCBStkPtr;从新任务的栈中恢复所有的处理器寄存器;执行一条中断返回指令,跳转到新任务;,OSCtxSw()伪代码,OS_TASK_SW();任务级的任务切换函数(1),OS_TASK_SWSTMFDsp!,lr;保存 pcSTMFDsp!,lr;保存 lrSTMFDsp!,r0-r12;保存寄存器和返回地址MRS r4,CPSRSTMFDsp!,r4;保存当前的PSRMRS r4,SPSRSTMFDsp!,r4;保存SPSR;OSPrioCur=OSPrioHighRdyLDR r4,addr_OSPrioCurLDR r5,addr_OSPrioHighRdyLDRBr6,r5STRBr6,r4,OS_TASK_SW():任务级的任务切换函数(2),;得到当前任务TCB地址LDRr4,addr_OSTCBCurLDRr5,r4STRsp,r5;保存sp在被占先的任务的 TCB;得到最高优先级任务TCB地址LDRr6,addr_OSTCBHighRdyLDRr6,r6LDRsp,r6;得到新任务堆栈指针;OSTCBCur=OSTCBHighRdySTRr6,r4;设置新的当前任务的TCB地址;保存任务方式寄存器 LDMFDsp!,r4MSR SPSR,r4LDMFDsp!,r4MSR CPSR,r4;返回到新任务的上下文LDMFDsp!,r0-r12,lr,pc,OSIntCtxSw(),功能:在ISR中执行任务切换功能。调用方式:低优先级任务A执行发生中断中断处理程序高优先级任务B就绪 OSIntExit()OSIntCtxSw()与OSCtxSw()相比:由于是在ISR中被调用,不仅中断返回地址已经在栈中,而且CPU寄存器的内容也已入栈;由于ISR中进行了OSIntExit()、OSIntCtxSw()等函数调用,栈中增加了额外内容,需清理。,回忆ISR的执行过程,(1)保存全部CPU寄存器的值;(2)调用OSIntEnter(),或直接把全局变量OSIntNesting(中断嵌套层次)加1;(3)执行用户代码做中断服务;(4)调用OSIntExit();(5)恢复所有CPU寄存器;(6)执行中断返回指令。,任务切换时的栈内容,栈指针调整,调整堆栈指针(加一个数在堆栈指针上)来完成的。加在堆栈指针上的数必须是明确的,而这个数主要依赖于移植的目标处理器(地址空间可能是16,32或64位),所用的编译器,编译器选项,内存模式等等。另外,处理器状态字可能是8,16,32甚至64位宽,并且OSIntExit()可能会分配局部变量。有些处理器允许用户直接增加常量到堆栈指针中,而有些则不允许。在后一种情况下,可以通过简单的执行一定数量的pop(出栈)指令来实现相同的功能。一旦堆栈指针完成调整,新的堆栈指针会被保存到被切换出去的任务的OS_TCB中,OSIntCtxSW的实现,void OSIntCtxSw(void)调整栈指针来去掉在调用OSIntExit()和 OSIntCtxSw()过程中压入栈的多余内容;/*将当前任务的栈指针保存到它的OS_TCB中*/OSTCBCur-OSTCBStkPtr=Stack pointer;调用用户自定义的OSTaskSwHook();OSTCBCur=OSTCBHighRdy;OSPrioCur=OSPrioHighRdy;/*恢复即将执行的新任务的栈指针*/Stack pointer=OSTCBHighRdy-OSTCBStkPtr;从新任务的栈中恢复所有的处理器寄存器;执行一条中断返回指令,跳转到新任务;,伪代码,OSIntCtxSW();中断级的任务切换函数(1),OSIntCtxSwadd r7,sp,#16;保存寄存器指针LDRsp,=IRQStack;FIQ_STACK mrs r1,SPSR;得到暂停的 PSRorr r1,r1,#0 xC0;关闭 IRQ,FIQ.msr CPSR_cxsf,r1;转换模式(应该是 SVC_MODE)ldr r0,r7,#52;从IRQ堆栈中得到IRQs LR(任务 PC)sub r0,r0,#4;当前PC地址是(saved_LR-4)STMFDsp!,r0;保存任务 PCSTMFDsp!,lr;保存 LRmov lr,r7;保存 FIQ 堆栈 ptr in LR(转到 nuke r