登录站点

用户名

密码

51单片机的实时操作系统电路设计

已有 150 次阅读  2013-04-16 14:51   标签操作系统  单片机 
 目前,大多数的产品开发是在基于一些小容量的单片机上进行的。51系列单片机,是我国目前使用最多的单片机系列之一,有非常广大的应用环境与前景,多年来的资源积累,使51系列单片机仍是许多开发者的首选。针对这种情况,近几年涌现出许多基于51内核的扩展芯片,功能越来越齐全,速度越来越快,也从一个侧面说明了51系列单片机在国内的生命力。 电子园51单片机学习网k5UJ"EhEaiOt

 

h1VX5P e7p46905 电子园51单片机学习网QFK:XLf

    多年来我们一直想找一个合适的实时操作系统,作为自己的开发基础。根据开发需求,整合一些常用的嵌入式构件,以节约开发时间,尽最大可能地减少开发工作量;另外,要求这个实时操作系统能非常容易地嵌入到小容量的芯片中。毕竟,大系统是少数的,而小应用是多数而广泛的。显而易见,uCOS-II是不太适合于以上要求的,而Keil C所带的RTX Tiny不带源代码,不具透明性,至于其FULL版本就更不用说了。

xh&S are0q/h \46905 电子园51单片机学习网xN[W \#U+{;ao

 

'z&\;f)c9P ttQ`46905

D$_}^0hn0|46905    1 KeiI C51与重入问题电子园51单片机学习网 eX Vo3H'n

电子园51单片机学习网{%N)AJ;ha5I1u4qy8K.B


S,fW G i'R46905
    说到实时操作系统,就不得不考虑重入问题。对于PC机这样的大内存处理器而言,这似乎并不是一个很麻烦的问题,借用uCOS-II RTOS的说法,即要求在重入的函数内,使用局部变量。但5l系列单片机堆栈空间很小,仅局限在256字节之内,无法为每个函数都分配一个局部堆空间。正是由于这个原因,Keil C51使用了所谓的可覆盖技术:电子园51单片机学习网/F]-\|*dYrg0ih|


6G2|)u*?'I8z~46905 电子园51单片机学习网$f)hi~#Na _g



  1. 局部变量存储在全局RAM空间(不考虑扩展外部存储器的情况);
  2. 在编译链接时,即已经完成局部变量的定位;
  3. 如果各函数之间没有直接或间接的调用关系,则其局部变量空间便可覆盖。
