0 引言
嵌入式系统已经广泛渗透到了人们工作、生活中的各个领域,嵌入式处理器已占分散处理器市场份额的94%,其中ARM的应用最为广泛。基于ARM内核的处理器以其诸多优异性能而成为各类产品中选用较多的处理器之一。
当系统越来越大,应用越来越多时,就出现了如何管理众多的硬件资源,以及如何满足系统的实时控制要求和如何提高系统软件开发效率等不可回避的问题。这时,使用嵌入式操作系统很有必要。操作系统的主要作用有:统一管理系统资源;为用户提供访问硬件的接口;调度多个应用程序和管理文件系统等。
1 概述
μC/OS-II是著名的、源码公开的实时内核,是专为嵌入式应用设计的,可用于各类8位、16位和32位处理器。μC/OS-II已经在世界范围内得到广泛使用,包括诸多领域,如手机、飞行器、医疗设备及工业控制等。实际上,μC/OS-II已经通过了非常严格的测试,并且得到了美国航空管理局的认证,可以用在飞行器上。这说明μC/OS-II是稳定可靠的。
Cortex-M3是一款低功耗处理器,具有门数目少,中断延迟短,调试成本低的特点,是为要求有快速中断响应能力的深度嵌入式应用而设计的。该处理器采用最新的ARMv7-M架构。还具有如下特性:
(1)采用Thumb-2指令集。在Thumb-2中,16位指令首次与32位指令并存,代码密度得到很大改善。
(2)Cortex-M3处理器可配置为具有SW-DP或JTAG-DP调试端口。
(3)使用可选的MPU对处理器提供存储器保护。
(4)具有嵌套向量中断控制器(NVIC),低延迟的异常处理。
本论文采用的是意法半导体ST公司生产的基于Cortex-M3内核的STM32F103ZE处理器,详细论述了μC/OS-Ⅱ的移植过程。编译环境采用的是RVMDKV3.7。
2 移植工作
所谓移植,就是使一个实时内核能在指定的微处理器上运行。为了方便移植,大部分μC/OS-II的代码都是用C语言编写的,但是仍需要用C语言和汇编语言编写一些与处理器硬件相关的代码。μC/OS-II是第一个支持Cortex的RTOS,图1显示应用程序、μC/OS-II、port和BSP四者之间的关系。具体移植工作主要涉及到头文件OS_CPU.H、C语言文件OS_CPU_C.C以及汇编格式文件OS_CPU_A.ASM。
2. 1 OS_CPU.H
OS_CPU.H包含处理器需要的用#defines语句定义的、与处理嘉相关的常数、宏以及类型。因为不同的处理器有不同的字长,μC/OS-II的移植包括了一系列的数据类型定义,以确保其可移植性。μC/OS-II内核代码不使用C语言中的short、int及long等数据类型,因为它们是编译器相关的,是不可移植的。Cortex-M3是32位处理器,同时参照RVMDK编译器文档,对μC/OS-II内核中的数据类型作如下定义:
…
typedef unsigned int OS_STK;
typedef unsigned int OS_CPU_SR;
OS_STK定义的是32位宽的堆栈入口地址数据类型,OS_CPU_SR定义的是32位宽的Cortex-M3处理器状态寄存器数据类型。
和所有的实时内核一样,μC/OS-II为了访问临界区的代码需要关闭中断,访问完,重新使能中断。为了增加可移植性,μC/OS-II定义了两个宏分别关闭和使能中断一OS_ENTER_CRITICAL()、OS_EXIT_CRITICAL()。μC/OS-II定义了三种关闭和使能中断方法,只需要使用其中一个。多数情况下,推荐使用OS_CRITICAL_METHOD#3。
OS_CRITICAL_METHOD#3通过写一个保存CPU状态寄存器在一个临时变量里的函数,实现OS_ENTER_CRITICAL()。OS_EXIT_CRITICAL()调用另一个函数从临时变量恢复CPU状态寄存器。
绝大多数微处理器和控制器的堆栈是从上往下递减的,但是也有些处理器使用的是相反的方式。μC/OS-II对两种都可以处理,只要配置常数OS_STK_GROWTH指定堆栈的方向就可以了。ARM Cortex-M3的堆栈增长是从高内存地址往低内存地址,因此,OS_STK_GROWTH应该设置为l。
2. 2 OS_CPU_C.C
移植μC/OS-Ⅱ要求编写10个简单的C语言函数,但唯一必要的函数是OSTaskStkInit(),其他9个函数必须申明,但并不一定要包含任何代码,允许用户在必要时添加扩展OS功能的代码。
OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStklnit(),初始化任务堆栈。因此,堆栈看起来像中断刚发生过一样,所有寄存器都保存在堆栈中。多数CPU寄存器的初始值并不重要,但是为了方便调试和检查堆栈,用寄存器序号去初始化它们。寄存器的顺序是重要的,应该和ARM Cortex-M3在发生异常时的堆栈顺序一样。
2. 3 OS_CPU_A.ASM
移植μC/OS-II要求编写5个简单的汇编语言函数。
OS_CPU_SR_Save()
OS_CPU_SR_Restore()
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OS_CPU_SR_Save()按照OS_CRITICAL_METHOD#3的方式,首先保存中断屏蔽寄存器,接着关闭中断。这个函数被宏OS_ENTER_CRITICAL()调用。当这个函数返回时,RO包含关闭中断前的中断屏蔽寄存器的状态。
OS_CPU_SR_Restore()恢复在调用OS_ENTER_CRITICAL()之前中断屏蔽寄存器的原值。也就是说,若在调用OS_ENTER_CKJTICAL()之前中断是关闭的,调用之后仍是关闭的。
OSStart()调用OSStartHighRdy()来运行在调用OSStart()之前创建的最高优先级任务。OSStart()设置OSTCBHighRdy指向最高优先级任务的OS_TCB。
当一个任务放弃对CPU的控制时,OS_TASK_SW()宏被调用,实际最终调用的是OSCtxSw()函数。通常,OSCtxSw()应该实现任务上下文切换,但是在ARM Cortex-M3中,所有的上下文切换由pendSV句柄来完成。OSCtxSw()仅仅触发PendSV句柄,再返回到调用处。
ISR完成后,调用OSIntExit()查看是否有比中断任务更重要的任务需要去执行。若确实如此,OSIntExit()调用OSIntCtxSw()决定下一步将要运行哪一个任务。然而,和OSCtxSw()一样,仅仅触发PendSV句柄,再返回到调用处。
限于篇幅,不详细列出5个函数的源代码。
2.4 PendSV句柄及系统滴答
PendSV句柄函数为μC/OS-Ⅱ实现所有的上下文切换。这是ARM Cortex-M3推荐的上下文切换方法。这是因为ARM Cortex-M3发生任何异常时,自动保存多个寄存器,异常返回时自动恢复这些寄存器。PendSV仅仅需要保存R4-Rll和调整堆栈指针。不管是任务的初始化还是中断或异常引发的PendSV异常都是采用同样的方法实现保存和恢复上下文。
μC/OS-Ⅱ要求用户提供一个周期性的时钟源来实现时间的延时和超时功能。Cortex-M3的系统滴答定时器是专门为RTOS设计的。可以在OSStart()运行后,μC/OS-II启动运行的第一个任务中调用OS_CPU_SysTickInit()初始化系统滴答定时器并使能中断。
需要注意的是:在系统启动代码中,初始化中断向量时,向量14和15应放置PendSV和SysTick对应的句柄。
3 测试移植代码
至此,基本完成移植源代码的编写和修改,紧接着的工作就是验证移植的μC/OS-Ⅱ是否正常工作,而这可能是移植中最复杂的一步。应该首先不加任何应用代码来测试移植好的μC/OS-Ⅱ,也就是说测试内核自身的运行情况。若有些部分没有正常工作,可以明白是移植本身的问题,而不是应用代码产生的问题。若将基本的任务和时钟节拍运行起来,接下来添加应用任务将是非常简单的。
首先在RVMDK中建立新工程,添加STM32F103ZE的启动代码,确保C编译器、汇编器和链接器能正常工作。将μC/OS-II移植代码添加到工程中,使用源代码调试器逐一验证移植函数。工程管理窗口如图所示:
4 结语
在成功验证移植代码之后,将μC/OS-II应用于移动多媒体直放站CMMB项目中,系统稳定运行,说明本移植是成功的。在CMMB项目采用μC/OS-Ⅱ实时操作系统,提高了研发效率,增强了系统的可维护性和扩展性。