μC/OS-II的应用设计

2014-07-19 12:50
科技视界 2014年14期
关键词:编译器堆栈内核

凌 云

(建东职业技术学院,江苏 常州213022)

0 引言

在嵌入式系统的设计和实现中,嵌入式实时操作系统得到了广泛应用,嵌入式实时操作系统为用户提供了一个开发环境,使用户可以集中精力于特殊应用的嵌入式软件的设计,简化了系统设计,提高了开发效率。

μC/OS-II 是一个开放源代码的,精简的实时内核。 它功能强大,提供了任务管理、进程调度、任务间通信、内存管理等功能。 更重要的是,μC/OS-II 自1992 年的第一版(μC/OS)以来已经有好几百个应用,是一个经实践证明好用且稳定可靠的内核。 下面将介绍的是如何将μC/OS-II 移植到TI 公司高性能定点DSP TMS320LF2407 上。

1 μC/OS-II 在TMS320LF2407 上的移植

μC/OS-II 的移植条件是:只要该处理器有堆栈,有CPU 内部寄存器入栈、出栈指令;使用的C 编译器支持内嵌汇编(inline assembly)或者该C 语言可扩展,可连接汇编模块,使得关中断、开中断能在C 语言程序中实现。

TMS320LF2407 是由美国德州仪器(TI)公司生成的高性能定点DSP[1]。 T1 公司提供的编译器CodeComposer V4.10. 36 支 持C 语 言 和汇编语言开发,本文在此编译器的基础上进行了μC/OS-II 的移植。CodeComposer V4.10. 36 内置编译器维持一个C 运行环境, 为了确保C 语言的成功执行,所有运行时代码都必须保持这个环境。 在编写汇编和C 代码的接口函数时也必须遵循一些规则,μC/OS-II 才可以完全移植到TMS320LF2407 上。

μC/OS-II 核心代码很小,程序开发人员要把它移植到自己的目标板中只需做少量的工作。 μC/OS-II 大部分源代码是用C 语言写的,但是完成和处理器一些相关的代码时, 还是必须要用汇编语言来实现的。寄存器的读、写只能通过汇编语言的存储和加载指令来实现。要使μC/OS-II 能够正常工作,处理器必须满足以下要求:①处理器的C 编译器能产生可重入代码;②用C 语言可以打开和关闭中断;③处理器支持中断,并且能够产生定时中断(通常在10~100Hz 之间);④处理器能够支持容纳一定量数据的硬件堆栈;⑤处理器有将堆栈指针和其它寄存器读出和存储到堆栈或内存中的指令。

2 移植过程

在移植之前,首先我们需要对μC/OS-II 的内核,特别是任务切换机制要有一个比较深刻的理解, 而具体的移植工作主要是修改μC/OS-II 中与处理器相关的三个文件:OS_CPU_A.ASM、OS_CPU_C.C 以及OS_CPU.H, 此 外 还 需 要 修 改INCLUDES.H 文 件, 以 及 针 对TMS320LF2407 最多扩展64K 程序存储器的限制修改CFG.H 文件,裁减μC/OS-II,但后两个文件改写较简单,这里不再赘说。

2.1 修改OS_CPU.H

此文件的内容可根据μC/OS-II 的内容进行修改, 这里仅给出关键内容:

unsigned int INT16U; /* 定义堆栈单位长度*/

unsigned int OS_STK;

#define OS STK_GROWTH 0 /* 定义堆栈由低地址向高地址递减*/

#define OS_ENTER_CRITICAL() asm(” SECT INTM”); /* 开关中断宏定义*/

#define 0S_EXTI_CRITICAL() asm(” CLRC INTM”);

#define OS_TASI_SW() asm(” INTR 8”); /* 任务切换宏定义*/

2.2 修改OS_CPU_C.C

在这个文件中需要用户定义6 个C 语言函数:OSTaskStkInit(),OSTaskCreateHook(),OSTaskDelHook(),OSTaskSwHook(),OSTaskStatHook(),OSTimeTickHook(),实际必须修改的只有OSTaskStkInit()。

