蓝桥杯单片机---独立按键单击、长按、双击检测

一、文章概述

  1. 本文章将介绍基于蓝桥杯单片机实现独立按键的单击、长按、双击状态检测。主要思路为主函数不断扫描按键状态,若有按键按下,记录从按键按下到按键松开所需时间,如果时间超过1s即为长按。如果时间小于500ms则开启双击检测,启动双击间隔时间计时,如果经过230ms还没有按键按下则为单击,如果在230ms内检测到按键按下返回双击状态。
  2. 代码实现功能:蓝桥杯开发板跳帽接到独立按键时可支持键盘第一列四个独立按键的单击、长按、双击状态检测(采用switch语句实现多按键同时检测)。

二、代码解析

  • separate_key.c

#include "separate_key.h"

sbit S7=P3^0;  //按键引脚定义
sbit S6=P3^1;
//================按键KEY7变量定义===================//
volatile bit KeyTimerState=0;  //控制定时器是否开始计时按键按下时间
volatile bit KeyDoubleTimerState=0; //控制定时器是否开始计时双击间隔时间
volatile u16 PressTime=0;      //按键按下时间
volatile u16 PressDoubleTime=0;  //双击间隔时间
volatile bit PressState=1;   //判断是否为误触
volatile bit KeyDoubleCount=0;  //双击标志位,可防止双击检测过程循环执行时重复执行判断按键初次按下是否误触和第一次按下是否长按
//================按键KEY6变量定义===================//
volatile bit KeyTimerState1=0;  //控制定时器是否开始计时按键按下时间
volatile bit KeyDoubleTimerState1=0; //控制定时器是否开始计时双击间隔时间
volatile u16 PressTime1=0;      //按键按下时间
volatile u16 PressDoubleTime1=0;  //双击间隔时间
volatile bit PressState1=1;   //判断是否为误触
volatile bit KeyDoubleCount1=0;  //双击标志位

extern void SmgDisplay(void);

void Delay2ms(void)	//@12.000MHz
{
	unsigned char data i, j;

	i = 24;
	j = 85;
	do
	{
		while (--j);
	} while (--i);
}

void Timer0Init(void)		//2毫秒@12.000MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x40;		//设置定时初始值
	TH0 = 0xA2;		//设置定时初始值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
    
    ET0=1;
    EA=1;
}

void Timer0(void)  interrupt 1 //定时器0中断服务函数中实现按键按下时间、双击间隔时间和数码管显示
{
    if(KeyTimerState==1)
        PressTime++;
    else 
        PressTime=0;
    
    if(KeyDoubleTimerState==1)
        PressDoubleTime++;
    else 
        PressDoubleTime=0;
    
    if(KeyTimerState1==1)
        PressTime1++;
    else 
        PressTime1=0;
    
    if(KeyDoubleTimerState1==1)
        PressDoubleTime1++;
    else 
        PressDoubleTime1=0;
    
    SmgDisplay();
}

u8 KeyScan(u8 key) //switch函数内部(KEY7和KEY6)即(case0和case1)的代码实现逻辑相同,以此类推将case后代码复制一次即可增加对一个按键的检测 
{
    switch(key)
    {
        case 0:if((S7==0)&&(KeyTimerState==0)&&(KeyDoubleCount==0))
        {
            Delay2ms();   //消抖
            if(S7==0)
            {
                KeyTimerState=1;    //开始计算按下时长
                PressState=0;       //按键按下标志
            }
            else
                KeyTimerState=0;
            
        }
        if((PressState==0)&&(S7==1)&&(KeyDoubleCount==0))    //判断按键松开时间
        {
            if(PressTime<250)   //500ms内松开为短按
            {
                KeyDoubleCount=1;  //进入双击检测
                PressState=1; 
                KeyTimerState=0;
                KeyDoubleTimerState=1;  //双击标志位置1,开始进入双击检测
            }
            if(PressTime>500)  //超过500ms为长按
            {
                KeyTimerState=0;
                PressState=1;
                return KEY7_LONG;
            }
        }
        if((KeyDoubleCount==1)&&(PressDoubleTime<115)&&(S7==0))  //如果双击间隔时间小于 
230ms时有按键按下为双击                                                                       
        {                                                        
                Delay2ms();
                if(S7==0)
                {
                    KeyDoubleCount=0;
                    KeyDoubleTimerState=0;
                    while(S7==0);
                    return KEY7_DOUBLE;
                }
            
        }
        if(PressDoubleTime>115) //双击间隔时间超过230ms仍无按键按下为单击
        {
            KeyDoubleCount=0;
            KeyDoubleTimerState=0;
            return KEY7_SHORT;
        }
        break;
    
    
    case 1:if((S6==0)&&(KeyTimerState1==0)&&(KeyDoubleCount1==0))
        {
            Delay2ms();   //消抖
            if(S6==0)
            {
                KeyTimerState1=1;    //开始计算按下时长
                PressState1=0;
            }
            else
                KeyTimerState1=0;
            
        }
        if((PressState1==0)&&(S6==1)&&(KeyDoubleCount1==0))    //判断按键松开时间
        {
            if(PressTime1<250)   //500ms内松开为短按
            {
                KeyDoubleCount1=1;  //进入双击检测
                PressState1=1; 
                KeyTimerState1=0;
                KeyDoubleTimerState1=1;
            }
            if(PressTime1>500)
            {
                KeyTimerState1=0;
                PressState1=1;
                return KEY6_LONG;
            }
        }
        if((KeyDoubleCount1==1)&&(PressDoubleTime1<115)&&(S6==0))
        {
                Delay2ms();
                if(S6==0)
                {
                    KeyDoubleCount1=0;
                    KeyDoubleTimerState1=0;
                    while(S6==0);
                    return KEY6_DOUBLE;
                }
            
        }
        if(PressDoubleTime1>115)
        {
            KeyDoubleCount1=0;
            KeyDoubleTimerState1=0;
            return KEY6_SHORT;
        }
        break;
    }
    return KEY_NO;
}






  • separate.h

