摘 要:从Keil C51的内存空间管理办法下手,侧重评论实时操作体系在使命调度时的重入问题,剖析一些处理重入的根本办法与办法:剖析实时操作体系使命调度的占先性,提出非占先的使命调度是能更适合于Keil C51的一种调度办法。为此,结构这一实时操作体系,并有针对性地介绍此体系的堆管理办法、使命的树立以厦使命的切换等。
要害词:51单片机 实时操作体系 使命重八调度
现在,大大都的产品开发是在依据一些小容量的单片机上进行的。51系列单片机,是我国现在运用最多的单片机系列之一,有十分广阔的运用环境与远景,多年来的资源堆集,使51系列单片机仍是许多开发者的首选。针对这种状况,近几年涌现出许多依据51内核的扩展芯片,功用越来越完全,速度越来越快,也从一个旁边面阐明晰51系列单片机在国内的生命力。
多年来咱们一向想找一个适宜的实时操作体系,作为自己的开发根底。依据开发需求,整合一些常用的嵌入式构件,以节省开发时刻,尽最大或许地削减开发作业量;别的,要求这个实时操作体系能十分简单地嵌入到小容量的芯片中。究竟,大体系是少量的,而小运用是大都而广泛的。清楚明了,μC/OS—II是不太适合于以上要求的,而Keil C所带的RTX Tiny不带源代码,不具透明性,至于其FULL版本就更不用说了。
1 KeiI C51与重入问题
说到实时操作体系,就不能不考虑重入问题。关于PC机这样的大内存处理器而言,这好像并不是一个很费事的问题,借用μC/OS—II RTOS的说法,即要求在重入的函数内,运用部分变量。但5l系列单片机仓库空间很小,仅限制在256字节之内,无法为每个函数都分配一个部分堆空间。正是因为这个原因,Keil C51运用了所谓的可掩盖技能:
①部分变量存储在大局RAM空间(不考虑扩展外部存储器的状况);
②在编译链接时,即现已完结部分变量的定位;
③假如各函数之间没有直接或直接的调用联系,则其部分变量空间便可掩盖。
正是因为以上的原因,在Keil C51环境下,朴实的函数假如不加处理(如添加一个模仿栈),是无法重人的。那么在Keil C5l环境下,怎么使其函数具有可重人道呢?下面剖析在实时操作体系下面,使命的根本结构与形式:
vold TaskA(void*ptr){
UINT8 vaL_a;
//其他一些变量界说
do{
//实践的用户使命处理代码
}while(1);
}
void TaskB(void*ptr){
UINT8 vaLb;
//其他一些变量界说
do{
Funcl();
//其他实践的用户使命处理代码
)while(1);
void Funcl(){
UlNT8 v al_fa;
//其他变量的界说
//函数的处理代码
}
在上面的代码中,TaskA与TaskB并不存在直接或直接的调用联系,因此其部分变量val_a与val_b便是可以被相互掩盖的,即其或许都被定坐落某一个相同的RAM空间。这样,当TaskA运转一段时刻,改动了val_a后,TaskB获得CPU操控权并运转时,便或许会改动val_b。因为其指向相同的RAM空间,导致TaskA从头获得CPU操控权时,val—a的值现已改动,然后导致程序运转不正确,反过来亦然。另一方面,Funcl()与TaskB有直接的调用联系,因此其部分变量val_fa与val_b不会被相互掩盖,但也不能确保其部分变量val_fa不会与TaskA或其他使命的部分变量构成可掩盖联系。
将val_a、val_b以及val_fa等部分变量界说为静态变量(加上static指示符)可以处理这一问题。但问题是,界说很多的static类型变量,将导致RAM空间的很多占用,有或许直接导致RAM空间不够用。尤其是在一些小容量的单片机内,一般只要128或256字节,很多的静态变量界说,在如此小的RAM资源状况下明显就不太适宜了。由此而有了另一种的处理办法,如下代码所示:
void TaskC(void){
UINT8 x,v;
whlk(1){
OS_ENTER_CRITICAL();
x=GetX(); (1)
y=GetY(); (2)
//使命的其他代码
OS_EXIT_CRITICAL(); (3)
0SSleep(100); (4)
}
}
以上代码TaskC中运用了临界维护的办法来维护代码不被中止占先,的确有效地处理了RAM空间太小,不宜很多界说静态变量的问题。可是假如每个使命都选用此种结构,使命一开端,就封闭中止,将使实时性得不到确保。现实证明,这种延时是相当可观的。用一个实例来阐明,假如想在体系中运用一个动态改写的LED显现器,就难以确保显现的安稳与接连,哪怕在体系中是运用一个独自的定时器来做这一作业(进入临界区后,EA=0)。其次,这种结构现实大将占先的使命调度转化为非占先的使命调度。实践上假如在(3)与(4)之间没有可巧产生中止并导致一个使命调度,那就可以理解为是使命自动抛弃CPU的操控。假如在(3)和(4)之间可巧产生了一个中止并导致了一个使命调度,仅仅履行了一次剩余的使命调度罢了,并且并不期望在(3)之后产生2次乃至屡次的使命调度,信任读者也有这一希望。
除此之外,还可以发现使命的一个特色:当使命从(1)从头开端时,部分变量x和y是一个什么值并不在乎,即x和y即便在(3)之后改动了,也现已不再重要,不会影响程序的正确性。其实这一特色也是大部分使命,至少是太部分使命的大部分部分变量的一个共性——假如使命在整个履行过程中,不会(被占先)抛弃CPU操控权,则其部分变量大大都并不需求进行特别的维护,即其效果域仅仅使命的当次履行,针对上面的代码,便是临界维护区内的代码区域。
2 实时操作体系要不要占先
由上面的剖析,假如要坚持一个函数可重人,就得运用静态变量,体系的RAM资源将是一个严峻的检测;假如运用临界区来维护运转环境,体系的实时性又得不到确保,并且有将占先式使命调度转为非占先使命调度之虞。明显,运用静态变量简略,但有更多的不适用性,对将来功用的调整也是一个阻止,一般不被选用。那么,就只能从环境维护上来下功夫了,可是果然只能以进入临界区献身体系的实时性来确保使命不被占先?下面看看临界维护这一办法的根本思路:
①在一个使命中,假如部分变量在其效果域内不被占先切换,则这些变量在使命被掠夺了CPU操控权后,不关心其值也不会影响使命的正确履行;
②运用临界区维护,可以到达上面所说到的要求;
③由此导致的实时功能与占先切换的削弱可以承受。由此可知,不被占先是使命维护部分变量的要害。既然如此,何不抛弃占先式的使命调度?这不失为一个好的起点。针对Keil C51,非占先式使命调度,或许是一种更好的办法,更能和谐51系列单片机的既定资源。下面编写这样一个体系:
①运用非占先式使命调度;
②可以在小容量的芯片中运用,开发方针是,即便是8051这样小的芯片,也可运用这个实时操作体系;
③支撑优先级调度,尽或许确保其实时性。
3 实时操作体系的完成
依据以上的剖析与意图,近来完结了这个操作体系。在仓库上借用RTx的管理办法,即当前使命运用悉数的堆空间,如图1所示。
3.1 仓库的初始化与使命的创立
仓库的初始化实践是初始化0STaskStackBotton数组,并将当前使命指定为闲暇使命,下一个运转使命指定为最高优先级使命,即优先级为零的使命。初始化时,将SP的值存人OSTaslkStackBotton[O],SP+2的值存入OSTaskStacKBotton[1],依此类推。而使命是调用0STa-skCreate函数树立的。实践上仅仅将使命(假设为n号使命)的地址填人到对应OSTaskStackBotton[n]所指向的方位,并将SP向后移动2个字节,如图2所示。
为什么要以这样一种规则而不是其他的办法呢?这是因为在使命树立后,还未进行使命调度之前,各使命的仓库实践上是它们本身的地址,因此其仓库深度为2,为了程序的简洁而直接填入。
void main(void){
OSInit(); /*初始化OSTaskStackBcBotton行列*/
TMOD=(TMOD&0XFO)│ 0XOl;
TL0=0xBF;
TH0=0xFC;
TRO=1;
ETO=1;
TFO=O:
OSTaskCreate(TaskA,NULL,0);
OSTaskCreate(TaskB.NULL,1);
OSTaskCreate(TaskC,NULL,2);
OSStart();
上面这段代码中,一切使命树立后,便调用OSStart()开端使命调度。OSStart()是一个宏界说,如下所示:
#deflne OSStart() d0{\
OSTaskCreate(TaskIdle,NULL,OS_MAX_TASKS);\
EA=l:\
return;\
}while(O)
首要,它创立了一个闲暇使命并翻开中止,然后便回来。回来到哪里了呢?咱们知道,闲暇使命是优先级最低的使命,当调OSTaskCreate树立时,会将其地址填人到SP的方位,并把SP向后移动2个字节(见图2及阐明),因此此刻处在仓库顶端的,一定是闲暇使命Taslddle。这就使得这儿的return一定会回来到闲暇使命。至此,体系进入正常运转状况。
3.2 使命的切换
使命的切换分两种状况,在当前使命优先级低于下一个获得CPU操控权的使命时,将下一个获得CPU操控权的使命的栈顶到当前使命的栈顶之间的内容向RAM空间的高端搬移,以空出悉数的RAM空间作下一个使命的堆空间,一起更新对应的OSTaskStackBotton,使其指向新的正确使命的仓库栈底。假如当前使命的优先级高于下一个使命的优先级,则作相反的搬移,如图3与图4所示。
一切使命有必要自动调用OSSleep,抛弃CPU的操控权。使命调用OSSleep后,将挑选优先级最高的安排妥当使命运转。
结 语
体系完结后,内核的代码量在400多个字节左右,占用1个定时器中止及小量的内存空间。体系设置容量为8个使命,用户实践可用使命为7个,可以满意一般需求,也到达了在小容量芯片中运用的开发要求。因为没有选用占先式的使命调度,除开全程相关的单个使命的一些部分变量外,其他部分变量现已不存在掩盖联系,因为是使命自动抛弃CPU操控权,关于单个需求维护的变量独自进行处理也变得简单。在体系中,全程不需求重复地开关中止,实时功能也很好。对单个时序要求严厉的外设(如DSl8820)在外。