OSTaskStkInit () 函数是由任务创建函数OSTaskCreate ()或OSTaskCreateExt()调用,功能是初始化任务堆栈。任务堆栈用于任务切换或中断发生时保护当前任务的上下文状态,以便中断返回或者任务下次被调度运行时能够接着运行。堆栈的结构可以按照自己的需要而定制,考虑到CC2000 的C 语言运行时支持库rts2xx.lib 中已经有用于保存中断上下文的库函数I$$SAVE 和I$$REST(可用Dspar 工具查看这一函数),为了重用这一库函数,这里按照这一库函数堆栈结构来设计堆栈,其结构如下图1 所示:

一些说明如下:

(1)第一级硬件堆栈(HW STACK LEVEL 1)不需要保存,原因是I$$SAVE 是通过CALL 指令来调用,CALL 指令会使用一个硬件堆栈用于保存返回地址。所以在保存另外七个硬件堆栈前会将第一级硬件堆栈弹出。

(2)当调度再次发生时,通过调用与I$$SAVE 对应的I$$REST 来恢复被中断的上下文。

2.3 修改OS_CPU_ASM.ASM

此文件包括的四个函数都跟处理器有关,由于不同的处理器有不同的寄存器, 所以操作系统在这个文件里给用户留下四个函数接口,以便用户根据所选处理器编写相应的汇编程序以完成固定的功能。四个函数分别是OSStartHighRdy(),OSCtxSw(),OSIntCtxSw(),OSTickISR()。

2.3.1 OSStartHighRdy()

该函数是由启动函数OSStart()调用的,功能是使系统能及时地运行优先级最高的就绪任务,由于系统中数据指针OSTCBHighRdy 一直指向就绪任务中优先级最高的任务控制块OSTCB,使得OSStartHighRdy()轻易就可获取最高优先级任务的栈顶指针,再将保存在此任务堆栈的寄存器值恢复到CPU 寄存器中,使该任务得以运行,实现多任务的启动。 对下TMS320LF2407 而言,OSStartHighRdy()代码编写如下:

_OSStartHighRdy:

CALL _OSTaskSwHook;调用钩子函数,实现用户自定义功能

LACK 1; OSRunning = TRUE;

LDPK _OSRunning

SACL _OSRunning

LDPK _OSTCBHighRdy; SP=OSTCBHighRdy->OSTCBStkPtr;

LAR AR3, _OSTCBHighRdy

MAR *, AR3

LAR AR1, *

B I$$REST, AR1; 上下文恢复,任务返回在调用函数OSTaskSwHook()时,由于当前任务控制块OSTCBCur仍然指向将要被切换出去的任务,而OSTCBHighRdy 则指向即将被运行的任务,因此用户可在OSTaskSwHook()中对它们操作,以实现特殊的功能,当然该函数也可定义为不做任何事的空函数。 从程序我们可以看出,要运行最高优先级的任务,首先得找到该任务堆栈指针,然后将寄存器内容及参数从堆栈中恢复到CPU 的寄存器中。

2.3.2 任务级上下文切换的实现函数OSCtxSw()

前面曾提过, 任务切换时使用了软中断, 并将中断向量指向OSCtxSw(),因此该函数所要做的就是执行任务级的任务切换。 其目的是为了保证CPU 永远运行就绪表中优先级最高的任务。 OSCtxSw()是任务调度函数OSSched()通过宏OS_TASK_SW()调用的,执行的是多任务的调度功能:不仅要使高优先级任务得以恢复运行,还得将待切换出去的任务保存起来,两者的差别也可以从程序代码中比较出来:

_OSCtxSw:

CALL I$$SAVE

LDPK _OSTCBCur; OSTCBCur->OSTCBStkPtr = SP;

LAR AR3, _OSTCBCur

MAR *, AR3

SAR AR1, * , AR1_

OSIntCtxSw:

CALL _OSTaskSwHook;OSTaskSwHook();

LDPK _OSTCBHighRdy;OSTCBCur = OSTCBHighRdy;

BLDD _OSTCBHighRdy,#_OSTCBCur

LDPK _OSPrioHighRdy;OSPrioCur = OSPrioHighRdy;

