当前位置:首页 > 单片机 > 单片机
[导读]在嵌入式系统中,用的最多的输入设备就是按键,用户的应用需求可通过相应按键传递到系统软件中,软件转而完成用户请求,实现简单的人机交互。笔者此处就矩阵按键的实现作一个简单的介绍。1. 按键输入概述按键是一种常

在嵌入式系统中,用的最多的输入设备就是按键,用户的应用需求可通过相应按键传递到系统软件中,软件转而完成用户请求,实现简单的人机交互。笔者此处就矩阵按键的实现作一个简单的介绍。

1. 按键输入概述

按键是一种常开型按钮开关,平时键的二个触点处于断开状态,按下键时它们才闭合。按键控制电路就是用来实时监视按键,当有键接下时,电路监控中的输入引脚电平发生变化,检测到这种变化后,控制电路进行按键扫描,定位按键的位置,并把相关的按键信息反馈回上一层应用中。常见的按键输入设计有独立式按键,矩阵式按键。独立式按键每个键占用一个IO口,电路配置灵活,软件简单,但按键较多时,IO口浪费大。矩阵式按键适用于按键数量较多的场合,由行线和列线组成,按键位于行列的交叉点上。节省IO口。通常按键控制电路通过查询方式或中断方式去检测按键的输入,查询方式需占用一定的cpu资源,查询频率太低可能造成按键输入丢失,太高浪费cpu资源,通常按键查询频率约50HZ较合适。中断方式需占用cpu一路外部中断,但不会占用cpu资源,只要有按键按下时,cpu即可马上检测到输入,进行扫描并得到按键值。

2. 硬件设计

笔者此处采用4x4的矩阵按键设计,当然,矩阵键盘可通过四个肖特基二极管构成四输入的与门(可参考笔者这篇文章<浅谈小信号肖特基二极管在数字电路中的应用>),连接到单片机的外部中断引脚,从而实现中断方式检测按键输入。为兼容目前开发板常见的矩阵按键设计,笔者把4x4的矩阵按键接口接在P1口,通过查询方式检测按键输入。


图2-1 4x4矩阵按键

3. 驱动实现

由于我们采用的是查询方式按键设计,因此单片机需一定的频率去扫描P1口的按键,通常这个频率约50HZ较合适,为保证这个扫描频率,通常是通过定时器产生时标周期性进行执行扫描。P1.4~P1.7列线通过上拉电阻接到VCC上,P1.0~P1.3行线产生相应的扫描信号,无按键,列线处于高电平状态,有键按下,列线电平状态将由与此列线相连的行线电平决定。行线电平为低,则列线电平为低,行线电平为高,则列线电平为高。

按键扫描函数如下,该函数需周期执行,以扫描按键的状态。以51单片机为例,P1.0~P1.3逐行输出扫描信号,在Key.h模块头文件实现接口宏KeyOutputSelect()

#define KeyOutputSelect(Select) {P1 = ~(1<<(Select));}

输出扫描线后,需要读取对应扫描线的按键状态(P1.4~P1.7),同样在Key.h模块头文件实现引脚状态读取接口宏KeyGetPinState()

#define KeyGetPinState() (P1>> 4)

读取了对应扫描线下的按键引脚状态,就需判断哪些引脚电平为0(按下),对读到的引脚状态进行取反转换成对引脚状态变量进行搜1算法,得到键值的速度能达到最快,并且多个按键同时按下时也能够正确得到优先级最高的按键。按键有效按下会得到0~15的键值,无按键按下时得到键值16。

voidKeyScan()

{

unsigned char i;

unsigned char KeyValue;

unsigned char PinState;

if (KeyState.State == STATE_DISABLE) {

return; // 按键禁用时,不对键盘进行扫描

}

// 键值为0~15,未按键键值为16,任意多的键按下均能

// 正确返回优先级最高的键值

KeyValue = 0;

for (i=0; i<4; i++) {

KeyOutputSelect(i); // 输出扫描线

// 得到对应扫描线时的按键状态

PinState = KeyGetPinState();

// 有键按下时,PinState中有0的位置即为键值位置

PinState = ~PinState;

// 搜索Pinstate第一个为1的位

if (!(PinState & 0xf)) {

KeyValue += 4;

continue; // 该扫描线没有按键按下,进入下一扫描线

}

// 该扫描线有键按下,对半进行检索1的位置

if (!(PinState & 0x3)) {

KeyValue += 2; // 低2位(P1.4~P1.5)没有按下

PinState >>= 2; // 移位检索(P1.6~P1.7)

}

if (!(PinState & 0x1)){

KeyValue += 1;

}

break; // 有鍵按下,退出继续扫描

}

KeyStore(KeyValue); // 保存按键状态

}

得到了按键值后,我们需要对按键值进行处理并根据按键状态把可能产生的按键消息保存进缓冲区中,以便用户程序读取处理。按键通常有按下、松手、长按这几个状态,需要支持按下检测、松手检测、长按、连击的功能,并且需要对按键进行去抖滤波。按键的状态往往会在这几种情况进行切换,因此,对按键进行状态机编程是相当清晰的思路。我们在KeyStore()函数中实现对按键状态的转移判断,在模块中我们通过按键状态结构变量KeyState来跟踪记录按键的状态

typedef struct {

unsigned char State; // 按键的各个状态转移

unsigned int TimeCount; // 用来跟踪各个状态的计时

} KEY_STATE;

static KEY_STATE KeyState; // 按键状态机状态转移

检测到相应的按键事件后(KEY_UP、KEY_DOWN、KEY_LONG),需产生相应的按键消息保存进按键缓存区,通常可以开辟一个按键队列缓存,以便保存多个产生的按键消息,不会因用户代码未能及时处理按键而造成按键丢失,笔者此处为避免复杂,以一个按键缓冲为例,按键事件结构变量KeyBuffer用来保存按键消息

typedef struct {

unsigned char Value;

unsigned char State;

} KEY_EVENT;

// 按键扫描得到的键值存放在KeyBuffer中,包含键值及键状态

static volatile KEY_EVENT KeyBuffer;

按键消抖以及长按均是需要以时间为判断标准,我们在模块中定义消抖时间以及长按时间判决以及相应的状态宏

// 按键的扫描周期为20ms

#define WOBBLE_COUNT 1 // 按键消抖计数,1个按键扫描周期(20ms)

#define LONG_COUNT 100 // 长按100个扫描周期判断为长按(2S)

#define STATE_INIT 0x0 // 按键初始化状态

#define STATE_WOBBLE 0x1 // 按键消抖状态

#define STATE_LONG 0x2 // 按键长按检测状态

#define STATE_RELEASE 0x3 // 按键释放状态

#define STATE_DISABLE 0x4 // 按键禁用状态

完整的KeyStore()函数实现如下

static voidKeyStore(unsigned char Value)

{

static unsigned char LastValue;

switch (KeyState.State) {

case STATE_INIT: // 初始状等待按键

if (Value < KEY_NULL) {

// 记录下按下的键并进入消抖状态

LastValue = Value;

KeyState.TimeCount = WOBBLE_COUNT -1;

KeyState.State = STATE_WOBBLE;

}

break;

case STATE_WOBBLE:

if (KeyState.TimeCount) {

KeyState.TimeCount--; // 消抖计时未到

break;

}

// 消抖后再次判断为同一键值则认为键按下保存键值

// 并进入到长按检测

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