写在前面:

按键去抖:由上图可以看出理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有关,一般为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 // variable reg [20:0] cnt; reg key_cnt; // debounce time passed, refresh key state always @(posedge clk or negedge nrst) begin if(nrst == 0) key_out <= 0;        else if(cnt == TIME_20MS - 1)            key_out <= key_in;    end // while in debounce state, count, otherwise 0 always @(posedge clk or negedge nrst) begin if(nrst == 0)            cnt <= 0;        else if(key_cnt)            cnt <= cnt + 1'b1; else            cnt <= 0;    end // always @(posedge clk or negedge nrst) begin if(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) begin if(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;    end always @(posedge clk or negedge nrst) begin if(nrst == 0)            cnt <= 0;        else if(key_cnt) begin if(key_out == key_in)                cnt <= 0;            else                cnt <= cnt + 1'b1; end else            cnt <= 0;    end always @(posedge clk or negedge nrst) begin if(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 test reg key_cnt; reg [20:0] cnt; always @(posedge clk or negedge nrst) begin if(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;    end always @(posedge clk or negedge nrst) begin if(nrst == 0)            cnt <= 0;        else if(key_cnt)            cnt <= cnt + 1'b1; else            cnt <= 0;    end always @(posedge clk or negedge nrst) begin if(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 parameter localparam T = 20; // variable reg clk, nrst; reg key_in; wire key_out; // instantiate debounce uut( .clk (clk ), .nrst (nrst ), .key_in (key_in ), .key_out(key_out) ); // clock initial begin clk = 1; forever #(T/2) clk = ~clk; end // reset initial begin nrst = 1; @(negedge clk) nrst = 0; @(negedge clk) nrst = 1; end // key_in initial begin // initial value key_in = 0; // wait reset repeat(3) @(negedge clk); // no bounce // key down key_in = 1; // last 60ms repeat(3000) @(negedge clk); // key up key_in = 0; // wait 50ms repeat(2500) @(negedge clk); // down 5ms, up 15ms // key down, bounce 5ms repeat(251) @(negedge clk) key_in = ~key_in; // last 60ms repeat(3000) @(negedge clk); // key up, bounce 15ms repeat(751) @(negedge clk) key_in = ~key_in; // wait 50ms repeat(2500) @(negedge clk); // down 19ms, up 19ms // key down, bounce 19ms repeat(951) @(negedge clk) key_in = ~key_in; // last 60ms repeat(3000) @(negedge clk); // key up, bounce 19ms repeat(951) @(negedge clk) key_in = ~key_in; // wait 50ms repeat(2500) @(negedge clk); // additional, this situation shoud not ever happen // down 25ms, up 25ms // key down, bounce 25ms repeat(1251) @(negedge clk) key_in = ~key_in; // last 60ms repeat(3000) @(negedge clk); // key up, bounce 25ms repeat(1251) @(negedge clk) key_in = ~key_in; // wait 50ms repeat(2500) @(negedge clk); // stop $stop; endendmodule

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

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

单片机消抖c语言程序,单片机中按键消抖程序相关推荐

  1. 小程序开发过程中常见问题[微信小程序、支付宝小程序]

    小程序开发过程中常见问题[微信小程序.支付宝小程序] 正文 一.样式中如何使用background-image呢? background-image支持网络的图片链接或者base64 二.使用自适应单 ...

  2. 程序员中一些老的程序员去哪了

    程序员中一些老的程序员去哪了 一.总结 一句话总结: 身边 老师 改行 发展 升职 公司 文中对应内容: 我的回答:就在您的身边.我迅速在脑子里过了一遍,成都SAP研究院每个敏捷开发小组都有至少两到三 ...

  3. 自学python单片机编程-用Python语言控制单片机

    早年,虽然Python是一款比较容易上手的脚本语言,而且有强大的社区支持,一些非计算机专业领域的人都选它作为入门语言.遗憾的是,它不能实现一些非常底层的操控,所以在硬件领域并不起眼.本文引用地址:ht ...

  4. 自学python单片机编程-用Python语言控制单片机-可编程逻辑-与非网

    早年,虽然 Python 是一款比较容易上手的脚本语言,而且有强大的社区支持,一些非计算机专业领域的人都选它作为入门语言.遗憾的是,它不能实现一些非常底层的操控,所以在硬件领域并不起眼. 然而今时不同 ...

  5. pic单片机配置字c语言,PIC单片机配置字说明及使用.pdf

    PIC单片机配置字说明及使用 PIC 单片机器件配置字简介和使用 在PIC系列单片机里,其芯片内部大都有设置一个特殊的程序存储单元,地 址为2007 ,由单片机的用户自由配置,用来定义一些单片机功能电 ...

  6. 单片机最小系统c语言报告,单片机最小系统实验报告.doc

    长沙学院 电子工艺实习报告 姓名: 潘舟 学号: 2009044211 系别: 电子与通信工程 时间: 2011.11.14-2011.11.25 专业: 电子信息工程 指导老师:龙英.刘辉.王新辉. ...

  7. 51单片机8255扩展c语言,51单片机8255A扩展IO口

    <51单片机8255A扩展IO口>由会员分享,可在线阅读,更多相关<51单片机8255A扩展IO口(23页珍藏版)>请在人人文库网上搜索. 1.一个芯片可扩展多个并行I/O口 ...

  8. Linux驱动中按键消抖原理

    为什么要用定时器来做按键消抖? 用到按键就要处理因为机械结构带来的按键抖动问题,也就是按键消抖.前面的实验中都是直接使用了延时函数来实现消抖,因为简单,但是直接用延时函数来实现消抖会浪费 CPU 性能 ...

  9. Verilog中按键消抖检测的实现

    Verilog按键消抖是FPGA学习时的一个入门教程,为避免眼高手低,还是再次分析与记录一下.此处着重介绍按键消抖的基本原理,对按键消抖与检测的关键技术进行分析,并进行功能仿真. 一.按键消抖基本原理 ...

  10. 用linux如何用vi编写c程序,linux中VI编写C程序。。。

    在linux中编写C程序时不像编写shell那样开头要#!/bin/bash,但是在C程序中要指定头文件(头文件是指输入输出,宏等,而且要首先声明,也是必须要开始就声明的) 写好C代码后要给C文件赋予 ...

最新文章

  1. opencv-视频处理-实时的前景检测-Vibe算法
  2. 阿里云邀您参加2020年数据湖高峰会议
  3. HDU 2588 GCD(欧拉函数)
  4. java关闭websocket_关闭代码1006关闭websocket的原因
  5. layuimini 图标颜色便捷指定
  6. springmvc处理器及前端控制器介绍
  7. halcon视觉框架源码_图像处理与机器视觉初学者学习路线
  8. 【Python学习笔记】列表生成式和生成器
  9. 四阶龙格库塔法的基本思想_经典四阶龙格库塔法解1阶微分方程组.doc
  10. mfc 调用绿色版的Foxit Reader / 迷你pdf阅读器.exe的同时打开一个pdf文件
  11. Android 热修复Bugly和Sophix介绍
  12. C语言——输入两个正整数m和n,求其最大公约数和最小公倍数
  13. linux的系统监视器图片_用Jetson Nano构建一个价值60美元的人脸识别系统
  14. Ubuntu 12.04 常用三种输入法及键盘错乱的调整
  15. vue run dev报错 缺少package.json文件、missing dev 命令 解决办法
  16. 2016,初入江湖,尚未见刀光剑影
  17. IPTV和OTT概念,这几个你必须知道
  18. height:100%和height:100vh的区别
  19. 微信小程序「柒留言」 — 无留言公众号开通微信公众号留言功能(建议收藏)
  20. 列车实时数据通信协议(TRDP)探索之路【三】

热门文章

  1. 手机锁屏后微信收款语音不播报?-by:nixs
  2. python数据清洗入门教程(完整版)
  3. 企业生存与发展的前提是安全
  4. java判断字符是否为0_Java判断字符串是否为空
  5. 多元函数的极限、连续
  6. 485芯片RO 口收不到信号问题分析
  7. HomeBrew 镜像加速
  8. 自带win10的笔记本电脑如何装win7
  9. MAC本地修改mysql数据库密码
  10. 【MAPBOX基础功能】12、mapbox点击点位图层高亮指定的点位