您的位置 首页 解答

一种根据C51单片机的非抢占式的操作系统架构

摘要:从KeilC51的内存空间管理方式入手,着重讨论实时操作系统在任务调度时的重入问题,分析一些解决重入的基本方式与方法:分析实时操作系统任务调度的占先性,提出非占先的任务调度是能更适合于Kei

  摘 要:从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)在外。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/changshang/jieda/229720.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部