找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 113|回复: 0
打印 上一主题 下一主题
收起左侧

51通用的按键长按单击双击单击读写方式

[复制链接]
跳转到指定楼层
楼主
ID:1155837 发表于 2025-7-3 15:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本代码参考了https://blog.csdn.net/m0_52596850/article/details/126776765#,在原代码的基础上按我的代码风格重写了,添加了三击功能。
具体原理在注释中已经给出,通读一遍便能理解,简单的概括就是通过switch在各种不同的状态之间切换实现按键的消抖,长短按识别,相当的巧妙。其中消抖直接使用了定时器中断的间隔进行消抖,我认为这是这个方案最巧思的一点。
如果要复用,直接根据你的按键IO口改变第一行的keyinput就行了,其他的不需要改变,然后在主循环中或者定时器中断调用就可以了。目前为P32低电平为有效按键输入,如有需求可以改为高电平。
#define KEYINPUT P32//按键输入为P32
#define NOKEY 0//无
#define SINGLEKEY 1//单键
#define DOUBLEKEY 2//双键
#define TRIPLEKEY 3//三键
#define LONGKEY 4//长键
#define KEYSTATE0 0
#define KEYSTATE1 1
#define KEYSTATE2 2
#define KEYSTATE3 3

unsigned char KEY_DRIVER(void){
    static unsigned char keystate = KEYSTATE0;
    static unsigned char keytime = 0;
    unsigned char keypress;
    unsigned char keyreturn = NOKEY;
    keypress = KEYINPUT;//读取P32电平
    switch(keystate){
        case KEYSTATE0://按键初始状态,按下后转换到消抖与确认态,用定时器中断间隔实现消抖
            if(!keypress){//P32==0
                keystate = KEYSTATE1;//如果无按键按下就始终返回为NOKEY
            }
            break;
        case KEYSTATE1:
            if(!keypress){//P32==0
                keytime = 0;
                keystate = KEYSTATE2;}//按键仍然处于按下,消抖完成,状态转换到计时
            else{
                keystate = KEYSTATE0;//低电平持续时间过小,只有一个定时器间隔
            }                        //认为是无效按键,清零状态,实现消抖
            break;
        case KEYSTATE2:
            if(keypress){//P32==1,按键释放,且间隔2个定时器中断,认为是无抖动的按键输入。
                keyreturn = SINGLEKEY;//返回单击
                keystate = KEYSTATE0;//清空状态
            }
            else if(++keytime >= 64){//P32=0,继续按下,计时加一个定时器中断间隔时间,
                keyreturn = LONGKEY;//在下次定时器中断直接输出为长按,不需要等待
                keystate = KEYSTATE3;//进入状态3,等待按键释放
            }
            break;
        case KEYSTATE3://等待按键释放,释放后清空状态
            if(keypress){//P32==1,按键已经抬起
                keystate = KEYSTATE0;//清空状态
            }
            break;
        }
            return keyreturn;
    }
//////////////////////////////////////////////////
unsigned char KEY_READ(void){
    static unsigned char key1 = KEYSTATE0;
    static unsigned char keytime1 = 0;//多次按键计数器
    unsigned char keyreturn = NOKEY;
    unsigned char keytemp;
    keytemp = KEY_DRIVER();//读取按键状态
    switch(key1){
        case KEYSTATE0:
            if(keytemp == SINGLEKEY){
                keytime1 = 0;//第一次单击,无返回值,到下个状态判断之后是否有再次单击
                key1 = KEYSTATE1;//切换单击
            }
            else{
                keyreturn = keytemp;//对于无键,长按时间返回原事件
            }
            break;
        case KEYSTATE1:
            if(keytemp == SINGLEKEY){//再次单击,间隔小于640ms
                key1 = KEYSTATE2;//切换到状态3,等待三击
            }//不清空计数器,因为要实现总间隔检测
            else{
                if(++keytime1 >= 32){//在这里实现等待双击
                    keyreturn = SINGLEKEY;//返回单击
                    key1 = KEYSTATE0;//清空状态
                }
            }
            break;
        case KEYSTATE2:
            if(keytemp == SINGLEKEY){//第三次单击,总间隔小于640ms,沿用state1中的计数器
                keyreturn = TRIPLEKEY;//输出为三击
                key1 = KEYSTATE0;//返回初始状态
            }
            else{
                if(++keytime1 >= 32){//沿用之前的计数器值,继续计数
                    keyreturn = DOUBLEKEY;//超时,输出双击
                    key1 = KEYSTATE0;
                }
            }
        }
    return keyreturn;
    }

下面是测试代码及调用功能示范,长按带点亮led,三击熄灭led,可以用于测试功能,需要手动
bit timer20msok = 0;
void TIMER0_ROUTINE(void) interrupt 1{
    timer20msok = 1;
}

unsigned char keyevent = NOKEY;
void main(void){
    TH0 = 0XD8;
    TL0 = 0XF0;
    IE = 0X8F;//允许中断
    TR0 = 1;//打开定时器电源
    while(1){
        if(timer20msok){
            timer20msok = 0;
            keyevent = KEY_READ();
            if(keyevent == LONGKEY){
                P30 = 0;
            }
            else if(keyevent == TRIPLEKEY)
                P30 = 1;
        }
    }
}

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏3 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表