孔宪青
(威海职业学院,山东 威海 264200)
基于单片机运行的多线程任务状态机
孔宪青
(威海职业学院,山东 威海 264200)
本文目的是改进单片机的编程方式。VHDL中常用的状态机编程方法,被引入到单片机的编程结构中,并对两种结构做出对比和阐述。本文阐述的程序结构可以提供一种低成本的实时系统解决方式,而其编程思路是比事件触发状态机更为复杂和有效的单片机编程方法。
定时器;状态机;PLD;并行线程
单片机通过C语言实现程序的运行,尽管通过操作系统可以实现多任务运行,但本质上还是通过程序分时复用实现的多任务处理。并且大多数的操作系统使用一个定时器实现操作系统的分时调用。本文通过对定时器的进一步挖掘,实现在低RAM下并行进程的程序运行。
定时器作为操作系统的时钟节拍,起到定时切换任务等作用,但需要消耗RAM空间,任务越多内容越复杂,对于RAM空间的消耗也越多。对于大RAM的单片机系统,直接上操作系统即可,而对于小RAM的单片机,若希望具有较好的实时性,则需要进一步挖掘单片机中冗余的定时器,耗尽硬件资源。下面就是通过定时器切换任务的代码(以最简的51单片机的T 2定时器下的C程序为例):
void _T2(void) interrupt 5{
static unsigned char m=0,i=j=0; //m为同属性任务分散标志,i,j为异属性任务分散标志
if(m==101) m=0; else m++;
if(m%50==0) aaa(); //同属性任务aaa
if((m+25)%50==0) bbb(); //任务bbb和aaa需要等间隔分离
i++;j++; //异属性任务标志i和j
if(i==20){i==0; ccc();} //i对应任务ccc具有独立性
if(j==30){j==0;ddd();} } //ccc和ddd是不相关任务,各自有自己的运行时间
以上的四个任务尽可能地执行后即实现,以节约RAM空间。静态标量m、i、j用于区别任务类型以平均时间轴分配任务。
一个定时器可以出现一个时间轴,在这个时间轴上来平摊任务。
状态机的使用在单片机中不多,因为单片机大多以时间轴来编写程序,方便快捷,最普通while(1){}类型。状态机的使用一般在硬件描述语言中,例如VHDL来描述PLD。时序并行逻辑都是时间脉冲的形式描述各种并行的时序逻辑电路。例如下面这四个进程:
process(clk,clk1,clk2); //对振荡器进行分频,得到时基脉冲clk1和clk2
process(clk1,aaa,bbb); //时基clk1处理aaa和bbb的输出宏单位
process(clk1,ccc.ddd); //时基clk1并行处理的ccc和ddd的输出宏单位
process(clk2,eee); //不同时基的并行eee输出宏单位
采用VHDL中常用的硬件编程方法状态机,可采用标准的三段式状态机如下:
process(reset,clk1,state); //时序逻辑转移
process(state,其他条件); //组合逻辑转移表
pocess(state); //组合逻辑输出
以上是PLD器件的基本而且有效的编程方式,以状态量连接程序段的各部分,它的优点是以事件(或者是条件)为核心驱动各个部分。状态机编程方式必须有个时基触发脉冲,没有这个时基,状态机就运行不下去。
单片机定时器可以固定时间提供中断,这个中断可以把时间轴划分成一段一段,类似脉冲的形式。这非常类似于PLD器件中的分频器的作用。通过中断的这个时基实现时序逻辑转移,而单片机中的各个任务就可以写成状态机的模式。例如中断按键程序写成状态机模式:
static void StateMachine_key(void){
static enum StateKey sKey=sInit; //采用C中的枚举类型,设置枚举变量的初值
static unsigned char key_now=1;
unsigned char key_past;
key_past=key_now; //三段式按键状态机,需要时钟脉冲
if(KEY!=0xff) key_now=0; //这里标准是时序逻辑转移
else key_now=1; //按键对比前次和这次的值,检测是否有按下
switch(sKey) { //组合逻辑转移
case sInit:if((key_past==1)&&(key_now==0)) sKey=sPush; break;
case sPush:if((key_past==0)&&(key_now==1)) sKey=sPop; break;
case sPop: sKey=sInit; break;
default: sKey=sInit;}
switch(sKey){ //sInit、sPush、sPop是枚举量sKey的枚举值
case sInit: break;
case sPush: switch(KEY){
case 0xfe: aaa(); break; //按下S1,执行任务aaa
case 0xfd: bbb(); break; //按下s2,执行任务bbb
……
default: xxx();}
break;
case sPop: break;
default: ;}}
以上这个状态机可以放在中断中,因为中断本身就是时基,也就是时钟脉冲驱动了状态机,程序运行实时性强,形成事件触发状态机模式,但缺点是状态机不能太长,不然会影响其他状态机的实时性;其他方式是定时中断提供时基标志,按键状态机可以放到while(1)中,只要能检测到时基标志即可,这样形成的就是前后台的程序。
定时中断使连续运行的时间轴变成了脉冲形式,即离散化了单片机程序。通过状态机判断运行这些离散化的程序段。这样通过定时器把单片机变成了PLD器件实现了并行逻辑。当然这种并行逻辑是伪的,因为程序执行还是顺序的,CPU也只有一个。单片机形成的时基可以有多个,例如之前的m、i、j形成的时基就是不同的。T2定时中断可以变成如下形式:
if(m==11){m=0;StateMachine_Key();}
else m++; //10次基础中断产生的时基触发驱动按键状态机
if(i==25){i==0; 状态机模块} //i和m是不同属性的时基,触发不同的状态机任务
else i++; //异属性时基i
可以看出,定时器就是PLD中的振荡器,由计数单位m和i实现了VHDL中的分频器。状态机模块放到定时中断中实现三段式状态机的时序逻辑转移,其模块内部实现状态转移表和状态输出。定时器中可以放多个事件状态机,数量需要由时间轴的消耗计算。因此定时器内部可以存在若干个事件状态机。
单片机内部一般有多个定时器,完成任务后还有冗余,则可以把这些冗余的定时器利用起来。一个单片机定时器就会形成一个时间轴,离散化后就是一个线程。因此定时器使用一个就是一个时间轴,它们互不关联,形成的就是真正的并行逻辑。也就是说多定时器的中断时基就是PLD的并行线程。挖掘冗余的定时器就可以耗尽单片机的硬件资源,让简单的单片机完成更复杂更实时性强的任务。具体框架如下:
void _xxx(void) interrupt ?{
//定时器xxx离散时间轴
static unsigned char m=0,i=0;
if(m==11){m=0; 状态机模块a;}
else m++;
if(i==25){i==0; 状态机模块b;}
else i++; }
void _yyy(void) interrupt ?{
//定时器yyy离散时间轴
{…………}
以上两个定时器形成不同的时间轴,即两个并行线程。
定时器作为中断时基使用最简单的即可,这样对于一些定时器多的单片机尤其有效。注意所有的任务中使用的RAM单元,防止事件状态机的数据覆盖。并行的线程之间如果需要数据沟通和相互作用,使用标志位和标志序列进行沟通。一些实时性不强的状态机完全可以放在while(1)中,使用标志位激活。
Multi Thread Task State Machine Based on Single Chip Microcomputer
Kong Xianqing
(WeihaiVocationalCollege,WeihaiShandong264200,China)
The purpose of this paper is to improve the programming method of MCU. The commonly used programming mode of state machine inVHDL is introduced into the microcontroller programming structure, and two kinds of structure are made comparison and elaboration. The program structure described in this paper can provide a real-time system with low cost solutions, and the programming is more complex microcontroller programming method and effective than the event triggered state machine.
timer; state machine; PLD; parallel thread
2016-10-13
孔宪青(1976- ),男,山东威海人,讲师,硕士研究生,专业:控制理论与工程,研究方向:单片机应用。
1674- 4578(2016)06- 0064- 02
TP368.1;TP311.11
A