蓝桥杯单片机---矩阵键盘单击、长按、双击检测

一:文章概述

      本文章基于我上篇博客有关独立按键单击、长按以及双击状态的检测思路进行分析改善实现了对蓝桥杯单片机4*4矩阵键盘的单击、长按、双击状态的检测。整个代码实现思路主要由两部分组成:1、在定时器中每隔2ms进行一次按键键值的读取判断矩阵键盘中哪个按键被按下(矩阵键盘按键键值规定为0-15)。2、Key_Scan_Board()函数读取到键值代表按键第一次已经被按下后执行KeyState1()判断该键值所代表按键的状态。具体实现思路见代码注释及下文分析。

二:键值获取原理(Key_Scan_Board()函数分析)

step1:依次将其中一行置为低电平,其他行为高电平

由图可知矩阵键盘4行都连接在单片机P3口上,因此可以直接对P3口进行操作不再单独进行位操作,使用for循环轮流将每一行置低电平。

for(i=0;i<=3;i++)
    {
        P3=(P3|0xFF)&(~(1<<i));

    }   

step2:依次判断矩阵键盘每一列是否读出低电平

如果在第i行为低电平时第j列读出低电平表示KEY[i,j]按下,无按键按下时键值为0xFF。

step3:返回按键键值

矩阵键盘每行四个,键值设置为0-15,第i行第j列的键值即为value=i*4+j;

三:代码解析

3.1 board_key.c

#include "board_key.h"

