[FPGA 学习记录] 按键消抖

Arduino程序设计(四)按键消抖+按键计数 本文主要介绍两种按键控制LED实验:第一种是采用软件消抖的方法检测按键按下的效果;第二种是根据按键按下次数,四个LED灯呈现不同的流水灯效果。 阅读详情
按键消抖


在前面几章节当中,我们讲解了计数器、分频器,那么分频器又讲解了偶数分频和奇数分频。那么在本章节当中,我们来讲一下按键的消抖。

按键消抖的内容分为两个部分:理论学习和实战演练。

那么在理论学习部分,我们会对按键消抖的相关知识做一个讲解;在实战演练部分我们通过实验工程,设计并实现一个具有按键消抖功能的电路。

那么首先是理论学习

1 理论学习

按键
按键是我们最为常见的电子元件之一,在电子设计当中应用广泛,在我们的日常生活中也经常见到,比如说:遥控器、玩具、计算器等等电子产品中都使用按键。

在我们 FPGA 的实验工程当中,我们可以使用其作为系统复位信号或者说控制信号的外部输入。

那么目前按键的种类有很多,常见的有自锁式按键,实物如下图1

自锁按键

还有机械按键,实物如下图2

轻触按键

机械按键又称为机械弹性开关。

在我们征途系列的开发板当中也使用了按键,比如说:我们的电源开关使用的就是自锁式按键

image-20231101064222941

我们的复位按键还有控制信号输入的按键都使用的是机械弹性开关也就是机械按键

image-20231101064440349

按键消抖

那么按键消抖主要针对的是机械弹性开关,就是机械按键。当我们的机械触点断开和闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定的导通,在断开时也不会一下子断开。

因而在闭合及断开的瞬间均伴随有一连串的抖动,如下图3

image-20231101163526618

那么按键没有按下时为高电平,按下时为低电平。那么按下的瞬间与松开的瞬间都有一系列的抖动,按键按下到键稳定就是前抖动,按键从键稳定到按键释放就是后抖动。

那么为了保证系统能够正确的识别按键的开关,就必须对按键的抖动进行处理,那么处理就是按键的消抖。

硬件消抖与软件消抖

那么按键的消抖分为硬件消抖和软件消抖。那么如果按键的个数比较少,可以用硬件方法消除按键抖动,就是硬件消抖;使用 RS 触发器就是常用的硬件去抖方法,就是下面这个电路图所示4

image-20231101171920206

如果按键的个数比较多时,我们常常使用软件消抖的方法。在我们的实战演练当中,就是要使用软件消抖的方式来实现按键的消抖。

2 实战演练

2.1 实验目标

我们的目标是设计并实现一个按键消抖模块,将外部输入的单比特按键信号做消抖处理后进行输出,那么输出的信号可以正常的被其他的模块调用。

我们为什么会选择软件消抖的方式呢?因为硬件消抖会使用一些额外的器件,占用我们电路板上的空间,从而在一定程度上增加了 PCB 布局布线的复杂度,所以说我们选用软件消抖的方式来实现去抖动的操作。去抖动后的效果是:当按键按下后,能够准确的检测到我们的按键被按下了一次,而不会因为抖动发生重复多次按下的一个现象。

2.2 程序设计

那么了解了实验目标之后,我们开始程序的设计。

先来搭建我们的文件体系

oNF1ONMEnM

然后打开 doc 文件夹,新建一个 Visio 文件,用来绘制我们的模块框图和波形图

twcnTDuttz

2.2.1 模块框图

下面开始模块框图的绘制

20231101073959_UeC6LZzXIT

如何来实现我们的软件消抖呢?我们打算使用延时程序来去除掉抖动的部分。

既然要通过计数过滤掉按键抖动的时间,所以说计数器是必不可少的,我们的设计当中一定有时序电路,所以说输入信号一定是时钟信号和复位信号;那么除了时钟信号和复位信号之外,还有第三路输入信号,就是我们输入的、没有经过消抖处理的按键信号,把它命名为 key_in

20231101074238_EqsVN1PiDg

那么这样输入信号就表示完成,下面就是输出信号。输出信号就是经过消抖处理后的稳定的按键信号,我们把它命名为 key_flag

20231101074416_sla8YlhC7K

那么这样,模块框图绘制完成,开始波形图的绘制。

2.2.2 波形图绘制

首先是时钟信号的波形;然后是我们复位信号的波形,系统上电后让它保持一段时间的低电平,然后让它一直保持为高电平

20231101074653_ClOOKXdZQT

