引 言
在嵌入式操作体系范畴,由Jean J. Labrosse开发的μC/OS,因为敞开源代码和强壮而安稳的功用,早年一度在嵌入式体系范畴引起强烈反响。而其自己也早已成为了嵌入式体系会议(美国)的参谋委员会的成员。
不管是关于初学者,仍是有经历的工程师,uC/OS敞开源代码的办法使其不光知其然,还知其所以然。经过关于体系内部结构的深化了解,能愈加方便地进行开发和调试;并且在这种条件下,完全能够依照规划要求进行合理的削减、扩大、装备和移植。一般,购买RTOS往往需求一大笔资金,使得一般的学习者望而生畏;而uC/OS关于校园研讨完全免费,只要在运用于盈余项目时才需求付出少数的版权费,特别合适一般运用者的学习、研讨和开发。自1992第1版面世以来,已有不计其数的开发者把它成功地运用于各种体系,安全性和安稳性现已得到认证,现现现已过美国FAA认证。
1 uC/OS II的几大组成部分
uC/OS II能够大致分红中心、使命处理、时刻处理、使命同步与通讯,CPU的移植等5个部分。
中心部分(OSCore.c) 是操作体系的处理中心,包含操作体系初始化、操作体系运转、中止进出的前导、时钟节拍、使命调度、事情处理等多部分。能够保持体系根本作业的部分都在这儿。
使命处理部分(OSTask.c) 使命处理部分中的内容都是与使命的操作密切相关的。包含使命的树立、删去、挂起、康复等等。因为uC/OS II是以使命为根本单位调度的,所以这部分内容也适当重要。
时钟部分(OSTime.c) uC/OS II中的最小时钟单位是timetick(时钟节拍)。使命延时等操作是在这儿完结的。
使命同步和通讯部分 为事情处理部分,包含信号量、邮箱、邮箱行列、事情标志等部分;首要用于使命间的互相联系和对临界资源的拜访。
与CPU的接口部分是指uC/OS II针对所运用的CPU的移植部分。因为uC/OS II是一个通用性的操作体系,所以关于关键问题上的完结,仍是需求依据详细CPU的详细内容和要求作相应的移植。这部分内容因为牵涉到SP等体系指针,所以一般用汇编语言编写。首要包含中止级使命切换的底层完结、使命级使命切换的底层完结、时钟节拍的发生和处理、中止的相关处理部分等内容。
2 关于MSP430的中止处理
2.1 函数调用和中止调用的操作
MSP430最常运用的C编译器应该便是IAR Embedd-ed WorkBench。关于这一编译器来说,经过剖析和研讨,发现它有以下规矩。
(1)函数调用
假如是函数级调用,编译器会在函数调用时先把当时函数PC压栈,然后调用函数,PC值改动。假如被调用的函数带有参数,那么,编译器依照以下的规矩进行。最左面的两个参数假如不是struct(结构体)或许union(联合体),将被赋值到寄存器,不然将被压栈。函数剩余的参数都将被压栈。依据最左面的那两个参数的类型,别离赋值给R12(关于32位类型赋值给R12:R13)和R14(关于32位类型赋值给R14:R15)。
(2)中止调用
假如是在中止中调用中止服务子程序的话,编译器将把当时履行句子的PC压栈,一起再把SR压栈。接着,依据中止服务子程序的杂乱程度,挑选把 R12~R15中的寄存器压栈。然后,履行中止服务子程序。中止处理完毕后再把Rx寄存器出栈,SR出栈,PC出栈。把体系康复到中止前的状况,使程序接着被中止的部分持续运转。
2.2 使命级和中止级的使命切换过程和原理
(1)使命级的使命切换原理
uC/OS II是一个多使命的操作体系,在没有用户自己界说的中止情况下,使命间的切换过程是这样的:使命间的切换一般会调用OSSched()函数。函数的结构如下:
void OSSched(void){
关中止
假如(不是中止嵌套并且体系能够被调度){
确认优先级最高的使命
假如(第一流的使命不是当时的使命){
调用OSCtxSw();
}
}
开中止
}
咱们把这个函数称作使命调度的前导函数。它先判别要进行使命切换的条件,假如条件答应进行使命调度,则调用OSCtxSw()。这个函数是真实完结使命调度的函数。因为期间要对仓库进行操作,所以OSCtxSw()一般用汇编语言写成。它将正在运转的使命的CPU的SR寄存器推入仓库,然后把 R4~R15压栈。接着把当时的SP保存在TCB->OSTCBStkPtr中,缓蟀炎罡哂畔燃兜腡CB->OSTCBStkPtr的值赋值给SP。这时分,SP就现已指到最高优先级使命的使命仓库了。然后进行出栈作业,把R15~R4出栈。接着运用RETI回来,这样就把SR和PC出栈了。简略地说,uC/OS II切换到最高优先级的使命,仅仅康复最高优先级使命一切的寄存器并运转中止回来指令(RETI),实际上,所作的仅仅人为地仿照了一次中止。
(2)中止级的使命切换原理
uC/OS II的中止服务子程序和一般前后台的操作有少量不同,往往需求这样操作:
保存悉数CPU寄存器
调用OSIntEnter()或OSIntNesting++
敞开中止
履行用户代码
封闭中止
调用OSIntExit();
康复一切CPU寄存器
RETI
OSIntEnter()便是将全局变量OSIntNesting加1。OSIntNesting是中止嵌套层数的变量。uC/OS II经过它保证在中止嵌套的时分,不进行使命调度。履行完用户的代码后,uC/OS II调用OSIntExit(),一个与OSSched()很像的函数。在这个函数中,体系首先把OSIntNesting减1,然后判别是否中止嵌套。假如不是的话,并且当时使命不是最高优先级的使命,那么找到优先级最高的使命,履行 OSIntCtxSw()这一出中止使命切换函数。因为,在这之前现已做好了压栈作业;在这个函数中,要进行R15~R4的出栈作业。并且,因为在之前调用函数的时分,或许现已有一些寄存器被压入了仓库。所以要进行仓库指针的调整,使得能够从正确的方位出栈。
3 运用uC/OS II存在的问题和处理办法
因为uC/OS II在运用的时分会占用单片机上的一些资源,如体系时钟、RAM、Flash或许ROM,然后减少了用户程序对资源的运用。关于 MSP430来说,RAM的占用是特别杰出的问题。关于8、16位的单片机来说,片内的RAM容量都很小,MSP430也是如此(最大的片内RAM也只要 2KB,例如MSP430F149)。假如运用扩展内存,会大大添加规划难度。
经过对 uC/OS II的剖析能够得知,μC/OS- II占用的RAM首要是用在每个使命的TCB、每个使命的仓库等方面。经过进一步剖析,发现使命仓库大的原因是因为MSP430的硬件规划中没有把中止仓库和使命仓库分隔。这样就造成了在运用uC/OS II的时分,考虑每个使命的使命仓库巨细时,不单单需求核算使命中局部变量和函数嵌套层数,还需求考虑中止的最大嵌套层数。因为,关于uC/OS II原始的中止处理的规划、中止处理过程中的中止嵌套中所需求压栈的寄存器巨细和局部变量的内存巨细,都需求算在每个使命的使命仓库中,则关于每一个使命都需求预留这一部分内存,所以很多的RAM被糟蹋。从这儿能够看出,处理这一问题的直接办法便是把中止仓库和每个使命自己的仓库分隔。这样,在核算每个使命仓库的时分,就不需求把中止处理中(包含中止嵌套过程中)的内存的占用核算到每个使命的使命仓库中,只需求核算每个使命自身需求的内存巨细,然后提高了 RAM的运用率,能够缓解内存严重的问题。
在这种规划方案中,中止仓库区也便是运用原有的 MSP430中的体系仓库区。在前后台的规划办法中,中止中的压栈和出栈的操作都是在体系的仓库区完结的。根据uC/OS II的使命切换的原理,咱们关于使命仓库的功用和体系仓库的功用做了以下区分:使命在运转过程中发生中止和使命切换的时分,PC和SR以及寄存器Rx都保存在各个使命自己的使命仓库中;而中止嵌套发生的压栈和出栈的操作都是放在体系仓库中进行的。这种区分办法是根据尽量将中止使命与一般使命分隔的思维规划的。
早年面关于IAR EW的默许操作剖析来看,仓库的结构能够有两种。一种是把uC/OS II的使命仓库规划成图1所示的办法。这种办法是把编译器默许的压栈操作放在前面,然后再把剩余的寄存器进栈。可是,因为编译器在处理杂乱程度不同的中止服务程序的时分,压入栈的寄存器的数量不定,所以会对今后其他寄存器的压栈和出栈操作添加杂乱度。这儿,咱们采用了图2所示的办法生成仓库。在这种仓库中,PC和SR压栈后,经过调整SP指针,使得R4~R15寄存器掩盖编译器默许压栈的寄存器。这样,处理的难度会小一点。