写在前面:

物联网STM32入门 - 直播课程 - 创客学院​www.makeru.com.cn

按键去抖:由上图可以看出理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有关,一般为5~10ms。通常我们手动按键然后释放,这个动作中稳定闭合的时间超过了20ms。因此单片机在检测键盘是否按下时都要加上去抖动操作,有专用的去抖动电路,也有专门的去抖动芯片,但通常我们采用软件延时的方法就可以解决抖动问题。

1、单片机中按键消抖程序

1.1 单片机中,比如STM32中,一般的方法(最简单的方法)

软件消抖程序:

if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14)==1)
{
delay_ms(20);//延时20ms再去检测按键值
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14)==0) // 相当于下降沿

{

KEY1 = 1; //表示KEY1被按下

}

}

1.2 比较全面的按键消抖程序及按键状态检测程序

第一步:初始化全局时间戳的定时器,一般采用SysTick定时器来产生,每ms一次tick即可。

第二步:初始化按键对应的IO,复用为边沿触发的外部中断。

第三步:在外部中断函数中添加按键事件处理函数。

代码部分:

typedef struct _Key_t

{

u32 last_time;

enum

{

May_Press,

Release,

}private_state;

enum

{

No_Press,

Short_Press,

Long_Press,

}state;

}Key_t;

#define Is_ShortPress_Threshold 1500

简单定义一个按键状态的结构体,用于管理每个按键的状态。顺便再定义一个长短按的识别阈值,用于区分按键的长短按。

if(key_state.private_state==Release)

{

if(KEY==0)

{

key_state.private_state=May_Press;

key_state.last_time=course_ms();

}

}

else if(key_state.private_state==May_Press)

{

if(KEY==1)

{

if((course_ms()-key_state.last_time>10)&&(course_ms()-key_state.last_time

{

key_state.state=Short_Press;

key_state.private_state=Release;

}

else if(course_ms()-key_state.last_time>Is_ShortPress_Threshold)

{

key_state.state=Long_Press;

key_state.private_state=Release;

}

else

key_state.private_state=Release;

}

}

以上为需要添加到中断处理函数的按键事件处理函数,算法的核心是一个状态机。在本例中,按键被默认上拉,按下接地。course_ms()为获取全局时间戳的函数。

思路解释如下:按键状态结构体有一个用于识别的状态位,默认处于Release,也就是释放的状态。一旦按键被按下,中断触发,此时检查是否是Relase状态,如果是就检查按键是否被拉低,如果是,此时进入May_Press状态,也就是可能是按下的,并且记录此时的时间戳,这一步是消抖的关键。当按键被释放,由于是边沿触发,会再次进行处理,此时检查和上一次触发之间的时间戳之差,如果小于10ms我们就认为是抖动,此时不会对按键输出状态进行修改,而是直接将按键状态置回Relase状态,反之检查差值和长短按阈值之间的关系,将state置位为对应的状态。消抖的核心在于记录时间戳,而这只是一个简单的赋值操作,并不耗费时间。

效率上来说,延时消抖花费时间在无意义延时上,而相对较好的定时轮询还是不可避免的在轮询,而现在这种方式完全是中断性质的。唯一多出的开销(全局时间戳)并不是只可以用于按键消抖,另外在HAL库中存在直接获取tick的函数,这样实现就更方便了。经实际测试,消抖效果可以达到其他两种消抖算法的水平。

2、FPGA按键消抖程序

首先,做两个假定,以方便后面的描述:

  • 假定按键的默认状态为0,被按下后为1
  • 假定按键抖动时长小于20ms,也即使用20ms的消抖时间

核心:方案

  • 最容易想到的方案

在按键电平稳定的情况下,当第一次检测到键位电平变化,开始20ms计时,计时时间到后将按键电平更新为当前电平。

  • 或许这才是最容易想的方案

在20ms计时的过程中,有任何的电平变化都立即复位计时

  • 消除按键反应延时抖方案

在有电平变化时立即改变按键输出电平,并开始20ms计时,忽略这其中抖动

测试平台设计(修改代码以仿真的1us代替实际1ms)

  • 无抖动 上升沿抖动5毫秒
  • 下降沿抖动15毫秒
  • 上升和下降沿均抖动19毫秒

  附加测试(可以不通过)

  • 抖动25毫秒

代码

方案1

module debounce(    input wire clk, nrst,    input wire key_in,    output reg key_out);    // 20ms parameter//    localparam TIME_20MS = 1_000_000;localparam TIME_20MS = 1_000;       // just for test    // variablereg [20:0] cnt;    reg key_cnt;    // debounce time passed, refresh key statealways @(posedge clk or negedge nrst) beginif(nrst == 0)key_out <= 0;        else if(cnt == TIME_20MS - 1)key_out <= key_in;    end// while in debounce state, count, otherwise 0always @(posedge clk or negedge nrst) beginif(nrst == 0)cnt <= 0;        else if(key_cnt)cnt <= cnt + 1'b1;elsecnt <= 0; end// always @(posedge clk or negedge nrst) beginif(nrst == 0)key_cnt <= 0;            else if(key_cnt == 0 && key_in != key_out)key_cnt <= 1;            else if(cnt == TIME_20MS - 1)key_cnt <= 0;     endendmodule

方案2

module debounce(    input wire clk, nrst,    input wire key_in,    output reg key_out);//    localparam TIME_20MS = 1_000_000;localparam TIME_20MS = 1_000;    reg key_cnt;    reg [20:0] cnt;    always @(posedge clk or negedge nrst) beginif(nrst == 0)key_cnt <= 0;        else if(cnt == TIME_20MS - 1)key_cnt <= 0;        else if(key_cnt == 0 && key_out != key_in)key_cnt <= 1;    endalways @(posedge clk or negedge nrst) beginif(nrst == 0)cnt <= 0;        else if(key_cnt) beginif(key_out == key_in)cnt <= 0;            elsecnt <= cnt + 1'b1;endelsecnt <= 0;    endalways @(posedge clk or negedge nrst) beginif(nrst == 0)key_out <= 0;            else if(cnt == TIME_20MS - 1)key_out <= key_in;     endendmodule

方案3

module debounce(    input wire clk, nrst,    input wire key_in,    output reg key_out);//    localparam TIME_20MS = 1_000_000;localparam TIME_20MS = 1_000;       // just for testreg key_cnt;    reg [20:0] cnt;    always @(posedge clk or negedge nrst) beginif(nrst == 0)key_cnt <= 0;        else if(key_cnt == 0 && key_out != key_in)key_cnt <= 1;        else if(cnt == TIME_20MS - 1)key_cnt <= 0;    endalways @(posedge clk or negedge nrst) beginif(nrst == 0)cnt <= 0;        else if(key_cnt)cnt <= cnt + 1'b1;elsecnt <= 0;    endalways @(posedge clk or negedge nrst) beginif(nrst == 0)key_out <= 0;        else if(key_cnt == 0 && key_out != key_in)key_out <= key_in;    endendmodule

测试代码

// 按键消抖测试电路// 时间单位`timescale 1ns/10ps// modulemodule  debounce_tb;    // time period parameterlocalparam T = 20;    // variablereg clk, nrst;    reg key_in;    wire key_out;    // instantiate    debounce uut(.clk    (clk    ),.nrst   (nrst   ),.key_in (key_in ),.key_out(key_out));    // clockinitial beginclk = 1;        forever #(T/2) clk = ~clk;    end// resetinitial beginnrst = 1;@(negedge clk) nrst = 0;@(negedge clk) nrst = 1;    end// key_ininitial begin// initial valuekey_in = 0;        // wait resetrepeat(3) @(negedge clk);        // no bounce        // key downkey_in = 1;        // last 60msrepeat(3000) @(negedge clk);        // key upkey_in = 0;        // wait 50msrepeat(2500) @(negedge clk);        // down 5ms, up 15ms        // key down, bounce 5msrepeat(251) @(negedge clk) key_in = ~key_in;        // last 60msrepeat(3000) @(negedge clk);        // key up, bounce 15msrepeat(751) @(negedge clk) key_in = ~key_in;        // wait 50msrepeat(2500) @(negedge clk);        // down 19ms, up 19ms        // key down, bounce 19msrepeat(951) @(negedge clk) key_in = ~key_in;        // last 60msrepeat(3000) @(negedge clk);        // key up, bounce 19msrepeat(951) @(negedge clk) key_in = ~key_in;        // wait 50msrepeat(2500) @(negedge clk);        // additional, this situation shoud not ever happen        // down 25ms, up 25ms        // key down, bounce 25msrepeat(1251) @(negedge clk) key_in = ~key_in;        // last 60msrepeat(3000) @(negedge clk);        // key up, bounce 25msrepeat(1251) @(negedge clk) key_in = ~key_in;        // wait 50msrepeat(2500) @(negedge clk);        // stop        $stop;    endendmodule

放在最后的,并不一定是最不重要的

对于上面的三种方案,我比较喜欢第三种方案,它更贴合实际的按键状态,以上的代码我都做过modelsim仿真,但还没有在实际的项目中验证。在整理准备这个博客的时候,我又想到了一个感觉是更巧妙的方案,具体是这样的:在第三个方案的基础上,因为按键输入有变化的第一时刻,输出就已经改变了,在这种情况下,我可以把计时的时长改为一个很小的值,该值只要比抖动中的最长高低电平变化时间长即可。但想想也没这个必要,且这个抖动的高低电平变化时长我也很难去给它界定一个值。

stm32 工业按键检测_STM32单片机按键消抖和FPGA按键消抖大全相关推荐

  1. stm32硬件消抖_STM32单片机按键消抖和FPGA按键消抖大全

    原标题:STM32单片机按键消抖和FPGA按键消抖大全 写在前面: 按键去抖:由上图可以看出理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有 ...

  2. STM32白话文教程(三)内置按键模块、外接按键检测 三句话让你玩明白按键 水银倾斜模块

    还不知道检测输入电平信号用什么输入模式? 就简单的记住: 检测端口输入低电平0要用上拉 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 检测端口输入高电平1 ...

  3. 51单片机入门之七:按键检测,单片机如何检测按键输入

    本文旨在介绍单片机入门的基础知识,为初接触或即将接触单片的新手提供一个入门指导.本文章会陆续推出,隔几天一个章节.所使用单片机为ATMEL公司的AT89C52,软件为PROTEUS和KEIL;只提供原 ...

  4. stm32芯片休眠模式_stm32单片机进入休眠模式后无法下载程序等问题的解决

    利用stm32单片机的休眠模式,使单片机间歇的休眠从而实现低功耗的目的.往往会出现进入休眠后无法唤醒,导致下一次程序烧不进去.通常的解决办法是:一般的开发板或单片机最小系统都会有复位键,按住复位键,点 ...

  5. stm32芯片休眠模式_stm32单片机休眠模式后无法程序怎么办

    诈骗stm32单片机的歇眠形式,使单片机间歇的歇眠从而告终低功耗的主意.往往会显现进入歇眠后无法叫醒,导致下一次圭臬烧不进去.时时的办理主意是:寻常的开辟板或单片机最幼编造都邑有复位键,按住复位键,点 ...

  6. stm32无源蜂鸣器定时器_STM32单片机实现无源蜂鸣器产生音乐声的设计

    使用有源蜂鸣器,只能发出固定的"滴滴"声,当然不能满足于此呀.使用无源蜂鸣器,只要输出不同频率的PWM波,即可发出不同的音符.不同的音符组合起来就是一个曲子了. 1 乐谱简析 1. ...

  7. STC15单片机-按键检测单击、双击和长按(状态机)

    按键检测(状态机) 传统的按键检测 在单片机的应用中,利用按键实现与用户的交互功能是相当常见的,同时按键的检测也是很讲究的,众所周知,在有键按下后,数据线上的信号出现一段时间的抖动,然后为低,当按键释 ...

  8. 蓝桥杯嵌入式第三课--LED与按键检测

    前言 纵观多年考题,LED和按键检测作为必考的一个部分同时也作为GPIO的重点考察内容一直都是我们必须要掌握的部分.本节课带着大家,从底层硬件开始,把GPIO的这两个考点学的清清楚楚! 点亮LED! ...

  9. 【STC8A8K64S4A12开发板】—4x4矩阵按键检测

    版权声明:本文为博主原创文章,转载请附上原文出处链接. 文章目录 前言 一.硬件电路设计 1.矩阵按键检测介绍 2.矩阵按键检测原理介绍 二.软件设计 1.矩阵按键扫描实验 – 指示灯闪烁 1.1.工 ...

最新文章

  1. 启发下一代AI研究,《认知神经智能科学》专刊发布[附资料下载]
  2. MFC界面库BCGControlBar v25.0新功能详解六:属性网格和其他
  3. studio 3t 在ubuntu中使用_在ubuntu中使用ufw配置防火墙
  4. 【实验】配置VRRP负载分担
  5. 有关数据库MySQL的演讲_有关Mysql数据库编程的文章推荐10篇
  6. Java基础学习总结(90)——Java单元测试技巧
  7. (转帖)無號數及有號數的乘加運算電路設計(Verilog)
  8. mysql中如何将一个表中的部分记录合并,mysql - 如何从一个表中获取所有产品并从另一个包含多行的表中合并一行? - SO中文参考 - www.soinside.com...
  9. Java中的关键字汇总(50个)
  10. android反编译干嘛,安卓反编译流程大解析 看完你就懂了!
  11. kux播放器android,KUX转换器
  12. 关于伺服驱动控制的课本_伺服电机的PLC控制详解
  13. 国考银保监会计算机类笔试,银保监会(计算机类)笔试资料(含2018-2019真题).zip...
  14. bootstrap组件——导航条
  15. 设备树基本语法及属性分析
  16. 销售的几种层次-销售笔记
  17. 浅谈ipad阅读类应用设计
  18. 程序员视角的计算机系统 第一章 计算机系统之旅
  19. 测试面试挫败_清晰度,初级工程师,要求和挫败感
  20. JDK动态代理底层源码剖析

热门文章

  1. SpringMvc自定义参数绑定
  2. HTTP协议与HTTPS的区别
  3. Spring MVC 的配置安装
  4. 使用SQL生成SQL语句时单引号的转义处理之q'{}'方法
  5. sudo rm /var/cache/apt/archives/lock sudo rm /var/lib/dpkg/lock
  6. 【记】接口自动化测试,完整入门篇
  7. 某些数组和字符串类型转换(转)
  8. 洛谷 P2515 [HAOI2010]软件安装 解题报告
  9. 《C++ Primer》读书笔记—第六章 函数
  10. oracle创建表空间语句分解