uCOS-II移值过程实例讲解
我将uCOS-II 移植到了EPONS 的C33209的平台上,接下来我就基于我移植好的代码讲解如何将uCOS-II从一种MCU移植到另一种MCU。
首先介绍uCOS-II的文件,如下表:
ucos_ii.h
os_cfg.h
os_cpu.h
os_core.c
os_dbg_r.c
os_flag.c
os_mbox.c
os_mem.c
os_mutex.c
os_q.c
os_sem.c
os_task.c
os_time.c
ucos_ii.c
os_cpu_c.c
os_cpu_a.asm
其中我们和硬件平台相关的文件的文件名被加粗了,也就是说若要将uCOS-II移植到新的平台上只要关心以上四个文件就行了。当然你也可以根据需要再添加你自己的和平台相关的文件,事实上我也是这么做的。在我移植的例子中就添加了四个和平台相关的文件,文件如下表:
crt0.c
drv_rtc.c
vector.c
ext.s
crt0.c是用来初始化系统的比如说MCU的一些特殊寄存器、设置外围的总线接口,等。drv_rtc.c是用来初始化系统中的一个RTC的,这个RTC可以为内核提供必要的基于时间片调度的时基。同时提供了对RTC开始和停止的操作函数。在我的例子中RTC会每秒产生32次中断。vector.c顾名思义,它是系统上电后为系统提供矢量入口表的文件,当然也包括中断向量表。ext.s是为uc/OS-II提供OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()函数的具体实现以及在用户程序的中断函数出入时要调用的状态保护和状态恢复函数OS_SAVEALL ()和OS_RESTOREALL ()。前面两个函数的功能是:OS_ENTER_CRITICAL()屏蔽中断;OS_EXIT_CRITICAL()恢复原来的中断使能状态。
1. os_cpu_a.asm的说明
要想顺利的移植首先要了解uCOS-II的一些基本概念。
uCOS-II实质上是一个嵌入式操作系统内核,她只负责管理各个任务,为每个任务分配CPU时间,并且负责任务之间的通讯。内核提供的基本服务是任务切换。这是个很重要的概念,可以说你只要掌握了任务切换的本质,可以说你就掌握了移植uCOS-II的技术。至于任务之间的通讯他们是建立在任务切换之上的或者说和系统平台关系不大(当然这也和操作系统通讯机制的实现相关,至少uCOS-II是这样的)。
接下来我们就有针对性的介绍什么是uCOS-II里的任务。一个任务通常是一个无限循环,如下程序所示。
void Task1(void *data)
{
INT8U err;
char *rxmsg;
data = data; /* Prevent compiler warning */
while(1) //这是一个无限循环
{
rxmsg = (char *)OSMboxPend(MAIL1, 0, &err); /* Wait for message from Task #2 */
OSTimeDlyHMSM(0, 0, 1, 0); /* Wait 1 second */
OSMboxPend(MAIL3, 0, &err); /* Wait for message from Task #3*/
OSMboxPost(MAIL2, (void *)1); /* Acknowledge reception of msg*/
}
}
(1). OSCtxSw()函数
在上面的例子里你也看到了任务和其他的C函数一样,有函数的返回类型,有形式参数变量,只是任务是绝不会返回。事实上任务也就是一个函数,内核在调度时是以这个函数为基础的,为了和其他函数区分,我们给了她另外一个名字——任务。也正因她是一个特殊的函数,而且和内核调度直接相关,所以不能随便返回和被用户调用,而要用内核的专用函数来“建立”和“删除”。所谓的“建立任务”其实是在内核处对该函数进行注册和相关数据结构的填充,比如该函数的入口地址、为函数分配专门的堆栈空间(为什么要为函数分配专门的地址空间呢?我们马上就会谈到)。“任务调度”就是根据情况(比如时间片被用完),来调用另一个被称为任务的函数(我们暂时称之为函数TA),同时停止当前的一个任务(其实也是一个函数,我们称之为TB)。问题出来了,若内核象普通函数那样直接调用TA,那么当内核要重新调用TB时怎么知道刚才TB执行到哪里了呢?若内核为TA和TB分配专用的两块空间,当内核要调用其他任务(其实就是函数)的时候先将当前任务(函数)运行的地址和状态保存起来,然后当要返回前再恢复,当然每个被称之为任务的函数都要有自己独立的保存运行地址和状态的空间,以免混乱。那问题就很好解决了。这也就是为什么任务都有自己的堆栈空间的原因。