有限状态机在嵌入式系统中的实现及应用
广西轻工业
GUANGXI JOURNAL OF LIGHT INDUSTRY 机械与电气2008 年 4 月
第 4 期( 总第 113 期)
【作者简介】李晓锋, 男, 教师, 研究方向: 嵌入式系统应用。
引言
从 20 世纪 70 年代世界上第一个为嵌入式应用而设计的
微处理器 Intel 4004 诞生以来, 嵌入式系统 ( embedded sys-
tem) 已经发展了 30 多年, 目前已经在工业生产智能化、自动化
生产、汽车电子、消费电子等各个领域得到越来越多的应用。部
分技术人员通过引入嵌入式操作系统来解决问题, 但是运行嵌
入式系统操作系统需要大量的硬件资源。然而对于部分资源较
少的单片机, 根本无法完成嵌入式操作系统的移植, 如果更换
资源更多的芯片, 又将带来硬件成本的增加。
为了解决这些问题, 笔者将有限状态机的理论应用到嵌入
式软件的开发实践中, 通过实践发现有限状态机技术的引入,
程序结构变得非常清晰, 大大降低了嵌入式软件开发的难度。
在此, 笔者将该技术作一介绍, 供同行参考。
1 有限状态机的基本原理
对于电子技术和电子工程类的读者, 最先接触和使用到的
状态机应该是在数字逻辑电路课程中, 状态机的思想和分析方
法被应用于时序逻辑设计。其实, 有限状态机 ( Finite State
Machie, FSM) 作为计算机的基础理论最早被应用于编译器的
句法分析和语言识别。
有限状态机由有限的状态及其相互之间的转移构成, 在任
意时刻 t 机器只能处于给定数目状态中的一个。当在某一瞬时
t 机器接收到一个输入事件时, 状态机产生一个输出, 同时也可
能伴随着状态的转移。状态的转移和输出信号不仅与输入有
关, 而且与机器在前一瞬间的状态有关。一个简单的有限状态
机在数学上可以描述如下:
一个有限状态机 M 是一个六有序组, 其中:( 1 ) Q 是状态
的有限集合;( 2 ) S 是有限的系统输入信号的集合;( 3 ) R 是有
限的输出信号的集合;( 4 ) 是状态转移函数;( 5 ) 是输出函数;
( 6 ) 是初始状态。
有限状态机 M 开始于状态, 在时间工作, 其中初始时间及
时间间隔都是任意的, 可把它们表示为。在以后的每一个瞬间
t, 机器 M 接收一个输入信号, 机器从前一瞬间状态根据转移函
数转移到下一个状态, 并根据输出函数产生输出信号 r(t)。
2 有限状态机的实现方法
在 C 语言中, 有好几种方法可用来实现有限状态机, 其中
使用 switch?case 语句判断和状态表是两种比较常用的方法。
下面对这两种实现方法作一介绍。
采 用 switch?case 语 句 的 实 现 方 法 可 用 下 面 的 伪 代 码
描述;
enum system_state = {START, S1, S2};
void system_loop()
{static enum system_state state = START;
switch(state){
case START:
do_start(); state = next_state();
break;
case S1:
do_s1(); state = next_state();
break;
case S2:
do_s2(); state = next_state();
break;
default:
break;
}
}
该函数在主程序中每个一个固定的周期被调用, 函数根据
用 switch 语句判断当前所处状态调用 do_start,, do_s1, do_s2
() 函 数 分 别 完 成 在 当 前 状 态 下 需 要 做 的 工 作 , 然 后 调 用
next_state()计算在下一个状态。这种实现方式有简单、结构清
晰的优点, 适合于系统状态数比较小的情况下。
第二种实现方法的基本思路是用一张表保存所有可能的
状态, 并列出进入每个状态时可能执行的所有动作, 其中最后
一个动作就是计算下一个应该进入的状态; 计算下一个状态的
方法也可以通过在表中列出当前状态下获取到某个输入时应
该转入的状态的方法来实现。具体实现方法在下一节的应用实
例中介绍。
3 应用举例
按键作为一种输入接口具有简单、价格低廉、可靠性高、
易于软件编程等优点, 在嵌入式系统中得到了广泛的应用。按
键判断的传统方法是检测到按键后, 采用软件延时 10~50ms,
有限状态机在嵌入式系统中的实现及应用
李晓锋,宋 锐,曾小宝
( 张家界航空工业职业技术学院, 湖南 张家界 427000)
【摘 要】 如何使嵌入式软件代码更加可靠, 增强程序的可维护性, 一直以来都是嵌入式程序员追求的目标。论述了有限
状态机的原理和其实现方法; 采用状态机方法编写了一个按键扫描程序, 介绍了状态机编程在嵌入式系统中的实际应用和优点。
【关键字】 有限状态机( FSM) ; 嵌入式系统; 按键扫描
【中图分类号】 TN402 【文献标识码】 A 【文章编号】 1003- 2673(2008)04- 38- 02
38
然后再次检测按键, 如果按键仍然按下则表示按键按下, 转入
执行按键处理程序。这种方法当然具有简单的优点, 但是有很
多缺点: 第一, 采用软件延时使得 MCU 的效率降低; 第二, 在现
在的应用系统中常常需要通过很少量的按键来实现多种功能,
这就需要诸如判断普通按键、长按键、连发等不同性质的按键。
这种情况下再使用传统的方法会使得软件异常复杂和难于维
护。而使用状态机来实现, 就变得异常简单了。
首先, 根据要求, 我们对按键过程进行状态机建模, 按键
扫描处理过程可划分为 5 个状态: 开始扫描(KEY_START)、键
按 下 判 断 是 否 抖 动 ( KEY_PRESS) 、 判 断 是 否 长 按 键
KEY_LONG_PRESS、判断是否连发 KEY_PDLF、连发状态
KEY_LIANFA。对应的状态机模型如图所示, 图中输入用 0 表
示没有按键, 1 表示有按键, 输出用 1 表示普通按键, 2 表示长
按键, 3 表示连发按键。根据状态图, 采用状态表的方法, 很容
易可以得到如下的 C 语言实现。
#define KTYPE_NO 0x00 /* 按键值的 b7 b6
被用来编码按键类型 */
#define KTYPE_NORMAL (1<<6)
#define KTYPE_LONG (2<<6)
#define KTYPE_LIANFA (3<<6
enum kscan_state{
KSCAN_START = 0, /* 无键按下, 键盘扫描初始状态 */
KSCAN_PRESS, /* 键按下, 判断抖动, >50ms */
KSCAN_LONGPRESS, /* 非 抖 动 , 判 断 是 否 长 按 键 ,
>0.8s */
KSCAN_PDLF, /* 判断连发, 得到一次长按键后 1s 开始
连发 */
KSCAN_LIANFA, /* 连发, 每 0.2s 产生一次连发按键 */
};
struct kstate_fsm{
enum kscan_state kpress_next; /* 键按下, 下一个状态
*/
enum kscan_state nokey_next; /* 键抬起, 下一个状态
*/
unsigned char kpress_time;/* 键 按 下 需 持 续 的 时 间 ,
10ms 的倍数 */
unsigned char kpress_type;/* 键按下满足条件, 输出按键
类型 */
};
/* 保证状态编号值与在数组中的位置相同 */
struct kstate_fsm kstate_fsm_table[] = {
/* kpress_next, nokey_next, time, type */
{KSCAN_PRESS, KSCAN_START, 0 , KTYPE_NO},
{KSCAN_LONGPRESS,KSCAN_START,5, KTYPE_NOR-
MAL },
{KSCAN_PDLF, KSCAN_START, 80, KTYPE_LONG},
{KSCAN_LIANFA, KSCAN_START, 100, KTYPE_NO},
{KSCAN_LIANFA,KSCAN_START,20,
KTYPE_LIANFA}
}
unsigned char key_scan(void)
{
static enum kscan_state keystate = KSCAN_START;
static unsigned char oldkey;
static unsigned char ktime_counter = 0;
unsigned char keyreturn = 0;
unsigned char currkey = key_read(); // (PINA & 0x0f) ^
0x0f;
if( (currkey != 0) && (currkey == oldkey) )
{/* 键仍处于按下状态, 并且和上次扫描时按键值相等 */
if(++ktime_counter >= kstate_fsm_table[keystate].
kpress_time )
{/* 键按下满足时间要求, 查表得出按键类型和下一
个状态 */
ktime_counter = 0;
keyreturn = kstate_fsm_table [keystate].kpress_type |
oldkey;
keystate = kstate_fsm_table[keystate].kpress_next;
}
}else{/* 按键释放, 或和上次扫描值不相等 */
keystate = kstate_fsm_table[keystate].nokey_next;
oldkey = currkey; /* 保存当前按键状态 */
}
return keyreturn;
}
在实践中, 表格 kstate_fsm_table 记录了: 键按下、无键按
下应该转入的下一个状态, 当前状态下按键转入下一个状态前
应该持续的按键时间( 10ms 的倍数) ; 以及当前状态下按键的
类型。值得强调的是每个状态在表格中出现的下标必须和该状
态的 enum 值相等, 否则会出现状态的误判问题。
通过实践, 用户只需每隔 10ms 调用一次 key_scan 就可
以得到不同类型的按键值。与传统方式比较省掉了软件延时,
大大提高了代码效率; 同时原本需要非常复杂判断程序变得非
常有条理。
4 小结
本文介绍了有限状态机的原理和在嵌入式系统的实现方
法和应用。并用状态机的方法实现了一个按键扫描程序。通过
状态机方法, 降低了系统的复杂性, 简化了软件结构, 提升了系
统软件的质量和可靠性。
参考文献
[1][美]Peter Van Der Linden.徐波译. C 专家编程[M]. 北京:人民邮电
出版社, 2002.
[2]马潮.AVR 单片机嵌入式系统原理与应用实践[M]. 北京:北京航空
航天大学出版社,2007.
[3]左孝凌等.离散数学[M].上海:上海科学技术文献出版社,1981.
39
有限状态机在嵌入式系统中的实现及应用.pdf