一.功能描述
- 设计密码为1234的电子门锁,4个按键分别对应密码的四个位,例如key[0]表示密码第一位,每按下一次该位加一。按错需要蜂鸣器响并回到初态。输入对密码led灯0.3s闪烁。
二.模块图

三.状态转移图

四.设计思路
- 状态空间设置初始状态IDLE,对了一位的状态RIGHT_1,对了两位的状态RIGHT_2,对了三位的状态RIGHT_3,对了四位的状态RIGHT_4和错误状态。
- 动作空间,IDLE状态灯全灭,RIGHT_1状态灯亮1个,RIGHT_2状态灯亮2个,RIGHT_3状态灯亮3个,RIGHT_4状态灯0.3s从1111-0000闪烁,WRONG状态蜂鸣器响。
- 设置计数器,分别计算按键按下的次数。
- 状态转移条件:IDLE_RIGHT_1按键key[0]按下1次,RIGHT_1_RIGHT_2按键key[1]按下2次,RIGHT_2_RIGHT_3按键key[2]按下3次,RIGHT_3_RIGHT_4按键key[3]按下4次。RIGHT_4_IDLE这里我设置按下key[1]返回IDLE。IDLE_WRONG,RIGHT_1_WRONG,RIGHT_2_WRONG,RIGHT_3_WRONG分别是该状态下出错的情况。WRONG_IDLE为计满0.5s。
- 0.5s计数器从WRONG状态开始计数,当回到IDLE态停止计数。0.3s计数器从RIGHT_4状态开始计数
五.代码
/**************************************功能介绍***********************************
Date : 2023年7月28日14:30:48
Author : Alegg xy.
Version : 1.0
Description: 四位按键消抖模块
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module key_debounce #(parameter WIDTH = 4,
parameter DELAY_20MS = 1000_000
)(
input clk ,
input rst_n ,
input [WIDTH-1:0] key_in ,
output [WIDTH-1:0] key_out
);
//---------<参数定义>---------------------------------------------------------
reg [WIDTH-1:0] key_out_r;
//状态机参数定义
localparam IDLE = 4'b0001,//空闲状态
FILETER_DOWN = 4'b0010,//按键按下抖动状态
HOLD_DOWN = 4'b0100,//按下稳定按下状态
FILTER_UP = 4'b1000;//按键释放抖动状态
//---------<内部信号定义>-----------------------------------------------------
reg [3:0] cstate ;//现态
reg [3:0] nstate ;//次态
reg [WIDTH-1:0] key_r0 ;//同步打拍
reg [WIDTH-1:0] key_r1 ;
reg [WIDTH-1:0] key_r2 ;
wire [WIDTH-1:0] n_edge ;//下降沿
wire [WIDTH-1:0] p_edge ;//上升沿
reg [19:0] cnt_20ms ;//20ms计数器
wire add_cnt_20ms;
wire end_cnt_20ms;
//状态转移条件定义
wire idle2filter_down ;
wire fiter_down2hold_down ;
wire hold_down2filter_up ;
wire filter_up2idle ;
//****************************************************************
//--cstate
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;//复位的初始状态
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE : begin
if(idle2filter_down)begin
nstate = FILETER_DOWN;
end
else begin
nstate = cstate;
// state_n = IDLE;
end
end
FILETER_DOWN : begin
if(fiter_down2hold_down)begin
nstate = HOLD_DOWN;
end
else begin
nstate = cstate;
end
end
HOLD_DOWN : begin
if(hold_down2filter_up)begin
nstate = FILTER_UP;
end
else begin
nstate = cstate;
end
end
FILTER_UP : begin
if(filter_up2idle)begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
default : nstate = IDLE;
endcase
end
assign idle2filter_down = cstate == IDLE && n_edge;
assign fiter_down2hold_down = cstate == FILETER_DOWN && end_cnt_20ms;
assign hold_down2filter_up = cstate == HOLD_DOWN && p_edge;
assign filter_up2idle = cstate == FILTER_UP && end_cnt_20ms;
//****************************************************************
//--n_edge、p_edge
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r0 <= {WIDTH{1'b1}};
key_r1 <= {WIDTH{1'b1}};
key_r2 <= {WIDTH{1'b1}};
end
else begin
key_r0 <= key_in;
key_r1 <= key_r0;
key_r2 <= key_r1;
end
end
assign n_edge = ~key_r1 & key_r2;
assign p_edge = ~key_r2 & key_r1;
//****************************************************************
//--cnt_20ms
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_20ms <= 'd0;
end
else if(add_cnt_20ms)begin
if(end_cnt_20ms)begin
cnt_20ms <= 'd0;
end
else begin
cnt_20ms <= cnt_20ms + 1'b1;
end
end
end
assign add_cnt_20ms = cstate == FILETER_DOWN || cstate == FILTER_UP;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == DELAY_20MS - 1;
//****************************************************************
//--key_out
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_out_r <= 'd0;
end
else if(hold_down2filter_up)begin
key_out_r <= ~key_r2;
end
else begin
key_out_r <= 'd0;
end
end
assign key_out = key_out_r;
endmodule
/**************************************功能介绍***********************************
Date : 2023年7月28日14:30:56
Author : Alegg xy.
Version : 1.0
Description: 控制密码锁
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module lock_ctrl(
input clk ,
input rst_n ,
input [3:0] key ,
output beep ,
output [3:0] led
);
//---------<参数定义>---------------------------------------------------------
parameter MAX_300ms = 24'd15_000_000;//0.3s
parameter MAX = 26'd25_000_000;//0.5s
parameter MAX_TIME = 4'd9;
//---------<内部信号定义>-----------------------------------------------------
reg [3:0] led_r;
reg [3:0] beep_r;
//状态空间
localparam IDLE = 3'd1,//初始状态
RIGHT_1 = 3'd2,//对了一位状态
RIGHT_2 = 3'd3,//对了两位状态
RIGHT_3 = 3'd4,//对了三位状态
RIGHT_4 = 3'd5,//全对状态
WRONG = 3'd6;//出错状态
reg [2:0] cstate ;//现态
reg [2:0] nstate ;//次态
//0.5s计数器
reg [25:0] cnt ;
wire add_cnt ;
wire end_cnt ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 26'd0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 26'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end
end
assign add_cnt = cstate == WRONG;
assign end_cnt = add_cnt && cnt == MAX - 1'd1;
//0.3s计数器
reg [26:0] cnt_300ms ;
wire add_cnt_300ms ;
wire end_cnt_300ms ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_300ms <= 'd0;
end
else if(add_cnt_300ms)begin
if(end_cnt_300ms)begin
cnt_300ms <= 26'd0;
end
else begin
cnt_300ms <= cnt_300ms + 1'b1;
end
end
end
assign add_cnt_300ms = cstate == RIGHT_4;
assign end_cnt_300ms = add_cnt_300ms && cnt_300ms == MAX_300ms - 1'd1;
//状态转移条件
//成功
wire IDLE_RIGHT_1 ;
wire RIGHT_1_RIGHT_2;
wire RIGHT_2_RIGHT_3;
wire RIGHT_3_RIGHT_4;
wire RIGHT_4_IDLE ;
//失败回到WRONG
wire IDLE_WRONG ;
wire RIGHT_1_WRONG ;
wire RIGHT_2_WRONG ;
wire RIGHT_3_WRONG ;
wire WRONG_IDLE ;
//key_in[0]计数器
reg [3:0] cnt_key0;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_key0 <= 'd0;
end
else if(key[0] && cnt_key0 <= MAX_TIME)begin
cnt_key0 <= cnt_key0 + 1'd1;
end
else if(cstate == IDLE)begin
cnt_key0 <= 1'd0;
end
else begin
cnt_key0 <= cnt_key0;
end
end
//key_in[1]计数器
reg [3:0] cnt_key1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_key1 <= 'd0;
end
else if(key[1] && cnt_key1 <= MAX_TIME)begin
cnt_key1 <= cnt_key1 + 1'd1;
end
else if(cstate == IDLE)begin
cnt_key1 <= 1'd0;
end
else begin
cnt_key1 <= cnt_key1;
end
end
//key_in[2]计数器
reg [3:0] cnt_key2;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_key2 <= 'd0;
end
else if(key[2] && cnt_key2 <= MAX_TIME)begin
cnt_key2 <= cnt_key2 + 1'd1;
end
else if(cstate == IDLE)begin
cnt_key2 <= 1'd0;
end
else begin
cnt_key2 <= cnt_key2;
end
end
//key_in[3]计数器
reg [3:0] cnt_key3;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_key3 <= 'd0;
end
else if(key[3] && cnt_key3 <= MAX_TIME)begin
cnt_key3 <= cnt_key3 + 1'd1;
end
else if(cstate == IDLE)begin
cnt_key3 <= 1'd0;
end
else begin
cnt_key3 <= cnt_key3;
end
end
assign IDLE_RIGHT_1 = cstate == IDLE && cnt_key0 == 1 ;
assign RIGHT_1_RIGHT_2 = cstate == RIGHT_1 && cnt_key1 == 2;
assign RIGHT_2_RIGHT_3 = cstate == RIGHT_2 && cnt_key2 == 3;
assign RIGHT_3_RIGHT_4 = cstate == RIGHT_3 && cnt_key3 == 4;
assign RIGHT_4_IDLE = cstate == RIGHT_4 && key[1] ;
assign IDLE_WRONG = (cstate == IDLE ) && (key[1] || key[2] || key[3]);
assign RIGHT_1_WRONG = (cstate == RIGHT_1) && (cnt_key1 < 2) && (key[0] || key[2] || key[3]);
assign RIGHT_2_WRONG = (cstate == RIGHT_2) && (cnt_key2 < 3) && (key[0] || key[1] || key[3]);
assign RIGHT_3_WRONG = (cstate == RIGHT_3) && (cnt_key3 < 4) && (key[0] || key[1] || key[2]);
assign WRONG_IDLE = end_cnt;
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE :begin
if (IDLE_RIGHT_1) begin
nstate = RIGHT_1;
end
else if(IDLE_WRONG) begin
nstate = WRONG;
end
else begin
nstate = cstate;
end
end
RIGHT_1 :begin
if (RIGHT_1_RIGHT_2) begin
nstate = RIGHT_2;
end
else if(RIGHT_1_WRONG) begin
nstate = WRONG;
end
else begin
nstate = cstate;
end
end
RIGHT_2 :begin
if (RIGHT_2_RIGHT_3) begin
nstate = RIGHT_3;
end
else if(RIGHT_2_WRONG) begin
nstate = WRONG;
end
else begin
nstate = cstate;
end
end
RIGHT_3 :begin
if (RIGHT_3_RIGHT_4) begin
nstate = RIGHT_4;
end
else if(RIGHT_3_WRONG) begin
nstate = WRONG;
end
else begin
nstate = cstate;
end
end
RIGHT_4 :begin
if (RIGHT_4_IDLE) begin
nstate = IDLE;
end
else begin
nstate = RIGHT_4;
end
end
WRONG :begin
if (WRONG_IDLE) begin
nstate = IDLE;
end
else begin
nstate = WRONG;
end
end
default : nstate = IDLE;
endcase
end
reg [3:0] led_r0;
//每0.3s从1111-0000闪烁的灯
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
led_r0 <= 4'b1111;
end
else if (end_cnt_300ms) begin
led_r0 <= ~led_r0;
end
else begin
led_r0 <= led_r0;
end
end
//控制beep
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
beep_r <= 1'b1;
end
else if(cstate == WRONG) begin
beep_r <= 1'b0;
end
else begin
beep_r <= 1'b1;
end
end
//第三段:描述输出,时序逻辑或组合逻辑皆可
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
led_r <= 4'd0000;
end
else begin
case (cstate)
IDLE :led_r <= 4'b0000;
RIGHT_1 :led_r <= 4'b0001;
RIGHT_2 :led_r <= 4'b0011;
RIGHT_3 :led_r <= 4'b0111;
RIGHT_4 :led_r <= led_r0;
default: led_r <= 4'b0000;
endcase
end
end
assign led = led_r;
assign beep = beep_r;
endmodule
/**************************************功能介绍***********************************
Date : 2023年7月28日15:14:23
Author : Alegg xy.
Version : 1.0
Description: 顶层模块
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module top_fsm_lock(
input clk ,
input rst_n ,
input [3:0] key_in ,
output beep ,
output [3:0] led
);
//---------<参数定义>---------------------------------------------------------
wire [3:0] key_link;
key_debounce u_key_debounce(
.clk (clk),
.rst_n (rst_n),
.key_in (key_in),
.key_out (key_link)
);
lock_ctrl u_lock_ctrl(
.clk (clk),
.rst_n (rst_n),
.key (key_link),
.beep (beep),
.led (led)
);
endmodule
六.引脚配置