下面开始绘制输入信号 key_in 的波形。我们要模拟真实的情况,绘制出带抖动的波形。首先,按键没有被按下时是保持高电平;那么按键按下后会产生抖动,我们来模拟一下抖动,抖动其实就是不规则的高低电平变化,那么这一段就是我们模拟产生的前抖动

20231101075119_xehtVY0e2l

那么前抖动完成之后,保持一段时间的稳定的低电平;接下来是后抖动;后部抖动过后就保持原来的高电平

20231101075335_Ca0gwsyo2G

前抖动和后抖动它的保持时间大概是 5ms~10ms 然后中间的稳定时间大概是 20ms

image-20231101080243860

前抖动与后抖动就像我们波形图当中绘制的这些毛刺一样,那么毛刺中会有高电平、低电平的情况,但是因为是机械抖动的原因,低电平会很快地变成高电平,如果我们把其中的每次的低电平和高电平都采集到,那么就相当于我们的按键被按下了好多次,不是我们想要的一次。所以说,我们一定要把这段前抖动和后抖动滤除掉;这段抖动的时间,我们通过前面的分析是已知的,前抖动和后抖动它的时间范围大概是 5~10ms 也就是小于 10ms;中间的稳定状态大于 20ms。如果说:当有 20ms 的时间内都没有产生抖动,那么这个按键就处于一个稳定的状态;换句话说就是:只要在 20ms 的时间内没有抖动的产生,就可以认为按键信号是可以被使用的。在这里我们需要做的就是,找到最后一次抖动的时间,是在什么时候开始的,才能开启这 20ms 的计数;否则在 20ms 之内都不能保证是我们的稳定时间。

那么有的朋友可能会说:如果在单片机的设计当中,我们都是检测第一次按键为低电平时开始计数,然后我们可以进行一段 10ms+20ms 的计数,经过一段大于 30ms 的延时之后,我们再检测按键电平,那么它就是稳定的按键信号。难道这种方式不行吗?这种方式当然是可以的,但不是最好的,因为这样会浪费我们不必要的时间;虽然我们的抖动时间理论上不会大于 10ms 但是它具体的时间是不确定的,因为我们每次按下按键时前抖动的时间都是不确定的;如果说,我们每次都按照最大抖动时间 10ms 来计算,这样就会多考虑一些时间。

我们这儿决定采用一种更节约时间的方法。我们这里添加一个计数器,它计数的时间间隔是 20ms

20231101084319_7vF3WpK4JR

每当系统检测到按键输入信号为低电平时,它就开始计数;在计数器计数期间,如果再次检测到高电平就说明:上一次检测到的低电平一定是一个抖动,那么我们就将这个计数器进行清零。总结为一句话就是:当系统检测到按键为低电平时候,我们的计数器就进行计数,当检测到按键为高电平时,它就进行清零。

那么讲到这里,主要的问题我们已经解决了。下面就要考虑计数器的计数个数、计数器计满之后该怎么处理,以及要考虑我们的输出信号 key_flag 什么时候拉高、什么时候拉低的问题。

那么首先是我们计数器计数值的问题。我们晶振的时钟频率是 50MHz 换算成为时间单位是 20ns 即时钟周期是 20ns,我们计数时间是 20ms 我们的计数值用 M 来表示,经过计算: M = ( 20 ms / 20 ns ) − 1 = 1 × 1 0 6 − 1 = 999999 \text{M} = \left(20\text{ms} / 20\text{ns}\right) - 1 = 1 \times 10^6 - 1 = 999999 M=(20ms/20ns)−1=1×106−1=999999 那么计数器的计数最大值已经确定了。

下面就要考虑计数器清零的问题。我们前面使用计数器过程中,都是计数器计满之后习惯性地先清零;我们这儿先习惯性地清零,如果有问题,后面根据分析再进行修改。

下面开始绘制计数器的波形

20231101090743_YkmBadYk2f

计数器的初值是 0 在时钟信号的上升沿,检测到输入信号 key_in 是低电平就进行计数,检测到高电平清零。

下面是输出信号波形的绘制。我们这儿就要考虑,我们的输出信号 key_flag 什么时候拉高的问题。我们的输出信号 key_flag 是一个脉冲信号,也就是说,只有一个时钟周期是高电平,当我们的计数器计数到最大值 999999 的时候,拉高一个时钟周期。

我们先来绘制一下波形图

20231101091037_EcVwUtrIq0

首先它初值为低电平;当计数到最大值,保持一个时钟周期的高电平。

那么输出信号的波形绘制完成。

那么大家肯定发现了一个问题:如果说我们的稳定时间足够长,它的低电平时间会远远大于 20ms,这样我们的计数器就会进行多次清零,会出现多个最大值,这样就导致了我们的输出信号 key_flag 产生多次脉冲。这显然不是我们想要的结果,下面就要分析,这种情况是计数器清零的问题导致的,还是我们的 key_flag 信号拉高时间的问题导致的。

