FPGA按键流水灯
1. 要求
使用开发板上的四个按键控制四个LED灯。按下不同的按键时,四个LED灯显示不同效果。
按键状态 | LED显示效果 |
---|---|
无按键按下 | 四个LED全灭 |
KEY[0] | 自右向左流水灯 |
KEY[1] | 自左向右流水灯 |
KEY[2] | 四个LED每隔0.2S同时闪烁 |
KEY[3] | 四个LED全亮 |
2. 按钮说明
开发板上采用的是轻触式按键,没按下时输出高电平,按下时输出低电平。
原理图:
3. 系统框图
4. 代码
4.1 key_led
module key_led
#(parameter MAX_0_2S = 24'd9_999_999)
(
input wire clk,
input wire rst_n,
input wire [3:0] key_in,
output reg [3:0] led
);
reg [23:0] cnt_0_2s;
wire flag_0_2s;
reg [1:0] state;
//0.2s计数器
assign flag_0_2s = cnt_0_2s == MAX_0_2S;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_0_2s <= 24'd0;
end
else if (flag_0_2s) begin
cnt_0_2s <= 24'd0;
end
else begin
cnt_0_2s <= cnt_0_2s + 24'd1;
end
end
//状态计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= 2'd0;
end
else if (flag_0_2s) begin
state <= state + 2'd1;
end
else begin
state <= state;
end
end
//根据按键进行状态转换
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led <= 4'b0000;
end
else if (key_in[0]==0) begin
case (state)
2'd0: led <= 4'b0001;
2'd1: led <= 4'b0010;
2'd2: led <= 4'b0100;
2'd3: led <= 4'b1000;
default: led <= 4'b0000;
endcase
end
else if (key_in[1]==0) begin
case (state)
2'd0: led <= 4'b1000;
2'd1: led <= 4'b0100;
2'd2: led <= 4'b0010;
2'd3: led <= 4'b0001;
default: led <= 4'b0000;
endcase
end
else if (key_in[2]==0) begin
case (state)
2'd0: led <= 4'b0000;
2'd1: led <= 4'b1111;
2'd2: led <= 4'b0000;
2'd3: led <= 4'b1111;
default: led <= 4'b0000;
endcase
end
else if (key_in[3]==0) begin
case (state)
2'd0: led <= 4'b1111;
2'd1: led <= 4'b1111;
2'd2: led <= 4'b1111;
2'd3: led <= 4'b1111;
default: led <= 4'b0000;
endcase
end
else begin
led <= 4'b0000;//其他情况默认4个led灯全灭
end
end
endmodule
4.2 key_debounce
//按键消抖
module key_debounce (
input wire clk ,
input wire rst_n ,
input wire [3:0] key_in ,
output wire [3:0] key_out
);
parameter MAX_20ms = 20'd1_000_000;
reg [19:0] cnt_20ms ;
reg [3:0] key_flag ;
reg start ; // 稳定信号开始
reg [3:0] key_r0 ; // 按键信号寄存器0
reg [3:0] key_r1 ; // 按键信号寄存器1
wire [3:0] nedge ;
reg [3:0] flag ;
// 20ms 倒计时计数器设计
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_20ms <= 20'd0;
end
else if (nedge) begin
cnt_20ms <= MAX_20ms;
end
else if(start) begin
if(cnt_20ms == 1'd1)begin
cnt_20ms <= 20'd0;
end
else begin
cnt_20ms <= cnt_20ms - 1'd1;
end
end
else if(flag) begin
cnt_20ms <= MAX_20ms;
end
else begin
cnt_20ms <= cnt_20ms;
end
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_r0 <= 4'b1111;
key_r1 <= 4'b1111;
end
else begin
key_r0 <= key_in; // 打一拍,同步时钟域
key_r1 <= key_r0; // 打一拍,检测按键下降沿
end
end
// start 信号约束
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
start <= 1'b0;
end
else if(nedge) begin
start <= 1'b1;
end
else if(cnt_20ms == 1'b1) begin
start <= 1'b0;
end
else begin
start <= start;
end
end
// 约束 flag 信号
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
flag <= 4'b0000;
end
else if(cnt_20ms == 1'b1) begin
flag <= ~key_r0;
end
else begin
flag <= 4'b0000;
end
end
assign nedge = ~key_r0 & key_r1;
assign key_out = flag;
endmodule
4.3 key_led_top
module key_led_top #(parameter MAX_0_2S = 24'd9_999_999)
(
input wire clk ,
input wire rst_n ,
input wire [3:0] key_in ,
output wire [3:0] led
);
wire [3:0] key_out;
reg [3:0] flag;
//按键状态保存,实现持续按下按钮
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
flag <= 4'b1111;
end
else if (key_out[0]) begin
flag <= 4'b1110;
end
else if (key_out[1]) begin
flag <= 4'b1101;
end
else if (key_out[2]) begin
flag <= 4'b1011;
end
else if (key_out[3]) begin
flag <= 4'b0111;
end
else begin
flag <= flag;
end
end
key_debounce u_key_debounce(
.clk (clk),
.rst_n (rst_n),
.key_in (key_in),
.key_out (key_out)
);
key_led #(.MAX_0_2S (MAX_0_2S)) u_key_led(
.clk (clk),
.rst_n (rst_n),
.key_in (flag),
.led (led)
);
endmodule
5. 效果
https://gitee.com/jiyny/photo/raw/master/202307131106346.gif