#ifndef separate_key_H
#define separate_key_H

#include "stc15.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int u16;

enum KeyNum  //按键键值
{
    KEY7=0,
    KEY6,
    KEY5,
    KEY4
};

enum KeyState  //按键状态
{
    KEY7_SHORT=0,
    KEY7_LONG,
    KEY7_DOUBLE,
    KEY6_SHORT,
    KEY6_LONG,
    KEY6_DOUBLE,
    KEY_NO
};

void Timer0Init(void);
u8 KeyScan(u8 key);

#endif

  • main.c

#include "stc15.h"
#include "intrins.h"
#include "separate_key.h"

#define HC138(x) P2=(P2&0x1F)|(x<<5)

u8 code smg_dm[]={0xc0, 0xf9, 0xa4, 0xb0, 
             0x99, 0x92, 0x82,0xf8, 
             0x80, 0x90,0xff};
u8 smg_num[]={10,10,10,10,10,10,10,10};
u8 com=0;

u8 KEY7_STATE=KEY_NO;
u8 KEY6_STATE=KEY_NO;

void SmgDisplay(void);

void Init(u8 i)
{
    P2&=0x1F;
    switch(i)
    {
        case 4:P0=0xFF;HC138(i);P2&=0x1F;P0=0x00;break;
        case 5:HC138(i);break;
        case 6:HC138(i);break;
        case 7:P2&=0x1F;P0=0xFF;HC138(i);break;
    }
    P2&=0x1F;
}

void main(void)
{
    Init(4);
    Init(5);
    Init(6);
    Init(7);
    Timer0Init();
    while(1)
    {
        KEY7_STATE=KeyScan(KEY7);  //读取按键状态
        KEY6_STATE=KeyScan(KEY6);
//采用数码管第零位显示数值来演示按键状态
        if(KEY7_STATE==KEY7_SHORT)
        {
            smg_num[0]=0;
        }
        if(KEY7_STATE==KEY7_LONG)
        {
            smg_num[0]=1;
        }
        if(KEY7_STATE==KEY7_DOUBLE)
        {
            smg_num[0]=2;
        }
        if(KEY6_STATE==KEY6_SHORT)
        {
            smg_num[0]=3;
        }
        if(KEY6_STATE==KEY6_LONG)
        {
            smg_num[0]=4;
        }
        if(KEY6_STATE==KEY6_DOUBLE)
        {
            smg_num[0]=5;
        }
    }
}

void SmgDisplay(void)
{
    P0=0xFF;
    HC138(6);
    P0=(1<<com);
    HC138(7);
    P0=0xFF;
    P0=smg_dm[smg_num[com]];
    (com==7)?(com=0):(com++);
}

三:总结

       上面所提供的代码可以实现对独立按键的单击、长按以及双击状态的检测,但其实时性和准确度仍有待提升,最后希望大家能从中有所收获,如有更好写法欢迎大家交流讨论,让我们共同学习进步!

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在51单片机中,独立按键的驱动可以采用轮询方式或者中断方式。下面分别介绍三种按键的驱动方式。 ### 单击按键 #### 轮询方式 在轮询方式中,通过不断查询按键的状态来判断是否按下。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; void main() { while(1) { // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; } } else { key_state = 0; } // 判断按键状态,执行相应的操作 if(key_state == 1) { // 执行单击操作 // ... } } } ``` #### 中断方式 在中断方式中,通过外部中断触发来判断按键是否按下。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; // 中断服务函数 void key_interrupt() interrupt 0 { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; } } void main() { // 配置外部中断0 IT0 = 1; EX0 = 1; EA = 1; while(1) { // 判断按键状态,执行相应的操作 if(key_state == 1) { // 执行单击操作 // ... } } } ``` ### 双击按键 双击按键的实现可以通过轮询方式或者定时器方式实现。 #### 轮询方式 在轮询方式中,需要记录上一次按键的时间,并通过计算两次按键时间的间隔来判断是否为双击。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int last_key_time = 0; void main() { while(1) { // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; } } else { key_state = 0; } // 判断按键状态,执行相应的操作 if(key_state == 1) { if((unsigned int)(get_sys_time() - last_key_time) < 500) { // 执行双击操作 // ... last_key_time = 0; } else { last_key_time = get_sys_time(); } } } } ``` #### 定时器方式 在定时器方式中,需要配置定时器,并记录上一次按键的时间。每次检测按键按下时,通过计算两次按键时间的间隔来判断是否为双击。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int last_key_time = 0; // 定时器0中断服务函数 void timer0_interrupt() interrupt 1 { // 定时1ms TH0 = (65536 - 1000) / 256; TL0 = (65536 - 1000) % 256; } void main() { // 配置定时器0 TMOD = 0x01; TH0 = (65536 - 1000) / 256; TL0 = (65536 - 1000) % 256; ET0 = 1; TR0 = 1; EA = 1; while(1) { // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; } } else { key_state = 0; } // 判断按键状态,执行相应的操作 if(key_state == 1) { if((unsigned int)(get_sys_time() - last_key_time) < 500) { // 执行双击操作 // ... last_key_time = 0; } else { last_key_time = get_sys_time(); } } } } ``` ### 按键 按键的实现可以通过轮询方式、中断方式或者定时器方式实现。 #### 轮询方式 在轮询方式中,需要记录按键按下的时间,并通过判断按键持续时间的短来判断是否为按。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int key_down_time = 0; void main() { while(1) { // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; key_down_time = get_sys_time(); } } else { if(key_state == 1) { if((unsigned int)(get_sys_time() - key_down_time) > 1000) { // 执行按操作 // ... key_state = 0; } else { // 执行单击操作 // ... key_state = 0; } } } } } ``` #### 中断方式 在中断方式中,需要配置外部中断,并记录按键按下的时间。每次触发中断时,通过判断按键持续时间的短来判断是否为按。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int key_down_time = 0; // 中断服务函数 void key_interrupt() interrupt 0 { // 消抖处理 delay_ms(10); if(key == 0) { key_down_time = get_sys_time(); } else { if((unsigned int)(get_sys_time() - key_down_time) > 1000) { // 执行按操作 // ... } else { // 执行单击操作 // ... } } } void main() { // 配置外部中断0 IT0 = 1; EX0 = 1; EA = 1; while(1) { } } ``` #### 定时器方式 在定时器方式中,需要配置定时器,并记录按键按下的时间。每次检测按键按下时,通过判断按键持续时间的短来判断是否为按。具体实现如下: ```c // 定义按键IO口和状态 sbit key = P1^0; bit key_state = 0; unsigned int key_down_time = 0; // 定时器0中断服务函数 void timer0_interrupt() interrupt 1 { // 定时1ms TH0 = (65536 - 1000) / 256; TL0 = (65536 - 1000) % 256; // 检测按键是否按下 if(key == 0) { // 消抖处理 delay_ms(10); if(key == 0) { key_state = 1; key_down_time = get_sys_time(); } } else { if(key_state == 1) { if((unsigned int)(get_sys_time() - key_down_time) > 1000) { // 执行按操作 // ... key_state = 0; } else { // 执行单击操作 // ... key_state = 0; } } } } void main() { // 配置定时器0 TMOD = 0x01; TH0 = (65536 - 1000) / 256; TL0 = (65536 - 1000) % 256; ET0 = 1; TR0 = 1; EA = 1; while(1) { } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值