资源描述
,8/17/2010,*,作者,:,四川师范大学成都学院 屈召贵,QQ:35247485,1,C/OS-II,的移植,2,7.1,概述,User,目录,Main.C,Main.H,Includes.H,OS_Cfg.H,C,/,OS-II,Source,目录,OS_CORE.C OS_FLAG.C OS_MBOX.C OS_MEM.C OS_MUTEX.C OS_Q.C OS_SEM.C OS_TASK.C OS_TIME.C OS_TMR.C uCOS_II.H,C,/,OS-II,Ports,目录,Cortex M3,OS_CPU_C.C OS_CPU_A.ASM OS_CPU.H,BSP,(,板级支持包,),Startup.S,Target.C,Target.H,ARM Cortex-M3,目标板,与处理器无关操作系统内核代码,与处理器有关需要移植代码,开发板相关代码,8/17/2010,3,内核文件,移植的部分,用户代码,板级代码,主程序,8/17/2010,4,ARM Cortex-M3,的寄存器模型,LM3S,系列单片机采用,ARM Cortex-M3,内核;在移植之前,先简述可见寄存器模型。,ARM Cortex-M3,总共有,20,个寄存器,每个都是,32,位宽度。,R0-R12,通用寄存器,可存储数据也可存放指针,R13,用于存放堆栈指针。实际上有两个堆栈指针,SP_process(,进程堆栈,),和,SP_main(,主堆栈,),,但任何时候只有一个是可见的。在本移植中,,SP_process,用于任务代码(即线程模式),,SP_main,用于异常代码,(即处理模式)。,R14,连接寄存器,LR,。在执行分支链接指令(,BL,)或带交换的分支链接指,令(,BLX,)时,存储来自,PC,的返回地址;也用作异常的返回。,R15,程序计数寄存器,PC,。用于指示当前正被执行的指令。根据不同的指,令,每执行一条,,PC,增加,2,或增加,4,。,8/17/2010,5,8/17/2010,6,状态,中断,8/17/2010,7,控制寄存器,8/17/2010,8,7.1.1,移植条件,移植,C/OS-II,到处理器上必须满足以下条件,(1),处理器的,C,编译器能产生可重入代码,C/OS,是多任务内核,函数可能会被多个任务调用,代码的重入性是保证完成多任务的基础。可重入代码指的是可被多个体任务同时调用,而不会破坏数据的一段代码,或者说代码具有在执行过程中打断后再次被调用的能力。,举例说明:,Swap1,函数代码:,Int temp;,void swap1(int*x,int *y),temp=*x;,*x=*y;,*y=temp;,举例说明:,Swap2,函数代码:,void swap2(int*x,int *y),int temp;,temp=*x;,*x=*y;,*y=temp;,可重入,不可重入,编译器还得支持,,MDK,开,发环境,可生成可重入代码,8/17/2010,9,(,2,)用,C,语言可打开和关闭中断,ARM,处理器核包含一个,CPSR,寄存器,该寄存器包括一个全局的中断禁止位,控制它便可打开和关闭中断。,PRIMASK,(,3,)处理器支持中断并且能产生定时中断,C/OS-II,通过处理器产生的定时器中断来实现多任务之间的调度。,ARM Cortex-M3,的处理器都支持中断并能产生定时器中断,专门有一个,SysTick,定时器来实现。,(4),处理器支持能够容纳一定量数据的硬件堆栈(通常需要几十,KByte,字节),比如,AT98C51,处理器,内部只有,128,字节的,RAM,,要运行,需外扩,RAM,。,CM3,的芯片,内部可多达,128KByte,的容量,因此可直接使用。,(,5,)处理器有将堆栈指针和其他,CPU,寄存器读出和存储到堆栈(或内存)的指令,C/OS-II,进行任务调度时,会把当前任务的,CPU,寄存器存到此任务的堆栈中,然后,再从另一个任务的堆栈中恢复原来的工作寄存器,继续运行另一个任务。所以,寄存器的入栈和出栈是,C/OS-II,多任务调度的基础。,运行,TCP,、,UDP,需要的内存会更大,通常要,100K,左右,8/17/2010,10,7.1.2,移植步骤,所谓移植,就是使一个实时操作系统能够在某个微处理器平台上或微控制器平台上运行。由,C/OS-II,的文件系统可知,在移植过程中,用户需要关注的就是与处理器相关的代码。这部分包括一个头文件,OS_CPU.H,、一个汇编文件,OS_CPU_A.ASM,和一个,C,代码文件,OS_CPU_C.C,。,OS_CPU.H,OS_CPU_C.C,OS_CPU_A.ASM,#define,设置一个常量的值,声明,10,个数据类型,用,#define,声明三个宏,用,C,语言编写六个简单的函数,编写四个汇编语言函数,移植,实际中,写一个就行,8/17/2010,11,1,、,INCLUDES.H,INCLUDES.H,是一个头文件,它在所有,.C,文件的第一行被包含。,#include includes.h,INCLUDES.H,使得用户项目中的每个,.C,文件不用分别去考虑它实际上需要哪些头文件。使用,INCLUDES.H,的唯一缺点是它可能会包含一些实际不相关的头文件。这意味着每个文件的编译时间可能会增加。但由于它增强了代码的可移植性,所以使用这一方法较为方便。用户可以通过编辑,INCLUDES.H,来增加自己的头文件,但是用户的头文件必须添加在头文件列表的最后。,2,、基本配置和定义,OS_CPU.H,(1),用,#define,设置一个常量的值,#ifdef OS_CPU_GLOBALS,#define OS_CPU_EXT,#else,#define OS_CPU_EXT extern,#endif,8/17/2010,12,(2),定义与编译器相关的数据类型,为了保证可移植性,程序中没有直接使用,C,语言中的,short,、,int,和,long,等数据类型的定义,因为它们与处理器类型有关,隐含着不可移植性。程序中自己定义了一套数据类型,如,INT16U,表示,16,位无符号整型。对于,ARM,这样的,32,位内核,,INT16U,是,unsigned short,型;如果是,16,位处理器,则是,unsinged int,型。,typedef unsigned char BOOLEAN;/*Boolean,布尔变量*,/,typedef unsigned char INT8U;/*,无符号,8,位实体 *,/,typedef signed char INT8S;/*,有符号,8,位实体 *,/,typedef unsigned short INT16U;/*,无符号,16,位实体 *,/,typedef signed short INT16S;/*,有符号,16,位实体 *,/,typedef unsigned int INT32U;/*,无符号,32,位实体 *,/,typedef signed int INT32S;/*,有符号,32,位实体 *,/,typedef float FP32 /*,单精度浮点数 *,/,typedef double FP64;/*,双精度浮点数 *,/,typedef unsigned int OS_STK;/*,堆栈是,32,位宽度*,/,typedef unsigned int OS_CPU_SR;/*,申明状态寄存器是,32,位*,/,C/OS-II,内核的代码需要与处理器位有关,8/17/2010,13,(3),定义临界段(允许和禁止中断宏),与所有实时内核一样,,C/OS-II,需要先禁止中断,再访问代码的临界区,并且在访问完毕后,重新允计中断。这就是,C/OS-II,能够保护临界段代码免受多任务或中断服务例程,ISR,的破坏。中断禁止时间是商业实时内核公司提供的重要指标之一,因为它将影响到用户的系统对实时事件的响应能力。虽然,C/OS-II,尽量使中断禁止时间达到最短,但是,C/OS-II,的中断禁止时间还主要依赖于处理器结构和编译器产生的代码的质量。通常每个处理器都会提供一定的指令来禁止,/,允许中断,因此用户的,C,编译器必须由一定的机制来直接从,C,中执行这些操作。,OS_ENTER_CRITICAL(),OS_EXIT_CRITICAL(),C/OS-II,定义了两个宏来禁止和允许中断:,#define OS_CRITICAL_METHOD 3,#define OS_ENTER_CRITICAL()cpu_sr=OS_CPU_SR_Save();#define OS_EXIT_CRITICAL()OS_CPU_SR_Restore(cpu_sr);,OS_CPU_A.ASM,中具体实现,C/OS-II,定义了三种方法关闭和打开中断(,OS_CRITICAL_METHED=1,2,3,),通常情况下,我们都是选用的方法,3,。,8/17/2010,14,OS_CPU_SR_Save,MRS R0,PRIMASK,CPSID I,BX LR,OS_CPU_SR_Restore,MSR PRIMASK,R0,BX LR,关中断,开中断,(4),定义栈的增长方向,C/OS-II,使用结构常量,OS_STK_GROWTH,来指定堆栈的增长方式:,置,OS_STK_GROWTH,为,0,,表示堆栈从下往上增长;,置,OS_STK_GROWTH,为,1,,表示堆栈从上往下增长。,Cortex-M3,支持从上往下增长的方式。因此,我们在移植时,需将,OS_STK_GROWTH=1,,如果是,51,系列单片机,则,OS_STK_GROWTH=0,。,#define OS_STK_GROWTH 1,8/17/2010,15,(5),定义,OS_TASK_SW(),宏,任务级上下文切换,任务级上下文切换(即任务切换)调用宏定义,OS_TASK_SW(),。因为上下文切换跟处理器有密切关系,,OS_TASK_SW(),实质上是调用汇编函数,OSCtxSW(),,它在,OS_CPU_A.ASM,文件中定义。,#define OS_TASK_SW()OSCtxSw(),OSCtxSw,PUSH R4,R5,LDR R4,=NVIC_INT_CTRL;,触发软件中断,LDR R5,=NVIC_PENDSVSET,STR R5,R4,POP R4,R5,BX LR,原型如下,:,打开,MDK,查看原始代码,NVIC_INT_CTRL EQU 0 xE000ED04,NVIC_PENDSVSET EQU 0 x10000000,当执行完这段代后,自运的产生,PendSV,中断,也即,14,号异常,自动跳到,14,号异常服务程序执行。,在本移植中则会直接去执行:,OSPendSV,部分内容,8/17/2010,16,中断控制及状态寄存器,ICSR 0 xE000_ED04,设置,1,将挂起中断,8/17/2010,17,#ifndef _OS_CPU_H,#define _OS_CPU_H,#ifdef OS_CPU_GLOBALS,#define OS_CPU_EXT,#else,#define OS_CPU_EXT extern,#endif,/*Date types(Compiler specific),数据类型(和编译器相关)*,/,typedef unsigned char BOOLEAN;/*Boolean,布尔变量 *,/,typedef unsigned char INT8U;/*Unsigned 8 bit quantity */,typedef signed char INT8S;/*Signed 8 bit quantity */,typedef unsigned short INT16U;/*Unsigned 16 bit quantity */,typedef signed short INT16S;/*Signed 16 bit quantity */,typedef unsigned int INT32U;/*Unsigned 32 bit quantity */,typedef signed int INT32S;/*Signed 32 bit quantity */,typedef float FP32;/*Single precision floating point*/,typedef double FP64;/*Double precision floating point*/,typedef unsigned int OS_STK;/*wide,堆栈是,32,位宽度 *,/,typedef unsigned int OS_CPU_SR;/*Define size of CPU statusregister */,/*Method of critical section management,临界区管理方法*,/,#define OS_CRITICAL_METHOD 4,/*Other definitions,其他定义*,/,#define OS_STK_GROWTH 1,#define OS_TASK_SW()OSCtxSw(),/*Prototypes(see OS_CPU_A.ASM),原型声明(见,OS_CPU_A.ASM,)*,/,#if OS_CRITICAL_METHOD=4,void OS_ENTER_CRITICAL(void);,void OS_EXIT_CRITICAL(void);,#endif,void OSCtxSw(void);,void OSIntCtxSw(void);,void OSStartHighRdy(void);,void OSPendSV(void);,OS_CPU_EXT INT32U OsEnterSum;,#endif,/*END FILE*/,8/17/2010,18,3,、移植汇编语言编写的,4,个与处理器相关的函数,OS_CPU_A.ASM,(1)OSStartHighRdy(),:运行优先级最高的就绪任务,OSStartHighRdy(),OSCtxSw(),OSIntCtxSw(),OSTickISR(),OSStartHighRdy(),函数是在,OSStart(),多任务启动之后,负责从最高优先级任务的,TCB,控制中获得该任务的堆栈指针,SP,,并通过,SP,依次将,CPU,现场恢复。这时系统就将控制权交给用户创建的任务进程,直到该任务被阻塞或都被其他更高优先级的任务抢占,CPU,。该函数仅仅在多任务启动时被执行一次,用来启动最高优先级的任务执行。移植该函数的原因是,它涉及将处理器寄存器保存到堆栈的操作。,8/17/2010,19,OSStartHighRdy,LDR R4,=NVIC_SYSPRI2 ;set the PendSV exception,;priority,设置,PendSV,优先级,LDR R5,=NVIC_PENDSV_PRI,STR R5,R4,MOV R4,#0 ;set the PSP to 0 for initial,;context switch call,使,PSP,等于,0,MSR PSP,R4,LDR R4,=OSRunning ;OSRunning=TRUE,MOV R5,#1,STRB R5,R4,LDR R4,=NVIC_INT_CTRL ;trigger the PendSV exception,;,触发软件中断,LDR R5,=NVIC_PENDSVSET,STR R5,R4,CPSIE I ;enable interrupts at processor,;level,使能所有优先级的中断,OSStartHang,B OSStartHang,8/17/2010,20,(2)OSCtxSw(),:任务优先级切换函数,该函数由,OS_TASK_SW(),宏调用,,OS_TASK_SW(),由,OSSched(),函数调用,,OSSched(),函数负责任务之间的调度。,OSCtxSw(),函数的工作是,先将当前任务的,CPU,现场保存到该任务的堆栈中,然后获得最高优先级任务的堆栈指针,并从该堆栈中恢复此任务的,CPU,现场,使之继续执行,该函数就完了一次任切换。,OSCtxSw,PUSH R4,R5,LDR R4,=NVIC_INT_CTRL;,触发软件中断,LDR R5,=NVIC_PENDSVSET,STR R5,R4,POP R4,R5,BX LR,产生,PendSV,异常,PendSV,并没有马上执行,因为,OS_TASK_SW(),(实际是,OSCtxSw(),)被调用前中断是关闭的。,PednSV,只能在中断使能后才能执行。,OS_TASK_SW(),总是被,OS_Sched(),调用(见,OS_CORE.C,文件),8/17/2010,21,8/17/2010,22,触发,PendSV,异常,当,PendSV,使能,执行此后将进入中断服务程序,8/17/2010,23,(3)OSInitCtxSw(),:中断级的任务切换函数,该函数由,OSIntExit(),调用。由于中断可能会使更高优先级的任务进入就绪态,因此,为了让更高优先级的任务能立即运行,在中断服务子程序的最后,,OSInitExit(),函数会调用,OSInitCtxSw(),做任务切换。这样做的目的主要是能够尽快地让高优先级的任务得到响应,保证系统的实时性能。,OSInitCtxSw(),与,OSCtxSw(),都是用于任务切换函数,其区别在于,在,OSIntCtxSw(),中无需再保存,CPU,寄存器,因为在调用,OSIntCtxSw(),之前已发生了中断,,OSIntCtxSw(),已将默认的,CPU,寄存器保存到被中断的任务堆栈中。,OSIntCtxSw,PUSH R4,R5,LDR R4,=NVIC_INT_CTRL;,触发软件中断,LDR R5,=NVIC_PENDSVSET,STR R5,R4,POP R4,R5,BX LR,NOP,OSCtxSw()OSIntCtxSw(),这两个函最终都会触发,PendSV,异常,8/17/2010,24,OSPendSV(),函数,OSPendSV(),是,PendSV(,可挂起中断服务,),的中断处理函数,它负责,C/OS-II,的全部上下文切换。这是,ARM Cortex-M3,提倡的上下文切换方法。使用这种方法的好处理当发生任何的异常时,,Cortex-m3,自动保存,CPU,的一半通用寄存器到预先指定的堆栈中,并且在退出异常前按顺序恢复寄存器。,OSPendSV(),只需保存剩下的,R4-R11,寄存器并且调整好堆栈指针。这种方法速度快,充分体现了,ARM Cortex-M3,的优势,而且无论是任务还是异常均可触发此函数切换上下文。注意使用前应在,Startup.S,中申明。,8/17/2010,25,8/17/2010,26,ARM Cortex-M3,任务切换示意图,8/17/2010,27,(4)OSTickISR(),:时钟节拍中断服务函数,时钟节拍是特定的周期性中断,是由硬件定时器产生的。时钟节拍式中断使得内核可将任务延时若干个整数时钟节拍,以及当任务等待事件发生时,提供等待超时的依据。时钟节拍频率越高,系统的额外开销越大。中断间的时间间隔取决于不同的应用。,OSTickISR(),首先将,CPU,寄存器的值保存在被中断任务的堆栈中,之后调用,OSIntEnter(),。随后,,OSTickISR(),调用,OSTimeTick,,检查所有处于延时等待状态的任务,判断是否有延时结束就绪的任务。,OSTickISR(),最后调用,OSIntExit(),。如果在中断中(或其他嵌套的中断)有更高优先级的任务就绪,并且当前中断为中断嵌套的最后一层,那么,OSIntExit(),将进行任务调度。,8/17/2010,28,*Function name:tickInit,*Descriptions:,初始化,uC/OS-II,的时钟源(系统定时器),static void tickInit(void),SysTickPeriodSet(INT32U)(SysCtlClockGet()/OS_TICKS_PER_SEC)-1);,SysTickEnable();,SysTickIntEnable();,*Function name:tickISRHandler,*Descriptions:,系统定时器超时中断,void tickISRHandler(void),#if OS_CRITICAL_METHOD=3,OS_CPU_SR cpu_sr;,#endif,OS_ENTER_CRITICAL();,OSIntNesting+;,OS_EXIT_CRITICAL();,OSTimeTick();,/*Call uC/OS-IIs OSTimeTick()*/,OSIntExit();,uC/OS-II,里面的东东,20,8/17/2010,29,4,、移植,C,语言编写的,6,个与操作系统相关的函数,OS_CPU_C.C,OS_CPU_C.C,文件中包含,6,个和,CPU,相关的函数,这,6,个函数为:,这些函数中,唯一必须移植的是任务堆栈初始化函数,OSTaskStkInit(),。这个函数在任务创建时被调用,负责初始化任务的堆栈结构并返回新堆栈的指针,stk,。堆栈初始化工作结束后,返回新的堆栈栈顶指针。,OSTaskStkInit(),OSTaskDelHook(),OSTaskSwHook(),OSTaskStartHook(),OSTimeHook(),8/17/2010,30,初始化堆栈,8/17/2010,31,8/17/2010,32,THE END,
展开阅读全文