前言
密码解锁的过程也是一个有限状态机,只有按正确密码的顺序输入密码,才能打开电子锁。本次课程就用开发板模拟这一过程。
一、设计规范(需求)
Cyclone IV开发板上有四个按键,每个按键通过消抖后作为密码按键KEY1,KEY2,KEY3,KEY4分别代表密码1,2,3,4。按下每个按键4个led灯做相应的动作,只有按照正确的密码1423输入,4个led灯才同时以20ms的间隔闪烁1s时间(同时闪2次),表示开锁成功。
二、原理图
三、设计输入
- 创建password.v文件,编写密码状态切换代码
/***密码1423***/
module password#(parameter TIME_1S = 26'd49_999_999,
parameter TIME_200MS = 24'd9_999_999)(
input wire clk,//时钟信号
input wire rst_n,//复位信号
input wire [3:0] key,//4个按键
output wire [3:0] led//4个led信号,用于指示密码输入
);
//状态空间
parameter IDLE = 3'd0;
parameter S1 = 3'd1;
parameter S2 = 3'd2;
parameter S3 = 3'd3;
parameter S4 = 3'd4;
reg [2:0] cstate;//状态寄存器
reg [25:0] cnt;//1s计数寄存器
reg [23:0] cnt_200ms;//200ms计数寄存器
wire add_cnt;
wire end_cnt;
wire add_cnt_200ms;
wire end_cnt_200ms;
reg [3:0] led_r;
//1s计数功能
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'd1;
end
end
else begin
cnt <= 26'd0;
end
end
assign add_cnt = (cstate==S4);
assign end_cnt = add_cnt && cnt == TIME_1S;
//200ms计数功能
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_200ms <= 24'd0;
end
else if(add_cnt_200ms)begin
if(end_cnt_200ms)begin
cnt_200ms <= 24'd0;
end
else begin
cnt_200ms <= cnt_200ms + 1'd1;
end
end
else begin
cnt_200ms <= 24'd0;
end
end
assign add_cnt_200ms =(cstate==S4);
assign end_cnt_200ms = add_cnt_200ms && cnt_200ms == TIME_200MS;
//密码状态切换功能
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cstate <= IDLE;
end
else begin
case (cstate)
IDLE: begin
if(key[0])begin//按下第1个按键,输入密码1
cstate <= S1;
end
else if(key[1]||key[2]||key[3]) begin//按下其他按键,状态回归
cstate <= IDLE;
end
else begin
cstate <= IDLE;
end
end
S1:begin
if(key[3])begin//按下第4个按键,输入密码4
cstate <= S2;
end
else if(key[0]||key[1]||key[2])begin//按下其他按键,状态回归
cstate <= IDLE;
end
else begin
cstate <= S1;
end
end
S2:begin
if(key[1])begin//按下第2个按键,输入密码2
cstate <= S3;
end
else if(key[0]||key[2]||key[3])begin//按下其他按键,状态回归
cstate <= IDLE;
end
else begin
cstate <= S2;
end
end
S3:begin
if(key[2])begin//按下第3个按键,输入密码3
cstate <= S4;
end
else if(key[0]||key[1]||key[3])begin//按下其他按键,状态回归
cstate <= IDLE;
end
else begin
cstate <= S3;
end
end
S4: begin//解锁状态
if(end_cnt)begin//维持1s
cstate <= IDLE;//状态回归
end
else begin
cstate <= S4;
end
end
default:;
endcase
end
end
//led灯功能,根据状态切换
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
led_r <= 4'b0000;
end
else begin
case(cstate)
IDLE: led_r <= 4'b0001;
S1: led_r <= 4'b0011;
S2: led_r <= 4'b0111;
S3: led_r <= 4'b1111;
S4: begin
if(end_cnt_200ms)begin
led_r <= ~led_r;
end
else begin
led_r <= led_r;
end
end
default:led_r <= 4'b0000;
endcase
end
end
assign led = led_r;
endmodule
- 创建key_debounce.v文件,编写按键消抖代码
module key_debounce
#(
parameter TIMER_20MS = 20'd1_000_000
)
(
input clk,
input rst_n,
input key_in,
output key_value,
output key_flag
);
//将按键值存入一拍
reg key_in_r;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
key_in_r <= 1'b0;
end
else begin
key_in_r <= key_in;
end
end
//按键消抖
reg [19:0]timer_20ms_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
timer_20ms_r <=20'd0;
end
else if (key_in == key_in_r && key_in !=1'b1) begin
if (timer_20ms_r <= TIMER_20MS-1'b1) begin
timer_20ms_r <= timer_20ms_r +1'b1;
end
else begin
timer_20ms_r <= TIMER_20MS;
end
end
else begin
timer_20ms_r <= 20'd0;
end
end
//按键按下标志
wire key_flag_w = (timer_20ms_r == TIMER_20MS - 1'd1) ? 1'b1:1'b0;
//保存当前按键值
reg key_value_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_value_r <= 1'b0;
end
else if (key_flag_w) begin
key_value_r <= ~key_value_r;
end
else key_value_r <= 1'b0;
end
//使能信号输出,延迟一个clk周期
reg key_flag_r;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_flag_r <= 1'b0;
end
else key_flag_r <= key_flag_w;
end
assign key_flag = key_flag_r;
assign key_value = key_value_r;
endmodule
- 编写digital_lock.v文件,编写电子锁顶层代码
module digital_lock#( parameter TIMER_20MS = 20'd1_000_000,
parameter TIME_1S = 26'd49_999_999,
parameter TIME_200MS = 24'd9_999_999
)
(
input wire clk,
input wire rst_n,
input wire [3:0] key,
output wire [3:0] led
);
wire [3:0] key_flag;
wire [3:0] key_value;
key_debounce#(TIMER_20MS) key_debounce_u1//实例化按键消抖
(
.clk (clk),
.rst_n (rst_n),
.key_in (key[0]),
.key_value (key_value[0]),
.key_flag (key_flag[0])
);
key_debounce#(TIMER_20MS) key_debounce_u2//实例化按键消抖
(
.clk (clk),
.rst_n (rst_n),
.key_in (key[1]),
.key_value (key_value[1]),
.key_flag (key_flag[1])
);
key_debounce#(TIMER_20MS) key_debounce_u3//实例化按键消抖
(
.clk (clk),
.rst_n (rst_n),
.key_in (key[2]),
.key_value (key_value[2]),
.key_flag (key_flag[2])
);
key_debounce#(TIMER_20MS) key_debounce_u4//实例化按键消抖
(
.clk (clk),
.rst_n (rst_n),
.key_in (key[3]),
.key_value (key_value[3]),
.key_flag (key_flag[3])
);
password#(TIME_1S,TIME_200MS) password_inst(//实例化密码模块
.clk (clk),
.rst_n (rst_n),
.key ({(key_flag[3]&&key_value[3]),(key_flag[2]&&key_value[2]),(key_flag[1]&&key_value[1]),(key_flag[0]&&key_value[0])}),
.led (led)
);
endmodule
四、电子门锁仿真
- 创建digital_lock_tb.v文件,编写仿真代码
`timescale 1ns/1ns
module digital_lock_tb();
parameter CYCLE = 20;
parameter TIMER_20MS = 4'd9;
parameter TIME_1S = 4'd9;
parameter TIME_200MS = 4'd9;
reg clk;
reg rst_n;
reg [3:0] key;
wire [3:0] led;
always #(CYCLE/2) clk = ~clk;
initial begin
clk = 1'b0 ;
rst_n = 1'b0 ;
#(CYCLE) ;
rst_n = 1'b1 ;
key = 4'b1101 ;
#(CYCLE*(TIME_1S+1)) ;
key = 4'b1110 ;
#(CYCLE*(TIME_1S+1)) ;
key = 4'b0111 ;
#(CYCLE*(TIME_1S+1)) ;
key = 4'b1101 ;
#(CYCLE*(TIME_1S+1)) ;
key = 4'b1011 ;
#(CYCLE*(TIME_1S+1)*10);
$stop ;
end
digital_lock#(TIMER_20MS,TIME_1S,TIME_200MS) digital_lock_inst(
.clk (clk),
.rst_n (rst_n),
.key (key),
.led (led)
);
endmodule
- 仿真结果
五、运行效果
电子门锁模拟
总结
电子门锁其实在状态机中提到过,可能同学们也只是听听原理,但是极少同学会去使用FPGA实现。实践是检验真理的唯一标准,懂的人很多,做的人很少。