实时操作系统μC/OS-II在MCF5272上的移植_嵌入式系统论文
关键词:μc/os-ii mcf5272 移植 gnu工具链
作为一个实时内核,μc/os从1992年开始为人们熟悉,到现在已经发展为μc/os-ii。Μc/os-ii最多支持56个任务,其内核为占先式,总是执行就绪态的优先级最高的任务,并支持semaphore(信号量)、mailbox(邮箱)、message queue(消息队列)等多种常用的进程间通信机制。与大多商用rtos不同的是,μc/os-ii公开所有的源代码,并可以免费获得,对商业应用收取少量license费用。一般商用操作系统如vxworks、psos、wince,购买费用动辄数万美元,而且每件产品都需要交纳运行费,开发、使用成本高昂。
目前mcf5272是motorola公司一款集成度最高的coldfire处理器,采用coldfire v2可变长risc处理器核心和digitaldna技术,在66mhz时钟下能够达到63dhrystone2.1mips。其内部sim(system integrated module)单元集成了丰富的通用模块,如10/100mhz快速以太网控制器,usb1.1接口等,并且能够与常用的外围设备(如sdram、isdn收发器)实现无缝连接,从而简化了外围电路设计,降低了产品成本、体积和功耗。
使用gnu工具链(包括交叉编译器gcc、汇编器as等)进行μc/os-ii内核的编译,host(宿主机)环境为16mb sdram。在宿主机上编译出mcf5272处理器的可执行代码,通过mcf5272的bdm调试工具下载到目标板调试运行。
1 μc/os-ii系统结构
图1说明了μc/os-ii的软硬件体系结构。应用程序处于整个系统的顶层,每个任务都可以认为自己独占了cpu,因而可以设计成为一个无限循环。μc/os-ii处理器无关的代码提供μc/os-ii的系统服务,应用程序可以使用这些api函数进行内存管理、任务间通信以及创建、删除任务等。
大部分的μc/os-ii代码是使用ansi c语言书写的,因此μc/os-ii的可移植性较好。尽管如此,仍然需要使用c和汇编语言写一些处理器相关的代码。μc/os-ii的移植需要满足以下要求:
(1)处理器的c编译器可以产生可重入代码;
(2)可以使用c调用进入和退出critical code(临界区代码);
(3)处理器必须支持硬件中断,并且需要一个定时中断源;
(4)处理器需要能够容纳一定数据的硬件堆栈;
(5)处理器需要有能够在cpu寄存器与内存和堆栈交换数据的指令。
移值μc/os-ii的主要工作就是处理器和编译器相关代码以及bsp的编写。
2 μc/os-ii dsp编写
bsp(板级支持包)是介于底层硬件和操作系统之间的软件层次,它完成系统上电后最初的硬件和软件初始化,并对底层硬件进行封装,使得操作系统不再面对具体的操作。
为μc/os-ii编写一个简单的bsp。它首先设置cpu内部寄存器和系统堆栈,并初始化堆栈指针,建立程序的运行和调用环境;然后可以方便地使用c语言设置mcf5272片选地址(cs0~cs7)、gpio以及sdram控制器,初始化串口(uart0)作为默认打印口,并向操作系统提供一些硬件相关例程和函数如dprintf(),以方便调试;在cpu、板级和程序自身初始化完成后,就可以把cpu的控制权交给操作系统了。
mcf5272处理器将系统上电作为2号异常,因此需要在异常矢量表中相应位置填写第一条命令的地址,这可以在编译时自动完成。该矢量表必须存放在cs0对应的flash中供cpu上电时自动读取。如:
_vectors: //矢量表起始地址
.long 0x0,_start,_fault,_fault,… //初始化1k字节矢量表
……
_start: nop //第一条指令
move.w #0x2700,%sr //屏蔽所有中断
move.1 #_vectors,%d0
move.c %d0,%vbr //#vectors->vbr
move.1 #0x10000001,%d0
move.c %d0,%mbar //sim单元基地址0x10000000
move.1 #0x20000001,%a0 //sram起始地址0x20000000
move.c %a0,%rambar0 //初始化内部sram
move.1 #0x20001001,%a7 //设置堆栈指针
……
jsr cpu_init //调用cpu_init初始化sim单元
jsr ucos_start //启动μc/os-ii
……
其中,cpu_init函数用于初始化cpu内部sim单元、sdram控制器、uart串口。值得注意的是sdram初始化,不同生产商的sdram的初始化时序有一定差异。
bsp在完成片级和板级初始化后,还负责初始化程序自身,如将.data段的内容从只读的rom复制到sdram中,建立运行时环境。以下是建立程序数据段的代码:
memcpy(&_sdata,&_etext,(&_edata-&_sdata)); //拷贝.data段
memset(&_sbss,0,(&_ebss - &_sbss)); //将.bss段清零
还需要为μc/os-ii编写4个简单的汇编函数。在每个硬件时钟到来后,μc/os-ii会在中断服务例程中调用osintctxsw()进行任务调度;另外,当某个任务因等待资源而被挂起时,没有必要等到自己的时间片全都用完,可以自己主动放弃cpu,这可以通过调用一个任务级的任务调度函数osctxsw()来实现。其中相对复杂的是osintctxsw()。由于ostickisr()调用了osintexit(),osintexit()又再次调用了osintctxsw(),如果进行任务切换,那么两次调用都不会返回,而不同的c编译器、不同的编译选项处理c调用时对堆栈的使用也不尽相同。因此osintctxsw()是编译器相关的。gcc在使用2~4级优化时,在主调函数中会是一个jsr跳转指令,而被调函数以linkw %fp,#<d0>开始。这两条指令都会影响堆栈指针。为了实现任务切换,必须重新调整堆栈指针以补偿调用的影响。一个完整过程如下:
osintctxsw:
adda.1 #16,%a7 //栈补偿,gcc-o2->-04优化
move.1 (ostcbcur),%a1
move.1 %a7,(%a1) //ostcbcur->ostcbstkptr=sp
jsr ostaskswhook //调和hook钩子函数
/*ostcbcur->ostcbstkptr=ostcbhighrdy->ostcbstkptr*/
move.1 (ostcbhighrdy),%a1
move.1 %a1,(ostcbcur)
move.b (ospriohighrdy),%d0
move.b %d0,(ospriocur) //ospriocur=ospriohighrdy
move.1 (%a1),%a7 //sp=ostcbcur->ostcbstkptr
movem.1 (%a7),%d0-%d7/%a0-%a6 //恢复cpu寄存器,切换到新任务
lea 60(%a7),%a7
rte
篇幅所限,其它三个函数就不述了。
3 μc/os-ii任务堆栈初始化
μc/os-ii中每个任务都有自己的任务堆栈,在任务创建初期由ostaskstkinit()初始化。初始化堆栈的目的就是模拟一次中断。任务堆栈中保存了任务代码的起始地址和一些cpu寄存器(初值是无关紧要的),这样一旦条件满足,就可以执行该任务了。mcf5272在中断发生时,会自动保存程序指针pc、状态寄存器sr以及其它一些,为四字帧结构。除此以外,%d0-%d7、%a0-%a6也必须按一定顺序入栈。ostaskstkinit()在完成堆栈初始化后,还要返回栈顶指针以用于该任务控制块tcb结构的初始化。该程序使用c语言编写。
4 μc/os-ii系统时钟
mcf5272处理器内置了4个定时器,使用timer0产生周期10ms的定时中断作为系统时钟。当pivr寄存器设置为0x40时,timer0为69号中断,在矢量表的相应位置需填入时钟服务程序ostickisr()的入口地址,并初始化时钟:
volatile unsigned short*ptimer;
ptimer=(unsigned short*)(0x10000000+0x200); //指向timer0
/*复位时钟*/
*ptimer &=0xfff9; //定时器处于stop状态
*ptimer=(*ptimer & 0x00ff)|0xfa00;//预分频=250
*ptimer |=0x0018; //计数满自动清零,中断方式
ptimer=165; //set trr=165
*ptimer=(*ptimer & 0xfff9) |0x0004; //clk=master/16,启动
上述程序段时钟节拍的周期为:(1/66mhz)×250×164×16=0.01秒。实时性要求高的场合可以使用更为精细的时钟。timer0一旦完成初始化,就开始工作,但是要让中断发生,还必须设置icr寄存器相应字段给该中断分配ipl(interrupt priority level,中断优先级),并保证该中断没有初状态寄存器sr屏蔽。
该时钟初始化代码可以放在第一个μc/os-ii任务中,在osstart()后执行。一旦内核可以进行正常的任务切换,移植工作也就基本完成了。
5 内核编译与下载
所有的c和汇编源文件经过编译、链接,最终形成一个二进制映像文件。由于μc/os-ii使用了自定义的数据类型,因此必须将其转变成为gcc(gnu c compiler)能识别的类型,如int8u可以定义为unsigned char。另外,还必须编写一个ld(链接脚本)文件控制编译,将程序定位到实际的rom和ram资源中。为了调试方便,通常是通过bdm工具将内核下载到目标板sdram中运行,调试通过后再固化到flash中。
rtos是当前嵌入式应用的特点。应用rtos,可以使产品更可靠、功能更强大而开发周期更短。μc/os-ii有着良好的实时性和很小的代码量,并被广泛移植到x86、68k、coldfire、mpc 8xx、arm、mips、c5409等许多处理器上。数百个成功的商业应用实例说明μc/os-ii是一个稳定可靠的内核,因此将μc/os-ii移植到mcf5272具有很强的实用前景。