一、概述
在上一篇文章中我们学习了按键的相关消抖及其使用,在这篇文章当中我们就针对通过按键实现LED的控制。
1、按键原理图
2、基本框架
通过我们前面编写的按键消抖的文件和LED文件将按键和LED两个模块进行交互,从而达到按键控制LED的目的。
二、代码编写
1、首先是按键相关设计文件的编写,新建key.v,如下:
//状态机实现
module key (input clk ,input rst_n ,input key_in , //输入原始的按键信号output reg key_out //输出处理之后的按键信号
);
//参数定义localparam IDLE = 4'b0001,//空闲JITTLE0 = 4'b0010,//滤除第一次抖动DOWN = 4'b0100,//稳定JITTLE1 = 4'b1000;//滤除第二次抖动parameter TIME_20MS = 1_000_000;//需要计数的值,20ms//内部信号reg [3:0] state_c;//现态reg [3:0] state_n;//次态reg [19:0] cnt_20ms;//计数20mswire add_cnt_20ms;wire end_cnt_20ms;wire nedge ;//下降沿信号wire pedge ;//上升沿信号reg key_in_r1 ;// 同步打拍//状态转移 同步时序逻辑描述状态转移always @(posedge clk or negedge rst_n) beginif(!rst_n)state_c <= IDLE;elsestate_c <= state_n;end//状态转移条件 组合逻辑always @(*) begincase (state_c)//一定是case 现态IDLE:beginif(nedge)state_n = JITTLE0;elsestate_n = state_c;endJITTLE0:beginif(end_cnt_20ms)state_n = DOWN;elsestate_n = state_c; endDOWN:beginif(pedge)state_n = JITTLE1;elsestate_n = state_c;endJITTLE1 :beginif(end_cnt_20ms)state_n = IDLE;elsestate_n = state_c; end default: state_n = IDLE;endcaseend//20ms计数器
always @(posedge clk or negedge rst_n) beginif(!rst_n)cnt_20ms <= 0;else if(add_cnt_20ms)beginif(end_cnt_20ms)cnt_20ms <= 0;elsecnt_20ms <= cnt_20ms + 1;end
endassign add_cnt_20ms = (state_c == JITTLE0) || (state_c == JITTLE1);
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == TIME_20MS - 1;//下降沿 上升沿
//同步 打拍
always @(posedge clk or negedge rst_n)begin if(!rst_n)beginkey_in_r1 <= 2'b11;end else begin key_in_r1 <= key_in; //同步按键输入信号end
end//r1当前状态
assign nedge = ~key_in_r1 && key_in;
assign pedge = key_in_r1 && ~key_in;//key_outalways @(posedge clk or negedge rst_n) beginif(!rst_n)key_out <= 0;else if(end_cnt_20ms &&(state_c== JITTLE1))key_out <= ~key_in_r1;//有效脉冲 20nselse key_out <= 0;endendmodule
2、LED相关设计文件编写
新建led_ctrl.v文件
module led_ctrl(input clk ,input rst_n ,input key_flag ,//输入原始的按键信号output reg led //输出处理之后的按键信号
);
always @(posedge clk or negedge rst_n )beginif(!rst_n)led<=1'b0;else if(key_flag)led<=~led;
end
endmodule
3、顶层文件编写
在顶层文件中,使用一个中间变量key_out将按键中的输出赋值给LED模块中的按键标志位输入key_flag。
module top(input clk,input rst_n,input key_in,output led);
wire key_out;
key key_inst(/*input */ .clk (clk ),/*input */ .rst_n (rst_n ),/*input */ .key_in (key_in), //输入原始的按键信号/*output reg */ .key_out (key_out) //输出处理之后的按键信号
);led_ctrl led_inst(/*input */ .clk (clk ),/*input */ .rst_n (rst_n ),/*input */ .led (led), //输入原始的按键信号/*output reg */ .key_flag (key_out) //输出处理之后的按键信号
);
endmodule
4、测试文件编写
`timescale 1ns/1nsmodule top_tb ;reg clk ;reg rst_n ;reg key_in ;wire led ;defparam top_inst.key_inst.TIME_20MS = 1000;top top_inst(.clk (clk ),.rst_n (rst_n ),.key_in (key_in ), //输入原始的按键信号.led (led ) //输出处理之后的按键信号
);
//激励信号产生
parameter CLK_CLY = 20;
//时钟
initial clk=1;
always #(CLK_CLY/2)clk=~clk;//复位
initial beginrst_n= 1'b0;#(CLK_CLY*3);#5;//复位结束避开时钟上升沿rst_n= 1'b1;
end//激励
integer i;
initial repeat(5)beginkey_in = 1;//模拟按键未按下i ={$random}%6;//给i赋值0-5#(CLK_CLY*500);//等待复位时间结束#3;repeat (3)begin key_in = 0;//前按键抖动开始#(CLK_CLY*1);//一个5-10ms的抖动时间repeat ((i+5)*50)beginkey_in = $random;#(CLK_CLY*1);end key_in = 0;//按键稳定#(CLK_CLY*100*50);//后抖动开始key_in = 1;#(CLK_CLY*1);repeat ((i+5)*50)beginkey_in = $random;#(CLK_CLY*1);end key_in = 1;//按键稳定#(CLK_CLY*10*500);end//模拟意外抖动repeat (3)begin repeat ((i+5)*50)beginkey_in = $random;#(CLK_CLY*1);end key_in = 1;//按键稳定#(CLK_CLY*500);end $stop;
end
endmodule
三、仿真波形图
通过波形图我们可以看见当按键按下一次,LED状态切换一次,实现了按键控制LED的功能实现。