导言
现在,RTOS特别是抢先式RTOS在嵌入式体系中的运用越来越广泛,可是还有很大一部分产品运用是小型单片机。这些体系因为本钱的约束,一般资源十分有限,比方ROM往往小丁32 KB,RAM小于2 KB,因为RTOS对每个使命都要拓荒独自内存区域,寄存使命的上下文和各使命独立的仓库,所以在这种体系中运用RTOS十分牵强。关于这些低本钱资源受限体系一般选用“前后台”(或许叫“超级循环”)结构进行编程,这实践上是一种事情触发的编程形式,当中止数目较多且体系完结的功用相对杂乱时,就会使体系的程序编写变得十分杂乱并使体系运转的可猜测性敏捷下降。
针对这个问题,Michael J.Pont提出了一种“依据时刻触发的编程形式”,这种办法有助于下降CPU的负荷并削减存储器的运用量,进步体系行为的可猜测性,并使程序的结构变得简练。可是在实践运用中,当体系中不同的使命对时刻要求差异较大时,“依据时刻触发的编程形式”难以给出简略有用的处理方案。为此,对“依据时刻触发的编程形式”进行了改善,使之适应性更强,可认为本钱和资源受限的小型嵌入式体系供给一致且有用的编程形式。
1 传统编程结构的局限性
当不运用RTOS时,嵌入式软件一般选用两种传统的编程结构进行编程,一种叫“前后台厅式”或许叫“超级循环结构”,实质上是事情触发的编程办法;另一种叫时刻触发编程形式,Michael J.Pont的“依据时刻触发的编程形式”即属于此。
在实践工作中,当体系略微杂乱时,会发现这两种办法都有必定局限性,下面以一个实践产品规划中遇到的问题为例来阐明。在规划一个用于配电柜的壁装式智能配电外表时,CPU的程序规划需完结以下使命:
①每半秒对前显现屏的显现数据进行一次改写。
②每0.1 s对DI/DO进行一次改写。
③每0.2 s对键盘进行一次扫描。
④每半秒对丈量数据进行一次从头收集和核算。
⑤异步串行口与上位机运用Modhus通讯,速率最高1 9 200 bps。
⑥CPU经过I2C总线与时钟芯片和EEPROM通讯。
⑦CPU经过SPI总线与LED数码管及收集芯片通讯。
⑧CPU要对所收集的6路信号进行FFT改换。
⑨当体系掉电时,CPU要能快速呼应,把当时的电度底数写入EEPROM中。
上述使命中,使命⑤和使命⑨是强实时性的,假如对串口的收发事情得不到及时呼应,接纳时会导致字节丢掉,发送时会导致字节间时刻距离太大,形成接纳方的Modbus帧定界过错,对体系掉电事情假如不能及时呼应会形成EEPROM的写入失利。其他使命只要在指定的周期内能得到履行就行,可是使命⑧比较特别,运用一般的8位CPU进行6种信号的FFT改换,哪怕每种信号只做128点的FFT,运算一次也要好几秒。下面来看用传统编程结构完结上述规划时遇到的困扰。
1.1 运用“前后台办法”进行编程
运用“前后台办法”进行编程时,为确保使命⑤的及时性,运用了UART中止,当UART完结一个字节的收发后产生中止,在中止程序中将接纳到的字符保存在接纳缓冲区或从发送缓冲区取下一个待发字符装入UART进行发送,对Modbus协议的处理能够独自用一个使命在中止外处理,这确保了巾断程序的简略。为确保使命⑨呼应的及时性,也有必要为它组织一个中止。因为当体系掉电时,体系只要不到10 ms的过渡时刻,体系假如不能在这个时刻内完结相关的操作,体系电压将下跌至有用电压以下而损失工作能力。
组织好了后台的中止使命后再来看看前台的使命怎么完结。这儿遇到的最大的应战是对使命⑧的处理,因为使命⑧需求的履行时刻太长了,简略的把它当成一个使命处理将影响体系对其他使命的呼应,在超级循环中的代码结构如下:
while(1){
使命①;
使命②;
……
使命⑧;
}
因为使命⑧履行一非有必要几秒钟的时刻,整个超级循环履行一次至少大于使命⑧需求的时刻,也便是说这个超级循环循环一非有必要几秒钟时刻,将满意不了各使命呼应时刻的要求。
要处理这个问题,只要把使命⑧拆分红许多个子使命,将每个子使命的耗时压缩到10 ms左右,并界说好各个子使命完结后的状况,在超级大循环中每次依据状况只履行一个子使命,程序结构如下:
while(1){
使命①;
使命②;
……
switch(子使命状况){
case 子使命状况①:
子使命①;
break;
……
case 子使命状况②:
子使命②;
break;
……
case 子使命状况:
子使命;
break;
}
}
这样,就需求把一个耗时几秒的FFT运算使命拆分红几百个耗时10 ms左有的子使命,这显然是不行承受的。除此之外,超级大循环结构隐含的一个缺陷便是跟着使命的添加,循环体的履行时刻是线性添加的,在实践规划中即便没有像使命⑧那样的高耗时使命,当体系功用添加时要确保体系呼应的及时性也是一个不小的应战。
1.2 运用“时刻触发编程形式”进行编程
“时刻触发编程形式”的中心是树立一个基丁时刻触发的协作式的使命调度器,在体系中尽量削减事情触发(削减中止的运用),体系经过使命调度器完结各使命的调度履行,下面是“时刻触发编程形式”的典型程序结构:
体系中每个使命都界说了优先级、使命循环周期和使命延迟时刻,体系守时器中止程序SCH Updatc()按设定的节拍对使命行列进行改写,在超级大循环中只履行使命调度器SCH_Dispatch_Tasks(),依据使命行列的状念组织使命的履行。
这种编程结构避免了超级大循环结构循环时刻随代码量的添加而线性添加的问题,可是因为使命是不行掠夺的,一旦使命发动履行,使命调度器只要在当时使命完结后才有时机履行,这就要求每个使命占用CPU的时刻不能太长,不然将影响整个体系的呼应速度。所以,FFT运算在这种编程形式下仍是有必要进行有用的拆分,不然就有必要进步CPU的层次或运用可掠夺型的抢先式RTOS,这必然形成体系本钱的添加。那么有没有更好的处理办法呢?
下面的编程结构埘“时刻触发编程形式”进行了改善,使之在不进步硬件本钱的情况下,使编程人员更直观地界说使命,削减使命特性对体系程序结构的冲击,使程序结构简略、明晰并进步体系的实时呼应速度。
2 对“时刻触发编程形式”的改善
依据多年嵌入式体系编程的经历,一般嵌入体系的使命能够区分红3种类型:
①及时型使命。这类使命是事情触发型的,一旦事情产生,体系有必要在限制的时刻内进行呼应,对这类使命,最天然的办法便是运用中止来完结,即界说成“前后台办法”中的后台使命。
②周期型使命。这类使命是时刻触发式周期型的,体系有必要确保在指定的周期内履行使命,“时刻触发编程形式”能够很好地满意这类使命的需求。
③布景型使命。这类使命对错实时型的,实时性不是十分重要,体系在运转过程中可随时中止这类使命以便履行前两类使命,体系只要能充分利用资源尽最大或许快速完结这类使命即可,这类使命最适合界说成“前后台办法”中的前台使命。
依据以上使命分类,对“时刻触发编程形式”的改善可归纳成以下需求:
◆使命分3类,1类使命优先级最高,3类使命优先级最低;
◆高优先级的使命可中止低优先级使命的履行,同级的使命之间不行彼此掠夺;
◆实践没计中为进步体系的可猜测性,应尽量削减1类使命的数量及1类使命的履行时刻;
◆为下降体系资源的占用,体系不给使命区分独自的仓库空间。
以上改善的实质是规划3个优先级的简略的使命调度机制,高优先级的使命可中止低优先级的使命,同优先级的使命之间不能彼此掠夺,该调度机制不为每个独自的使命保存使命上下文和独自的仓库,这样能够削减该编程形式对体系资源的需求。
可掠夺式RTOS中的一个高优先级使命中止一个低优先级的使命时,会保存好低优先级使命的上下文并把该低优先级使命的局部变量保存在本使命独自的仓库中,假如体系不给使命分配独自的仓库,怎么确保高优先级使命退出后,低优先级使命履行环境的康复呢?
对这个问题,能够学习中止的处理机制用以下办法予以处理:
①在体系中规划一个守时中止函数,该函数的功用便是履行周期性使命的调度,该守时中止在所有中止中优先级最低。
②在体系中规划另一个守时中止函数,该函数的功用是改写周期型使命的使命办理行列,为使命调度供给支撑,本守时中止函数的优先级在体系中次低。
③周期型使命便是一个函数,该函数进口的第一个操作是开中止,答应使命履行期间被中止以便呼应及时型使命。
④布景型使命便是在主函数超级循环中履行的代码,该代码可随时被及时型和周期型使命中止,当体系没有及时型使命和周期型使命时才循环履行布景型使命的代码。
经过以上办法,“改善型时刻触发编程形式”的程序结构如下:
结语
运用“改善型时刻触发编程形式”进行小型嵌入式体系编程,就像运用RTOS进行编程相同,规划者规划好使命后,就能够专注于每个使命的规划,使命对处理器时刻的占用能够由体体系一办理,削减使命之间的耦合,使产品的程序规划和改动都变得简练清楚。运用该编程形式很好地处理了譬装式智能配电外表所面对的杂乱的规划问题,证明该办法简略有用。现在该规划形式只是规划了使命调度器,使命间的变量传递还需求运用全局变量,假如能参加信号量和音讯机制,那么该形式将愈加完善。