基于fpga的图像处理

描述

设计规划

开发板上使用的机械按键在闭合及断开的瞬间均伴随有一连串的抖动,按键抖动会引起一次按键被误读多次,需要进行消抖处理:在按键闭合稳定时读取按键的状态,并且必须判别到按键释放稳定后再作处理 。

如果按键个数少,可以用硬件消抖,按键多时需要用软件消抖:检测出按键闭合后执行一个 20ms的延时程序 (抖动的时间为5ms~10ms)再一次检测键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。硬件消抖需要有额外的电路,软件消抖没有这种顾虑。

先用波形图模拟出按键被按下和释放时抖动的毛刺状态:

时钟

计数器一节中已经介绍过计数的实现方法,这里20ms的延迟需要一个20ms的计数器cnt_20ms。系统检测到按键输入为低电平就开始计数,如果20ms(50MHz的晶振需要计数个数为999_999)内检测出高电平说明是一个抖动,计数器清零。计数器计满999_999之后,key_flag拉高,一个时钟周期后变低,因此key_flag是脉冲信号。计数器计满后的状态至关重要,如果清零,当输入是低电平的时间过长就会造成一次按键却有多个key_flag脉冲的情况。

时钟

如果计满后不清零,到kin_in为高电平时再清零也会有新的问题,就是key_flag维持高电平时间太长,不再是一个脉冲信号。

时钟

此时如果令计数器记到999_998,就可以达到预期的效果。

时钟

编写代码

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 //key_flag为1时表示消抖后检测到按键被按下
//key_flag为0时表示没有检测到按键被按下
);


//reg define
reg [19:0] cnt_20ms ; //计数器


//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_20ms <= 20'b0;
else if(key_in == 1'b1)
cnt_20ms <= 20'b0;
else if(cnt_20ms == CNT_MAX && key_in == 1'b0)
cnt_20ms <= cnt_20ms;
else
cnt_20ms <= cnt_20ms + 1'b1;


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'b1)
key_flag <= 1'b1;
else
key_flag <= 1'b0;
endmodule

首先定义了参数CNT_MAX是计数器的最大值,然后定义了输入输出和计数器。2^19<999999<2^20,因此需要20位的计数器。然后,还是要分析两个信号的变化,一个是计数器的状态,一个是标志位的状态。他们变化的条件都是时钟上升沿和复位有效(下降沿)。

cnt_20ms的变化是,如果复位有效就清零;如果检测到输入为高电平就清零;如果计满且输入为低电平就保持;如果没计满就+1。

时钟

key_flag的变化是,复位有效就清零,计数到999999-1就拉高,其他时候都为0。

时钟

编写testbench

`timescale 1ns/1ns
module tb_key_filter();
 parameter CNT_1MS = 20'd19 ,
 CNT_11MS = 21'd69 ,
 CNT_41MS = 22'd149 ,
 CNT_51MS = 22'd199 ,
 CNT_60MS = 22'd249 ;


 wire key_flag ; 
 reg sys_clk ; 
 reg sys_rst_n ; 
 reg key_in ; 
 reg [21:0] tb_cnt ; 


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


 //sys_clk:模拟系统时钟,每10ns电平翻转一次,周期为20ns,频率为50MHz
 always #10 sys_clk = ~sys_clk;


 //tb_cnt:按键过程计数器,通过该计数器的计数时间来模拟按键的抖动过程
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 tb_cnt <= 22'b0;
 else if(tb_cnt == CNT_60MS)
 tb_cnt <= 22'b0;
 else
 tb_cnt <= tb_cnt + 1'b1;


 //key_in:产生输入随机数,模拟按键的输入情况
 always@(posedge sys_clk or negedge sys_rst_n)
 if(sys_rst_n == 1'b0)
 key_in <= 1'b1; //按键未按下时的状态为为高电平
 else if((tb_cnt >= CNT_1MS && tb_cnt <= CNT_11MS)
||(tb_cnt >= CNT_41MS && tb_cnt <= CNT_51MS))
 key_in <= {$random} % 2;  //随机数模拟抖动
 else if(tb_cnt >= CNT_11MS && tb_cnt <= CNT_41MS)
 key_in <= 1'b0;
 else
 key_in <= 1'b1;


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


 endmodule

1、定义参数,这里和上一节一样,为了缩短仿真时间,将参数值设置得小一些。

2、定义几个信号:时钟、复位、输入、计数、输出

3、初始化:时钟信号为高,复位为低,输入为低,延迟20ns后,复位为高

4、定义时钟:每10ns翻转一次,周期为20ns,频率为50MHz

5、定义计数器:计数器计数到CNT_60MS,计数0-249。复位变为0,计满变为0.否则+1。

6、定义输入:输入在前后10ms内抖动,因此输入数在CNT_1MS-11MS和41MS-51MS内取0或1的随机数来模拟抖动。在11MS-41MS内为低电平,持续时间要超过CNT-MAX(80>24)。0MS-1MS和41MS以上都为高电平。

7、实例化:这里定义了CNT_MAX,要小于11MS-41MS,否则会一直抖动。

对比波形

整体波形图,达到了预期效果:key_in低电平开始计数到CNT_MAX-1时一旦检测到输入高电平就清零,但是没有检测到输入信号为高电平,那么key_flag拉高成为一个脉冲信号。

时钟

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

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分