唐 飞, 查长礼
(安庆师范学院 物理与电气工程学院,安徽 安庆 246133)
按键是用户与嵌入式系统进行交互的常用设备,因其简单实用、成本低,因而得到了广泛应用。嵌入式系统因体积所限,一般使用非编码按键,依靠程序识别按键的动作和按键编码。按键控制程序应能够管理按键按下、按键防抖、键值判别、按键弹起等任务,并识别单击、双击、长按、连发等按键模式。
当今的嵌入式系统体积越来越小,需要实现一键多“能”,因此,识别按键的单击、双击、长按的功能也越来越受到重视。
嵌入式系统工作时,用户使用按键的时间对于系统而言是随机的,因此,系统需要采用一定的策略对按键进行扫描,以识别按键的动作。常用按键扫描方法有以下几种[1]。
利用CPU的空闲时间不断对按键进行扫描,直到检测到按键动作,转去处理按键为止。此种方法简单易行,程序容易编写,但CPU效率低,CPU繁忙时,按键不能得到及时响应,实时性差。
按键按下时触发中断,CPU暂停当前执行程序,转向处理按键,处理完毕再返回主程序运行。此种方法CPU的利用率高,但占用了中断系统资源,中断系统发生冲突时不能及时响应按键动作。
借鉴了操作系统中时间片的思想,使用定时器每隔一定时间对包括按键在内的各个任务进行扫描,如按键动作,则处理按键,否则执行下一项任务。该方法CPU利用率高,各个任务划分时间片轮流执行,不会因为某个任务占用CPU时间过长造成其它任务没有响应。
综上所述,定时器扫描法在处理多任务时具有较大的优势,因此在嵌入式系统中应优先使用,同时,该方法还需要配合有限状态机才能达到理想的效果。
有限状态机(Finite-State Machine,FSM)或有限状态自动机简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型[2]。有限状态机的思想广泛应用于硬件控制电路设计,也是软件上常用的一种处理方法。它把复杂的控制逻辑分解成有限个稳定状态,在每个状态上判断事件,变连续处理为离散数字处理,符合计算机的工作特点。同时,有限状态机具有有限个状态,所以可以在实际的工程上实现。
有限状态机可归纳为4个要素,即现态、条件、动作、次态。现态和条件是事件起因,动作和次态是最后的结果。当条件被满足时将会触发做动作,动作执行完毕后,可以迁移到新的状态(次态),也可以保持原状态。
依据有限状态机的状态转移关系和转移条件,可以把一个非常复杂的事件变成一个依据状态编码内容进行转移的多分支的结构,很容易用C语言来实现。因此,把有限状态机作为一种方法导入程序设计中,实现程序对流程和行为的准确分析和表达,缩短了系统的开发时间,增强了系统的可靠性[3-7]。
在嵌入式系统中,按键识别的任务是确定按键的键值和按键动作模式。键值可由程序判别并分配,常用的按键模式分为单键模式和复键模式两类。单键模式一次按键只输出一个有效按键,而复键模式一次按键可以输出多个有效按键,通常通过按键按下时间的长短来区别。单键类一般有3种模式:琴键模式、单发模式和乒乓模式;复键类一般有长按模式、连发模式和组合键模式[8]。具体区别见表1。
表1 按键模式分类
在STM32系统中,单发模式、长按模式和连发模式较为常用,单发模式组合还可形成双击动作。进一步分析每一次的按键动作,也可以看作一个状态机,每次的击键动作使按键形成了弹起、抖动、短按、长按和释放等状态。当按键按下之后触发动作,动作执行完毕之后按键迁移到新的状态,直至最终确定按键的模式。按键的状态迁移如图1所示[9]。
图1 按键状态转换图
嵌入式系统中,基于Cortex-M3架构的32位ARM处理器发展迅速,STM32微控制器是意法半导体(ST Microelectronics)公司推出的基于Cortex-M3内核的系列微处理器,具有高性能、低成本、低功耗的特点,得到了广泛应用。因此,研究基于STM32的按键驱动程序具有十分积极的意义[10]。
GPIO(General Purpose Input Output)是STM32的输入、输出设备,STM32提供了80个双向GPIO口,分布在A~E这5个端口中。文中设按键接于GPIOA.0口,通过读取GPIOA.0口的状态即可检测出按键的状态。STM32的按键接口如图2所示。
图2 STM32的按键接口
根据上文分析,程序采用定时扫描法定时对按键进行扫描。综合考虑按键的响应速度和其它任务需求,确定按键扫描时间为10ms。因此,需要使用STM32中的SysTick Timer进行定时,配置相应的时钟,产生中断标志位,控制主程序每隔10ms扫描一次按键。
STM32通过GPIO口扫描按键主要有以下几个步骤:
1)开启所用端口的时钟;
2)配置GPIOA.0口为上拉输入模式;
3)配置SysTick时钟,使之每10ms产生一个标志,控制对按键的扫描;
4)调用按键扫描函数扫描按键。
按键的每次击键动作可分为4个状态,按键的动作模式分为3种,分别见表2和表3。
表2 按键状态表
表3 按键模式表
主程序调用函数KeyScan()扫描按键,若按键动作,则判明按键状态,在动作的触发下完成按键状态的迁移,返回按键动作模式的类型。具体描述如下:
1)按键初始为弹起状态,标记为状态0。每隔10ms主程序调用函数扫描按键,若GPIOA.0电平为1,则按键未按下,按键状态保持在状态0;若GPIOA.0电平为0,则按键按下,按键状态迁移至防抖状态。
2)按键进入防抖状态,标记为状态1。每隔10ms主程序再次扫描按键,若GPIOA.0电平为1,则上次得到的按键按下信息是由于抖动或外界干扰造成的误判,按键状态返回状态0;若GPIOA.0电平为0,表明按键确实按下,按键状态迁移至短按状态。
3)按键进入短按状态,标记为状态2,同时启动计数变量对按键按下的时间开始计数。每隔10ms主程序扫描一次按键,若GPIOA.0电平为1,则按键已经弹起,按键状态返回状态0,同时返回按键动作模式为单击S;若GPIOA.0电平为0,表明按键持续按下,按键状态保持在短按状态,同时计数变量开始加1计数,若计时时间(计数值乘以10ms)大于1s而按键状态仍维持在状态2,表明按键动作模式为长按,按键状态转移至长按状态。
4)按键进入长按状态,标记为状态3。每隔10ms主程序扫描一次按键,若GPIOA.0电平为1,则按键已经弹起,按键状态返回状态0,此次长按状态结束,返回按键动作模式为长按L;若GPIOA.0电平为0,表明该长按状态持续,按键保持在长按状态3。
按键扫描的函数如下所示,返回按键模式类型。其中,变量Key存储读取的按键引脚电平,变量KeyState表示按键的状态,变量KeyPress-Time记录按键按下时的计数数值,计数值乘以10ms即为按键按下的时间,变量KeyPressStyle表示按键模式的类型。因变量KeyState和Key-PressTime在函数每次调用时需要保存上一次调用时的数值,因此将其设置为静态变量。
#define N 0#define S 1#define L 2#define C 3#define KEY_ON 0
unsigned char KeyScan()
{
static unsigned char KeyState=0,KeyPress-Time=0;//按键状态,按键按下时间
unsigned char Key,KeyPressStyle;//按键动作模式
KeyPressStyle=N;//按键动作模式初始化为未正常按
Key=GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==KEY_ON//检测按键电平
switch(KeyState)
{
case 0://弹起状态
if(!Key)KeyState=1;//按键按下则转移至状态1 else KeyState=0;//按键弹起返回至状态0
break;
case 1://防抖状态
if(!Key){KeyState=2;KeyPress-Time=0;}//按键按下则转移至状态2,计时变量清零
else KeyState=0;
break;
case 2://短按状态
if(Key){KeyState=0;KeyPressStyle=S;}//按键弹起返回状态0,按键动作模式为单击
else//按键保持按下
{
KeyPressTime++;//计数变量加1
if(KeyPressTime>50)KeyState=3;//按键按下时间大于1秒,状态转移至状态3
}
break;
case 3://长按状态
if(Key)KeyState=0;//按键弹起返回状态0,按键动作模式为长按
else KeyPressStyle=L;//按键动作模式为长按
break;
}
return KeyPressStyle;//返回按键动作模式
}
该函数由定时器控制,每隔10ms执行一次,每次执行时读取GPIOA.0口的电平,存储在变量Key中,然后进入由switch语句构成的状态机中,根据按键状态变量KeyState和Key的数值,确定输出的按键动作类型KeyPressStyle和下一个状态KeyState,返回给主调函数使用,返回值为0,表明按键无动作,返回值为1,表明按键单击动作,返回值为2,表明按键长按动作。主程序中调用该函数,编写程序即可实现按键的单击、长按、连发、双击等功能,极大地扩展了单个按键的作用。
介绍了有限状态机的原理和在嵌入式系统上进行程序设计的方法,并在此基础上研究了基于定时器的按键扫描识别方法。将有限状态机的思想引入按键识别的程序设计中,把按键过程划分为多个状态,以用户的按键动作驱动在按键各个状态之间的迁移,在STM32平台上实现了对单个按键单击、长按、连击的判别,扩展了单个按键的应用。整个过程高效简洁,降低了系统的复杂性,提升了系统的可靠性,是一种适合工程实际应用的方案。
[1]章乐多,兰琴丽.嵌入式设备的按键设计优化研究[J].广西轻工业,2011(7):77-78.
[2]百度百科.有限状态机[EB/OL].[2013-01-21].http://baike.baidu.com/view/115336.htm.
[3]刘媛媛.51单片机用有限状态机算法实现顺序控制[J].机械工程与自动化,2011(4):42-44.
[4]何剑宇,刘兢兢.有限状态机建模在嵌入式按键设计中的应用[J].沈阳师范大学学报:自然科学版,2012,30(2):168-171.
[5]秦国栋.有限状态机的嵌入式Linux按键驱动设计[J].单片机与嵌入式系统应用,2010(4):79-81.
[6]黄新林,王钢,刘春刚.有限状态机在单片机编程中的应用[J].哈尔滨理工大学学报,2008,13(4):7-9.
[7]管庶安.单片机程序的状态机模型[J].武汉工业学院学报,2004,23(2):1-2.
[8]肖看,朱光喜,刘文予.FPGA按键模式的研究与设计[J].电子技术应用,2008(10):45-47.
[9]马潮.AVR单片机嵌入式系统原理与应用实践[M].北京:北京航空航天大学出版社,2007.
[10]蒙博宇.STM32自学笔记[M].北京:北京航空航天大学出版社,2012.