本文主要是介绍5、SCM 按键消抖 vs FPGA 按键消抖,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
;SCM(使用的锐志实验板) 时钟周期为11.0592Mhz FPGA (Altrea BJ-EPM240)时钟周期为50Mhz(C语言和Verilog语言的语法不做详细讲解,可以查看相关资料以下基础实验都是基于两个原理图(http://pan.baidu.com/s/1sl56yc9))
- 按键在闭合和断开时,触点会存在抖动现象。在按键按下或者是释放的时候都会出现一个不稳定的抖动时间,如果不处理好这个抖动时间,我们就无法处理好按键编码;首先介绍串行代码的描述思想,然后介绍硬件并行代码的思想
因为 指令周期>(~4) 机器周期>状态周期>(~12) 时钟周期,因此对于按键的延迟要根据执行一条指令的执行时间;理论的计算是 一条指令执行时间=m个机器周期xn1个时钟周期+p个状态状态周期xn2个机器周期
- 时钟周期,是晶振频率的倒数。
- 状态周期,是时钟周期的二倍。
- 机器周期,是时钟周期的 12 倍。
- 指令周期,执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期也不同。
#include<reg52.h>
#define uint unsigned int
sbit clk_div=P3^5; //按键
sbit led=P0^7;
void delay(uint div_delay)
{uint i,j;for(i=div_delay;i>0;i--)for(j=110;j>0;j--); //延迟1ms
}void main()
{ led=1;//可省略
while(1)
{if(clk_div==0){ delay(20); //按键延迟20msif(clk_div==0) while(!clk_div); led=~led;}}}
上述代码为一个简单的SCM按键点亮小灯;
- (个人见解,还望前辈批评)首先FPGA 的硬件编程语言是并行执行的,但是每一个always
语句的触发的逻辑操作一般都是有逻辑的制约关系的,即下一个always
语句的逻辑操作成立条件,一般都是建立在上一个always语句的逻辑操作的寄存器的数值变化,这是阻碍初学者晦涩难懂的串行代码转并行硬件电路代码思维主要障碍。verilog
的基本语法不做讲解,可以查看相关资料;还是以源码促进学习;
`timescale 1ps/1ns
module div
( clk,rst_n,sw,led );input clk;
input rst_n;
input [2:0] sw;
output [2:0] led ;reg [2:0]clk_div;
reg [2:0]clk_div_t;always@(posedge clk or negedge rst_n)
if(!rst_n) clk_div<=3'b111;
else clk_div<={sw[0],sw[1],sw[2]}; always@(posedge clk or negedge rst_n)
if(!rst_n) clk_div_t<=3'b111;
else clk_div_t<=clk_div;wire [2:0] key_an=(~clk_div) & clk_div_t;reg [20:0] cnt;always@(posedge clk or negedge rst_n)
if(!rst_n) cnt<=20'h0;
else if(key_an) cnt<=20'h0;
else cnt<=cnt+1'b1;reg [2:0] key_clk_div;
reg [2:0] key_clk_div_t;always@(posedge clk or negedge rst_n)
if(!rst_n) key_clk_div<=3'b111;
else if(cnt==20'hfffff) key_clk_div<={sw[0],sw[1],sw[2]};always@(posedge clk or negedge rst_n)
if(!rst_n) key_clk_div_t<=3'b111;
else key_clk_div_t<=key_clk_div;wire [2:0] key_an_clk= (~key_clk_div) &(key_clk_div_t); reg [2:0] d;always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
d[0]<=1'b0;
d[1]<=1'b0;
d[2]<=1'b0;
end
else
begin
if(key_an_clk[0]) d[0]<=~d[0];
if(key_an_clk[1]) d[1]<=~d[1];
if(key_an_clk[2]) d[2]<=~d[2];
end assign led[0]=d[0]?1'b1:1'b0;
assign led[1]=d[1]?1'b1:1'b0;
assign led[2]=d[2]?1'b1:1'b0;endmodule
上面是源代码,下面详细分析一下代码的含义;
reg [2:0]clk_div;
reg [2:0]clk_div_t;always@(posedge clk or negedge rst_n)
if(!rst_n) clk_div<=3'b111;
else clk_div<={sw[0],sw[1],sw[2]}; always@(posedge clk or negedge rst_n)
if(!rst_n) clk_div_t<=3'b111;
else clk_div_t<=clk_div;wire [2:0] key_an=(~clk_div) & clk_div_t;
首先假设在按键未按下状态时,sw[0],sw[1],sw[2] 都为高电平 , 在每一个时钟周期上升沿过程中,clk_div 和clk_div_t 、key_an寄存器的数值分别会有下面的状态(每一列的一组数据代表一个上升沿的此刻寄存器中的数值)
reg | posedge clk | posedge clk | posedge clk | posedge clk | posedge clk |
---|---|---|---|---|---|
clk_div | 111 | 111 | 111 | 111 | 111 |
clk_div_t | 111 | 111 | 111 | 111 | 111 |
key_an | 000 | 000 | 000 | 000 | 000 |
当按键在第二个时钟周期上升沿之前按下sw[1]按键的时候,各个寄存器的状态如下
reg | posedge clk | posedge clk | posedge clk | posedge clk | posedge clk |
---|---|---|---|---|---|
clk_div | 111 | 101 | 101 | 101 | 101 |
clk_div_t | 111 | 111 | 101 | 101 | 101 |
key_an | 000 | 000 | 010 | 000 | 000 |
当在第三个时钟周期时候检测到有按键按下去(主要是因为非阻塞电路),即key_an 状态为 010
reg [20:0] cnt;always@(posedge clk or negedge rst_n)
if(!rst_n) cnt<=20'h0;
else if(key_an) cnt<=20'h0;
else cnt<=cnt+1'b1;reg [2:0] key_clk_div;
reg [2:0] key_clk_div_t;always@(posedge clk or negedge rst_n)
if(!rst_n) key_clk_div<=3'b111;
else if(cnt==20'hfffff) key_clk_div<={sw[0],sw[1],sw[2]};always@(posedge clk or negedge rst_n)
if(!rst_n) key_clk_div_t<=3'b111;
else key_clk_div_t<=key_clk_div;wire [2:0] key_an_clk= (~key_clk_div) &(key_clk_div_t);
首先因为按键的抖动原因 ,需要延迟20ms 在检测一下按键 是否真呈按下状态; 因为假设时钟周期为50Mhz ; 1/50Mhz=0.02us 若要延迟20ms 则首先让让寄存器cnt数值累加到20’HFFFFF 也就等同于累计了20’HFFFFF 时钟周期;计算为 0.02220=0.021000000=20ms (代码里面的20’HFFFFFF ${\approx}$220) 这样就完成了延迟20ms 时间,然后在锁定此时的按键状态,进行按键小灯点亮或者关闭;
reg [2:0] d;always@(posedge clk or negedge rst_n)
if(!rst_n)
begin
d[0]<=1'b0;
d[1]<=1'b0;
d[2]<=1'b0;
end
else
begin
if(key_an_clk[0]) d[0]<=~d[0];
if(key_an_clk[1]) d[1]<=~d[1];
if(key_an_clk[2]) d[2]<=~d[2];
end assign led[0]=d[0]?1'b1:1'b0;
assign led[1]=d[1]?1'b1:1'b0;
assign led[2]=d[2]?1'b1:1'b0;
xdc文件
set_property PACKAGE_PIN K17 [get_ports clk]
set_property PACKAGE_PIN E17 [get_ports rst_n]
set_property PACKAGE_PIN M15 [get_ports {led[0]}]
set_property PACKAGE_PIN G14 [get_ports {led[1]}]
set_property PACKAGE_PIN M17 [get_ports {led[2]}]
set_property PACKAGE_PIN G15 [get_ports {led[3]}]
set_property PACKAGE_PIN M19 [get_ports {key[0]}]
set_property PACKAGE_PIN M20 [get_ports {key[1]}]
set_property PACKAGE_PIN L16 [get_ports {key[2]}]
set_property PACKAGE_PIN F16 [get_ports {key[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
源代码
`timescale 1ns / 1ps
module led_demo(input clk ,
input rst_n ,
input [3:0]key,
output [3:0]led );//50MHZ 1/50MHZ=0.02us 1s=1000000/0.02us=1000 000 00/2=50 000 000
//key 20ms/0.02us=2000000/2=1000000reg [3:0] key_first;always@(posedge clk or negedge rst_n)
if(!rst_n)key_first<=4'b1111;
elsekey_first<=key;wire [3:0]key_an=key_first&~key;reg [20:0] cnt ;
always@(posedge clk or negedge rst_n)if(!rst_n)cnt <= 0 ;
else if(key_an)cnt <= 0 ;
elsecnt<=cnt+1'b1;// 当加法超过24位位数之后,又变成0reg [3:0] key_second; always@(posedge clk or negedge rst_n)
if(!rst_n)key_second<=4'b1111;
else if(cnt==20'd1_000_000)key_second<=key;reg [3:0] key_three;
always@(posedge clk or negedge rst_n)
if(!rst_n)key_three<=4'b1111;
else key_three<=key_second;wire [3:0]key_an_real=key_three&~key_second;
//wire [3:0]key_an_real=key_second&~key; //不能这样写 这样写会在按键之后, 这里会立刻记录按键bitreg [3:0] ledreg;
always@(posedge clk or negedge rst_n)
if(!rst_n)ledreg <=4'b1111 ;
else
begin
if(key_an_real[0]) ledreg[0] <= ~ledreg[0];
if(key_an_real[1]) ledreg[1] <= ~ledreg[1];
if(key_an_real[2]) ledreg[2] <= ~ledreg[2];
if(key_an_real[3]) ledreg[3] <= ~ledreg[3];
endassign led[0]=ledreg[0]?1'b1:1'b0;
assign led[1]=ledreg[1]?1'b1:1'b0;
assign led[2]=ledreg[2]?1'b1:1'b0;
assign led[3]=ledreg[3]?1'b1:1'b0;
endmodule
这篇关于5、SCM 按键消抖 vs FPGA 按键消抖的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!