电子园51单片机学习网}"~'lbK

    正是由于以上的原因,在Keil C51环境下,纯粹的函数如果不加处理(如增加一个模拟栈),是无法重人的。那么在Keil C5l环境下,如何使其函数具有可重人性呢?下面分析在实时操作系统下面,任务的基本结构与模式:电子园51单片机学习网(e1?*{(d2~ ^PC9NKp

电子园51单片机学习网#Vk]:BQ9Na'bQ

 

6tz@Em it8Q#b n%G46905

$_)E-S v'p46905    vold TaskA(void*ptr){电子园51单片机学习网/qhN'~g;C#Nj
    UINT8 vaL_a;
\3VC!w+~Op)Z46905    //其他一些变量定义电子园51单片机学习网KDVvG/m
    do{
us[P)e!W)a,d46905    //实际的用户任务处理代码
*A@;`K L1k)`},H46905    }while(1);
vW^-q,b-oVj46905    }
:^ Bn'Sa ^%w0|46905    void TaskB(void*ptr){
0F0\3O,ifm Qp8Y+j46905    UINT8 vaLb;电子园51单片机学习网8{ D*b%JiL'R
    //其他一些变量定义
_&kZf(T J7[P46905    do{电子园51单片机学习网KJ+eq0_W5i
    Funcl();
.i0Hv;IF z46905    //其他实际的用户任务处理代码电子园51单片机学习网#cY)_-X!P'zI5[
    )while(1);
T6U$JU+e+vY-S/f46905    void Funcl(){电子园51单片机学习网"NG*DB0s;L
    UlNT8 v al_fa;
(fK F7i9yM q46905    //其他变量的定义
sb:b!c ][$G)|s*C46905    //函数的处理代码
0Pif`&UESt46905    }电子园51单片机学习网 ~${Z @m(WZ_A


n/NCI{G1T46905 电子园51单片机学习网UM Og4Fl3h;JF*hn

电子园51单片机学习网v%A?t j9?2]2i3l-M

    在上面的代码中,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或其他任务的局部变量形成可覆盖关系。电子园51单片机学习网\ cFD2s}


:Gm:MVu-a)}46905 电子园51单片机学习网-_v"~Kg8yp2C

电子园51单片机学习网#Hb{~B

    将val_a、val_b以及val_fa等局部变量定义为静态变量(加上static指示符)可以解决这一问题。但问题是,定义大量的static类型变量,将导致RAM空间的大量占用,有可能直接导致RAM空间不够用。尤其是在一些小容量的单片机内,一般只有 128或256字节,大量的静态变量定义,在如此小的RAM资源状况下显然就不太合适了。由此而有了另一种的解决方法,如下代码所示:

aujYydu mZ46905 电子园51单片机学习网R4Czd"E4QM

 

A%Os\3vC Cf9s?46905

&r6Z)h$T tl Q46905    void TaskC(void){电子园51单片机学习网!skcU/lpi]8{'e
    UINT8 x,v;
PSM&]^0o/Wp46905    whlk(1){电子园51单片机学习网)Z;i7`)@!G!c(?+P
    OS_ENTER_CRITICAL();电子园51单片机学习网\+u]Fr1B5l IGq:I
    x="GetX"(); (1)电子园51单片机学习网^{])EeW
    y="GetY"(); (2)
Zd UlSfw} wmG46905    //任务的其他代码电子园51单片机学习网`5@9@P$?dNh
    OS_EXIT_CRITICAL(); (3)电子园51单片机学习网8y A"^;xg@$iWM lp
    0SSleep(100); (4)
H3Q!{aG46905    }
Q@qW#k\3b46905    }电子园51单片机学习网+A"nQB:rky7A


/sa/f1o `%q R+K46905 

)C*i*mK.uR46905 电子园51单片机学习网E^-nI'@*{4cu~ F

    以上代码TaskC中使用了临界保护的方法来保护代码不被中断占先,确实有效地解决了RAM空间太小,不宜大量定义静态变量的问题。然而如果每个任务都采用此种结构,任务一开始,就关闭中断,将使实时性得不到保证。事实证明,这种延时是相当可观的。用一个实例来说明,如果想在系统中使用一个动态刷新的LED 显示器,就难以保证显示的稳定与连续,哪怕在系统中是使用一个单独的定时器来做这一工作(进入临界区后,EA=0)。其次,这种结构事实上将占先的任务调度转化为非占先的任务调度。实际上如果在(3)与(4)之间没有碰巧发生中断并导致一个任务调度,那就可以理解为是任务主动放弃CPU的控制。如果在 (3)和(4)之间碰巧产生了一个中断并导致了一个任务调度,只是执行了一次多余的任务调度而已,而且并不希望在(3)之后发生2次甚至多次的任务调度,相信读者也有这一愿望。电子园51单片机学习网"^H8aucU t6G)T


3Zfm9Q(gVdu46905 电子园51单片机学习网,s/o E*W;l9x

电子园51单片机学习网[ n3H/Ih${7n}_

    除此之外,还可以发现任务的一个特点:当任务从(1)重新开始时,局部变量x和y是一个什么值并不在乎,即x和y 即使在(3)之后改变了,也已经不再重要,不会影响程序的正确性。其实这一特点也是大部分任务,至少是太部分任务的大部分局部变量的一个共性——如果任务在整个执行过程中,不会(被占先)放弃CPU控制权,则其局部变量大多数并不需要进行特别的保护,即其作用域只是任务的当次执行,针对上面的代码,就是临界保护区内的代码区域。

&J+kQJ0`b*W46905

.R V/mF"hl-H46905 电子园51单片机学习网2san!N#V IS-Pvy \ _

电子园51单片机学习网*N` dI lAC U%v}

    2 实时操作系统要不要占先

%HV7xVk/Ct3w46905

@lV@ P3Vq0KP D A/e46905电子园51单片机学习网Z YWKHd+P
    由上面的分析,如果要保持一个函数可重人,就得使用静态变量,系统的RAM资源将是一个严峻的考验;如果使用临界区来保护运行环境,系统的实时性又得不到保证,而且有将占先式任务调度转为非占先任务调度之虞。显然,使用静态变量简单,但有更多的不适用性,对将来功能的调整也是一个阻碍,一般不被采用。那么,就只能从环境保护上来下功夫了,但是果真只能以进入临界区牺牲系统的实时性来保证任务不被占先?下面看看临界保护这一方法的基本思路:电子园51单片机学习网_.|0Wk0p


lp'k~^N1q m3I?,e46905 

Eu\!U8It0x#J46905

  1. 在一个任务中,如果局部变量在其作用域内不被占先切换,则这些变量在任务被剥夺了CPU控制权后,不关心其值也不会影响任务的正确执行;
  2. 使用临界区保护,可以达到上面所提到的要求;
电子园51单片机学习网9er7d*Uj6L6}.K^I

由此导致的实时性能与占先切换的减弱可以接受。由此可知,不被占先是任务保护局部变量的关键。既然如此,何不舍弃占先式的任务调度?这不失为一个好的出发点。针对Keil C51,非占先式任务调度,可能是一种更好的方法,更能协调51系列单片机的既定资源。下面编写这样一个系统:电子园51单片机学习网6Li)uVEM/v



  1. 使用非占先式任务调度;
  2. 可以在小容量的芯片中使用,开发目标是,即使是8051这样小的芯片,也可使用这个实时操作系统;
  3. 支持优先级调度,尽可能保证其实时性。
电子园51单片机学习网6m,n#_d U&}

    3 实时操作系统的实现电子园51单片机学习网9oj^fkzZ
      基于以上的分析与目的,近日完成了这个操作系统。在堆栈上借用RTx的管理方法,即当前任务使用全部的堆空间,如图1所示。
N5?pYR&pZJ46905 

4C5\/L K8w [46905 电子园51单片机学习网#^+y(Cw0Q{

    3.1 堆栈的初始化与任务的创建
/ptK_:J3wu46905      堆栈的初始化实际是初始化 0STaskStackBotton数组,并将当前任务指定为空闲任务,下一个运行任务指定为最高优先级任务,即优先级为零的任务。初始化时,将SP的值存人OSTaslkStackBotton[O],SP+2的值存入OSTaskStacKBotton[1],依此类推。而任务是调用0STa- skCreate函数建立的。实际上只是将任务(假设为n号任务)的地址填人到对应OSTaskStackBotton[n]所指向的位置,并将SP向后移动2个字节,如图2所示。电子园51单片机学习网N&C3ewZ_
 
|`0p E \s.i46905    为什么要以这样一种规律而不是其他的方式呢?这是由于在任务建立后,还未进行任务调度之前,各任务的堆栈实际上是它们自身的地址,因而其堆栈深度为2,为了程序的简便而直接填入。电子园51单片机学习网:Xu7D[ _~C"k)W


*BR z2@%HL/l:GeF|46905    void main(void){
@\$r }\&z"tf]46905    OSInit(); /*初始化OSTaskStackBcBotton队列*/电子园51单片机学习网xze[2?NE
    TMOD=(TMOD&0XFO)│ 0XOl;电子园51单片机学习网"D L(N_e3l
    TL0=0xBF;电子园51单片机学习网7A}@jvf t?/h
    TH0=0xFC;电子园51单片机学习网v!`cv+Q9M6y"Y!o#O.}
    TRO="1";
I+@pc r/C,Oq46905    ETO="1";电子园51单片机学习网 vV+? lf.vm
    TFO="O":
xl:q8|Q@\6w46905    OSTaskCreate(TaskA,NULL,0);
QFt6{s7Rd [!g46905    OSTaskCreate(TaskB.NULL,1);电子园51单片机学习网`SDg^V
    OSTaskCreate(TaskC,NULL,2);
4[N(MO.kV'B bn46905    OSStart();

1b+o:y @j'p AK#Y3~46905 电子园51单片机学习网ZN6Y J*J;|

    上面这段代码中,所有任务建立后,便调用OSStart()开始任务调度。OSStart()是一个宏定义,如下所示:电子园51单片机学习网vpqpD.UO&K


j P,bj Aa#z@46905    #deflne OSStart() d0{\电子园51单片机学习网&C P.YJ q%K$t#_
    OSTaskCreate(TaskIdle,NULL,OS_MAX_TASKS);\电子园51单片机学习网sdxtG9o6j
    EA="l":\电子园51单片机学习网o2A*xKr)o#Hp
    return;\电子园51单片机学习网| bB,k2S-|"j
    }while(O)

$p-?tx0`X.\46905 电子园51单片机学习网J7~p'nJ-x,^JI#\0x

    首先,它创建了一个空闲任务并打开中断,然后便返回。返回到哪里了呢?我们知道,空闲任务是优先级最低的任务,当调OSTaskCreate建立时,会将其地址填人到SP的位置,并把SP向后移动2个字节(见图2及说明),因而此时处在堆栈顶端的,一定是空闲任务Taslddle。这就使得这里的 return一定会返回到空闲任务。至此,系统进入正常运行状态。电子园51单片机学习网%e:[ ]A$Q TX&P

电子园51单片机学习网;e4iI;F0K4WSo

    3.2 任务的切换
P`2C9MqV46905    任务的切换分两种情况,在当前任务优先级低于下一个取得CPU控制权的任务时,将下一个取得CPU控制权的任务的栈顶到当前任务的栈顶之间的内容向RAM空间的高端搬移,以空出全部的RAM空间作下一个任务的堆空间,同时更新对应的OSTaskStackBotton,使其指向新的正确任务的堆栈栈底。如果当前任务的优先级高于下一个任务的优先级,则作相反的搬移,如图3与图4所示。电子园51单片机学习网oe4_X4p4o&QM
 
0^6| ?f bp46905    所有任务必须主动调用OSSleep,放弃CPU的控制权。任务调用OSSleep后,将选择优先级最高的就绪任务运行。电子园51单片机学习网 D'oeja*\B


&TS+TR9D+u| \#`46905    结 语电子园51单片机学习网d8h h6[d;K[N btj
    系统完成后,内核的代码量在400多个字节左右,占用1个定时器中断及小量的内存空间。系统设置容量为8个任务,用户实际可用任务为7个,能够满足一般需求,也达到了在小容量芯片中应用的开发要求。由于没有采用占先式的任务调度,除开全程相关的个别任务的一些局部变量外,其他局部变量已经不存在覆盖关系,由于是任务主动放弃CPU控制权,对于个别需要保护的变量单独进行处理也变得容易。在系统中,全程不需要反复地开关中断,实时性能也很好。对个别时序要求严格的外设(如DSl8820)除外。电子园51单片机学习网:pidu/{(KH{ E-OC

上一篇: stm32应用例程 下一篇: 51单片机输出PWM的两种方法

分享 举报