关于uCOS-II操作系统的说明
一、 工程文件的说明
工程的所有文件在ucos目录下,打开工程文件后可以看到下面一些源文件 ·cmd文件
工程的CMD文件与一般程序中的CMD 文件相同。由F2812.cmd和DSP281x_Headers_nonBIOS.cmd两个文件组成。其中对F2812.cmd文件进行了一些修改,将代码段定义在FLASH中,.bss和.ebss段都定义在低64K的数据空间中,以保证定义在.ebss段中的人物堆栈可以通过堆栈指针被访问。
·C语言和汇编源文件
这部分文件在CCS窗口的Source文件夹下可以看到,主要有下面一些文件: Ucos_ii.c:操作系统的头文件包含文件。
OS_TickISR.asm:主要是OSTickISR( )函数,是操作系统时钟节拍中断的服务函数。
OS_Sw.asm:主要是OSCtxSw( )函数,非中断级任务切换函数,也是系统完成任务切换调用的30号中断(即USER 11 软中断)的中断服务函数。
OS_StartHighRdy.asm:主要是OSStartHighRdy()函数,在操作系统开始运行后(OSStart()函数开始执行以后)启动优先级最高的任务,一般情况下该任务是TaskStart任务。
OS_ISR.asm:中断服务程序的汇编源文件,尽量把要使用的中断服务程序放在这个文件中,使用汇编语言按照OS_SCIARXISR( )的结构编写。关于中断服务程序后面会进一步加以说明。
OS_IntSw.asm:主要包含中断级任务切换函数OSIntCtxSw( ),完成中断级任务切换。
OS_GlobalStack.c:该文件中主要是全局任务堆栈的定义。 OS_cpu_c.c:该文件中主要是任务堆栈初始化函数OSTaskStkInit( )及其他一些与CPU相关的C语言函数的定义。
Ex1l.c:工程的主函数文件。
DSP281x_xxxx.c:例程中DSP2812外设操作的一些源代码,可以在这些文件中添加必要的操作外设的函数。
·头文件
这部分主要是操作系统的配置可提供的一些函数的源文件,以及2812一些寄存器的定义,主要有两个文件需要注意:
os_cfg.h:移植操作系统得配置文件,其中的宏定义参考蓝色的《嵌入式实时操作系统uCOS-II原理与应用》一书的第九章。
os_globalstack.h:该文件主要是关于系统堆栈的一些宏定义,主要有两个量:TASK_STK_SIZE,任务堆栈大小的定义,这里定义为128;N_TASKS,系统中除了空闲任务,开始任务(TaskStart)外的任务个数,这里为2,当系统中需要更多的任务时,这个值夜需要修改。
堆栈的大小与进行任务切换时需要保存的寄存器的个数和任务中局部变量的个数都有关,因为CCS环境中局部变量的存储空间在堆栈中,一般情况下128是够用的,也是必须的,可以根据实际需要增加。
二、 uCOS-II操作系统的说明
uCOS-II操作系统实际上只是一个进行任务调度和进程同步的一个实时内核。或者说,系统只提供了一些进行任务调度和进程间通信的函数和机制,用户在这个内核的基础上构建自己的实时系统。在uCOS-II中编写程序和在不用操作系统得环境中编写程序基本上是一样的。不同的是,没有操作系统的程序中,只有主程序是一个无限循环,其它所有的函数都通过中断和主循环被调用。由主循环串起常用的操作,用中断串起由异步事件触发的操作。而在uCOS-II中,系统需要完成的功能被划分为若干个相对独立的任务,单独来看,每一个任务都如主程序一样是一个无限循环。操作系统根据优先级优先的原则调度不同的就绪任务对CPU的使用权。不同的任务之间可以通过信号量,消息队列和消息邮箱等实现通信。当一个任务由于等待某任务而处于等待状态时,则已就绪的最高优先级任务获得CPU的使用权,进入运行状态,从而保证了当某个任务处于等待状态时CPU没有停止下来,其它任务可以在CPU上运行。
·uCOS-II中的程序框架
uCOS-II的程序包括main函数,开始任务(Taskstart)和若干其它任务构成。绝大部分操作都是由任务完成的,操作系统不停的在不同的任务之间切换。而中断服务程序则尽可能简单,只完成发送信号量等操作。程序从main函数开始执行,完成一些初始化操作之后,启动多任务操作,开始运行优先级最高的任务(很多情况下是开始任务TaskStart)。操作系统通过定时中断或其它方式(如OSTimeDly()函数)触发一次任务调度,完成在不同的任务之间切换。
Main函数的示意性代码如下: void main (void) {
若干系统的初始化操作;
OSInit();//完成操作系统,任务控制块(TCB)、事件控制块(ECB)//的初始化,并完成空闲任务和统计任务的创建(如果运行统计任务) 信号量、消息邮箱、消息队列的初始化; 创建开始任务; OSStart();//开始多任务的运行 }
注:在调用OSStart()函数之前,至少要求启动任务已被创建。 ·操作系统中的任务 1. 任务
任务是操作系统调度CPU的基本单位,一个任务的代码由初始化代码和一个无限循环组成。在无限循环中必须在适当的位置调用延时函数OSTimeDly(INT16U ticks)(该函数的参数ticks是任务需要等待的时钟节拍数),以保证即使优先级最高的任务业不是一直处于就绪状态,低优先级的任务可以在高优先级的任务等待时获得CPU的使用权。需要注意的是,在uCOS-II的任务代码中尽量不要使用软件延时,必要时可以调用OSTimeDly()进行延时。
一个任务的示意性代码如下: void Task( void *data) {
局部变量定义; 必要的初始化; While(1)
{
任务的操作; OSTimeDly(x); }
2. 任务堆栈
任务在创建之前必须首先分配任务堆栈,用于在任务失去CPU的使用权的时候保护所有的寄存器和局部变量,从而保证在任务再次获得CPU的使用权的时候能够继续顺利地运行。任务堆栈的分配是通过全局变量的定义实现的。本工程中通过“volatile OS_STK TaskStk[N_TASKS][TASK_STK_SIZE];”(OS_Globalstack.c 文件中)定义TaskStk这一二维数组实现的。
任务堆栈中主要在任务调度或中断时保存CPU寄存器,而任务调度实际上也是通过使用软件中断实现的。在CPU中,有一些寄存器在发生中断时会被自动压入堆栈中(可以参见中断过程,《TMS320C28x系列DSP的CPU 和外设(上)》,P58),我将这些寄存器称为系统状态字,另一些是需要程序员手动压栈的,称为用户状态字,为了保证系统的健壮性,这两类寄存器都需要压入任务堆栈,DSP2812的堆栈生长方向是由低地址向高地址生长,即用户状态字在靠近栈底的低地址中,用户状态字的靠近栈顶的高地址中。用户堆栈的设计如下图所示:
任务堆栈的初始化是通过调用OSTaskStkInit()实现的,该函数主要给堆栈的内容赋初值,当任务开始运行的时候这些值被从堆栈弹出到相应的寄存器中。这些寄存器中比较重要的是两个状态寄存器(ST0,ST1),不要随便修改初始化函数中要赋的初值,在IER寄存器中,中断全部使能,同时返回地址,RPC等寄存器都指向该任务。
3. 任务的创建
任务的创建是通过调用任务创建函数OSTaskCreate()来实现,当运行了OSStart()函数以后,任何被创建的任务都处于就绪状态,并在下一次进行任务调度的时候可能获得CPU的使用权(前提是该任务是当前处于就绪状态的优先级最高的任务)。
OSTaskCreate()函数说明如下: INT8U OSTaskCreate( void(* task)(void * pd),void *pdata,OS_STK *ptos,INT8U prio): 返回值为出错信息,pd指针是指向任务的指针,即任务的名称。pdata是传递给任务的参数,一般为一个空指针,ptos是任务堆栈的栈顶指针,prio是任务的优先级,一般来说开始任务(TaskStart)有最高优先级0,其它的任务优先级可以随具体情况而定。对实时性要求较高的任务优先级较高(即数字较小),对实时性要求不高的任务优先级较低。任务的优先级不能低于任务的个数,这个参数(OS_LOWEST_PRIO,系统中任务的最低优先级,系统除了开始任务的总任务个数为OS_LOWEST_PRIO-2)在cfg.h文件中定义。
尽管操作系统允许在任何地方创建任务,但任务的创建应尽可能在main函数或开始任务中实现。
4. 任务调度的实现
在uCOS-II中任务调度有很多方式实现的。具体来说,系统在每个事件片结束时检查任务控制块,得到优先级最高的就绪任务,并在CPU上运行。系统在中断结束的时候也会检查任务控制块,运行优先级最高的任务;另外当一个任务由于调用OSTimeDly()函数或等待信号量、消息队列、消息邮箱而由运行态转变为等待态时,也会引起一次任务调度,相应的,在某任务发送信号量、消息对了和消息邮箱的时候也会触发一次任务调度。
·中断服务程序
在uCOS-II中中断服务程序的设计要尽量简单,只实现向任务发送信号量等操作,具体的操作由任务实现。
在本移植中,由于操作系统要在中断结束的时候进行任务切换,为保证任务堆栈的一致性,要在进入中断的时候手动把所有的用户寄存器压入堆栈,这一操作最好用汇编语言实现。这就是本工程中用汇编实现中断服务程序编写的原因。中断服务程序汇编代码的结构可以参见SCIA的RX接受中断服务程序OS_SCIARXISR。
中断服务程序的流程如下( 见下页:)
开始用户状态字压栈堆栈对准给出当前中断的ACK信号提示CPU进入中断调用用户中断服务程序取消堆栈对准通知CPU中断服务程序结束用户状态字出栈中断返回 ·信号量、消息队列和消息邮箱
这三种机制都是实现进程间通信和同步的,并保证当一个任务在等待某一事件的时候CPU可以运行其它的任务,一旦等待的事件到来,该任务可以立即获得CPU的使用权。它们的使用参见《嵌入式实时操作系统uCOS-II原理及应用》或《嵌入式实时操作系统uCOS-II 》(PDF版,邵贝贝译)