那么如果说,我们的 key_flag 信号不在最大值拉高,在其他位置,比如说在 999000 位置拉高,那么同样的也会出现这种问题;那么我们只能怀疑,我们的计数器清零条件是不对的。

在刚刚开始的时候,我们的计数器清零条件已经明确了:就是当输入信号 key_in 为高电平时候,对它进行清零。那么这里我们就让我们的计数器计满之后保持最大值,不清零;等待输入的信号为高电平时,再进行清零。

我们来修改一下我们波形图

20231101091626_1lluklx7pB

什么意思呢?计数到最大值 999999 的时候不再进行清零,保持我们的最大值;只有在输入信号为高电平时,才对计数器进行清零。

那么根据计数器的波形,我们修改一下输出信号的波形。当计数到最大值让它保持高电平,那么这样,它的波形就应该是这样的

20231101091833_TZ3Vi7N90A

那么修改了计数器的清零之后,我们可以发现:我们的 key_flag 信号确实不会产生多个了。但是出现了一个新的问题:我们的 key_flag 信号不是脉冲信号了,而是一个长长的高电平信号了,这也不是我们想要的结果。

那么根本的原因就是:我们的计数器计数到最大值,保持在最大值时间太长导致的。针对这种情况我们想到:如果说,我们的计数器计数到最大值减一也就是 999998 的时候,拉高我们的 key_flag 信号,那么其他时刻让它保持低电平,这样就能保证我们的 key_flag 信号是一个脉冲信号,而且拉高了一次;因为这样,计数器计数到最大值减一只有一次,而且最大值与最大值减一是非常接近的,这样就得到了我们需要的波形图

20231101092227_JLTQbP2Yku

2.2.3 代码编写

那么波形图绘制完成之后,下面开始代码的编写

20231101092508_36cWNbNa4G

我们参照我们的波形图,编写我们的代码。首先是模块开始、模块名称、端口列表,然后是模块结束;那么输入信号有三路:时钟信号、复位信号和未消抖的按键信号;输出信号只有一路:就是经过消抖处理后的 按键脉冲信号

83SHybEImG

那么端口列表编写完成,下面开始声明变量。首先是我们的计数器变量,它的位宽是多少我们来算一下 999999 需要 20 个位宽也就是 [19:0],因为后面赋值语句是 always 语句,所以说它的变量类型是 reg 型

r1n5qr76ta

下面开始对计数器进行赋值,使用异步复位;当我们的复位信号有效时,给我们的计数器赋一个初值为 0;当我们输入的按键信号为高电平时候对它进行清零。

那么这里为了方便代码的编写,可以声明一个参数 CNT_MAX 这儿定义最大值是十进制的 999999 继续我们计数器的赋值。当我们的输入信号为高电平,我们计数器清零;当我们的计数器计数到最大值,然后让它保持这个最大值;如果说复位信号无效,输入的按键信号是低电平,而且没有计数到最大值,让它自加一

zJGT4pfpi6

那么下面开始输出信号的赋值,我们同样使用 always 语句。当我们的复位信号有效时,给它赋一个初值就是低电平;如果说,当我们的计数器计数到最大值减一,让它保持一个时钟周期的高电平;那么其他时刻是低电平

YwDs340WBj

那么这样代码编写完成,我们保存

key_filter.v

module key_filter
#(
    parameter CNT_MAX = 20'd999_999;
)
(
    input   wire        sys_clk     ,
    input   wire        sys_rst_n   ,
    input   wire        key_in      ,
    
    output  reg         key_flag
);

reg [19:0]  cnt_20ms;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        cnt_20ms <= 20'd0;
    else if (cnt_20ms == CNT_MAX)
        cnt_20ms <= CNT_MAX;
    else if (key_in == 1'b1)
        cnt_20ms <= 20'd0;
    else
        cnt_20ms <= cnt_20ms + 20'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else if (cnt_20ms == (CNT_MAX-1))
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

2.2.4 代码编译

回到我们的桌面,建立我们的实验工程

kzbmGb5afM

然后添加我们编写的代码,进行全编译;出现报错信息,更正代码后

key_filter.v

module key_filter
#(
    parameter CNT_MAX = 20'd999_999
)
(
    input   wire        sys_clk     ,
    input   wire        sys_rst_n   ,
    input   wire        key_in      ,
    
    output  reg         key_flag
);

