嵌入式实时操作系统RTOS 与通用操作系统相比较, 它具有实时性、可裁减、低资源占用等特点。而与传统的嵌入式设计方法相比, 实时多任务内核的运用允许程序员将具体的应用程序模块化, 更易于项目的开发。 1 uC/OS-II的简介 目前市场上的商用嵌入式操作系统, 如Vxworks、PSOS 和Windows CE 等已经十分成熟, 提供有力的开发和调试工具, 但开发成本昂贵, 实时嵌入式操作系统uCOS-II 是基于优先级的抢占式实时多任务操作系统, 包含了实时内核、任务管理、时间管理、任务间通信同步( 信号量, 邮箱, 消息队列) 和内存管理等功能。绝大部分代码用C 语言写成, 与硬件相关部分用汇编语言编写。 uC/OS-II是面向中小型嵌入式系统的。包含全部功能模块的内核大约为10KB, 如果经过裁减只保留核心代码, 则可压缩到3KB 左右。RAM的应用量与系统中的任务数有关, 任务的堆栈要占用大量的RAM空间, 堆栈的大小取决于任务的局部变量、缓冲区大小及可能的中断嵌套层数。应用程序的精度由系统的时钟节拍决定, uC/OS-II要用户提供周期性的时钟信号源, 用于实现时间延时和确认超时。 uC/OS-II的工作原理如下: 首先把CPU 初始化, 再进行操作系统初始化。主要完成任务控制块(TCB) 初始化、TCB 优先级表初始化、TCB 链表初始化、事件控制块ECB 链表初始化和空任务的创建等; 然后开始创建新任务, 并可在新创建的任务中再创建其它的新任务; 最后调用OSSTART() 函数启动多任务调度。在多任务调度开始后, 启动时钟节拍源开始计时, 此节拍源给系统提供周期性的时钟中断信号, 实现延时和超时确认。当时钟中断来临时, 系统把正在执行的任务挂起, 保护现场, 进行中断处理, 判断有无任务延时到期, 若有则使该任务进入就绪态, 并把所有进入就绪态的任务的优先级进行比较, 通过任务切换去执行最高优先级的任务。若没有别的任务进入就绪态, 则恢复现场继续执行原 任务。另一种的调度方式是任务级的调度, 是通过发软中断命令或依靠处理器在任务执行中调度。如任务要等待信号量或一个正在执行的任务被悬挂起来时, 就需要在此任务中调度, 找出目前处于就绪态的优先级最高的任务去执行。当没有任何任务进入就绪态时, 就去执行新任务。 2 uC/OS-II的移植 本文是以三星公司的S3C44B0X 微处理器作为基础, 在S3C44B0X 微处理器上实现移植。S3C44B0X 是ARM CPU 嵌入式控制器总线结构, 特别适用于低成本、低功耗的应用。其CPU 采用ARM公司的ARM7 TDMI RISC 结构。ARM7 TDMI 系统扩充包括thumb 协处理器和32 位乘法器等。 要实现uC/OS-II的移植, 最关键的就是要修改与处理器类型有关部分的代码, 也就是进行以下几项工作: (1)修改OS_CPU.H 文件 (2)修改OS_CPU_C.C 文件 (3)修改OS_CPU_A.ASM文件
2.1 OS_CPU.H 文件 OS_CPU.H 文件包括了用#define 语句定义的、与处理器相关的常数、宏以及类型。 2.1.1 数据类型 定义ARM中的数据类型 #define unsigned char BOOLEAN; #define unsigned char INT8U; /*8 位无符号整数*/ #define signed char INT8S; /*8 位有符号整数*/ #define unsigned int INT16U; /*16 位无符号整数*/ #define signed int INT16S; /*16 位有符号整数*/ #define unsigned long INT32U; /*32 位无符号整数*/ #define signed long INT32S; /*32 位有符号整数*/ #define float FP32; /* 单精度浮点数*/ #define double FP64; /* 双精度浮点数*/ typedef unsigned int OS_STK; /* 堆栈入口宽度为16 位*/
2.1.2 代码临界区 RTOS 在进入系统临界区前必须关闭中断, 退出临界区后再开中断。uC/OS-II定义了两个宏来开关中断: OS_ENTER_CRITICAL() 和OS_EXIT_CRITICAL( ) 。 为实现开关中断, 宏定义为: #define OS_ENTER_CRITICAL( ) ARMDisableInt() /* 关闭中断*/ #define OS_EXIT_CRITICAL( ) ARMEnableInt() /* 开启中断*/
2.1.3 栈增长方向 在uC/OS-II中, 用OS_STK_GROWTH 来设置堆栈的增长方向, OS_STK_GROWTH 为0 表示堆栈从低地址向高地址增长;OS_STK_GROWTH 为1 表示堆栈从高地址向低地址增长, 其宏定义为: #define OS_STK_GROWTH 1; /* 堆栈从高地址向低地址增长*/ #define OS_STK_GROWTH 0; /* 堆栈从低地址向高地址增长*/
2.2 OS_CPU_C.C 文件 在此文件中, 要求我们必须编写10 个简单的C 函数, 唯一必要的函数是OsTaskStkInt()函数。其他9 个函数必须声明, 但可以不包含任何代码。OsTaskStkInt()由任务创建函数OSTaskCreate()或OSTaskCreateEXT()调用, 用来初始化任务的堆栈。 Void OSTaskStkInt (void (*task) (void *pd),void *pdata,void *ptos, INT16U opt) {unsigned int *stk; stk=(unsigned int*)ptos; /* 装载堆栈指针*/ *-- stk=(unsigned int) task; /*pc*/ *-- stk=(unsigned int) task; /*lr*/ *-- stk=0; /*r1- r12*/ *-- stk=(unsigned int) pdata; /*r0*/ *-- stk=(SVC32MODE/0x0); /*cpsr IRQ,关闭FIQ*/ *-- stk=(SVC32MODE/0x0); /*spsr IRQ,关闭FIQ*/ return ((void*)stk);} void OStaskCreateHook () void OStaskDelHook () void OStaskSwHook () void OStaskStatHook () void OSTimeTickHook () void OStaskIdleHook () void OStaskHookBegin () void OStaskHookend ()
2.3 OS_CPU_A.ASM文件 在此文件中需改写4 个简单汇编语言函数: OSStartHighRdy()、OSCtxSw()、OSIntCtxSw()和OSTickISR()。 2.3.1 OSStartHighRdy()函数 该函数由Osstart()函数调用, 功能是运行优先级最高的就绪任务, 代码为: LDR r4,addr_OSTCBCur; /* 得到当前任务的TCB 地址*/ LDR r5,addr_OSTCBHighRdy; /* 得到高优先级任务的TCB 地址*/ LDR r5,[r5]; /* 得到堆栈指针*/ LDR sp,[r5]; /* 切换到新的堆栈*/ STR r5,[r4]; /* 设置新的当前任务的TCB 地址*/ LDMFD sp! , {r4}; /* 从栈顶得到新的声明*/ LDMFD sp! , {r0- r12,lr,pc}; /* 恢复任务环境*/ RETI ; /* 中断返回, 开始新的任务*/
2.3.2 OSCtxSw()函数 OSCtxSw()函数是一个任务级的任务切换函数。软中断向量指向此函数。在uC/OS-II中, 如果任务调用了某个函数, 而该函数的执行结果可能造成系统任务的重新调度, 则在函数的末尾会调用OSSched(),OSSched()查找当前就绪最高优先级的任务, 如果不是当前任务, 则找该任务的TCB 地址, 并复制到变量OSTcbHigh-Rdy()中, 然后通过宏OS_TASK_SW( ) 执行软中断调用OSCtxSw()进行任务切换。变量OSTCBCur 始终包含指向当前运行任务TCB的指针, 代码如下: STMFD sp! ,{lr}; /* 保存PC 指针*/ STMFD sp! ,{r0- r12}; /* 保存寄存器文件和RET 地址*/ STMFD sp! ,{r4}; /* 保存当前PSR*/ LDR r4,addr_OSTCBCur; /* 得到当前任务的TCB 地址*/ STR sp,[r5]; /* 保存栈指针在占先任务的TCB 上*/ LDR r6,addr_OSTCBHighRdy; /* 取得高优先级任务的TCB 地址*/ LDR sp,[r6]; /* 得到新任务的堆栈指针*/ LDMFD sp!,{r4} ; /* 设置当前新任务的TCB 地址*/
2.3.3 OSIntCtxSw()函数 OSIntCtxSw()函数进行中断级任务切换。中断可能引起任务切换, 在中断服务程序的最后会调用OSIntExit()函数检查任务就绪状态, 如果需要任务切换则调用OSIntCtxSw()函数。其代码如下: LDMIA sp! ,{a1- v1,lr} SUBS pc,lr,#4 MOV r12,lr MRS lr,SPSR AND lr,lr,#0xFFFFFFE0 MSR CPSR_cxsf,lr
2.3.4 OSTickISR()函数 OSTickISR()是中断处理函数, 其主要任务是处理时钟中断,调用系统实现的OSTimeTick()函数, 如果有等待时钟信号的高优先级任务, 则需要在中断级别上调度执行。其代码如下: ORR r0,r0,#0x80; /* 设置中断禁止标志*/ MSR CPSR_cxsf,r0; /* 中断结束*/ LDR r0,=I_ISPC LDR r1,=BIT_TIMER0 LDREQ pc,=_CON_SW 3 结束语 嵌入式实时操作系统RTOS 的使用使得应用程序的设计过程大为简化。并且程序的可读性、可靠性、可扩展性有很大的改善。本文从实际出发, 论述了uC/OS-II 操作系统在ARM处理器移植。经过移植后的操作系统经过测试, 运行稳定, 并达到了实时系统的要求。
|