μCOS-ii初接触探索心得
μC/OS-II 是一个源代码公开、可移植、可裁剪的实时多任务操作系统,具有稳定可靠、实时性好等优点,是专门针对微处理器和微控制器设计的实时内核,它的内核可以做到很小,很适合在单片机系统上移植。移植了μC/OS-II 的嵌入式系统可以使各个任务独立工作,互不干涉,很容易实现准时而且无误执行,使实时应用程序的设计和扩展变得容易,使应用程序的设计过程大为减化。在这个星期内,我通过对禁用了邮箱、队列、文件系统的最简单的μCOS-ii操作系统进行了学习,现将收获分享如下。
1 μCOS-ii运行过程
μCOS-ii的主函数不到十行代码,但是因为其高度的结构化以及良好的函数封装,对其运行过程的了解对于学习μCOS-ii的编程思想以及设计思路具有重大的意义。
我通过将μCOS-ii代码移植到SST89V564RD单片机中,并且利用TI公司的温度传感器TMP124创建了两个任务:读TMP122数据、向串口发送读取的温度值。要求两个任务轮流执行,读TMP122的优先级高于向串口发送数据的优先级。
1.1 系统初始化
在μCOS-ii中,系统初始化可以分为全局变量初始化、创建空闲任务任务、堆栈初始化、任务控制块初始化等部分内容。
全局变量初始化。由于μCOS-ii操作系统中定义了众多与系统参数有关的全局变量,因此全局变量的初始化包含众多内容。这些系统参数的初始化大都被封装在OSInit()函数中。尤其要注意的是,在OSInit函数中,OSRunning变量一定要被定义成FALSE,否则在OSStart函数中,系统无法启动创建的任务,系统因此变成了一个有始有终的函数,创建的任务永远得不到执行。在实际运行过程中表现为主函数运行一次之后,系统不再运行。另外需要注意的是OSTCBCur、OSTCBList等这几个变量需要定义为(OS_TCB DT_XDATA *)0的类型。因为这些变量是指向结构体TCB(Task Control Block)的指针,并不是数字〇。
创建空闲任务。创建空闲任务包括进入临界区、任务堆栈初始化、TCB初始化、退出临界区等内容,大部分是一些简单的赋值操作,比较简单,不再赘述。其中OSRdyGrp |= ptcb->OSTCBBitY; OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;这两句话要尽量看懂。因为这两句话牵扯到后面OSUnMapTbl这个矩阵的理解。其含义为一旦任务就绪队列中有“1”(即对应的任务就绪),则相应的任务组OSRdyGrp 相应的位变成1,OSRdyTbl中的相应的位也变成1.
堆栈初始化以及任务控制块初始化。比较容易理解,简单的赋值操作。不过在堆栈初始化中的ppdata = ppdata;opt = opt; 以及任务控制块初始化中的pext= pext; stk_size= stk_size;pbos=pbos;opt=opt; id= id;这些语句刚开始的时候很令人费解。其实这些是防止编译器不断的报warning,以免影响正常的调试的。
1.2 任务创建
同上述创建空任务大体雷同。需要注意的是此时操作系统的OSRunning变量还是处于FALSE状态,因此创建任务的过程中,操作系统并没有开始运行。这就是为什么操作系统在开始运行的时候不是选择第一个创建的任务开始运行,而是从所有的任务里面选择优先级最高的运行的原因。
1.3 任务调度
任务调度是从OSStart函数开始的。具体包括OSStart、OsStartHighRdy、Task1~n、OSTimeDlyHMSM、OSSched等函数,其中任务交换是在汇编语言文件的OS_TASK_SW函数中运行的。
OsStart在整个系统运行的过程中只会运行一次。在系统创建任务之后运行。主要目的是从任务就绪表中挑选出优先级最高的任务,并开始运行优先级最高的任务。
OsStartHighRdy为asm文件中的一部分。主要是堆栈的操作。目的是要把高优先级任务的任务堆栈复制到寄存器中,为即将开始的运行最高优先级的任务做好准备。
Task1~n。Task为一个无限循环函数。虽然是无限循环函数,但是与平常前后台系统中的无限循环具有区别。因为μCOS-ii操作系统并不是一个像Linux那样的时间片轮流处理的操作系统,它仅仅是一个处理完一部分内容之后再去处理另一部分内容的实时操作系统。这就要求在每一个任务的每一个无限循环中都要加一个调用OSSched函数的函数。
OSTimeDlyHMSM主要是为用户提供一个良好的借口,将用户输入的时、分、秒、毫秒这四个数据转换成系统的滴答数,然后调用OSTimeDly实现。在OSTimeDly中调用OSSched,确保系统的实时性。OSSched函数结构也类似于OSStart,主要是计算出任务就绪表中最高优先级的任务,通过OS_TASK_SW中的堆栈的操作实现任务的交换。
2 μCOS-ii查找最高优先级算法的实现
指导思想:以空间换时间。
在查看源代码的过程中,最令人费解的或许就是OSUnMapTbl这个矩阵了。现将此矩阵复制如下:
INT8U const DT_XDATA OSUnMapTbl[] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
在了解这个矩阵的工作原理之前,我们有必要了解一下几个变量的含义:
OSRdyGrp 是一个8位的unsigned char型数。由于该μCOS-ii系统最多允许prio为63,也就是说最多允许有64个优先级。64个优先级被分成了八组,每组有八个优先级。如果有任何一组中的任务进入了就绪状态,则该组所对应的位变为1。比如优先级为4,23,56的任务同时进入了就绪态,则对应第0,2,7组中有任务进入了就绪态,则此时OSRdyGrp 应为 10000101。
OSRdyTbl为一个有8个元素的8位数的数组,分别为OSRdyTbl[0]…OSRdyTbl[7]。从OSRdyTbl[0]到OSRdyTbl[7]的每一位数对应相应优先级的任务是不是进入了就绪态。还是上面的例子,假如优先级为4,23,56的任务进入了就绪态 ,则OSRdyTbl[0]的第4位,OSRdyTbl[2]的第7位,OSRdyTbl[7]的第0位变成1,其他的位仍然保持零。
介绍完两个变量之后,就可以很容易理解这个矩阵的作用了。矩阵的第i个数字表示用二进制表示的i中1所出现的最小位数。比如对于矩阵的第48个数字,48用二进制表示为00110000,在第4位以及第5位中出现了1,故去最小值,则OSUnMapTbl[48]=4 。也就是说,通过查这个矩阵得到的是最小的出现1的位数。假如对于OSRdyGrp 来说,在OSRdyGrp 等于48的情况下,意味着第四组与第五组中有任务处于就绪状态,则通过此表可以得出最高优先级的任务在第四组中。假如对于OSRdyTbl来说,OSRdyTbl[4]表示第四组中每一个元素是不是处于就绪态。还是拿48来打比方,假如OSRdyTbl[4]等于48,有48用二进制表示为00110000可得第四位与第五位中有两个任务处于就绪态,此时查询OSUnMapTbl[48]=4可得优先级最高的任务处于第四位上。由此很容易理解这两行代码:
y= OSUnMapTbl[OSRdyGrp];
x = OSUnMapTbl[OSRdyTbl[y]];
因此得到的y为最高优先级所在的组号。X为最高优先级所在的组中最高优先级所在的组的组号。由于每组有8个成员,对于y组第x个来说,优先级自然是 8*y+x 。由此得到了最高优先级。下面的代码不难理解。
OSPrioHighRdy = (INT8U)((y << 3) + x);
此外,因为这个矩阵无论在何种情况下,都是不变的,因此个人认为原μCOS-ii系统中定义为DT_XDATA完全没有必要,只是增加了系统的开销。因此我尝试将此变量类型改成DT_CODE ,经过运行TMP124的尝试,系统运行几十分钟后仍然正常。因此得出了此处可以改进的建议。而且改进之后系统的占用xdata从600多字节减少到400多字节,系统资源占用减少很明显。
可能编写μCOS-ii的工程师为了提高可移植性,将OSUnMapTbl定义为DT_XDATA吧。仅仅猜测而已。
3 代码感想
就我个人而言,整个移植当中经历了一些波折,让我深刻体会到团队建设的重要性。阅读代码,不仅需要有坚定的意志,更重要的是要对所从事的任务有明确的认识。清楚自己要干什么,才是最重要的。