【2022.05西南交大数电实验】
【本代码及波形已通过老师验收。仅供参考。】
【参考博客:Verilog实现独立按键消抖(状态机)_ty_xiumud的博客-CSDN博客_verilog按键消抖】
【参考视频(强推这个up主):[录播] 数字电子技术实验_哔哩哔哩_bilibili】
【2022.05.11更新:目前仅有一人反映,拨动开关(不是按钮开关)关闭时计数的,有助教判为一验不过。该情况则是需要改为在高电平时计数。修改建议:在s1高电平稳定时的key_flag取1,s2计数满后认定为低电平稳定的key_flag取为0:
S1:
begin
key_flag <= 1'b1;
key_state <= 1'b1;
en_counter <= 1'b0;
S2:
if(cnt_full) //计数满,说明达到稳定状态,关闭计数器,进入下一个状态
begin
state <= S3;
en_counter <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b0;
这样跑出来的波形是这样:
因大部分人反映用正文代码验收无问题(老师验收的学生也过了),就不再修改正文代码。】
目录
1 Quartus代码编写
2 test代码生成
3 ModelSim仿真
4 实验记录
5 其他
5.1 显示单位的切换
5.2 缩放界面
module yck_1716_6_1(clk, rst_n, key_in, key_flag, key_state, codeout, Q, CO);
input clk; //时钟50MHz
input rst_n; //复位信号
input key_in; //按键输入
output key_state; //按键状态,高电平为未按下,低电平为按下状态
output key_flag; //经消抖后可确认的按下动作
output CO; //进位信号
output reg [6: 0] codeout;
output reg [3: 0] Q;
parameter S1 = 2'b00, //松开稳定时
S2 = 2'b01, //按下毛刺时
S3 = 2'b10, //按下稳定时
S4 = 2'b11; //松开毛刺时
/*===============================================================20ms计数器=============================================================*/
reg en_counter; //计数使能
reg [19:0] cnt; //计数
reg cnt_full;
//只有当计数使能为高电平的时候,计数器才会计数
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt <= 0;
else if(en_counter)
cnt <= cnt + 1'b1;
else
cnt <= 0;
end
//计数满信号(数数到1000000计数满时间到。1000000是1M,当基于clk信号频率进行计数时,cnt_full走过(1/50M)s*1M的时间,即20ms)
always@(posedge clk or negedge rst_n) //当clk接50MHz的信号时,相当于clk在1s内进行了50M次计数,相邻上升沿相间(1/50M)s
begin
if(!rst_n)
cnt_full <= 1'b0;
else if(cnt == 20'd1000000) //1000000的二进制数有20位(2^20=1048576)
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
end
/*==============================================================判断边沿模块=============================================================*/
reg key_tmp0,key_tmp1;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
key_tmp0 <= 1'b1;
key_tmp1 <= 1'b1;
end
else
begin
key_tmp0 <= key_in; //key_in按键输入
key_tmp1 <= key_tmp0;
end
end
wire pedge,nedge;
assign nedge = (!key_tmp0) & key_tmp1; //下降沿(下一clk时为0,上一clk时为1)
assign pedge = key_tmp0 & (!key_tmp1); //上升沿(下一clk时为1,上一clk时为0)
/*=============================================================状态机模块================================================================*/
reg [1:0] state;
reg key_flag; //经消抖后可确认的按下动作
reg key_state; //按键状态,高电平为未按下,低电平为按下状态
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state <= S1;
en_counter <= 1'b0; //计数器归零
key_state <= 1'b1; //按键未按下
key_flag <= 1'b0;
end
else
begin
case(state)
S1: //高电平(松开稳定)
begin
key_flag <= 1'b0; //按键未按下,不计
key_state <= 1'b1; //按键松开状态
en_counter <= 1'b0; //计数器归零
if(nedge) //检测到下降沿,进入下一个状态同时打开计数器
begin
state <= S2;
en_counter <= 1'b1; //计数器使能
end
else
state <= state; //保持目前状态
end
S2: //下降沿抖动(按下毛刺)
if(cnt_full) //计数满,说明达到稳定状态,关闭计数器,进入下一个状态
begin
state <= S3;
en_counter <= 1'b0; //计数器归零
key_flag <= 1'b1; //按键可确认已按下
key_state <= 1'b0; //按键按下状态
end
else if(pedge) //检测到上升沿(毛刺),跳回S1状态同时关闭计数器
begin
en_counter <= 1'b0; //计数器归零
state <= S1;
end
else
state <= state; //保持目前状态
S3: //低电平(按下稳定)
begin
key_flag <= 1'b0; //一个按键周期测到一次就行,现在可清零了(后面代码编写只在意其posedge)
if(pedge) //检测到上升沿,进入下一个状态同时打开计数器
begin
state <= S4;
en_counter <= 1'b1; //计数器使能
end
else
state <= state; //保持目前状态
end
S4: //上升沿抖动(松开毛刺)
if(cnt_full)
begin
state <= S1;
key_state <= 1'b1;
end
else
if(nedge)
begin
en_counter <= 1'b0; //计数器归零
state <= S3;
end
else
state <= state; //保持目前状态
default:
state <= S1;
endcase
end
end
/*===========================================================计数值输出与译码器模块========================================================*/
always@(posedge key_flag,negedge rst_n) //key_flag:经消抖后可确认的按下动作
if(!rst_n)
Q <= 0;
else
begin
if(Q < 4'd9)
Q <= Q + 1'b1;
else
Q <= 4'b0;
end
assign CO = (rst_n & Q == 4'd9);
always@(Q)
begin
case(Q)
4'd0:codeout <= 7'b1111110;
4'd1:codeout <= 7'b0110000;
4'd2:codeout <= 7'b1101101;
4'd3:codeout <= 7'b1111001;
4'd4:codeout <= 7'b0110011;
4'd5:codeout <= 7'b1011011;
4'd6:codeout <= 7'b1011111;
4'd7:codeout <= 7'b1110000;
4'd8:codeout <= 7'b1111111;
4'd9:codeout <= 7'b1111011;
default:codeout <= 7'bx;
endcase
end
endmodule
对 yck_1716_6_1.v 进行 Start Compilation ,而后自动生成test bench代码(如果没有Start Compilation 直接生成test bench代码会报错的)
【注:若出现Can't generate test bench files -- select a valid simulation tool,则通过菜单栏Assignments—Settings弹出窗口中EDA Tool Settings—Simulation设置为ModelSim,重新编译即可解决:】
得到测试v文件的步骤见上一博客【数电实验5】Verilog—可控分频器设计 & ModelSim的使用_kokoのadventure的博客-CSDN博客_可控分频器设计
测试v文件的代码如下:
`timescale 1 ns/ 1 ns
module yck_1716_6_1_vlg_tst();
// constants
// general purpose registers
// test vector input registers
reg clk;
reg key_in;
reg rst_n;
// wires
wire CO;
wire [3:0] Q;
wire [6:0] codeout;
wire key_flag;
wire key_state;
// assign statements (if any)
yck_1716_6_1 i1 (
// port map - connection between master ports and signals/registers
.CO(CO),
.Q(Q),
.clk(clk),
.codeout(codeout),
.key_flag(key_flag),
.key_in(key_in),
.key_state(key_state),
.rst_n(rst_n)
);
initial
begin
// code that executes only once
// insert code here --> begin
clk = 1'b0;
rst_n = 1'b0;
key_in = 1'b1;
// Wait 100 ns for global reset to finish
#200
rst_n = 1'b1;
// --> end
$display("Running testbench");
end
always
begin
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#(50*1000000);
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#1000000;
key_in = 1'b0;#1000000;
key_in = 1'b1;#(50*1000000);
end
always
// optional sensitivity list
// @(event1 or event2 or .... eventn)
begin
// code executes for every event on sensitivity list
// insert code here --> begin
#10 clk = ~clk; // (1/50M)/2 ns = 10 ns
// --> end
end
endmodule
相关步骤见上一博客【数电实验5】Verilog—可控分频器设计 & ModelSim的使用_kokoのadventure的博客-CSDN博客_可控分频器设计
测试波形如下:
10ms毛刺区:
Top-level Entity name |
Family |
Device |
yck_1716_5_1 |
Cyclone IV E |
EP4CE6E22C8 |
Total logic elements |
Total registers |
Total pins |
59 / 6,272 ( < 1 % ) |
34 |
17 / 92 ( 18 % ) |
Total memory bits |
Embedded Multiplier 9-bit elements |
Total PLLs |
0 / 276,480 ( 0 % ) |
0 / 30 ( 0 % ) |
0 / 2 ( 0 % ) |
信号名 |
主板器件 |
PIN |
信号名 |
主板器件 |
PIN |
|
clk |
50MHz |
PIN_90 |
Q[3] |
IO3/LED3 |
PIN_54 |
|
rst_n |
Key0/SW0/LED8 |
PIN_24 |
Q[2] |
IO2/LED2 |
PIN_52 |
|
key_in |
Key1/SW1/LED9 |
PIN_31 |
Q[1] |
IO1/LED1 |
PIN_50 |
|
codeout[6] |
a |
PIN_112 |
Q[0] |
IO0/LED0 |
PIN_46 |
|
codeout[5] |
b |
PIN_100 |
||||
codeout[4] |
c |
PIN_104 |
||||
codeout[3] |
d |
PIN_111 |
||||
codeout[2] |
e |
PIN_106 |
||||
codeout[1] |
f |
PIN_110 |
||||
codeout[0] |
g |
PIN_103 |
Wave—WavePreference
显示ms:
显示Hz:
ctrl + 滚轮缩放界面(慢一点,modelsim加载很慢)