reg [19:0]  cnt_20ms;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        cnt_20ms <= 20'd0;
    else if (cnt_20ms == CNT_MAX)
        cnt_20ms <= CNT_MAX;
    else if (key_in == 1'b1)
        cnt_20ms <= 20'd0;
    else
        cnt_20ms <= cnt_20ms + 20'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else if (cnt_20ms == (CNT_MAX-1))
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

再次编译,那么编译通过有 7 个警告,我们点击 OK

20231101094807_9dC8KqYo1O

那么代码编译通过之后,我们编写我们的仿真代码。

2.2.5 仿真验证

2.2.5.1 仿真文件编写

首先是时间参数,然后是模块开始、模块名称、端口列表(空的)、结束

lE4GNUbnF7

然后声明我们的变量。然后是我们的按键输入信号,然后使用 wire 型,将消抖后的按键信号引出

20231101095443_nyCDVRh49L

首先是赋初值,我们使用 initial 语句

20231101095602_VfKAJttf50

下面是模拟产生我们的按键输入信号,那么按键输入信号怎么产生呢?

首先,它的初值应该是一个高电平,然后模拟一段时间的抖动就是前抖动,然后再有一段时间是低电平就是稳定状态,然后,后面会有一段时间的抖动就是后抖动;那么后抖动完成之后,就保持它的高电平。这样就模拟一次按键按下。

那么高低电平直接赋值就可以,那么抖动怎么产生呢?我们可以使用随机数。那么具体抖动的时间和低电平保持的时间怎么确定呢?我们使用计数器

这儿还需要再声明一个计数器。那么我们使用计数器来进行一个周期的计数,模拟一次按键按下。那么计数的最大值,我们暂定为 250 次;那么它的位宽应该是多少呢?那么它的位宽是 8 位宽。

首先开始给计数器进行赋值,我们使用 always 语句,当我们的复位信号有效时给它一个初值 0,当它的最大值是 249,也就是说它完成了 250 次计数我们给它归零,那么如果上面两个条件都不满足,让它不断加一,就是进行计数

20231101101702_0UdQ8oi2RG

我们可以使用这个计数器来控制我们模拟按键信号的产生,那么怎么产生呢?

我们来这样产生:我们的复位信号有效时,给它一个初值,初值要给它一个高电平;下面就使用我们的计数器对我们的 key_in 进行赋值

20231101102000_jy3g9IuE8e

这是我们的计数器,它计数的初值是 0 最大值是 249 它完成了 250 次计数。在这一个完整的计数周期内我们分为五段

20231101102607_N8IxFtZRN5

当我们的计数器计数范围在 0~19 时,让我们的 key_in 保持一个高电平;计数范围在 19~69 之间,我们使用随机数产生一个前抖动;当计数范围在 69~149 之间让它保持一个低电平,也就是我们的稳定状态;当我们的计数范围在 149~169 之间,让它产生后抖动;当计数范围在 169~249 之间,保持一个高电平。这样我们的 key_in 信号就可以模拟一次完整的一个按键过程。

我们来继续编写代码。当计数器大于等于 19 并且计数器小于等于 69 或者它大于等于 149 小于等于 169,那么当它满足这个条件时,我们给我们的 key_in 赋值一个随机数,模拟抖动;当我们的计数器的范围小于 19 大于 169 时,让它保持高电平;那么剩下的情况就是,我们的计数器计数范围处于 69 与 149 之间,我们让它保持一个低电平,模拟我们稳定的一个状态

20231101103439_V7Snv8ksO2

还需要生成时钟信号 sys_clk

20231101103918_oLSdAnNvVQ

那么这样三路模拟输入信号都已经编写完成。

那么下面就要开始我们的实例化。

那么这儿减小参数,给它一个 24 相当于计数 25

20231101104135_AhlCNNbWjR

这样仿真文件编写完成,我们保存

tb_key_filter.v

`timescale 1ns/1ns

module tb_key_filter();

reg         sys_clk;
reg         sys_rst_n;
reg         key_in;

reg [7:0]   tb_cnt;

wire        key_flag;

initial
    begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #20
        sys_rst_n <= 1'b1;
    end

always #10 sys_clk = ~sys_clk;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        tb_cnt <= 8'd0;
    else if (tb_cnt == 8'd249)
        tb_cnt <= 8'd0;
    else
        tb_cnt <= tb_cnt + 8'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        key_in <= 1'b0;
    else if ((tb_cnt<20'd19) || (tb_cnt>20'd169))
        key_in <= 1'b1;
    else if ((tb_cnt>=20'd19)&&(tb_cnt<=20'd69)
            ||(tb_cnt>=20'd149)&&(tb_cnt<=20'd169))
        key_in <= {$random} % 2;
    else
        key_in <= 1'b0;

key_filter
#(
    .CNT_MAX (20'd24)
)
key_filter_inst
(
    .sys_clk  (sys_clk  ),
    .sys_rst_n(sys_rst_n),
    .key_in   (key_in   ),
    
    .key_flag (key_flag )
);

endmodule

回到我们的实验工程,加载我们的仿真文件;我们的仿真文件添加完成之后,我们先进行一下全编译,查看一下我们的语法错误

20231101104609_NUUrkxSKD7

编译完成点击 OK。更正一下仿真代码

20231101104854_DHuYGFohxH

tb_key_filter.v

`timescale 1ns/1ns

module tb_key_filter();

reg         sys_clk;
reg         sys_rst_n;
reg         key_in;

reg [7:0]   tb_cnt;

wire        key_flag;

initial
    begin
        sys_clk = 1'b1;
        sys_rst_n <= 1'b0;
        #20
        sys_rst_n <= 1'b1;
    end

always #10 sys_clk = ~sys_clk;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        tb_cnt <= 8'd0;
    else if (tb_cnt == 8'd249)
        tb_cnt <= 8'd0;
    else
        tb_cnt <= tb_cnt + 8'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        key_in <= 1'b0;
    else if ((tb_cnt<8'd19) || (tb_cnt>8'd169))
        key_in <= 1'b1;
    else if ((tb_cnt>=8'd19)&&(tb_cnt<=8'd69)
            ||(tb_cnt>=8'd149)&&(tb_cnt<=8'd169))
        key_in <= {$random} % 2;
    else
        key_in <= 1'b0;

key_filter
#(
    .CNT_MAX (20'd24)
)
key_filter_inst
(
    .sys_clk  (sys_clk  ),
    .sys_rst_n(sys_rst_n),
    .key_in   (key_in   ),
    
    .key_flag (key_flag )
);

endmodule

进行仿真设置

20231101105048_r1OXnXQh1E

开始仿真

20231101105251_fo4y3tWk8Q

那么仿真编译完成之后,打开 sim 窗口,添加我们的模块波形,然后回到波形界面,全选、分组、消除路径;然后点击 Restart 运行 10us 查看全局视图

20231101134332_x5Ay6Tb4y4

我们来参照着我们的波形图看一下我们的仿真波形。

2.2.5.2 仿真波形分析

首先是模拟的时钟和复位信号

image-20231101135413699

这个应该是没有问题的。

然后看一下我们仿真文件当中的计数器

image-20231101142117068

那么计数器它的初值是 0 每个时钟周期加一,它的最大值是 249,没有问题;计数到最大值清零,重新进行计数。

我们缩小来看一下我们模拟生成的按键信号

20231101142636_bOimAbq0jB

当我们的计数值在 0~19 计数范围内,那么这个 key_in 是保持高电平;在 19~69 这个范围内它是一个随机数,模拟的是前部抖动;那么 69~149 是模拟的稳定状态,没有问题;那么 149~169 之间是模拟的后部抖动;那么 169 到最大值,就是保持高电平。这样我们模拟输入的信号,它的波形是正确的。

那么下面看一下我们的模块它的波形,首先是我们的计数器

20231101143736_3BHwn2nURM

我们的计数器初值为 0 当按键输入信号是高电平它就一直是 0;当按键信号输入为低电平,它进行计数;高电平又归零;这儿全部是低电平,然后进行计数,计数到最大值 24 就保持;当我们的输入信号为高电平时候,它本应该再次归零,计数器的波形这里出现问题了,回到代码修改

zyfDxDn4rQ

key_filter.v

module key_filter
#(
    parameter CNT_MAX = 20'd999_999
)
(
    input   wire        sys_clk     ,
    input   wire        sys_rst_n   ,
    input   wire        key_in      ,
    
    output  reg         key_flag
);

reg [19:0]  cnt_20ms;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        cnt_20ms <= 20'd0;
    else if (key_in == 1'b1)
        cnt_20ms <= 20'd0;
    else if (cnt_20ms == CNT_MAX)
        cnt_20ms <= CNT_MAX;
    else
        cnt_20ms <= cnt_20ms + 20'd1;

always@(posedge sys_clk or negedge sys_rst_n)
    if (sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else if (cnt_20ms == (CNT_MAX-1))
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

这时计数器的波形与我们绘制的波形图是完全吻合的。

下面看一下输出标志信号

20231101144912_RZ9qAEgiBJ

当我们的计数器计数到最大值减一的时候,在下一个时钟周期让我们的输出信号保持一个时钟周期的高电平,这儿是没有问题的,我们的输出信号也是正确的。

这样我们的仿真波形与我们绘制的波形图是对应的,仿真验证通过。

按键消抖这个工程不再进行上板验证。

参考资料:

19-第十六讲-按键消抖(一)

20-第十六讲-按键消抖(二)

13. 按键消抖模块的设计与验证


  1. 图片来源:立创商城 ↩︎

  2. 图片来源:立创商城 ↩︎

  3. 图片参考来源:手把手教你学51单片机-C语言版.pdf、图 19‑2 机械按键抖动原理图 ↩︎

  4. 图片参考来源:图 19‑3 硬件消抖原理 ↩︎

按键消抖(Verilog&Vivado) 按键作为基本的人机输入接口,由于其机械特性,在按键按下或松开的时候,都是会有抖动的。按键小豆的方式有很多。我的方法是通过计时来消抖,通过一个计数器,当按键输入有变化时,计数器清零,否则就累加,直到加到一个预定值,就认为按键稳定,输出按键值,这样就得到了没有抖动的按键值。 阅读详情

相关推荐

用RS485实现fpga控制89c51上的led

2019年10月26日晚第一次实现了上位机控制下位机的操作 可惜的是第一次的“hello world”和点亮的第一个led时间没有记录。 实现功能:通过fpga上的按键控制51上led的亮灭。 FPAG 的模块图 首先经过一个按键消抖,将当前按键情况存储到uart_din 中。然后发送出去。 下面是51的部分。 说到这个我就来气,我看了暑假里我写的关于51串口通信的博客。写的和一坨屎一模一样。 ...

qq_44266648的博客 859

【单片机】按键消抖及原理(硬件和软件方法详解)

浅谈:在设计单片机按键输入的时候,进行按键消抖是防止按键输入被CPU误读多次的必要手段。 一、按键抖动 通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。 抖动时间的长短由按键的机械特性决定,一般为5ms~10ms。这是一个很重要的时间参数,在很多场合都要用到。按键稳定闭合时间的长短则是由操作人员的按键动作决定的,一般为零点几

电子通信仔的学习杂记 11万+

Verilog中按键消抖检测的实现

Verilog按键消抖是FPGA学习时的一个入门教程,为避免眼高手低,还是再次分析与记录一下。此处着重介绍按键消抖的基本原理,对按键消抖与检测的关键技术进行分析,并进行功能仿真。 一、按键消抖基本原理 1、按键消抖必要性 物理按键释放与按下时均会出现抖动,抖动时间约为5-10ms。这就意味着,在按键按下以及释放的5-10ms内,按键的输出为高电平还是低电平是不断抖动的,在0-1之间频繁切换。...

CLL_caicai的博客 5570

【外围电路】按键电路设计&外接信号输入设计

硬件消抖:适用于对实时性要求较高的场景,能够有效地滤除按键抖动信号,但可能会带来阻尼振荡等问题。RC滤波电路是最常用的硬件消抖方法之一,其参数选择需要根据具体的抖动时间和电路要求进行计算。软件消抖:具有灵活性高、成本低等优点,但对处理器的性能有一定要求,且可能会增加软件的复杂度。在实际应用中,可以根据具体的项目需求和资源限制,选择合适的消抖方法或结合硬件和软件消抖的方法,以实现最佳的按键处理效果。

学习日记 1万+

FPGA(八)---按键消抖

一、按键状态 按键未按下时:空闲态,FPGA的引脚通过上拉电阻置为高电平。 按键按下时:FPGA的引脚接地,变为低电平。 **在按键按下和释放的过程中:**会出现一段时间的抖动,可以通过示波器观察抖动时间,不超过20ms(如果是50M的晶振,就会经过多个时钟周期,前一个时钟是高电平,下一个可能就是低电平,也就是会出现抖动的现象,这是我们不想要的) IDEL:空闲态 DOWN:按下状态 ***消抖原理:***当按键按下时,检测到下降沿进入FILTER,设置FILTER时间(信号抖动的最大时间)为20ms,

qq_42428154的博客 3552

STM32单片机按键消抖和FPGA按键消抖大全

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

weixin_42976659的博客 2574

周记0050

<b></b><h1>周记0050</h1> 2019-07-07到2019-07-13   首先缅怀一下7·7事变。卢沟桥我还没有去过,过几个月得了闲去看看。   7月7号似乎是在折腾Linux。之前不是把Ubuntu整崩了吗?我就想试着恢复它。我首先试图联网,毕竟连不上网的Linux就是条咸鱼。我发现不管是用网线,还是用无线网,都无法...

yuankx13ustc的博客 267

正点原子视频学习笔记—Verilog下按键消抖, 实现LED灯的点亮和熄灭

Verilog按键消抖控制LED灯的点亮和熄灭 在学习FPGA的基础阶段会有按键控制LED灯亮灭的实验,其中避免不了要对按键进行消抖处理,还包括LED的控制模块,和顶层例化模块。本文包括详细的源代码和注释。 按键消抖部分的代码如下所示 //key_debance module key_d( input clk,//时钟 input rst_n,//复位 input key,//按键输入 output key_value,//定义key_value,来作为消抖后的按键值 output key_fl

qq_46367577的博客 2007

FPGA之没事敲例程

FPGA新手入门 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、系统时钟二分频总结 前言 新手入门,大概展示下在书上看到的例程,以及自己的思考以及modelsim的仿真结果,希望大家多多批评。 第一天:系统时钟的二分频; 一、系统时钟二分频 师兄毕业留下的Xilinx的开发板一块,看了下特权的例程,今天复现一下系统时钟的二分频。 module test (clk_25m,rst_n,out_clk_12m5) input clk_25m;//系统时钟25M inp

Leslie_ZJ的博客 244

按键消抖的三种方案

首先,做两个假定,以方便后面的描述: 假定按键的默认状态为0,被按下后为1 假定按键抖动时长小于20ms,也即使用20ms的消抖时间 方案1:在按键电平稳定的情况下,当第一次检测到键位电平变化,开始20ms计时,计时时间到后将按键电平更新为当前电平 方案2:在20ms计时的过程中,有任何的电平变化都立即复位计时 方案3:在有电平变化时立即改变按键输出电平,并开始20ms计时,忽略这其中抖动 1、方案1代码: module debounce( input wire clk, nrst, i

耐心的小黑的博客 2万+

FPGA 20 ,FPGA按键消抖功能解析与实现( 开发板按键消抖 )

⭐在数字电路FPGA(Field-Programmable Gate Array,现场可编程门阵列)设计中,按键开关的使用非常普遍。然而,由于机械按键的物理特性,在按下或释放瞬间会经历一段不稳定的时间,即所谓的“抖动”(Bounce)。这种抖动会在信号上产生短暂的、不稳定的高低电平变化,如果不处理,可能会导致误触发或计数错误。 FPGA的消抖方法通常分为硬件消抖和软件消抖。硬件消抖依赖外部电路(如RC网络),而软件消抖则通过FPGA内部的逻辑设计实现(如状态机或计数器)。

AI Agent 全栈架构师(AI + GIS + Backend)& 数字芯片系统架构师(FPGA),NorthCity Smile的博客(NcSmile,博主域名:https://ncmile.blog.youkuaiyun.com) 1685

关于小白如何学FPGA这件事

关于小白如何学FPGA这件事 注意点:如果输入信号在最终没有输出,verilog是不会各这个信号分配资源的。 学习编程就是一个从学会初步,到自己时间发现预期效果和真实情况相背,再到重新学习,再做出来的过程, 前言 由于某些原因,我要开始学习FPGA了。之前一直是接触的单片机,积累了一定的编程知识,以及模电数电知识。现在想要挑战编程FPGA.首先第一个映入眼帘的是verilog这门语言,不能说这门语言和C语言毫不相关,但也可以说是大相径庭。虽然看上去,语法都是差不多的。但是C语言编程的思想完全不能用在veri

ponieer的博客 1046

按键消抖电路

用触发器、计数器、和适当门电路设计的按键消抖电路,可以消除按键的机械抖动

ZYNQ 7020 学习记录-2呼吸灯(模块化)

由于研究生课题组所研制的设备HIGH-POWER SHM SYSTEM是基于ZYNQ的,以下是本人自学ZYNQ的学习记录,所用板卡为:基于 XILINX ZYNQ7000 开发平台的开发板(型号:AX7021B) 2022 款。以上就是今天要讲的内容,本文仅仅简单介绍了XILINX ZYNQ7020的使用,如有任何问题请发送邮件至:zelinliu@nuaa.edu.cn。

layra_liu的博客 1101

易康eCognition9.0分类|规则集和机器学习的方法的面向对象的多尺度分割分类

基于易康eCognition9.0软件的规则集和机器学习的方法的面向对象的多尺度分割分类

weixin_46493028的博客 1万+

ZYNQ实战:如何用AXI-stream FIFO解决跨时钟域数据同步难题(附完整代码)

本文深入探讨了在ZYNQ项目中,如何利用AXI-Stream FIFO有效解决PS与PL之间的跨时钟域数据同步难题。文章详细分析了异步FIFO的架构设计、关键配置参数,并提供了PS端软件策略与PL端逻辑设计的完整代码示例,旨在帮助开发者构建稳定可靠的数据流通道,优化系统性能。

nginx7reverse的博客 754

FPGA之按键消抖

按键是一种常用的人机交互输入接口,对于机械按键来说,在按下或弹起的时候,按键输入值往往伴随着输入抖动。消除抖动的方式有很多种,以下是用FPGA实现按键消抖。实现原理:当检测到按键按下(一般按下为低电平)时开始计时(用计数器实现),大概10ms后检测按键状态,如果按键状态为低电平,说明按键按下,输出低电平;如果按键状态为高电平,说明是按键抖动。按键弹起同理。 下面对按键消抖的代码(ax_debounce.v)进行分析: module ax_debounce( input clk, input rst, inp

csdnqiang的博客 5528

SpringBoot+Vue实现图片上传

SpringBoot+Vue实现图片上传

叶♥璃 2731

FPGA入门实验之按键消抖

实验要求 生活中很多时候都会用到按键,但按键并不是按下,抬起那么的简单,机器在检测按键按下时,也会进行一系列的判断。但在按下时不可避免的会出现按键抖动,也就是产生多个按下信号,又比如按下过后又再次快速的松开按下,这就使得机器检测到了多个按下信号脉冲,它不知道哪一个是有效的。所以此次实验就是为了消除按键抖动。 设计思路 在检测到按键按下时给它延迟20ms(因为绝大多数按键抖动都在这个时间左右)。也...

name_un的博客 2163

BEV感知技术全景解析:从纯视觉到多传感器融合的实战指南

本文全面解析了自动驾驶中的BEV感知技术,从纯视觉到多传感器融合的实战应用。文章深入探讨了BEV如何提供“上帝视角”,对比了IPM、Lift-Splat-Shoot及Transformer等核心实现范式,并详细阐述了多传感器融合(尤其是中融合)的工程实践与优势,为开发者提供了从理论到原型搭建的完整指南。

win55的博客 1141

飞书接入OpenClaw:从零到一的保姆级图文教程

本文主要介绍了飞书接入OpenClaw:从零到一的保姆级图文教程,希望能对学习使用OpenClaw 的同学们有所帮助。 文章目录 1. 背景 2. 解决方案 2.1 步骤一:安装插件 2.2 步骤二:创建机器人应用 2.3 步骤三:测试 3. 注意事项

weixin_43178406的博客 13万+

FPGA实例——按键消抖和自定义IP封装

按键消抖及IP核的自定义封装和调用

joker0518的博客 1372

如何解除Uniaccess管控??

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 2390

学习特权同学按键消抖实验课程的一点看法

最近看了特权同学09年录的FPGA视频学习课程,其中讲到第9课——按键消抖实验课程,有些体会、迷茫和感受,记录一下。首先,特权同学介绍了边沿检测法,我也总结学习一下;其次,对特权同学课程中的例子谈一下自己的理解,因为一开始自己丝毫看不懂,在网上搜索到的各种解释都和特权同学说的、注释的没有什么区别,我自己还是没理解透彻,于是自己边仿真、边看rtl视图,边啃代码,终于明白了一些,但竟在网上无人提起自己

joygo007的专栏 4182

模型瘦身术:YOLOv5无损剪枝实战教程

本文介绍了一种YOLOv5模型的无损剪枝方法,通过"稀疏训练+结构化剪枝+微调"的流程,在不损失精度的前提下显著减小模型尺寸并提升推理速度。具体步骤包括:1)稀疏训练给参数排优先级;2)裁剪不重要通道;3)微调恢复精度;4)验证效果。实战部分详细展示了从环境搭建到剪枝实现的完整流程,并分析了剪枝后在边缘设备部署、实时检测等场景的优势。该方法可使YOLOv5s模型从14MB缩减至2-3MB,推理速度提升30%-50%,适用于资源受限设备的轻量化部署需求。

LXY_LOVE36的博客 104

OWASP 2025版AI大模型攻击安全风险汇总(攻击方法种类)

提示词注入是大模型最基础、最常见的核心漏洞,攻击者通过构造直接或间接的恶意自然语言输入,篡改大模型原本的正常行为与输出结果。哪怕这类恶意内容对人类是隐藏、不可见的,只要模型能够正常解析读取,就会触发漏洞。它和大家常说的模型“越狱”相关,但并不等同:越狱是提示词注入的极端形式,会直接让模型彻底无视所有内置安全协议,完全脱离管控;而普通提示词注入,更多是针对性篡改模型行为、诱导执行特定操作。

墨痕诉清风的博客 519
上一篇: [FPGA 学习记录] 分频器---奇分频
下一篇: [FPGA 学习记录] 触摸按键控制LED灯
cedtek
博客等级 码龄7年 190粉丝 35原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值