μC/OSⅡ实时内核在TMS320C6201上的移植
2009-10-09
作者:熊志金 郭洪娜
摘 要: 将嵌入式实时内核μC/OSⅡ移植到TI公司的DSP处理器TMS320C6201上的方法。重点说明了内核中与处理器相关部分的编程及其在系统中的作用。
关键词: μC/OSⅡ内核 嵌入式操作系统 DSP 移植
嵌入式系统设计及其应用已对人类生活产生了巨大影响,并使人们未来的生活方式产生变化。进行嵌入式系统开发的一个基础工作是实现嵌入式操作系统在相关处理器平台上的移植。本文基于目前应用非常广泛的DSP处理器体系结构,对μC/OS-Ⅱ嵌入式实时操作系统内核的移植做了分析和介绍,并给出了相应的移植源代码。
1 μC/OSⅡ实时内核介绍
μC/OS-Ⅱ是一个简单、高效的嵌入式实时操作系统内核,已被应用到各种嵌入式系统中。它支持x86、ARM、PowerPC、MIPS、DSP等众多体系结构,并有上百个商业应用实例,其稳定性和可用性是经过实践验证的。同时,它的源代码公开,可以从www.ucos-ii.com网站上获得全部源码以及其在各种体系结构平台上的移植范例。
μC/OS-Ⅱ 2.0版以上的内核都具有可抢占的实时多任务调度功能。另外它还提供了许多系统服务,例如信号量、消息队列、邮箱、内存管理、时间函数等,这些功能可以根据不同的需求进行裁减。可以说,μC/OS-Ⅱ是一个具备现代操作系统特点的RTOS。它结构清晰、注解详尽,具有良好的可扩展性和可移植性,被广泛地应用于各种架构的微处理器上。
2 TMS320C6201芯片介绍
TMS320C6201是TMS320系列产品中的新一代高性能的DSPs芯片。它是16位的定点数字信号处理器,在200MHz速率工作时可达1 600Mips;四通道DMA控制端口;最大3Mb片上存储器;备有三种掉电模式;二个多通道缓冲串口;二枚32位定时器;超薄256/352脚BGA封装;先进超长指令字结构;每周期执行八条32位指令,八个独立通用功能单元;业内最先进的DSP C语言编译器;一个新直观性而又类似RISC的指令集,方便易用;汇编优化程序调度任务,方便汇编语言编程。
3 实时内核的移植
移植工作包括以下内容:修改OS_CPU.H中常量、数据类型和宏;用C语言改写OS_CPU_C.C中六个简单的函数;用汇编语言改写OS_CPU_A.ASM中的四个函数。
3.1 OS_CPU.H文件的修改
在OS_CPU.H头文件中定义了与处理器相关的常量OS_STK_GRCWTH、宏OS_ENTER_CRITICAL( )、宏OS_EXIT_CRITICAL( )、宏OS_TASK_SW( )以及可移植的数据类型等。
(1)可移植的数据类型
由于C语言中的short、int、long等数据类型的数位数随着所使用处理器的不同而变化,所以移植性不强,μC/OSⅡ不支持。μC/OSⅡ定义了可移植的数据类型,包括8位、16位、32位的有符号数和无符号数等。此外还需要根据DSP芯片TMS320C6201的堆栈宽度定义任务堆栈数据类型OS_STK。具体定义如下:
typedef unsigned char BOOLEAN;布尔量
typedef unsigned char INT8U;8位无符号数
typedef signed char INT8S;8位有符号数
typedef unsigned short INT16U;16位无符号数
typedef signed short INT16S;16位有符号数
typedef unsigned int INT32U;32位无符号数
typedef signed int INT32S;32位有符号数
typedef float FP32;32位单精度浮点数
typedef double FP64;64位双精度浮点数
typedef unsigned int OS_STK;TMS320C6201的堆栈入口宽度为32位
(2)宏定义
由于某些代码在执行时不可分割,μC/OSⅡ实时内核在访问这些代码临界区时必须禁止中断,因此μC/OSⅡ实时内核在头文件OS_CPU.H中还定义了宏OS_ENTER_CRITICAL( )和OS_EXIT_CRITICAL( ),分别用于开中断和关中断。对TMS320C6201而言,可用如下代码实现:
extern cregister volatile unsigned int IER;
extern volatile unsigned int Always_Enabled_Interrupts;
extern volatile unsigned int Normally_Enabled_Interrupts;
static inline void OS_ENTER_CRITICAL(void)
{ IER=Always_Enabled_Interrupts;
asm(″NOP 4″);
} /*关全局中断,进入临界区*/
static inline void OS_EXIT_CRITICAL(void)
{ IER=Normally_Enabled_Interrupts;
asm(″NOP 5″);
} /*开全局中断,进入临界区*/
#define OS_STK_GROWTH 1/*TMS320C6201的堆栈是由
低地址向高地址递增*/
3.2 OS_CPU_C.C文件的修改
OS_CPU_C.C文件中需要用户修改六个函数:OSTaskCreateHook( )、OSTaskDelHook( )、OSTaskSwHook( )、OSTaskStatHook( )、OSTaskCreateHook( )、OSTaskStkIinit( )。而实际需要修改的只有OSTaskStkIinit( )函数,其他函数是为方便用户扩展功能而设,其定义可为空。
函数OSTaskStkIinit( )用于系统创建用户任务时,建立并初始化任务堆栈。该函数与处理器的硬件体系密切相关,它将所有的寄存器压栈,返回新的堆栈栈顶,并将它们保存在该任务的任务控制块OS_TCB中,最终使初始化后的堆栈跟刚发生过一次中断一样。这样,系统无需对调度程序作特殊处理即可直接对新任务进行调度。由于在TMS320C620中堆栈是按32位数据类型进行操作,所以堆栈数据类型OS_STK声明为32位无符号整数。OSTaskStkIinit( )函数代码如下:
void *OSTaskStkInit(void(*task)(void*pd),void*pdata,
void *ptos,INT16U opt)
{ INITIAL_REGISTER_FRAME *Frame,*StackBottom
int*FirstFreeCellInStack
Frame=(INITIAL_REGISTER_FRAME*)((int)ptos &~7)
StackBottom=Frame
Frame--
Frame->A0.integer=0x0A0 /*初始化寄存器*/
Frame->A1.integer=0x0A1
Frame->A2.integer=0x0A2
Frame->A3.integer=0x0A3
Frame->A4.integer=(int) pdata
Frame->A5.integer=0x0A5
Frame->A6.integer=0x0A6
Frame->A7.integer=0x0A7
Frame->A8.integer=0x0A8
Frame->A9.integer=0x0A9
Frame->A10.integer=0x0A10
Frame->A11.integer=0x0A11
Frame->A12.integer=0x0A12
Frame->A13.integer=0x0A13
Frame->A14.integer=0x0A14
Frame->A15.integer=0xA15
Frame->B0.integer=0x0B0
Frame->B1.integer=0x0B1
Frame->B2.integer=0x0B2
Frame->B3.integer=0x0B3
Frame->B4.integer=0x0B4
Frame->B5.integer=0x0B5
Frame->B6.integer=0x0B6
Frame->B7.integer=0x0B7
Frame->B8.integer=0x0B8
Frame->B9.integer=0x0B9
Frame->B10.integer=0x0B10
Frame->B11.integer=0x0B11
Frame->B12.integer=0x0B12
Frame->B13.integer=0x0B13
Frame->B14.integer=ReturnCurrentDP()
Frame->B15.integer=(int)StackBottom
Frame->AMR_Adressing_Mode_Register=0
Frame->CSR_Control_Status_Register=(0<<8)+3
Frame->IER_Interrupt_Enable_Register=Normally_
Enabled_Interrupts
Frame->IRP_Interrupt_Return_Pointer=0xdeadbeef
Frame->Start_Address=task
rsFreeCellInStack=(int*) Frame
rstFreeCellInStack--
turn (FirstFreeCellInStack)
}
3.3 OS_CPU_A.ASM文件的修改
此文件包括的四个函数都涉及对寄存器的处理,与处理器有关。由于不同的处理器有不同的寄存器,所以操作系统在这个文件中给用户留下四个函数接口,以便用户根据所选处理器编写相应的汇编程序以完成固定的功能。四个函数分别是:多任务启动函数中调用的OSSTartHightRdy( )、任务切换函数OSCtxSw( )、中断任务切换函数OSIntCtxSw( )、时钟节拍服务函数OSTickISR( )。
(1)OSSTartHightRdy( )
该函数是由启动函数OSStart( )调用的,功能是使系统能及时运行优先级最高的就绪任务。由于系统中数据指针OSTCBHighRdy一直指向就绪任务中优先级最高的任务控制块OS_TCB,使得OSSTartHightRdy( )可以轻易地获取最高优先级任务的栈顶指针,再将保存在此任务堆栈的寄存器值恢复到CPU寄存器中,使该任务得以运行,实现多任务的启动。对于TMS320C6201,其代码如下:
B .S1 _OSTaskSwHook /*调用用户自
定义的_OSTaskSwHook*/
NOP 3
MVKL .S2 OSStartHighRdy_1,B3
MVKH .S2 OSStartHighRdy_1,B3
OSStartHighRdy_1:
LDW .D2T2 *+DP(_OSTCBHighRdy),B4
/*获得最高优先级任务的TCB地址*/
LDB .D2T2 *+DP(_OSPrioHighRdy),B5
NOP3
STW .D2T2 B4,*+DP(_OSTCBCur)
STB .D2T2 B5,*+DP(_OSPrioCur)
|| mvk 1,b1
STB .D2T2 B1,*+DP(_OSRunning)
LDW .D2T2 *B4,SP
NOP 4
(2)OSCtxSw( )
该函数由任务切换函数OS_TASK_SW( )进入。如果任务执行了某个函数,其结果若改变了当前任务的状态(如OSTaskSuspend( )、OSTimeDly( ))或是改变了别的任务的状态(如OSTaskResume( )、OSTimeDlyResume( )),则都要引起新的任务调度函数(OSSched( ))执行OS_TASK_SW( )。其代码如下:
addk .s2 (4 -_Framesize),SP
STW .D2T2 B3,*+SP(_StartAddress)
STW .D2T2 B0,*+SP(_B0)
|| mvc AMR,B0
STW .D2T2 B0,*+SP(_AMR)
|| mvc CSR,B0
STW .D2T2 B0,*+SP(_CSR)
|| mvc IER,B0
STW .D2T2 B0,*+SP(_IER)
STW .D2T1 A0,*+SP(_A0)
|| MV .L1X SP,A0
STW .D2T1 A1,*+SP(_A1)
|| addk .s1 (_IRP),A0
STW .D2T1 A0,*+SP(_B15)
STW .D2T1 A2,*+SP(_A2)
STW .D2T1 A3,*+SP(_A3)
STW .D2T1 A4,*+SP(_A4)
STW .D2T1 A5,*+SP(_A5)
STW .D2T1 A6,*+SP(_A6)
STW .D2T1 A7,*+SP(_A7)
STW .D2T1 A8,*+SP(_A8)
STW .D2T1 A9,*+SP(_A9)
STW .D2T1 A10,*+SP(_A10)
STW .D2T1 A11,*+SP(_A11)
STW .D2T1 A12,*+SP(_A12)
STW .D2T1 A13,*+SP(_A13)
STW .D2T1 A14,*+SP(_A14)
STW .D2T1 A15,*+SP(_A15)
LDW .D2T2 *+DP(_OSTCBCur),B0
STW .D2T2 B1,*+SP(_B1)
STW .D2T2 B2,*+SP(_B2)
STW .D2T2 B3,*+SP(_B3)
STW .D2T2 B4,*+SP(_B4)
STW .D2T2 B5,*+SP(_B5)
STW .D2T2 B6,*+SP(_B6)
STW SP,*B0
STW .D2T2 B7,*+SP(_B7)
STW .D2T2 B8,*+SP(_B8)
STW .D2T2 B9,*+SP(_B9)
STW .D2T2 B10,*+SP(_B10)
STW .D2T2 B11,*+SP(_B11)
STW .D2T2 B12,*+SP(_B12)
STW .D2T2 B13,*+SP(_B13)
STW .D2T2 B14,*+SP(_B14)
(3)OSIntCtxSw( )
μC/OS-Ⅱ中,中断的产生可能会引起任务的切换,中断服务程序的最后会调用OSIntExit( )检查任务就绪状况。如果需要进行任务切换,将调用OSIntCtxSw( )。所以SIntCtxSw( )又称为中断级的任务切换函数。需要注意是,任何中断服务程序ISR前面都要像下文介绍的时钟节拍函数STickISR( )流程的第②步那样保存上下文环境。SIntCtxSw( )和OSCtxSw( )的后半部分几乎相同,不同处是对当前任务的堆栈指针进行了调整。其代码如下:
LDW .D2T2 *+DP(_OSTCBCur),B0
|| mv SP,B1
|| addk .s2 (8),B1
STW .D2T2 B1,*B0
NOP 4
(4)OSTickISR( )
μC/OS-Ⅱ中,时钟节拍中断是一个非常重要的中断,因为整个操作系统的活动都受到它的激励。OSTickISR( )的执行流程为:①硬件进入中断处理;②保护上下文环境;③调用OSIntEnter( ),记录中断嵌套层数;④调用OSTimeTick( ),检查处理各个任务的延时,并根据情况修改就绪任务表;⑤调用OSIntExit( ),检查就绪任务表,看是否有比当前任务优先级更高的任务就绪。如果有,则进行调度;如果没有,OSIntExit( )返回并恢复②所保存的上下文环境,并执行RETI回到被中断的那个任务中继续运行。
4 结束语
μC/OS-Ⅱ作为一个优秀的实时操作系统已经被移植到各种体系结构的微处理器上,而DSP体系结构在嵌入式领域也获得了广泛的应用和支持。将μC/OS-Ⅱ移植到DSP平台上,能够更深入地了解实时操作系统的构造,加快在DSP平台上的应用和开发,并为更高层次上的扩展和改进打下基础。
参考文献
1 LABROSSE J J著,邵贝贝译.μC/OS-Ⅱ—源码公开的实时嵌入式操作系统.北京:中国电力出版社,2001