企业档案
会员类型:会员
已获得易推广信誉 等级评定
(0 -40)基础信誉积累,可浏览访问
(41-90)良好信誉积累,可接洽商谈
(91+ )优质信誉积累,可持续信赖
易推广会员:6年
最后认证时间:
注册号: 【已认证】
法人代表: 【已认证】
企业类型:个体商户 【已认证】
注册资金:人民币万 【已认证】
产品数:3623
参观次数:1458331
手机网站:http://m.yituig.com/c143187/
旗舰版地址:http://www.miaosenbo.com
技术文章
51单片机对4x4矩阵按键的驱动设计,硬件测试服务,信号质量测试,硬件整改,硬件维修,硬件组装测试
点击次数:5823 发布时间:2019/10/19 9:37:39
在嵌入式系统中,用的*多的输入设备就是按键,用户的应用需求可通过相应按键传递到系统软件中,软件转而完成用户请求,实现简单的人机交互。笔者此处就矩阵按键的实现作一个简单的介绍。
1. 按键输入概述
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;
}
// 消抖后再次判断为同一键值则认为键按下保存键值
// 并进入到长按检测
来源;21ic
支持如下硬件测试项目:
以太网一致性测试、USB2.0测试、USB3.0测试、MIPI测试、HDMI测试、眼图测试
抖动测试、纹波测试、时序测试、DDR测试、CAN/LAN测试、I2C测试和SPI测试
安规测试、噪声测试、S参数测试、WIFI测试、阻抗测试、静电放电测试
有需要可以联系我们:
原创作者:北京淼森波信息技术有限公司