sbit L0=P4^4;  //矩阵键盘每列引脚定义
sbi
<think>我们使用51单片机,通常使用Keil C51编译器。按键检测需要处理机械按键的抖动,并区分单击双击按。 实现思路: 1. 使用定时器中断(例如每5ms)来检测按键状态,这样可以实现去抖动,并且不会阻塞主程序。 2. 定义一个按键状态机,状态包括:空闲、按下抖动、按下稳定、释放抖动、释放稳定等。 3. 通过记录按键按下的时间和释放的时间,来判断是单击双击还是按。 状态机设计(状态转移): - 状态0(KEY_IDLE):空闲状态。当检测到按键按下时,进入状态1(KEY_DEBOUNCE_DOWN),并开始计时。 - 状态1(KEY_DEBOUNCE_DOWN):按下抖动。等待一段时间(比如10ms)后再次检测,如果仍然按下,则进入状态2(KEY_DOWN),并记录按下时间;如果弹起,则回到状态0。 - 状态2(KEY_DOWN):按下稳定状态。在此状态下,持续检测按键是否释放。如果释放,则进入状态3(KEY_DEBOUNCE_UP);同时,如果按下时间超过按时间阈值(比如1000ms),则触发按事件,并标记按已触发,防止在释放时再触发单击- 状态3(KEY_DEBOUNCE_UP):释放抖动。等待一段时间(比如10ms)后再次检测,如果仍然释放,则进入状态4(KEY_UP),并记录释放时间;如果按下,则回到状态2。 - 状态4(KEY_UP):释放稳定状态。在此状态下,判断从按下到释放的时间: 如果时间小于按阈值且大于单击阈值(比如20ms),则判断为一次单击候选(注意:双击中的第一次单击也会到这里,所以不能立即触发单击,需要等待双击时间窗口)。 此时启动一个双击等待计时器(比如300ms),在等待时间内如果有第二次按下,则进入双击处理;如果等待时间到都没有第二次按下,则触发单击事件。 双击处理: - 在第一次释放后,启动一个双击等待计时器(比如300ms),在计时器超时前,如果检测到第二次按下,则记录第二次按下,并同样经过去抖动等状态。 - 第二次释放后,判断两次单击的时间间隔是否在双击时间窗口内,如果是,则触发双击事件,并重置状态。 注意:按事件在按下稳定状态中,当按下时间超过按阈值时立即触发,并且标记已经触发了按,那么在释放时就不会触发单击。 由于51单片机资源有限,我们使用一个定时器中断来扫描按键。这里假设只有一个按键,如果多个按键,可以类似扩展。 代码结构: 1. 定义按键状态和变量。 2. 定时器中断服务函数(例如每5ms执行一次)中调用按键扫描函数。 3. 按键扫描函数实现状态机。 变量定义: - 按键状态(state) - 按键按下的时间(press_time) - 释放后的时间(用于等待双击,double_click_wait_time) - 按标志(long_press_flag)避免按后释放时触发单击 - 第一次单击释放的时间(first_click_time)用于判断双击时间窗口 事件触发方式: 由于在中断中不宜执行复杂操作,我们可以设置事件标志(如click_event, double_click_event, long_press_event),在主循环中检测这些标志并执行相应操作。 这里我们使用一个按键,连接在P3.2(外部中断0引脚,但这里我们不使用外部中断,而是用普通IO扫描)或其他IO口。假设按键接在P3.2,低电平表示按下。 步骤: 1. 初始化定时器,设置定时器中断时间为5ms。 2. 初始化按键相关的变量。 注意:去抖动时间、按时间、双击等待时间等需要根据实际情况调整。 下面给出示例代码框架: 注意:在Keil C51中,中断服务函数的写法。 我们使用定时器0,模式1(16位定时器),假设晶振为11.0592MHz,5ms中断一次。 定时器初值计算: 机器周期 = 12 / 11.0592MHz ≈ 1.085us 5ms = 5000us,需要计数次数 = 5000 / 1.085 ≈ 4608,则初值 = 65536 - 4608 = 60928 = 0xEE00 实际中,我们可以用更精确的调整,或者使用自动重装载模式(模式2,8位重装),但这里使用模式1,需要重装初值。 代码:</think>### 51单片机按键检测实现(单击/按/双击) 下面是用C语言为51单片机实现按键检测的完整方案,支持单击按和双击功能: #### 实现原理 1. **状态机设计**:使用状态机处理按键状态转换 2. **去抖动处理**:消除机械按键抖动影响 3. **时间判定**:通过定时器区分单击双击- 按:按下时间 > 500ms - 双击:两次按下间隔 < 300ms - 单击:无按且无后续按下的单次操作 #### 示例代码 ```c #include <reg52.h> #define KEY_PIN P3_2 // 按键连接P3.2 #define LONG_PRESS_TIME 500 // 按判定时间(ms) #define DOUBLE_CLICK_INTERVAL 300 // 双击间隔时间(ms) sbit LED = P1^0; // 指示灯引脚 // 按键状态定义 enum KeyState { IDLE, // 空闲状态 PRESS_DOWN, // 按下状态 PRESS_DEBOUNCE, // 按下消抖 RELEASE_DEBOUNCE, // 释放消抖 WAIT_DOUBLE // 等待双击 }; volatile enum KeyState keyState = IDLE; volatile unsigned int pressTime = 0; volatile unsigned int lastReleaseTime = 0; // 定时器0初始化 (1ms中断) void Timer0_Init() { TMOD |= 0x01; // 定时器0模式1 TH0 = 0xFC; // 1ms定时(11.0592MHz) TL0 = 0x18; ET0 = 1; // 允许定时器0中断 EA = 1; // 开总中断 TR0 = 1; // 启动定时器0 } // 定时器0中断服务函数 void Timer0_ISR() interrupt 1 { TH0 = 0xFC; // 重装初值 TL0 = 0x18; static unsigned int debounceCnt = 0; static unsigned int waitDoubleCnt = 0; switch(keyState) { case IDLE: if(!KEY_PIN) { // 检测到按键按下 keyState = PRESS_DEBOUNCE; debounceCnt = 0; } break; case PRESS_DEBOUNCE: if(++debounceCnt >= 10) { // 10ms消抖 if(!KEY_PIN) { keyState = PRESS_DOWN; pressTime = 0; // 按下时间清零 } else { keyState = IDLE; } } break; case PRESS_DOWN: pressTime++; if(KEY_PIN) { // 按键释放 keyState = RELEASE_DEBOUNCE; debounceCnt = 0; } else if(pressTime > LONG_PRESS_TIME) { // 按处理 LED = ~LED; // 按切换LED状态 keyState = IDLE; // 按后直接返回空闲 } break; case RELEASE_DEBOUNCE: if(++debounceCnt >= 10) { // 10ms消抖 if(KEY_PIN) { // 判断是否双击 if((lastReleaseTime > 0) && (pressTime < DOUBLE_CLICK_INTERVAL)) { // 双击处理 LED = 0; // 双击点亮LED lastReleaseTime = 0; keyState = IDLE; } else { // 进入等待双击状态 lastReleaseTime = pressTime; keyState = WAIT_DOUBLE; waitDoubleCnt = 0; } } else { keyState = PRESS_DOWN; } } break; case WAIT_DOUBLE: if(++waitDoubleCnt > DOUBLE_CLICK_INTERVAL) { // 等待超时,确认为单击 if(lastReleaseTime < LONG_PRESS_TIME) { // 单击处理 LED = 1; // 单击熄灭LED } keyState = IDLE; lastReleaseTime = 0; } else if(!KEY_PIN) { // 再次按下 keyState = PRESS_DEBOUNCE; debounceCnt = 0; } break; } } void main() { Timer0_Init(); // 初始化定时器 LED = 1; // 初始LED熄灭 while(1) { // 主循环可执行其他任务 // 按键处理在中断中完成 } } ``` #### 代码说明 1. **状态机流程**: - `IDLE` → 检测按键按下 → `PRESS_DEBOUNCE`(消抖) - 消抖成功 → `PRESS_DOWN`(检测按) - 按键释放 → `RELEASE_DEBOUNCE`(消抖) - 释放确认 → `WAIT_DOUBLE`(等待双击)或触发单击 2. **时间参数**: - 消抖时间:10ms(可调整) - 按时间:500ms(可调整) - 双击间隔:300ms(可调整) 3. **LED指示**: - 单击:LED熄灭 - 双击:LED点亮 - 按:LED状态切换 4. **硬件连接**: - 按键:连接P3.2(INT0)和GND - LED:连接P1.0(需串联限流电阻) #### 使用说明 1. 将代码烧录至51单片机 2. 按键按下时触发不同功能: - 快速点击两次:双击(LED亮) - 按下保持>500ms:按(LED状态切换) - 单次点击:单击(LED灭) 3. 可通过修改宏定义调整时间参数 > 提示:实际应用中可根据需求替换LED操作部分为具体功能代码[^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值