嵌入式培训
达内IT学院
400-996-5531
当不使用RTOS时,嵌入式软件通常采用两种传统的编程结构进行编程,一种叫“前后台结构”或者叫“超级循环结构”,本质上是事件触发的编程方式,另一种叫时间触发的编程方式,Michael J.Pont 的“基于时间触发的编程模式”即属于此。
在实际工作中,当系统稍微复杂时,会发现这两种方式都有一定局限性,下面以一个实际产品设计中遇到的问题为例来说明。在设计一个用于配电柜的壁装式智能配电仪表时,CPU的程序设计需完成以下任务:
(1)每半秒对前显示屏的显示数据进行一次刷行。
(2)每0.1秒对DI/DO进行一次刷新。
(3)每0.2秒对键盘进行一次扫描。
(4)每半秒对测量数据进行一次重新采集和计算。
(5)异步串行口与上位机使用Modbus通信,速率最高19200bps。
(6)CPU通过IIC总线与时钟芯片和EEprom通信。
(7)CPU通过SPI总线与LED数码管及采集芯片通信。
(8)CPU要对所采集的6路信号进行FFT变换。
(9)当系统掉电时,CPU要能快速响应以把当前的电度底数写入EEprom中。
上述任务中,任务(5)和任务(9)是强实时性的,如果对串口的收发事件得不到及时响应,接收时会导致字节丢失,发送时会导致字节间时间间隔太大造成接收方的Modbus帧定界错误,对系统掉电事件如果不能及时响应会造成EEprom的写入失败。
其它任务只要在指定的周期内能得到执行就行,但是任务(8)比较特殊,使用通常的8位CPU进行6种信号的FFT变换,哪怕每种信号只做128点的FFT,运算一次也要好几秒。下面来看用传统编程结构实现上述设计时遇到的困扰。
1.1 使用“前后台方式”进行编程
使用“前后台方式”进行编程时,为保证任务(5)的及时性,使用了UART中断,当UART完成一个字节的收发后产生中断,在中断程序中将接收到的字符保存在接收缓冲区或从发送缓冲区取下一个待发字符装入UART进行发送,对Modbus协议的处理可以单独用一个任务在中断外处理,这保证了中断程序的简短。
为保证任务(9)响应的及时性,也必须为它安排一个中断。因为当系统掉电时,系统只有不到10ms的过渡时间,系统如果不能在这个时间内完成相关的操作,系统电压将跌落至有效电压以下而丧失工作能力。
安排好了后台的中断任务后再来看看前台的任务如何完成。这里遇到的最大的挑战是对任务(8)的处理,因为任务(8)需要的执行时间太长了,简单的把它当成一个任务处理将影响系统对其它任务的响应,在超级循环中的代码结构如下:
while(1)
{
任务(1);
任务(2);
………
任务(8);
}
由于任务(8)执行一次要几秒钟的时间,整个超级循环执行一次至少大于任务(8)需要的时间,也就是说这个超级循环循环一次要几秒钟时间,将满足不了各任务响应时间的要求。
要解决这个问题,只有把任务(8)拆分成很多个子任务,将每个子任务的耗时压缩到10个毫秒左右,并定义好各子任务完成后的状态,在超级大循环中每次根据状态只执行一个子任务,程序结构如下
while(1)
{
任务(1);
任务(2);
………
Switch (子任务状态)
{
case 子任务状态1:
子任务1;
break;
case 子任务状态2:
子任务2;
break;
…………
case 子任务状态n:
子任务n;
break;
}
}
这样,就需要把一个耗时几秒的FFT运算任务拆分成几百个耗时10ms左右的子任务,这显然是不可接受的。
除此之外,超级大循环结构隐含地一个缺点就是随着任务的增加,循环体的执行时间是线性增加的,在实际设计中即使没有象任务(8)那样的高耗时任务,当系统功能增加时要保证系统响应的及时性也是一个不小的挑战。
1.2 使用“时间触发编程模式”进行编程
“时间触发编程模式”的核心是建立一个基于时间触发的合作式的任务调度器,在系统中尽量减少事件触发(减少中断的使用),系统通过任务调度器完成各任务的调度执行,下面是“时间触发编程模式”的典型程序结构:
/*--------------------主函数-----------------------*/
Void main(void)
{
SCH_Init();//设置调度器
SCH_Add_Task(任务函数名,任务调度延迟,任务调度周期);//将任务加入调度器的任务队列
SCH_Start();//刷新任务队列
while(1)
{
SCH_Dispatch_Tasks(); //执行任务调度器
}
}
/*-------------------定时中断函数---------------------*/
Void SCH_Update(void) interrupt
{
//刷新任务队列
}
系统中每个任务都定义了优先级、任务循环周期和任务延迟时间,系统时器中断程序SCH_Update()按设定的节拍对任务队列进行刷新,在超级大循环中只执行任务调度器SCH_Dispatch_Tasks(),根据任务队列的状态安排任务的执行。
这种编程结构避免了超级大循环结构循环时间随代码量的增加而线性增加的问题,但是,由于任务是不可剥夺的,一旦任务启动执行,任务调度器只有在当前任务完成后才有机会执行,这就要求每个任务占用CPU的时间不能太长,否则将影响整个系统的响应速度。
所以,FFT运算在这种编程模式下还是必须进行有效的拆分,否则就必须提高CPU的档次或使用可剥夺型的抢先式RTOS,这势必造成系统成本的增加。那么有没有更好的解决办法呢?
编程结构对“时间触发编程模式”进行了改进,使之在不提高硬件成本的情况下,使编程人员更直观地定义任务,减少任务特性对系统程序结构的冲击,使程序结构简单明了并提高系统的实时响应速度。
对于“时间触发编程模式”咱们下一篇详细了解。最后,达内嵌入式培训机构提醒每一个it爱好者:如果你想要在短时间内快速入门,顺利掌握一门技术,建议还是认真学习视频。多练习,多动手。
版权声明:转载文章来自公开网络,版权归作者本人所有,推送文章除非无法确认,我们都会注明作者和来源。如果出处有误或侵犯到原作者权益,请与我们联系删除或授权事宜。
填写下面表单即可预约申请免费试听!怕钱不够?可就业挣钱后再付学费! 怕学不会?助教全程陪读,随时解惑!担心就业?一地学习,可全国推荐就业!
Copyright © 京ICP备08000853号-56 京公网安备 11010802029508号 达内时代科技集团有限公司 版权所有
Tedu.cn All Rights Reserved