Verilog实现按键消抖
时间:2022-10-27 23:00:00
目录
- 1、实验平台
- 2、实验目的
-
- 2.1、实验要求
- 3、实验流程
-
- 3.1、实验原理
- 3.2、系统架构
- 3.3、功能模块划分
-
- 3.3.按钮抖动模块
-
-
- 模块框图
- 信号定义
- 时序信号图
- 设计文件
- 仿真文件
- 仿真图
-
- 3.3.2、LED驱动模块
-
-
- 模块框图
- 信号定义
- 信号时序图
- 设计文件
- 仿真文件
- 仿真图
-
- 3.3.3、顶层文件
- 3.4、上板验证
- 4、总结
1、实验平台
软件:PC、Quartus Prime 18.1、Modelsim 10.5b 硬件:Altera FPGA
开发板(EP4CE6E22F17C8)
2、实验目的
- 1.按钮通过延迟抖动
- 2、状态机demo熟悉
- 3、状态机实现按键消抖(多位宽)
2.1、实验要求
消除抖动后使用按键信号控制LED呈现两种不同的状态 a) 流水灯 b) 闪烁
3、实验流程
3.1、实验原理
根据开发板的原理图,可以获得以下信息
独立按键
由于弹簧片的存在,这种机械按钮默认处于高电平。按下和释放时会有一定的抖动时间。抖动时,其电平状态为不定值,因此需要在稳定时读取信号,通常抖动时间为5~10ms,故设置一个20ms用于计时低电平时长的计数器。
LED:
根据硬件原理图,8根发光二极管连接3根阳极.3V正电压,也就是高电平,所以如果我们想要的话 如果发光二极管导通,需要在阴极接通低电平,才能让LED亮起来。
3.2、系统架构
根据系统要求,可获得以下框架分布
3.3、功能模块划分
以下模块可根据系统构建获得
3.3.按钮抖动模块
模块框图
信号定义
信号名 | 端口类型 | 数据位宽 | 信号说明 |
---|---|---|---|
Clk | i | 1 | 输入时钟信号,50MHz |
Rst_n | i | 1 | 输入复位信号,低电平有效 |
key | i | 1 | 输入按键信号,低电平有效 |
flag | o | 1 | 输出按钮抖动信号,低电平有效 |
时序信号图
设计文件
/*================================================*\ Filename ﹕key_filter.v Author ﹕Adolph Description ﹕计数器实现按钮抖动 Called by ﹕key_top.v Revision History ﹕ 2022-5-9 Revision 1.0 Email﹕ adolph1354238998@gamil.com \*================================================*/ module key_filter( input clk ,//system clock ,50MHz input rst_n ,//system reset ,low valid input key_in ,//key input,high valid output key_flag //filter out,high valid ); parameter CNT_20MS = 20'd100_0000;//20ms counter value //20ms counter reg [19:0] cnt_delay; always@(posedge clk or negedge rst_n)begin if(!rst_n) cnt_delay <= 20'd0; else if(!key_in)begin if(cnt_delay >= CNT_20MS) cnt_delay <= cnt_delay;//just valid once else cnt_delay <= cnt_delay + 20'd1; end else cnt_delay <= 20'd0; end assign key_flag = (cnt_delay == CNT_20MS - 20'd1); endmodule
仿真文件
/*================================================*\ Filename ﹕tb_key_filter.v Author ﹕Adolph Description ﹕按键消抖测试文件 Called by ﹕No file Revision History ﹕ 2022-5-9 Revision 1.0 Email﹕ adolph1354238998@gamil.com \*================================================*/
`timescale 1ns/1ns
module tb_key_filter();
reg tb_clk;
reg tb_rst_n;
reg tb_key;
wire tb_flag;
parameter clk_period = 20;
defparam U_key_filter.CNT_20MS = 1400; //
key_filter U_key_filter(
/*input */.clk (tb_clk ),//system clock ,50MHz
/*input */.rst_n (tb_rst_n ),//system reset ,low valid
/*input */.key_in (tb_key ),//key input,high valid
/*output*/.key_flag (tb_flag ) //filter out,high valid
);
initial tb_clk = 1'b0;
always #(clk_period / 2) tb_clk = ~tb_clk;
integer i; //整形信号,32-bit
initial begin
tb_rst_n = 1'b0;
tb_key = 1'b1;
#(clk_period * 20 + 3);
tb_rst_n = 1'b1;
press_key; //调用任务
#(clk_period * 4000);
press_key;
$stop;
end
task press_key;
begin
tb_key = 1'b1;
repeat(48)begin //模拟前抖动
i = {
$random} % 300;
#(i * clk_period); tb_key = ~tb_key;
end
tb_key = 1'b0;
#(1500 * clk_period);
repeat(55)begin //模拟后抖动
i = {
$random} % 300;
#(i * clk_period); tb_key = ~tb_key;
end
tb_key = 1'b1;
#(3000 * clk_period);
end
endtask
endmodule
仿真图
3.3.2、LED驱动模块
模块框图
信号定义
信号名 | 端口类型 | 数据位宽 | 信号说明 |
---|---|---|---|
Clk | i | 1 | 输入时钟信号,50MHz |
Rst_n | i | 1 | 输入复位信号,低电平有效 |
key1 | i | 1 | 输入按键消抖信号,高电平有效 |
key2 | i | 1 | 输入按键消抖信号,高电平有效 |
led_o | o | 8 | 输出按键消抖信号,低电平有效 |
信号时序图
设计文件
/*================================================*\ Filename ﹕led_driver.v Author ﹕Adolph Description ﹕按键消抖——LED驱动部分 Called by ﹕key_top.v Revision History ﹕ 2022-5-10 Revision 1.0 Email﹕ adolph1354238998@gmail.com \*================================================*/
module led_driver(
input wire Clk , //system clock 50MHz
input wire Rst_n , //reset ,low valid
input wire key_in1 , //消抖后的按键信号输入
input wire key_in2 , //消抖后的按键信号输入
output reg [07:00] led_o //LED信号输出
);
//Parameter Declarations
parameter CNT_MAX = 24'd1000_0000; //50MHz主频下200ms计数值
//Interrnal wire/reg declarations
reg [23:00] cnt ; //200ms Counter
wire add_cnt ; //Counter Enable
wire end_cnt ; //Counter Reset
reg flag_1 ; //key_in1 ctrl blink led
reg [07:00] r1_led ; //key_in1 ctrl blink led
reg flag_2 ; //key_in2 ctrl water led
reg [07:00] r2_led ; //key_in2 ctrl water led
//Logic Description
always @(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
cnt <= 24'd0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 24'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
else begin
cnt <= 24'd0;
end
end
assign add_cnt = flag_1 || flag_2;
assign end_cnt = add_cnt && cnt >= CNT_MAX - 24'd1;
always @(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
flag_1 <= 1'b0;
flag_2 <= 1'b0;
end
else begin
case({
key_in1,key_in2})
2'b10:begin flag_1 <= 1'b1; flag_2 <= 1'b0; end
2'b01:begin flag_1 <= 1'b0; flag_2 <= 1'b1; end
default: ;//当两个按键同时按下,则维持不变
endcase
end
end //always end
//闪烁
always @(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
r1_led <= 8'd0;
end
else if(key_in1)begin
r1_led <= 8'd255;
end
else if(end_cnt)begin
r1_led <= ~r1_led;
end
else begin
r1_led <= r1_led;
end
end //always end
//流水
always @(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
r2_led <= 8'd1;
end
else if(key_in2)begin
r2_led <= 8'd1;
end
else if(end_cnt)begin
r2_led <= {
r2_led[6:0],r2_led[7]};
end
else begin
r2_led <= r2_led;
end
end //always end
always @(posedge Clk or negedge Rst_n)begin
if(!Rst_n)begin
led_o <= 8'd0;
end
else if(flag_1)begin
led_o <= r1_led;
end
else if(flag_2)begin
led_o <= r2_led;
end
else begin
led_o <= led_o;
end
end //always end
endmodule
仿真文件
/*================================================*\ Filename ﹕tb_led_driver.v Author ﹕Adolph Description ﹕按键消抖——LED驱动部分 测试文件 Called by ﹕No file Revision History ﹕ 2022-5-10 Revision 1.0 Email﹕ adolph1354238998@gmail.com \*================================================*/
`timescale 1ns/1ns //仿真系统时间尺度定义
`define clk_period 20 //时钟周期参数宏定义
module tb_led_driver();
//参数重定义
defparam U_led_driver.CNT_MAX = 200;
//激励信号定义
reg Clk ;
reg Rst_n ;
reg key_in1 ;
reg key_in2 ;
//响应信号定义
wire [07:00] led_o ; //
//实例化
led_driver U_led_driver(
/*input wire */.Clk (Clk ), //system clock 50MHz
/*input wire */.Rst_n (Rst_n ), //reset ,low valid
/*input wire */.key_in1 (key_in1), //消抖后的按键信号输入
/*input wire */.key_in2 (key_in2), //消抖后的按键信号输入
/*output reg [07:00]*/.led_o (led_o ) //LED信号输出
);
//产生时钟
initial Clk = 1'b0;
always #(`clk_period / 2) Clk = ~Clk;
//产生激励
initial begin
Rst_n = 1'b0;
key_in1 = 1'b0;
key_in2 = 1'b0;
#(`clk_period * 10 + 3);
Rst_n = 1'b1;
#(`clk_period * 10);
key_in1 = 1'b1; //闪烁灯
#`clk_period;
key_in1 = 1'b0;
#(10 * `clk_period * U_led_driver.CNT_MAX);
key_in2 = 1'b1; //流水灯
#`clk_period;
key_in2 = 1'b0;
#(10 * `clk_period * U_led_driver.CNT_MAX);
key_in1 = 1'b1; //闪烁灯
#`clk_period;
key_in1 = 1'b0;
#(10 * `clk_period * U_led_driver.CNT_MAX);
key_in2 = 1'b1; //流水灯
#`clk_period;
key_in2 = 1'b0;
#(10 * `clk_period * U_led_driver.CNT_MAX);
$stop(2);
end
endmodule
仿真图
3.3.3、顶层文件
顶层文件在此不作讲解,根据下列RTL视图,相信读者可以很轻易的完成相应代码设计
RTL视图
3.4、上板验证
基于前面的步骤的结束,我们开始上板验证
在quartus的Pin planner 中进行引脚绑定
后面补上
然后进行全编译,待到全编译通过后,连接好开发板,电源线和下载都要连接好,然后打开电源
下载编程文件
如果是第一次使用开发板的童鞋,参看这里更新驱动,切记,前提条件是开发板正确和PC连接,并且已经通电!!!
驱动更新成功后,点击“Start”进行编程,右上角的Progress为下载进度,成功后会有“100% Successful”提示字样,然后在开发板上可以看到相应的效果——流水灯。
4、总结
到这里基本上就结束了,给大家提几点在学习过程中可能会出现的错误 1、我们的文件名和Module后面的模块名要保持一致,不然在仿真的时候会找不到文件的 2、reg和wire信号的使用规则一定要分清楚 3、任何信号在使用之前一定要先声明 ... 然后大家可以在提供的基础代码上进行
创新,比如: 1、使用状态机实现按键消抖 2、实现一个文件同时进行多位按键消抖 3、修改LED驱动部分显示效果 ...