BLDD _OSPrioHighRdy,#_OSPrioCur

LDPK _OSTCBHighRdy;SP=OSTCBHighRdy->OSTCBStkPtr;

LAR AR3, _OSTCBHighRdy

MAR *, AR3

LAR AR1, *

B I$$REST, AR1

显然,对当前任务相关内容的保存、给当前优先级数据结构赋值以及给当前任务控制块赋值都是OSStartHighRdy()所不具有的。

2.3.3 时钟中断服务程序的实现

OSTickISR()也是μC/OS -II 操作系统中要求用户提供的汇编程序,其具体实现与中断级上下文切换的实现有很大的关系, 它是时钟中断服务程序,主要调用函数OSTimeTick(),处理与系统时钟相关的工作, 如将每个任务的等待时间减1、更新系统时间。 OSTickISR()具体代码如下所示:

_OSTickISR:

CALL I$$SAVE

CALL _OSIntEnter

LAC _OSIntNesting; 保 存 堆 栈 指 针AR1 到 当 前OSTCB 的

OSTCBStkPtr;

SUBK 1 BNZ L1

LDPK _OSTCBCur

LAR AR3, _OSTCBCur

MAR *, AR3

SAR AR1, * , AR1

L1: CALL _OSTimeTick

CALL _OSIntExit

BI$$REST,AR1

其中OSTimeTick()函数定时对所有的任务控制块中的OSTCBDly减一,当某任务的OSTCBDly 减为零时,就将其转到就绪态,以备运行。 而OSIntExit()主要用于判别中断的执行是否使得更高优先级的任务进人就绪态,如有,则进行任务切换,否则返回。 至于时钟中断可由DSP 的定时器1 的周期计数器PRT 产生。

2.3.4 中断级上下文切换的实现函数OSIntCtxSw()

与OSCtxSw()相比较,OSIntCtxSw()也是执行任务切换的,但它执行中断级的任务切换。 不使用现成的OSCtxSw()进行切换主要基于这样的考虑:它是在中断处理程序中调用的,类似于OSCtxSw()中保存寄存器的工作在进人中断时就做过了。 具体实现代码参考函数OSCtxSw()代码。

2.4 内核的测试

内核移植选用三知公司的SZ-2407IV 作为测试平台, 测试使用LED 显示模块和数字信号处理模块。 本文设计了Task0(),Task1()两个任务,Task0()为LED 显示任务,Task1()为数字信号处理模块,执行一次DFT 算法,计算一个采样点的DFT 值。

μC/OS-II 的Tick 时钟是周期性中断, 每1.66ms 触发一次,Task0和Task1 在延时32 个Ticks 的情况下即20ms 切换一次LED 闪烁显示一位。 最后观测结果,得到了预期效果。

3 结束语

为了保证移植的成功并使系统可靠运行, 除了要熟悉μC/OS-II和所用的处理器外,熟悉CC 编译器C 开发环境也是移植能否成功的一个关键。 而且,μC/OS-II 在TMS320LF2407 上的移植成功只是第一步,今后还需要对μC/OS-II 的内核进行扩展,例如添加TCP/IP 软件包,以适应网络化的需求,使之真正成为一个可重用的开发平台。

[1]Jean Labrosse.μC/OS-II, 源码公开的实时嵌入式操作系统[M].邵贝贝,译.北京:中国电力出版社,2001.

[2]TMS320C2x/C2xx/C5xx Optimizing C Compilaer User’s Guide[Z].Texas Instruments,1999.

猜你喜欢
编译器堆栈内核
强化『高新』内核 打造农业『硅谷』
基于相异编译器的安全计算机平台交叉编译环境设计
基于嵌入式Linux内核的自恢复设计
Linux内核mmap保护机制研究
嵌入式软件堆栈溢出的动态检测方案设计*
基于堆栈自编码降维的武器装备体系效能预测
通用NC代码编译器的设计与实现
一种用于分析MCS-51目标码堆栈深度的方法
编译器无关性编码在微控制器中的优势
基于ARM嵌入式平台的x86译码SOC架构设计