nulluCOS II 移植uCOS II 移植詹劲松
zjs8012@yahoo.com.cn
Qq:327395709一:引入一:引入前面介绍了基于ucos的编程,但是前提是我们的板子已经移植好了ucos,我们才可以调用ucos自带的函数,才能实现多任务管理以及其它的功能。
那么ucos该如何移植呢?我们这次课来解决这个问题。
一:引入一:引入uCOS II是一个源码公开、可移植、可固化、可剪裁和抢占式的实时多任务操作系统。
移植:就是使得一个实时内核,或者应用的代码在某个微处理器活微控制器平台上运行。一:引入一:引入我们移植ucos之前必须下载ucos源码!
http://www.micrium.com/php/download.php?file=Micrium-uCOS-II-V286.ZIP下载ucos 2.86源码。这个需要注册用户才可以下载,注册一下很简单。
现在会有更新的版本了,这是我前年移植时使用的源码。 这样的版本几乎也够用了。一:引入一:引入为了方便移植:ucos其大部分源码是用ANSI C编写,与处理器硬件相关的部分使用汇编语言编写。总量约200行的汇编语言部分被压缩到最低限度。通常移植操作系统时候,汇编是无法避免的,因为处理器的寄存器只能通过汇编语言来实现。
事实上很多人都在arm上移植过ucos,我们直接使用人家的代码就可以了,但是我们依旧有必要了解移植原理和过程。
一:引入一:引入uC/OS-II硬件和软件结构体系
一:引入一:引入uC/OS-II硬件和软件结构体系
一:引入一:引入硬件平台
要求
对教师党员的评价套管和固井爆破片与爆破装置仓库管理基本要求三甲医院都需要复审吗
1. 处理器的C编译器能产生可重入代码。
2. 用C 语言就可以打开和关闭中断。
3. 处理器支持中断,并且能产生定时中断(通常在10 至100Hz 之间)。
4. 处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。
5. 处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令。一:引入一:引入现在使用的处理器大都可以满足这方面的要求。(简单单片机,MOTOROLA 6805等无法满足)一:引入一:引入移植主要工作
用#define 设置一个常量的值(OS_CPU.H)
声明10 个数据类型(OS_CPU.H)
用#define 声明三个宏(OS_CPU.H)
用C 语言编写六个简单的函(OS_CPU_C.C)
编写四个汇编语言函数(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
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示一个8bits的整型数据类型。然而,这些类型,编译器是不认识,编译器只认识int之类的定义类型。所以我们移植的第一个工作,就是将BOOLEAN,INT8U,INT8S, INT16U,INT16S, INT32U,INT32S, FP16, FP32, OS_STK.转换成编译器能识辨的类型。二:移植二:移植对于32位处理器来说,我们可以这样
typedef unsigned char 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; /* 单精度浮点数(32位长度)*/
typedef INT32U OS_STK; /* 堆栈是32位宽度 */二:移植二:移植对于16位处理器来说,我们可以这样
typedef unsigned char BOOLEAN; /* 布尔 */
typedef unsigned char INT8U; /* 无符号8位整型*/
typedef signed char INT8S; /* 有符号8位整型 */
typedef unsigned int INT16U; /* 无符号16位整型*/
typedef signed int INT16S; /* 有符号16位整型*/
typedef unsigned double INT32U; /* 无符号32位整型*/
typedef signed double INT32S; /* 有符号32位整型*/
typedef double FP32; /* 单精度浮点数(32位长度)*/
typedef INT32U OS_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()二:移植二:移植因为不同的处理器对中断的控制有所不同,所以移植工程师需要根据自己的情况编写这一段代码:
二:移植二:移植AREA OS_CPU_SR_Save_CODE,CODE,READONLY CODE32
OS_CPU_SR_Save
MRS R0,CPSR
ORR R1,R0,#NO_INT
MSR CPSR_c,R1
MRS R1,CPSR
AND R1,R1,#NO_INT
CMP R1,#NO_INT
BNE OS_CPU_SR_Save
MOV PC,LR
OS_CPU_SR_Restore
MSR CPSR_c,R0
MOV PC,LR二:移植二:移植#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();
#define OS_EXIT_CRITICAL() {OS_CPU_IntDisMeasStop();}二:移植二:移植3:OS_STK_GROWTH 设置系统堆栈的生长方式。
UCOS工作的时候,我们要使用到堆栈,如任务切换时候,要将当前正在处理的任务工作状态零时保存到堆栈中去,以备这个任务重新执行时候,ucos知道任务执行的切入点。而堆栈的生长方式对于不同处理器是不同的,有些处理器只能支持单向增长方式。二:移植二:移植对于arm来说,支持双向增长方式,所以这个很简单。
#define OS_STK_GROWTH 1 /* Stack grows from HIGH to LOW memory on ARM */
或者
#define OS_STK_GROWTH 0 /* Stack grows from LOW to HIGH memory on ARM */二:移植二:移植4: OS_TASK_SW()
OS_TASK_SW()是一个宏,它是在μC/OS-Ⅱ从低优先级任务切换到最高优先级任务时被调用的。OS_TASK_SW()总是在任务级代码中被调用的。另一个函数OSIntExit()被用来在ISR使得更高优先级任务处于就绪状态时,执行任务切换功能。任务切换只是简单的将处理器寄存器保存到将被挂起的任务的堆栈中,并且将更高优先级的任务从堆栈中恢复出来。
二:移植二:移植#define OS_TASK_SW() OSCtxSw()
至于OSCtxSw()如何实现。具体实现参看移植工程。
至此,第一个文件移植的主要工作已经完成,下面进入OS_CPU_A.ASM的移植工作。二:移植二:移植在OS_CPU_A.ASM 中,uC/OS-II需要用户编写的7个简单的汇编语言函数:
OS_CPU_SR_Save
OS_CPU_SR_Restore
OSStartHighRdy
OSCtxSw
OSIntCtxSw
OS_CPU_IRQ_ISR
OS_CPU_FIQ_ISR二:移植二:移植5:OS_CPU_SR_Save 和OS_CPU_SR_Restore
前面已经介绍过如何实现,主要实现对中断的使能和禁止二:移植二:移植6:OSStartHighRdy
Ucos刚开始启动时,会调用OSStartHighRdy用于启动当前优先级最高的任务。在后面正常代码执行时,就不会用这个函数来启动优先级最高的函数了。具体代码参考移植工程。
二:移植二:移植 MSR CPSR_c, #(NoInt | SYS32Mode) ;进入管理模式关掉中断
LDR R4, =OSRunning ;告诉OS任务正在运行
MOV R5, #1
STRB R5, [R4]
BL OSTaskSwHook ;调用钩子函数
LDR R6, =OSTCBHighRdy ;获取最高就绪任务堆栈指针的指针
LDR R6, [R6] ;获取新任务堆栈指针
LDR R4, [R6] ;得到确切地址
ADD SP, R4, #68 ;17寄存器CPSR,OsEnterSum,R0-R12,LR,SP
LDR LR, [SP, #-8]
MSR CPSR_c, #(NoInt | SVC32Mode) ;进入管理模式
MOV SP, R4 ;设置堆栈指针
LDMFD SP!, {R4, R5} ;CPSR,OsEnterSum
;恢复新任务的OsEnterSum
LDR R3, =OsEnterSum
STR R4, [R3]
MSR SPSR_cxsf, R5 ;恢复CPSR
LDMFD SP!, {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。所以我们需要将这两个函数映射到这两个地址中去。实现方法:
二:移植二:移植 b HandlerIRQ; 0x18
…………….
HandlerIRQ HANDLER HandleIRQ
;可见调用地址为HandleIRQ的函数二:移植二:移植^ _ISR_STARTADDRESS;这一段空间存储终端服务函数的地址。
HandleReset # 4
HandleUndef # 4
HandleSWI # 4
HandlePabort # 4
HandleDabort # 4
HandleReserved # 4
HandleIRQ # 4
可见HandleIRQ的地址位于(*(unsigned *)(_ISR_STARTADDRESS+0x18))二:移植二:移植而44b.h文件中又有另一段定义:
#define pISR_IRQ (*(unsigned *)(_ISR_STARTADDRESS+0x18))
#define pISR_FIQ (*(unsigned *)(_ISR_STARTADDRESS+0x1c))二:移植二:移植 事实上pISR_IRQ和HandleIRQ是完全相同的,只是前者习惯于C语言上用,而后者习惯于汇编。二:移植二:移植后面只需要将ucos提供的OS_CPU_IRQ_ISR服务函数存储在地址为pISR_IRQ 既可以了
:
pISR_IRQ=(int)OS_CPU_IRQ_ISR;
二:移植二:移植OS_CPU_IRQ_ISR如何工作呢?它首先将当前任务的寄存器状态压入堆栈,然后调用OS_CPU_IRQ_ISR_Handler函数。
OS_CPU_IRQ_ISR_Handler是要求我们来写的,我们根据不同的中断号调用不同的函数,不过这就可以用C语言实现了,如:
nullvoid OS_CPU_IRQ_ISR_Handler(void)
{
INT32U temp,temp1;
temp1 = ~rINTMSK;
temp = rI_ISPR&temp1;
switch (temp)
{
case 0x00000080:rI_ISPC=0x00000080;
Uart0irq();
break;
case 0x00002000:rI_ISPC=0x00002000;
timer0_int();
break;
default :break;
}
}二:移植二:移植void timer0_int(void)//很重要
{
OSTimeTick();
}
这个函数用于提供时钟节拍,当然之前要将timer0初始化到200Hz的中断频率。二:移植二:移植void Uart0irq()
{
char ch;
while(rUFSTAT0&0x0f)
{
ch=rURXH0;
UartSend(0,ch);
}
}二:移植二:移植OS_CPU_C.C文件的实现
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()二:移植二:移植事实上,我们只需要实现第一个函数就可以了。而其它函数成为挂钩函数。是ucos源码作者为应用工程师提供扩展功能用的,当然一般是不用实现的。二:移植二:移植10: OSTaskStkInit()
OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStkInt()来初始化任务的堆栈结构.实现起来很简单,根据堆栈的增长方式,随机赋予初始值就可以了。
二:移植二:移植OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
opt = opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
*(stk) = (OS_STK)task; /* Entry Point */
*(--stk) = (INT32U)0x14141414L; /* R14 (LR) */
*(--stk) = (INT32U)0x12121212L; /* R12 */
*(--stk) = (INT32U)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7 */
*(--stk) = (INT32U)0x06060606L; /* R6 */
*(--stk) = (INT32U)0x05050505L; /* R5 */
*(--stk) = (INT32U)0x04040404L; /* R4 */
*(--stk) = (INT32U)0x03030303L; /* R3 */
*(--stk) = (INT32U)0x02020202L; /* R2 */
*(--stk) = (INT32U)0x01010101L; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : argument */
*(--stk) = (INT32U)ARM_SYS_MODE; /* CPSR (Enable both IRQ and FIQ interrupts) */
return (stk);
}二:移植二:移植 而其它几个hook函数,我们是无需作改变的。就已经满足要求了。如:
当用OSTaskCreate() 或OSTaskCreateExt() 建立任务的时候就会调用OSTaskCreateHook()。该函数允许用户或使用用户的移植实例的用户扩展μC/OS-Ⅱ的功能。当μC/OS-Ⅱ设置完了自己的内部结构后,会在调用任务调度程序之前调用
OSTaskCreateHook()。该函数被调用的时候中断是禁止的。因此用户应尽量减少该函数中的代码以缩短中断的响应时间。
二:移植二:移植 其它几个函数也类似,至此移植工作完成,但是我们说嵌入式操作系统还具有裁剪的功能,就是根据需要去除不需要的代码,以使得系统更小更稳定。主要有文件OS_CFG.H来完成的。三:裁剪三:裁剪参看OS_CFG.H里面内容设置