uCOSII移植一:引入•前面介绍了基于ucos的编程,但是前提是我们的板子已经移植好了ucos,我们才可以调用ucos自带的函数,才能实现多任务管理以及其它的功能。•那么ucos该如何移植呢?我们这次课来解决这个问题。一:引入•uCOSII是一个源码公开、可移植、可固化、可剪裁和抢占式的实时多任务操作系统。•移植:就是使得一个实时内核,或者应用的代码在某个微处理器或微控制器平台上运行。一:引入•我们移植ucos之前必须下载ucos源码!=Micrium-uCOS-II-V286.ZIP下载ucos2.86源码。这个需要注册用户才可以下载,注册一下很简单。现在会有更新的版本了,这是我前年移植时使用的源码。这样的版本几乎也够用了。一:引入•为了方便移植:ucos其大部分源码是用ANSIC编写,与处理器硬件相关的部分使用汇编语言编写。总量约200行的汇编语言部分被压缩到最低限度。通常移植操作系统时候,汇编是无法避免的,因为处理器的寄存器只能通过汇编语言来实现。•事实上很多人都在arm上移植过ucos,我们直接使用人家的代码就可以了,但是我们依旧有必要了解移植原理和过程。一:引入•uC/OS-II硬件和软件结构体系一:引入•uC/OS-II硬件和软件结构体系一:引入•硬件平台要求1.处理器的C编译器能产生可重入代码。2.用C语言就可以打开和关闭中断。3.处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)。4.处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。5.处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令。一:引入•现在使用的处理器大都可以满足这方面的要求。(简单单片机,MOTOROLA6805等无法满足)一:引入•移植主要工作1.用#define设置一个常量的值(OS_CPU.H)2.声明10个数据类型(OS_CPU.H)3.用#define声明三个宏(OS_CPU.H)4.用C语言编写六个简单的函(OS_CPU_C.C)5.编写四个汇编语言函数(OS_CPU_A.ASM)根据处理器的不同,一个移植实例可能需要编写或改写50至300行的代码,需要的时间从几个小时到一星期不等。二:移植•第一部分工作,从OS_CPU.H开始1:Ucos作者为了让ucso具有较强的移植性,在ucos源码中所有的数据类型,均不使用我们C语言中的int,short,long,double之类,而是用BOOLEAN,INT8U,INT8S,INT16U,INT16S,INT32U,INT32S,FP16,FP32,OS_STK.为什么呢?二:移植•int,short,long,double的数据宽度与CPU的数据的宽度有关。比如对于16位处理器来说,编译器将int编译成16位宽度的整型数据;而对于32位处理器来说,编译器将int编译成32位宽度的整型数据。如果将ucos中的数据类型是用int,short,long,double定义,就会导致在不同的处理器中数据宽度是不一样的,很容易导致溢出等一些问题。二:移植•为此ucos源码作者直接使用而是用BOOLEAN,INT8U,INT8S,INT16U,INT16S,INT32U,INT32S,FP16,FP32,OS_STK.作为数据变量类型。比如INT8U表示一个8bits的整型数据类型。然而,这些类型,编译器是不认识,编译器只认识int之类的定义类型。所以我们移植的第一个工作,就是将BOOLEAN,INT8U,INT8S,INT16U,INT16S,INT32U,INT32S,FP16,FP32,OS_STK.转换成编译器能识辨的类型。•对于32位处理器来说,我们可以这样typedefunsignedcharBOOLEAN;/*布尔*/typedefunsignedcharINT8U;/*无符号8位整型*/typedefsignedcharINT8S;/*有符号8位整型*/typedefunsignedshortINT16U;/*无符号16位整型*/typedefsignedshortINT16S;/*有符号16位整型*/typedefunsignedintINT32U;/*无符号32位整型*/typedefsignedintINT32S;/*有符号32位整型*/typedeffloatFP32;/*单精度浮点数(32位长度)*/typedefINT32UOS_STK;/*堆栈是32位宽度*/二:移植•对于16位处理器来说,我们可以这样typedefunsignedcharBOOLEAN;/*布尔*/typedefunsignedcharINT8U;/*无符号8位整型*/typedefsignedcharINT8S;/*有符号8位整型*/typedefunsignedintINT16U;/*无符号16位整型*/typedefsignedintINT16S;/*有符号16位整型*/typedefunsigneddoubleINT32U;/*无符号32位整型*/typedefsigneddoubleINT32S;/*有符号32位整型*/typedefdoubleFP32;/*单精度浮点数(32位长度)*/typedefINT32UOS_STK;/*堆栈是32位宽度*/二:移植二:移植2:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()与所有的实时内核一样,μC/OS-Ⅱ需要先禁止中断再访问代码的临界段,并且在访问完毕后重新允许中断。这就使得μC/OS-Ⅱ能够保护临界段代码免受多任务或中断服务例程(ISRs)的破坏。中断禁止时间是商业实时内核公司提供的重要指标之一,因为它将影响到用户的系统对实时事件的响应能力。虽然μC/OS-Ⅱ尽量使中断禁止时间达到最短,但是μC/OS-Ⅱ的中断禁止时间还主要依赖于处理器结构和编译器产生的代码的质量。二:移植通常每个处理器都会提供一定的指令来禁止/允许中断,因此用户的C编译器必须要有一定的机制来直接从C中执行这些操作。有些编译器能够允许用户在C源代码中插入汇编语言声明。这样就使得插入处理器指令来允许和禁止中断变得很容易了。其它一些编译器实际上包括了语言扩展功能,可以直接从C中允许和禁止中断。为了隐藏编译器厂商提供的具体实现方法,μC/OS-Ⅱ定义了两个宏来禁止和允许中断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()二:移植•因为不同的处理器对中断的控制有所不同,所以移植工程师需要根据自己的情况编写这一段代码:二:移植AREAOS_CPU_SR_Save_CODE,CODE,READONLYCODE32OS_CPU_SR_SaveMRSR0,CPSRORRR1,R0,#NO_INTMSRCPSR_c,R1MRSR1,CPSRANDR1,R1,#NO_INTCMPR1,#NO_INTBNEOS_CPU_SR_SaveMOVPC,LROS_CPU_SR_RestoreMSRCPSR_c,R0MOVPC,LR二:移植#defineOS_ENTER_CRITICAL(){cpu_sr=OS_CPU_SR_Save();#defineOS_EXIT_CRITICAL(){OS_CPU_IntDisMeasStop();}二:移植3:OS_STK_GROWTH设置系统堆栈的生长方式。UCOS工作的时候,我们要使用到堆栈,如任务切换时候,要将当前正在处理的任务工作状态零时保存到堆栈中去,以备这个任务重新执行时候,ucos知道任务执行的切入点。而堆栈的生长方式对于不同处理器是不同的,有些处理器只能支持单向增长方式。二:移植对于arm来说,支持双向增长方式,所以这个很简单。#defineOS_STK_GROWTH1/*StackgrowsfromHIGHtoLOWmemoryonARM*/或者#defineOS_STK_GROWTH0/*StackgrowsfromLOWtoHIGHmemoryonARM*/二:移植4:OS_TASK_SW()OS_TASK_SW()是一个宏,它是在μC/OS-Ⅱ从低优先级任务切换到最高优先级任务时被调用的。OS_TASK_SW()总是在任务级代码中被调用的。另一个函数OSIntExit()被用来在ISR使得更高优先级任务处于就绪状态时,执行任务切换功能。任务切换只是简单的将处理器寄存器保存到将被挂起的任务的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。二:移植#defineOS_TASK_SW()OSCtxSw()至于OSCtxSw()如何实现。具体实现参看移植工程。至此,第一个文件移植的主要工作已经完成,下面进入OS_CPU_A.ASM的移植工作。二:移植在OS_CPU_A.ASM中,uC/OS-II需要用户编写的7个简单的汇编语言函数:OS_CPU_SR_SaveOS_CPU_SR_RestoreOSStartHighRdyOSCtxSwOSIntCtxSwOS_CPU_IRQ_ISROS_CPU_FIQ_ISR二:移植5:OS_CPU_SR_Save和OS_CPU_SR_Restore前面已经介绍过如何实现,主要实现对中断的使能和禁止二:移植6:OSStartHighRdyUcos刚开始启动时,会调用OSStartHighRdy用于启动当前优先级最高的任务。在后面正常代码执行时,就不会用这个函数来启动优先级最高的函数了。具体代码参考移植工程。二:移植MSRCPSR_c,#(NoInt|SYS32Mode);进入管理模式关掉中断LDRR4,=OSRunning;告诉OS任务正在运行MOVR5,#1STRBR5,[R4]BLOSTaskSwHook;调用钩子函数LDRR6,=OSTCBHighRdy;获取最高就绪任务堆栈指针的指针LDRR6,[R6];获取新任务堆栈指针LDRR4,[R6];得到确切地址ADDSP,R4,#68;17寄存器CPSR,OsEnterSum,R0-R12,LR,SPLDRLR,[SP,#-8]MSRCPSR_c,#(NoInt|SVC32Mode);进入管理模式MOVSP,R4;设置堆栈指针LDMFDSP!,{R4,R5};CPSR,OsEnterSum;恢复新任务的OsEnterSumLDRR3,=OsEnterSumSTRR4,[R3]MSRSPSR_cxsf,R5;恢复CPSRLDMFDSP!,{R0-R12,LR,PC}^;运行新任务二:移植7:OSCtxSw当优先级更高的任务已经就绪时,当前任务需要交出cpu控制权,而进行切换,这是通过OSCtxSw实现的,主要任务为,保存当前任务的寄存器状态至堆栈,装在更高任务的堆栈内容至寄存器。二:移植8:OSIntCtxSw这个函数和OSCtxSw类似,也用于任务的切换,所不同的是,是从中断服务程序退出时,进行任务切换时调用的函数。同为任务切换,为何需要两个函数来实现。因为从任务切换到另一个任务时,需要保存任务状态,和装载任务状态过程。而从中断切换另一个任务时,因为中断时,任务的工作状态已经保存,现在切换只需要装载更高级的任务状态就可以了。二:移植大家可以观察工程中这两个函数的源码,OSIntCtxSw的工作量确实只有OSCtxSw的一半二:移植9:OS_CPU_IRQ_ISR和OS_CPU_FRQ_ISR这连个函数是arm被中断时,ucos提供的响应函数,分别对应普通中断和快速中断。实现方法类似,都包括寄存器数据保存,和根据中断号调用对应中断服务。二:移植我们知道,当arm中断时,程序会跳入0x18地址或0x1c运行,主要看是普通中断和快速中断了。而ucos提供的中断相应函数是OS_CPU_IRQ_ISR和OS_CPU_FRQ_ISR。所以我们需要将这两个函数映射到这两个地址中去。实现方法:二:移植bHandlerIRQ;0x18…………….HandlerIRQHANDLERH