杜隆胤 方冰 郑军
摘 要: 针对51单片机内存资源非常有限,很难布设操作系统,从而导致51单片机中多个功能有机融合比较困难的问题,首先分析了使用轻型的循环轮询多任务编程思想在51单片机编程中的优势;然后针对真实任务中存在阻塞问题,讲述了如何对真实任务进行分解以满足系统实时性要求;最后分析了循环轮询系统中周期性实时任务和非周期性实时任务的触发方式,设计出了较为通用的、以周期性实时任务和中断服务任务为基本触发源的系统实现模板。
关键词: 51单片机; 循环轮询系统; 多任务; 触发条件
中图分类号:TP368.1 文献标志码:A 文章编号:1006-8228(2018)10-01-03
Abstract: This paper is aimed at the problem that it's difficult to implement multifunctional system in 51 single chip microcomputer, because the RAM of 51 single chip microcomputer is so limited that it's impossible to place operating system into it. The advantage of the method of polling loop system multitask programming is analyzed. The really task is divided into a lot of tasks in order to ensure the real-time characteristic of the system, and avoid the task to be blocked. The trigger conditions of periodic real-time task and aperiodic real-time task are analyzed, and a software template for 51 single chip microcomputer with the basic trigger sources of periodic real-time tasks and interrupt service tasks is designed.
Key words: 51 single chip microcomputer; polling loop system; multitask; trigger condition
0 引言
今天,智慧城市、智慧小区、数字化校园等概念逐步深入人们的生活,嵌入式系统正悄然进入到人们的生活。作为低成本、低功耗和易上手的嵌入式芯片典型代表——51单片机一开始就成为嵌入式家族中不可或缺的重要一分子[1]。但是,51单片机最典型的缺点就是片载资源少,一般不能采用操作系统作为系统的支撑,这就使得系统实现时缺少了进程或任务的概念。在实现一些较复杂的应用时,多个真实任务相互制约使得系统实现非常困难,也为调试带来较大的挑战。因此、研究如何在51单片机有限资源的环境里实现轻型任务、以实现多任务编程以降低系统实现难度是非常有意义的。特别对于一些刚接触51编程的人来说,多任务的思想无疑为编码和调试带来极大的便利。
1 背景
由于51单片机片载资源非常有限,因此在51单片机中布设操作系统非常困难[2]。没有操作系统的支持,就为多功能有机融合带来了困难。虽然没有操作系统的支撑,但在系统实现过程中,采用任务的概念还是很必要的,否则将导致各个功能的融合很艰难。
一般情况下、任务可能的状态有就绪态、执行态和阻塞态,在一些大型系统中会考虑的多任务执行时资源的短缺而引入挂起态[3]。在51系统中一般不考虑任务的挂起问题,因为任务的挂起也需要额外的资源;同时因为51系统中内存资源非常短缺,一般不考虑将任务包装成“进程”。因此51系统中、可仿照操作系统实现多任务的理念,将真实的任务转换成无需占用太多资源的轻型任务。
系统多任务的实现,按其最终的实现方式可分为批处理系统的多任务方式、分时系統多任务方式和循环轮询方式。批处理系统的多任务方式是按一定先后顺序将多个任务逐个完成的。为避免整个计算机系统处于阻塞态、需将任务包装成“进程”,这会引起额外的资源开销,同时批处理系统在保证系统的实时性方面也表现得不够好;在分时实现方式中,采用合适的时间片和任务优先级可以较好的保证系统的实时性,但是分时系统中的任务需要进行包装。即为了实现任务的自由切换而将从执行态的任务执行现场进行保存,为下一次投入运行作准备。因此采用分时系统理念的多任务实现方式也会产生额外的资源需求,因此在51系统中一般不考虑采用分时系统的多任务实现方式。
因此,在资源短缺的51单片及系统中,循环轮询的轻型多任务思想是一个不错的选择。经过笔者多年的实践证明,该实现方法能有效降低系统实现的复杂性,使得编程思路清晰,实现的系统调试方便,且能满足应用的实时性方面的需求。
2 循环轮询任务的设计
循环轮询的实现方式中,认为每个任务的执行都是有一定的前提条件的,常见的执行条件有时间点到、前驱任务执行结束、某一特殊事件发生等等。因此设计系统时在系统的主循环里不断地查询各个任务的执行条件,一旦条件满足就立即执行任务。一个简单的循环轮询系统程序流程图如图1所示。
循环轮询中查询的顺序与任务的优先级有一定关系,通过改变查询顺序可以改变任务之间的优先级。各任务的执行条件可能在其他任务的执行期间产生,也可能在中断服务程序执行期间产生。对于执行条件在其他任务中产生的那种具有前驱和后继关系的任务,不能简单改变查询顺序来改变任务之间的优先级。而在循环轮询系统中,任务的前驱/后继关系的应用是非常重要的。
为了能够保证系统的实时性,在循环轮询系统中各任务的执行是不允许出现“阻塞态”的。因为一旦某一任务进入阻塞态,特别是一些需要长时间延时的阻塞或需要其他任务或中断程序的执行而产生条件的阻塞,轻则降低系统的实时性,严重时可能导致系统无法继续运行。在设计循环轮询系统时尽量避免在任务执行中有长时间的延时或无时间限制地等待某一事件的产生[4]。
因此可以说循环轮询系统中的任务只有执行态和就绪态,没有阻塞态。即每个任务都是当条件满足就得以顺利执行、直至执行结束。正因为取消了阻塞的概念,在设计任务时就不能将一些需要等待某一事件的发生或等待某一事件发生的过程设计在任务内。每个任务都应该是“一帆风顺”地执行的,当条件成熟就顺利地执行完所有操作,而且这些操作不应该占用太多处理器时间。然而实际应用中真实任务的往往并非"一帆风顺",一般都需要延时或等待某一条件的产生才能往下执行。即一般意义的任务往往是具有“阻塞”情况的。
因此在设计循环轮询任务时不能再按照原始的真实任务设计,而应将阻塞前的任务和阻塞后的任务分成不同的轮询任务。具体分解方法如图2所示。
因为阻塞后的代码是等待某个事件的发生才能执行的,而该事件的捕捉一般是在其他任务里或中断中。因此可以在等待某个事件发生时设置一个轮询条件,而阻塞后的任务以此为轮询条件即可实现等待事件发生后执行相应任务。
该解决方案也存在一个缺陷:当阻塞条件满足后,等待该阻塞条件的任务不一定马上能执行,不能保证后续任务的实时性。可以通过合理安排轮询顺序,或者在捕捉阻塞条件的任务或中断中,立即执行后续任务进行改善。
3 循环轮询系统多任务的实现
循环轮询系统中的任务都是通过查询执行条件执行的,因此、任务执行条件的产生就成了循环轮询系统设计中非常关键的一环。只有每个任务的执行条件都按要求实时地产生了,在系统轮询优先级恰当的情况下才能保证系统的实时性。为了准确及时的产生各任务的执行条件,我们首先必须对任务本身的特性进行分析。
实时系统中的任务可根据其执行与时间的关系分为周期性实时任务和非周期性实时任务。等待某一特定事件发生的任务一般为非周期性实时任务,如按键响应任务、温度值达到某一水平时执行的任务等;需要按固定周期执行的任务叫做周期性实时任务,如多位数码管的刷新任务、按键扫描任务、系统时钟更新任务等等。经分析发现,实时系统中一般非周期性任务的执行条件都是由其他任务或中断服务程序产生的,即非周期性实时任务的执行条件发生源包括周期性实时任务、中断服务程序和其他非周期任务,而非周期性实时任务最终都是由周期性实时任务和中断服务程序驱动的[5]。因此设计循环轮询系统时,首先需要设计好系统中各中断服务程序来及时产生触发条件,其次要设计好各周期性实时任务的触发。
对于中断服务程序驱动非周期性实时任务的情况,相对比较简单,只要在中断服务程序中判断其需触发的程序的先决条件是否达到要求,一旦满足要求就将其循环轮询条件值置真即可。
系统中周期性实时任务的触发条件产生方式有两种:硬触发和软触发。所谓软触发就是通过软件延时实现周期性任务的触发。该方式实现思路简单,但触发周期不够准确,只能实现比较初略的周期定时,在一些小型的对实时性要求不高的应用中采用;所谓硬触发就是利用系统内的定时/计数器周期性地产生触发信号。该方式能较为准确的产生周期性的触发信号,保证每个周期任务能及时执行,但该方式需要程序员对系统整体结构要有清晰的认识。两种方式在不同周期的周期性任务的触发条件产生方式上是一致的,本文就以硬触发方式为例,探讨如何为不同周期的周期性实时任务产生触发条件。
假设系统内有n个周期性实时任务t1,t2,……,tn,它们的执行周期分别为c1,c2,……,cn,我们可以计算出c1,c2,……,cn的最大公约数Δt,因此,可以得到每个任务的执行周期与Δt的倍数关系值M1,M2,……,Mn。可以通过设置定时/计数器以实现每Δt发生一次定时/计数器中断,在编写中断服务程序时就可以根据需要周期性的产生各周期性任务的触发信号了。其关键代码如下:
#defind MMAX Mm //Mm=MAX(M1,M2,……,Mm)
void interrupt_timerX() interrupt Y using Z
//每Δt执行一次该中断服务程序
{ ……
count++;
count %=MMAX;
if(count % M1==x1) condition_t1=1; //触发t1
if(count % M2==x2) condition_t2=1; //触发t2
……
if(count % Mn==xn) condition_tn=1; //触发tn
}
其中MMAX为M1,M2,……,Mn中的最大值,condition_tm为任务m的触发条件。通过巧妙设置x1,x2,……,xn可以尽量避免在同一个时间点执行多道任务的情况发生,同时也可以实现将多个执行周期相同的任务的按一定时延(Δt的整数倍)先后被触发的效果。该方法针对一些需要周期性执行、而且在执行期间有较长延时而分解成多个任务的情况非常有用。对于一些应用中计算出的Δt比较小的情况,可以根据实际情况适当调整各任务的周期,使得它们的最大公约数更大,一般最理想的情况是Δt为最小任务周期,即Δt=min(c1,c2,……,cn),这样可以减少因为定时中断而产生的CPU时间开销。
由于非周期性实时任务的执行条件一般在周期性任务或中断服务程序中产生,因此通过以上方式完成所有周期性实时任务的准时触发后,就能保证系统中所有任务的到实时的触发了。
4 结束语
本文从51单片机资源短缺、不适宜于布设操作系统说起,分析了51单品机系统开发中采用循环轮询多任务思想的优势;然后针对循环轮询中任务的特性和真实任务之间的不同,讲述了如何根据真实任务本身的执行流程,对其进行有目的拆分,以使得最终在系统布设的每个任务的执行均不会出现长时间延时或等待事件产生的阻塞情况发生;最后分析了周期性实时任务和非周期性实时任务的触发条件的产生源,以硬触发为例设计了一个可以保证各任务准时触发的系统实现模板。但是由于真实系统中任务的复杂性远远超出了本文所分析的范畴,因此该系统实现模板很难适应所有开发场景,还需要针对具体应用进行改进。由于作者才疏学浅,不妥或错误之处在所难免,恳请同行、专家批评指正。
参考文献(References):
[1] 张威等.MCS-51嵌入式系统原理及应用[M].中国石化出版社,2015.
[2] CSDN.51單片机多任务操作系统的原理与实现[EB/OL].[2016年03月14日].https://blog.csdn.net/wuhenyouyuyouyu/article/details/50883704l.
[3] 汤小丹等.计算机操作系统(第3版)[M].西安电子科技大学出版社,2007.
[4] 张仁宽.51单片机多任务编程设计及应用浅谈[J].中国培训,2016.8:260
[5] 李鹏.51单片机多任务机制的实现策略研究[J].山东工业技术,2016.7:252