uCOS-II 在 ARM上的移植很常见,而在 KEIL MDK 下的移植却不是很多,根据 ARM 下的移植范例,结合自己最近使用 KEIL MDK 的稍许心得,记录一下 uCOS-II 在 KEIL MDK 下的移植过程。在移植过程中也参考了他人的资料如有部分雷同请谅解,本文仅也供个人参考,共同学习。
uCOS II是一个源码公开、可移植、可固化、可剪裁和抢占式的实时多任务操作系统。
移植:就是使得一个实时内核,或者应用的代码在某个微处理器或微控制器平台上运行。
一:准备源码
1、在官网上下载了 uCOS-II 的源码,好像是 2..91 的了.
官网下载地址:http://www.micrium.com
NXP相关下载地址:http://micrium.com/downloadcenter/download-results/?searchterm=hm-nxp&supported=true
各模块下载网址:http://micrium.com/downloadcenter/
2、开发平台:Keil MDK for arm 4.12
3、CPU有关的源码
4、和厂商有关的
二、解压uCOS-II的源码
解压官网上下载的源码 Micrium-uCOS-II-V290.zip。里面的源码主要有如图:
三、硬件平台要求
1. 处理器的C编译器能产生可重入代码。
2. 用C 语言就可以打开和关闭中断。
3. 处理器支持中断,并且能产生定时中断(通常在10 至100Hz 之间)。
4. 处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。
5. 处理器有将堆栈指针和其它CPU寄存器读出和存储到堆栈或内存中的指令。
四、移植主要工作
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 行的代码,需要的时间从几个小时到一星期不等。
五、创建一个工程
1、使用Keil MDK 创建工程
2、选择芯片型号
3、选择”OK” 自动创建startup_LPC11xx.s文件
这时我们要修改自动创建的startup_LPC11xx.s文件。因为.s里的头文件里的中断函数和uCOS-II里os_cpu.h的定义的不相同。所以进行修改。
下图就是OS_CPU.H的中断函数的定义。
修改startup_LPC11xx.s如下:
经过查找是系统的任务切换函数的原因,这个文件是keil自动生成的,里面的中断向量分配的地址的函数名是SysTick_Handler,而ucos的名称是OS_CPU_PendSVHandler,函数的代码是在.asm里面,所以我们需要加上“IMPORT OS_CPU_PendSVHandler”让系统在其他文件寻找。我们将DCD PendSV_Handler改为DCD OS_CPU_PendSVHandler 修改完后发现,系统不但能进入创建的任务,也是按照我们设置的优先级顺序执行的,但是我们建立的任务都只执行了一次(应该说调用了OSTimeDly)就再没有返回来执行。
进一步查找发现我们还没有对操作系统的时钟初始化,我在系统建立的第一个任务中初始化系统时钟OS_CPU_SysTickInit (INT32U cnts),这个函数在os_cpu_c.c文件里面。和上面一样我们将DCD SysTick_Handler改为DCD OS_CPU_SysTickHandler,同样需要加上“IMPORT OS_CPU_SysTickHandler”。OS_CPU_SysTickInit会调用OS_CPU_SysTickHandler中断,在初始化时我们需要注意OS_CPU_SysTickInit的参数,他决定了系统的时钟节拍是多少如果太小操作系统一样不能正常工作,系统建议的时钟节拍是10ms到100ms,我设置的是1ms,OS_CPU_SysTickInit的代码如下所示。
void OS_CPU_SysTickInit (INT32U cnts)
{
INT32U prio;
OS_CPU_CM0_NVIC_ST_RELOAD = cnts - 1u;
prio = OS_CPU_CM0_NVIC_PRIO_ST;
prio &= ~(OS_CPU_CM0_NVIC_PRIO_MIN);
prio |= OS_CPU_CM0_NVIC_PRIO_MIN;
/* Set prio of SysTick handler to min prio. */
OS_CPU_CM0_NVIC_PRIO_ST = prio;
/* Enable timer. */
OS_CPU_CM0_NVIC_ST_CTRL |= OS_CPU_CM0_NVIC_ST_CTRL_CLK_SRC | OS_CPU_CM0_NVIC_ST_CTRL_ENABLE;
/* Enable timer interrupt. */
OS_CPU_CM0_NVIC_ST_CTRL |= OS_CPU_CM0_NVIC_ST_CTRL_INTEN;
}
4、修改Micrium-uCOS-II-V290\Micrium\Software\uCOS-II\Source的os_cfg_f.h为os_cfg.h。因为在其他.c里面要调用os_cfg.h头文件。
5、添加CPU相关代码
我从官网上下载的源码,是STM32的因为没有LPC11C14相关的资料。
因此就想借用以下试试看可以用否。结果好像可以(忒高兴)。
路径:Micrium_STM320518-EVAL_uCOS-II\Micrium_STM320518-EVAL_uCOS-II\Micrium\Software\uCOS-II\Ports\ARM-Cortex-M0\Generic\RealView
6、删除ucos_ii.c
因为重复定义头文件。
7、工程目录
8、头文件的路径配置
9、编译
附件:
1、IMPORT的运用
IMPORT伪操作告诉编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号,而且不论本源文件是否实际引用该符号,该符号都将被加入到本源文件的符号表中。
语法格式:
IMPORT symbol{[WEAK]}
其中,symbol为声明的符号的名称。它是区分大小写的。
[WEAK]指定这个选项后,如果symbol在所有的源文件中都没有被定义,编译器也不会产生任何错误信息,同时编译器也不会到当前没有被INCLUDE进来的库中去查找该符号。
使用说明:
使用IMPORT伪操作声明一个符号是在其他源文件中的定义。如果连接器在连接处理时不能解析该符号,而IMPORT伪操作中没有指定[WEAK]选项,则连接器将会报告错误。如果连接器在连接处理时不能解析该符号,而IMPORT伪操作中指定了[WEAK]选项,则连接器将不会报告错误,而是及进行下面的操作: 如果该符号被B或者BL指令引用,则该符号被设置成下一条指令的地址,该B或者BL指令相当于一条NOP指令
其他情况下该符号被设置为0
2、修改os_cpu_a.asm文件
根据我们移植的源码情况来修改os_cpu_a.asm文件,如果我们用的是MDK 软件,并且.S源码还是PUBLIC的话就进行相应的改写。
因为我们使用的是keil,所以需要建里面的public改为EXPORT如下图所示。
另外还有段代码需要修改,否则编译不过。
IAR汇编:
RSEG CODE:CODE:NOROOT(2)
THUMB
Keil汇编:(这段不知)
AREA |.text|, CODE, READONLY, ALIGN=2
THUMB
REQUIRE8
PRESERVE8
修改完以上内容编译并下载进目标板,发现操作系统的函数都执行了,但是始终不能进入我们自己创建的任务。
3、始终不能进入我们的任务
我就出现这种情况,经调试现象是OSInit()、OSStart()、 OSTaskCreateExt()都已经执行。唯独就是第一个任务Task_Start()进不去。错误原因:
startup_LPC11xx.s的两个中断函数和uCOS-II的内核的中断函数定义的不相同。也就是uCOS-II自己定义的有相关中断,我们要用到uCOS-II的内核的中断函数。
修改如下: