嵌入式设备中按键的硬件消抖, 软件消抖和按键消息处理

发布者:SereneJourney最新更新时间:2022-06-20 来源: eefocus关键字:嵌入式设备  按键  软件消抖 手机看文章 扫描二维码
随时随地手机看文章

按键

按钮就是一种配备了弹性装置的双状态开关: 连通和断开. 由于弹性部件的作用, 大部分时间按钮是断开的. 从电路角度看, 按钮扮演的角色就是开路和短路. 按钮在嵌入式设备中是常见组件, 通常情况下, 一个按钮需要有一个弱上拉或下拉电阻, 对于STM32而言, GPIO口已经自带了弱上拉电阻, 可以在程序中设置是否使用, STC系列的MCU, 要看具体型号和具体的IO口, 例如经典的stc89c51/stc89c52, P0口就是漏极开路的双向IO口, 使用时当电流流出需外接上拉电阻.


将按钮连接到MCU通常有两种方式, 一种是低电平有效, 另一种是高电平有效, 在低电平有效的电路中, 当按钮按下时, 将在引脚上读取到逻辑0, 按钮释放后读取的是1; 在高电平有效电路中则正好相反.


上拉/下拉电阻阻值选取

如果将一个IO口等价为一个电容, 那么低电平有效的等价电路为下图


如果电阻太小, 电流过大可能会损坏元件, 一般这个阻值在几K到几十K欧. 阻值的大小受GPIO的逻辑转换时间限制, 对于STM32, IO口电容为5pF, 上拉电阻可以为10K欧.


按键抖动效应 The bounce effect


按钮在按下和释放时都有可能产生抖动效应, 会导致过程中产生多次短路与开路之间的切换, 对于这个问题, 需要从硬件和软件方面来解决:


硬件上, 低通滤除抖动

软件上, 增加第一次检测到动作后的 dead time

硬件处理

硬件消除抖动(debouncing)是需要优先考虑的方法, 比软件方式更稳定和高效. 可以通过在GPIO口和按键之间添加一个低通滤波电路实现.

实现低通滤波最简单的电路就是 RC滤波. 其阻值和容值怎么计算呢? 取决于抖动的容忍频率. 可以使用以下计算式

image.png


低通频率不能太低, 否则会滤除正常的操作, 在正常情况下, 一个人不太可能以100赫兹的频率去按按键, 所以


将低通频率设为10KHz, 对应的就是160欧的电阻和100nF的电容, 或1K欧电阻和16nF电容

将低通频率设为1KHz, 对应的就是1K欧电阻和160nF电容

将低通频率设为100Hz, 对应的就是10K欧电阻和160nF电容

下面的电路中, 使用了10KR电阻和100nF(104)电容作为硬件防抖处理


软件处理

软件处理分两种情况, 如果仅仅需要检测短按, 是比较简单的, 声明一个volatile static a变量用于表示按键状态, 声明一个static uint8_t b变量用于计数, 每个循环的检测中, 低电平(假定按下为低电平)b加1, 当b值计数到达一个阈值时表示按钮按下, 将a置位, 当循环中检测到高电平时将a和b都清零.


如果需要检测短按和长按, 就需要三个变量, 除了上面的a和b以外, 再增加一个循环计数c. 检测的每个循环中, 先按检测短按的方式, 做短按判断, 另外再通过第三个变量记录短按的次数, 当达到预设的长按判断的次数阈值时, 判断为长按. 要注意的是


短按的置位要由按钮释放触发

长按的置位由按钮按下触发

长按释放时, 要避免判断为短按

下面是一段实际应用中的代码, 会在一个间隔10ms的定时器中调用, 其中


KEY1 为按键对应的IO口, 例如P01

debounce[0] 为按键1对应的防抖延时计数器

k1_pressed 当判断按键1为按下时置位, 全局使用

switchcount[0] 按键1对应的长按键计数器, SW_CNTMAX为判断阈值

k1_long_pressed 当判断按键1为长按时置位, 全局使用

event 按键事件, 全局使用

void read_key1(void)

{

    //未按下时, KEY1处于高电平, 因此debounce为0xFF

    debounce[0] = (debounce[0] << 1) | KEY1;

    if (debounce[0] == 0x00) { // 8次检测都为0, 按下置位

        k1_pressed = 1;

        if (!k1_long_pressed) { // 如果长按未置位, 计数加1

            switchcount[0]++;

        }

    } else { // 按键已松开或未按下

        if (k1_pressed) {

            if (!k1_long_pressed) {

                // 如果短按已置位, 但是长按未置位, 按短按发出系统消息

                event = EV_K1_SHORT;

            }

            // 清理状态和计数器

            k1_pressed = 0;

            k1_long_pressed = 0;

            switchcount[0] = 0;

        }

    }

    if (switchcount[0] > SW_CNTMAX) {

        // 如果长按计数器已经达到阈值, 长按置位(避免松开时发出短按消息), 发出长按系统消息

        k1_long_pressed = 1;

        switchcount[0] = 0;

        event = EV_K1_LONG;

    }

}


按键消息处理

按键的系统消息是通过状态机模型进行处理的, 在每个按键处理循环中,


清除全局消息

根据当前的按键状态, 判断长按和短按对应的下一个状态

下一个循环, 会跳到对应的按键状态, 再去判断下一个状态

根据按键状态决定当前的显示模式

void main(void)

{

    //...

    while (true)

    {

        while (!loop_gate); // wait for open every 100ms

        loop_gate = 0; // close gate


        ev = event;

        event = EV_NONE;

        switch (kmode)

        {

            case K_DISP_SEC:

                dmode = D_DISP_SEC;

                if (ev == EV_K2_SHORT) {

                    kmode = K_DISP_ALARM;

                    m_timeout = TIMEOUT_SHORT;

                }

                break;

            //...

            case K_NORMAL:

            default:

                dmode = D_NORMAL;

                

                if (ev == EV_K1_SHORT) {

                    kmode = K_DISP_DATE;

                    m_timeout = TIMEOUT_SHORT;

                } else if (ev == EV_ALARM) {

                    kmode = K_BUZZ_ALARM;

                    m_timeout = TIMEOUT_LONG;

                }

                else if (ev == EV_K1_LONG)

                    kmode = K_SET_MINUTE;

                else if (ev == EV_K2_SHORT)

                    kmode = K_DISP_SEC;

                else if (ev == EV_K2_LONG)

                    kmode = K_SET_ALARM_MINUTE;

        }

//...

    }

}


参考

按键防抖的硬件和软件处理 https://www.playembedded.org/blog/buttons-stm32/

例子中的完整代码 HML_FwLib_STC12/blob/master/example/others/ds12c887/alarm_clock.c


关键字:嵌入式设备  按键  软件消抖 引用地址:嵌入式设备中按键的硬件消抖, 软件消抖和按键消息处理

上一篇:联盛德 HLK-W806 (七): 兼容开发板 LuatOS Air103
下一篇:联盛德 HLK-W806 (六): I2C驱动SSD1306 128x64 OLED液晶屏

推荐阅读最新更新时间:2024-10-14 11:25

单片机按键原理
由上图可以看出理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有关,一般为5~10ms。通常我们手动按键然后释放,这个动作中稳定闭合的时间超过了20ms。因此单片机在检测键盘是否按下时都要加上去抖动操作,有专用的去抖动电路,也有专门的去抖动芯片,但通常我们采用软件延时的方法就可以解决抖动问题。 \/* 软件去抖 */ if (0 == K1 ) //如果有键按下 { delay_ms(8); //延时一段时间去抖 if (0 == K1) //如果真的有键按下,检测到得是稳定闭合状态 { ... //按键以后需要做的事情
[单片机]
单片机<font color='red'>按键</font>去<font color='red'>抖</font>原理
利用51单片机按键设置ztw电调
/* ============================================================================ Name : ZTW_51.c Author : clare_liu Version : Copyright : Your copyright notice Description : Hello World in C, Ansi-style ============================================================================ */ // // Update to MPU6050 by sh
[单片机]
单片机按键学习总结
基本的按键程序结构分析: 1 void key_scan(void) 2 { 3 if (!key) //检测按键是否按下 4 { 5 delay_ms(20); //延时去抖,一般20ms 6 if(!key) 7 { 8 ...... 9 } 10 while (!key); //等待按键释放 11 } 12 } 注意:以上基本按键程序中,在按键执行之后必须要加上等待按键释放,否则程序会出现一些奇怪的问题,比如说按键累加时按键一次,却累加了多次。 可
[单片机]
单片机P1口高四位做按键,低四位做输出显示程序
;有对应的C51程序 ;本程序主要是初步体现基于单片机的按键的设计, ;用P1口的低八位发光二极管显示,只是为了显示实验结果 ;2007-06-05 org 0000h ljmp start org 0060h start: mov a,#0f0h ;准备给P1口高四位置高,做输入 mov p1,a ;给P1口高四位置高 mov a,p1
[单片机]
单片机单按键控制led台灯
按键控制LED台灯亮度 C语言方案 功能要求:单独一个按键控制LED台灯的亮度,上电默认关机,分5个档位。 第一档:100%亮度 第二档:65%亮度 第三档:35%亮度 第四档:20%亮度 第五档:10%亮度 第六档:关机 思路:设定一个改变占空比的变量PWM,每按一次按键PWM值自加一次(自加值看需要定,比如PWM+=100),值越小产生的驱动脉冲频率越高。PWM函数采用模拟方法产生,从成本上考虑,带中断,定时器等功能的单片机价格比较高。所以采用价格低廉的低档单片机。鉴于仿真方便,本程序采用PIC16F505(当然也可以用12F508等芯片)。 端口连接:RC1 LED RC0----蜂鸣器 RC5----按键 程序代码:
[单片机]
基于STM32F103入门2——按键点灯
1:按键不带锁存 不带锁存的意思就是 比如你按下按键灯亮,但是你一旦松手了灯就熄灭了,所以你想这个灯一直亮,那么你就一直按着按键不松手。 1.1例程 key.c /*========================key.c=========================*/ #include stm32f10x.h #include key.h /*按键初始化函数*/ void KEY_Init(void) { //1.打开控制GPIOA的时钟(APB2) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //2.配置结构体
[单片机]
单片机按键扫描方法
我在网上游逛了很久,也看过不少源程序了,没有发现这种按键处理办法的踪迹,所以,我将他共享出来,和广大同僚们共勉。我非常坚信这种按键处理办法的便捷和高效,你可以移植到任何一种嵌入式处理器上面,因为C语言强大的可移植性。 同时,这里面用到了一些分层的思想,在单片机当中也是相当有用的,也是本文的另外一个重点。 对于老鸟,我建议直接看那两个表达式,然后自己想想就会懂的了,也不需要听我后面的自吹自擂了,我可没有班门弄斧的意思,hoho~~但是对于新手,我建议将全文看完。因为这是实际项目中总结出来的经验,学校里面学不到的东西。 以下假设你懂C语言,因为纯粹的C语言描述,所以和处理器平台无关,你可以在MCS-51,AVR,PIC,甚至是ARM
[单片机]
单片机-4x4个矩阵按键控制数码管显示数字程序
1 #include 8051.h 2 typedef unsigned char u8; 3 typedef unsigned int u16; 4 u8 smgduan = { 5 /*0 1 2 3 4 5 6 7 */ 6 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 7 /*8 9 A B C D E F */ 8 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71}; 9 10 // P0口为数码管的位选的8位输入引脚 11 // P
[单片机]
热门资源推荐
热门放大器推荐
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved