0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

STM单片机中的按键消抖和FPGA消抖

454398 来源:alpha007 作者:alpha007 2022-11-30 17:39 次阅读

写在前面:

按键去抖:理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有关,一般为 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 仿真,但还没有在实际的项目中验证。在整理准备这个博客的时候,我又想到了一个感觉是更巧妙的方案,具体是这样的:在第三个方案的基础上,因为按键输入有变化的第一时刻,输出就已经改变了,在这种情况下,我可以把计时的时长改为一个很小的值,该值只要比抖动中的最长高低电平变化时间长即可。但想想也没这个必要,且这个抖动的高低电平变化时长我也很难去给它界定一个值。

审核编辑黄昊宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • FPGA
    +关注

    关注

    1611

    文章

    21396

    浏览量

    595912
  • 单片机
    +关注

    关注

    6012

    文章

    44173

    浏览量

    624652
  • STM
    STM
    +关注

    关注

    1

    文章

    555

    浏览量

    42141
  • 按键消抖
    +关注

    关注

    2

    文章

    23

    浏览量

    10392
收藏 人收藏

    评论

    相关推荐

    单片机按键与几种按键电路

    按键电路 一、 硬件按键电路控制电路 所示利用RC 积分电路来达成杂波的滤除与波形修整的
    的头像 发表于 12-17 07:45 10.6w次阅读
    <b class='flag-5'>单片机</b>的<b class='flag-5'>按键</b><b class='flag-5'>消</b><b class='flag-5'>抖</b>与几种<b class='flag-5'>按键</b>电路

    fpga教程之——按键

    电平,这次对按键进行操作则是对FPGA进行输入了。  2. 按键  2.1 按键输入原理  
    发表于 02-27 11:49

    按键

    请问大家的按键是用什么方法解决的,如普通的按键如何
    发表于 09-26 22:17

    FPGA按键的方法

    FPGA按键的方法
    发表于 05-01 16:12

    技术分享:明德扬按键的原理和基于fpga设计

    本帖最后由 明德扬吴老师 于 2017-8-2 11:45 编辑 按键1功能概述按键开关是各种电子设备不可或缺的人机接口,如电脑的键盘等。实际应用
    发表于 08-02 10:38

    51单片机_独立按键延时

    51单片机_独立按键延时_独立按键定时器_矩
    发表于 07-16 13:56

    按键及原理是什么

    浅谈:在设计单片机按键输入的时候,进行按键是防止按键输入被CPU误读多次的必要手段。一、
    发表于 07-21 06:02

    MCU按键问题

    按键问题机械按键是必须的,1、延时
    发表于 11-04 06:37

    按键延时的方法和目的分别是什么

      按键的延时是初学单片机的必经之路,因为只要是机械开关所传递的信号,都会存在波动,有时这些波动是"致命"的,所以消除其影响就是一门手艺了。硬件
    发表于 01-17 06:26

    STM32单片机按键FPGA按键的相关资料分享

    写在前面:STM32单片机按键FPGA按键
    发表于 01-18 06:39

    基于FPGA按键电路设计

    采用了VHDL语言编程的设计方法,通过FPGA来实现按键的硬件电路。论述了基于计数器、RS触发器和状态3种方法来实现
    发表于 12-05 14:13 224次下载

    单片机按键程序

    效率上来说,延时花费时间在无意义延时上,而相对较好的定时轮询还是不可避免的在轮询,而现在这种方式完全是中断性质的。唯一多出的开销(全局时间戳)并不是只可以用于按键
    的头像 发表于 06-19 08:55 3.1w次阅读
    <b class='flag-5'>单片机</b><b class='flag-5'>中</b><b class='flag-5'>按键</b><b class='flag-5'>消</b><b class='flag-5'>抖</b>程序

    51单片机的独立按键按键及矩阵按键的电路与程序免费下载

    本文档的主要内容详细介绍的是51单片机的独立按键按键及矩阵按键的电路与程序免费下载。
    发表于 07-26 17:36 28次下载
    51<b class='flag-5'>单片机</b>的独立<b class='flag-5'>按键</b>和<b class='flag-5'>按键</b><b class='flag-5'>消</b><b class='flag-5'>抖</b>及矩阵<b class='flag-5'>按键</b>的电路与程序免费下载

    使用51单片机实现按键的资料和程序免费下载

    抖动是机械按键存在的现象,是必须要进行处理的。一般处理有两种方式:一种是硬件,另一种是软件。硬件
    发表于 07-05 17:41 5次下载
    使用51<b class='flag-5'>单片机</b>实现<b class='flag-5'>按键</b><b class='flag-5'>消</b><b class='flag-5'>抖</b>的资料和程序免费下载

    单片机按键电路图免费下载

    本文档的主要内容详细介绍的是单片机按键电路图免费下载。
    发表于 07-01 08:00 10次下载
    <b class='flag-5'>单片机</b><b class='flag-5'>按键</b><b class='flag-5'>消</b><b class='flag-5'>抖</b>电路图免费下载