|
摘要:UC/OS-II是一个实时内核软件,它用来管理微处理器、微控制器和数字信号处理器的CPU的任务调度,以保证它们的高效运行。当前数字信号处理芯片在电子信息的各个领域得到越来越广泛地应用。本文阐明如何将UC/OS-II移植到数字信号处理器ADSP-21535的实用技术。
关键词:UC/OS-II实时内核、数字信号处理器、ADSP-21535、任务。
一.前言 当前操作系统解决的问题是如何充分提高CPU的利用率。因为慢速运行的外围设备严重影响了CPU能力的发挥;程序中的延时和等待,浪费了大量的CPU时间。如果把程序分成若干个任务,那么,遇到延时或等待事件时,就将挂起正在执行的任务,调换另一个就绪任务给CPU去执行,仅在延时时间到或等待的事件出现,再调原挂起的任务继续执行。有关任务切换、调度和分配CPU的一些操作是操作系统的核心内容,称作操作内核(Operating Kernel),有时也叫实时执行程序(RTX)。操作内核加上内存管理、I/O管理等就够成较完整的操作系统。UC/OS-II就是一个比较小的操作系统。 本文详细阐述了基于ADSP-21535的UC/OS-II的移植技术。 二.UC/OS-II简介 1. UC/OS-II的特点 UC/OS-II是专门为计算机的嵌入式应用而设计的,绝大部分代码是用C语言编写的。与CPU硬件紧密相关部分代码是用汇编语言编写的,可移植性强。 UC/OS-II是基于优先级的占先式(preemptive)多任务实时内核,即总是让进入就绪态的诸任务中优先级最高的那个任务先运行。UC/OS-II最多可同时管理64G个任务。如图1所示。 UC/OS-II是可裁剪的,即只嵌入用户程序需要的函数。 UC/OS-II提供的系统服务函数的执行时间都是可知的。函数执行时间与任务数目无关,且不大于某一确定值。
|
|
|
图1 占先式内核的简单执行过程 |
2. 在UC/OS-II内核中与任务有关的服务函数及数据结构: 1) 创建任务 UBYTE OSTaskCreate(void(far task)(pd), void pdata, void *pstk, UBYTE p); 2) 删除任务 UBYTE OSTaskDel(UBYTE p); 3) 任务调度 void OSSched(void); 4) 任务切换 void OSCtxSw(void) 5) 任务控制块结构 Typedef structos_tcb { void *OSTCBStkPtr; UBYTE OSTCBState; UBYTE OSTCBPrio; UWORD OSTCBDly; UBYTE OSTCBX; UBYTE OSTCBY; UBYTE OSTCBBitX; UBYTE OSTCBBitY; struct os_tcb *OSTCBNext; struct os_tcb *OSTCBPrev; OS_EVENT *OS_TCBEventPtr; }OS_TCB; 用MAX_TASK个OS_TCB空结构构成空白单链表,供OSTCBInit()申请一个空结构。初始化后的OS_TCB还要链入TCB双链表。 3. 与中断有关的服务函数: 1) 退出中断 void OSIntExit( ); 2) 检查任务的延迟时间到达没有 void OSTimeTick( ); 4. 与移植有关的主要函数: 1) 启动多任务 void OSStartHighRdy( ); 2) 任务切换 void OSCtxSw( ); 3) 中断级任务切换 void OSIntCtxSw( ); 4) 提供节拍中断向量 void OSTickISR( ); 5) 初始化任务堆栈 void OSTaskStkInit( );
三.ADSP-21535简介 1.ADSP-21535特性 ADSP-21535是Blackfin系列的16位定点DSP。Blackfin核包含两个乘累加器(MAC),具有一个正交的精简指令集(RISC),并且将单指令多数据(SIMD)和多媒体操作引入单指令集结构。同时还具有低功耗的性能。ADSP-21535在内核基础上集成了各种外设如通用异步发送接收端口(UART)、串行外设接口(PCI)、实时时钟(RTC)、通用串行总线接口(USB)等。 2.ADSP-21535的特点 1) 两个独立的成法累加器MAC。 2) 两个40位的ALU。 3) 四个视频ALU和一个移位器ALU。 4) 支持改进型哈佛结构和分级存储器结构。 5) 1级存储器(L1)在片内,访问周期只有一个处理器周期,包括4K高速暂存存储器、16K指令存储器和32K数据存储器。 6) 2级存储器(L2)的访问需要多个时钟周期,包括片内256K SRAM及片外SDRAM和异步存储器。 7) ADSP-21535支持动态电源管理。
四.基于ADSP-21535的UC/OS-II的移植 1.总括 在UC/OS-II中,不同大小的任务有不同大小的堆栈空间。ADSP-21535有3种工作模式:用户模式、监控模式和仿真模式。应用程序代码和大部分的UC/OS-II的服务程序一般在用户模式下被执行。当有中断或异常现象发生,或者当UC/OS-II内核在执行上下文切换时,ADSP-21535会被自动切换为监控模式。 三种模式的转换如图2所示:
|

|
|
图2 模式转换图 |
为一个应用程序新编写实时内核时,首先要根据具体情况重新做好配置,然后编译/汇编,连接定位。最后启动实时内核。配置与启动的主要步骤如下: 创建所需任务,至少有一个可修改头文件(如UCOS.H)#define的命名常量。 根据目标CPU,重新检查typedef中数据类型与其别名是否符合实际并作出相应的更正。 根据目标CPU,重写某些与CPU紧密相关的汇编程序。 将函数OSTimeTick()改装成中断服务程序。 为所有的ISR填写中断向量表。 在应用主程序main()中 ――分配堆栈、定义变量; ――调用OSInit(),对内核有关变量初始化; ――调OSStart(),进入实时多任务环境,调度就绪任务表中优先级最高者转入运行态,获得CPU,运行开始。 2.UC/OS-II的启动过程 多任务的启动是用户通过调用OSStart()实现的。 调用OSStart()函数后,要从就绪表中找到优先级最高的任务控制块,启动高优先级就绪任务启动函数OSStartHighRdy(),再启动多任务内核。当有中断或者是有优先级更高的任务出现的时候就会转入相应的函数进行处理;如果没有就会依任务的需求进行任务之间的转换或是任务自身的状态转换。移植过程主要是编写这里面的与处理器相关的函数。 启动过程见图3:
|
|
|
图3 多任务启动内核 |
在图3中,与内核相关函数的编写如下: 3.OSStartHighRdy()函数的实现 OSStartHighRdy()是一个高优先级就绪任务启动函数,主要是将任务栈中的保存值弹回到CPU寄存器中,然后执行返回指令,中断返回指令强制执行该任务代码。 OSStartHighRdy_: { sar = toppcstack; /*清除返回地址*/ call OSTaskSwHook_; ar = 1; /*启动多任务内核*/ dm(OSRunning_) = ar; /*更新OSTCBCur*/ i4 = dm(OSTCBHighRdy_); dm(OSTCBCur_) = i4; /*取下一个堆栈的指针*/ ar = dm ( i4 , m4 ); i4 = ar; jump res_reg; /*恢复CPU寄存器 */ } 4.OSCtxSw()函数的实现 OSCtxSw() 和OSIntCtxSw()都是任务切换函数,不同的是后者为中断级任务切换函数。它们的主要功能是将要挂起的任务的寄存器值保存起来,将要执行的任务的寄存器的内容恢复。上下文切换过程如图4所示:
|
|
|
图4 上下文切换过程 |
以上两个函数主要是通过将要执行的任务的任务控制块赋给当前任务控制块来实现它们的功能。其程序如下: OSCtxSw_: { sto_reg;/*保存寄存器内容*/ ar = i4;/*保存当前任务堆栈指针*/ i1 = dm ( OSTCBCur_ ); dm ( i1 , m1 ) = ar; call OSTaskSwHook_; /*更新当前的任务块和就绪表*/ i4 = dm ( OSTCBHighRdy_ ); dm ( OSPrioCur_ ) = i4; ar = dm ( OSPrioHighRdy_ ); dm ( OSPrioCur_ ) = ar; /*获取新任务的堆栈指针*/ ar = dm ( i4 , m4 ); i4 = ar; jump res_reg;/*恢复寄存器内容*/ } 5.UC/OS-II系统中时钟节拍中断的实现 UC/OS-II系统中,我们创建的中断服务例程格式如下: Interrupt(1) _using(0x8F00) _frame() void YourTickISR(void) { CPU_PushAllToUserStk(); OSIntNesting++; /*调用函数处理中断*/ OSIntExit(); CPU_PopAllFromUserStk(); }
- _interrupt()函数详细规定了中断句柄所获得的中断向量。
- _frame()函数规定了在一开始执行ISR将哪一些寄存器压入堆栈。
- CPU_PushAllToUserStk()需要一个空链表来存放这些寄存器的内容。
上面的函数功能可以由ADSP-21535的内核定时器来提供。ADSP-21535内核定时器可以产生周期性中断。一般取值为10~100Hz。 ADSP-21535定时器是通过减数来产生溢出,并发生中断。 通过设定定时器控制寄存器(TCNTL), 使能内核定时器;设定定时器计数寄存器(TCOUNT), 设定计数初始值;设定定时器周期寄存器(TPERIOD), 当计数寄存器TCOUNT计数溢出后,将TPERIOD的值加载到TCOUNT中;设定定时器比例寄存器(TSCALE), 通知内核经过多长指令周期计数器计一次数。 定时器控制寄存器的位结构:
|
bit 3 |
bit 2 |
bit 1 |
bit 0 |
|
0定时器未产生中断 1定时器产生中断
|
0 禁止定时器计数寄存器重载 1 使能定时器计数器寄存器重载
|
1 定时器使能 0 定时器禁能
|
0 低功耗 1 正常 |
产生的节拍中断向量是通过调用汇编语言编写的OSTickerISR()函数实现的。程序如下: OSTickISR_: { pop sts; /*弹出状态寄存器imask,astat等*/ sto_reg;/*保存寄存器内容*/ call OSIntEnter_; /*增加OSIntNesting*/ TCNTL = dm ( 0006h ); TCOUNT = dm ( 0fffh ); TPERIOD = dm ( 0fffh ); TSCALE = dm ( 0005h ); /*定时器的溢出产生中断*/ call OSTimeTick_; /*通知内核产生时钟节拍*/ call OSIntExit_; /*t通知内核结束中断*/ jump res_reg;/*恢复寄存器值*/ } 6.与编译器相关的数据类型的定义: #define OS_ENTER_CRITICAL() asm ("dis ints;") #define OS_EXIT_CRITICAL() asm("ena ints;") #define OS_STK_GROWTH 1 #define OS_TASK_SW() OSCtxSw() 移植结束后,就要对内核进行检测,看移植成功没有。测试一个UC/0S-II这样的多任务实时内核可以没有应用程序。但我所移植的内核是基于一个任务来检测的,这个任务就是将《UC/OS-II-源码公开的实时嵌入式操作系统》书中的例1的程序改成适合于ADSP-21535运行的程序。经检测是成功的。
参考文献
1. 《UC/OS-II-源码公开的实时嵌入式操作系统》 JEAN J.LABROSSE著,邵贝贝译,中国电力出版社 2001年8月。 2. 《2001嵌入式系统及单片机国际学术交流会 论文集》 沈绪榜 何立民 主编 北京航空航天大学出版社 2001年10月。 3. ANALOG DEVICES INC.,ADSP-21535 Blackfin DSP Hardware Reference,2001。 4. ANALOG DEVICES INC.,ADSP-21535 Blackfin DSP Instruction Set Reference,2000。
|