数字密码锁(下)
上篇整理了前两个模块,可以做出来功能按键以及数字按键的信号,接着就要利用这些信号对密码锁进行控制。
3、控制模块
第三个模块为控制模块,主要实现的是一些信号的处理,也是整个工程的重中之重,主要完成了密码的接收、核对、更改、以及确认
首先就是接收从功能分配模块接受传送过来的信号,包括:
input clk;
input check;
input [3:0] number_key;
input state_lock; //密码锁开启/关闭状态
input reset;
input clear_flag; //清零状态
input lock;
input rece_flag; //密码输入标志
input [3:0] rece_cnt;
接着就考虑输出的信号都有哪些。与控制模块的相接的就是最后的显示模块了,所以要考虑与显示相关的信号有哪些:
output reg led;
output reg [3:0] disp_in1;
output reg [3:0] disp_in2;
output reg [3:0] disp_in3;
output reg [3:0] disp_in4;
output reg beep;
做好接口以后,就开始做内部的逻辑电路设计。
首先做一个密码的初始化,即刚开机时候的密码:
initial
begin
password1<=4'd1; //初始密码为1234
password2<=4'd2;
password3<=4'd3;
password4<=4'd4;
end
然后接下来的设计主要分为三块:一是密码的接收,二是密码的判断,三是密码的更改
一共定义两组密码变量,一组是存储在寄存器中的真正密码,一组是用于接收按键输入的预备密码。
下面为密码的接收部分:
always@(posedge clk)
if(state_lock==1) begin
case(reset_state)
0:if(clear_flag==1) begin
preword1<=4'd0;
preword2<=4'd0;
preword3<=4'd0;
preword4<=4'd0;
end
else begin
case(rece_cnt)
0:begin
preword1<=4'd0;
preword2<=4'd0;
preword3<=4'd0;
preword4<=4'd0;
end
1:preword1<=number_key;
2:preword2<=number_key;
3:preword3<=number_key;
4:preword4<=number_key;
default:begin
preword1<=preword1;
preword2<=preword2;
preword3<=preword3;
preword4<=preword4;
end
endcase
end
1:begin
preword1<=password1;
preword2<=password2;
preword3<=password3;
preword4<=password4;
end
endcase
end
当输入的预备密码与存储在寄存器中的真正密码一致时,按下确认键才会打开密码锁
密码判断部分:
/****************密**码**核**实***************/
always@(posedge clk)
begin
if(check==1) confirm<=1;
else if(clear_flag==1) confirm<=0;
else if(state_lock==0) confirm<=0;
else confirm<=confirm;
end
assign right0[0]=(password1==preword1);
assign right0[1]=(password2==preword2);
assign right0[2]=(password3==preword3);
assign right0[3]=(password4==preword4);
always@(posedge clk)
if(state_lock==1) begin
if(confirm==1) begin
if(right0==4'b1111)begin
led<=0; //输入密码正确指示灯亮
right<=1;
end
else begin
led<=1;
right<=0;
end
end
else if(reset_state==1)begin
led<=0;
right<=1;
end
else if((reset_state==0)&&(clear_flag==1))begin
led<=1;
right<=0;
end
else begin
led<=led;
right<=right;
end
end
else begin
led<=1;
right<=0;
end
由于只有三次输入密码的机会,所以也要对密码的确认次数进行限制,根据确认的次数以及密码的正确与否来对输入密码次数做出限制:
//*************密码确认次数控制****************//
always@(posedge confirm)
begin
if(state_lock==1) begin
if(right==0) begin
if(w_cnt==2'b11) w_cnt<=2'b00;
else w_cnt<=w_cnt+2'b1;
end
else w_cnt<=2'b00;
end
else w_cnt<=2'b00;
end
密码更改部分:
/*************更**改**密**码**********************/
always@(posedge clk)
if(right==1) begin //输入密码正确才可进入修改密码状态
if(reset==1) reset_state<=1;
else if(lock==1) reset_state<=0;
else reset_state<=reset_state;
end
else reset_state<=0;
always@(posedge clk)
if(state_lock==1) begin
case(reset_state)
1: if(clear_flag==1) begin
password1<=4'd0;
password2<=4'd0;
password3<=4'd0;
password4<=4'd0;
end
else begin
case(rece_cnt)
0: begin
password1<=password1;
password2<=password2;
password3<=password3;
password4<=password4;
end
1:password1<=number_key;
2:password2<=number_key;
3:password3<=number_key;
4:password4<=number_key;
default:
begin
password1<=password1;
password2<=password2;
password3<=password3;
password4<=password4;
end
endcase
end
0:begin
password1<=password1;
password2<=password2;
password3<=password3;
password4<=password4;
end
endcase
end
always@(posedge clk)
if(state_lock==1) begin
disp_in1<=preword1;
disp_in2<=preword2;
disp_in3<=preword3;
disp_in4<=preword4;
end
该模块完整程序
module lock_ctrl(clk,reset,clear_flag,lock,rece_cnt,rece_flag,
check,number_key,led,beep,disp_in1,
disp_in2,disp_in3,disp_in4,
state_lock
);
input clk;
input check;
input [3:0] number_key;
input state_lock; //密码锁开启/关闭状态
input reset;
input clear_flag; //清零状态
input lock;
input rece_flag; //密码输入标志
input [3:0] rece_cnt;
output reg led;
output reg [3:0] disp_in1;
output reg [3:0] disp_in2;
output reg [3:0] disp_in3;
output reg [3:0] disp_in4;
output reg beep;
reg [3:0] preword1; //输入准密码
reg [3:0] preword2;
reg [3:0] preword3;
reg [3:0] preword4;
reg [3:0] password1; //设定密码
reg [3:0] password2;
reg [3:0] password3;
reg [3:0] password4;
reg warnning; //密码错误三次警告
reg reset_state; //重置密码状态
reg right; //密码输入正确标志
reg clk_beep; //蜂鸣器驱动脉冲
reg [1:0] w_cnt; //输入密码次数计数器
reg [15:0] delay_beep; //延时计数器
reg confirm; //密码确认延时一个时钟
reg [23:0] cnt_beep; //分频计数器
wire [5:0] right0;
initial
begin
password1<=4'd1; //初始密码为1234
password2<=4'd2;
password3<=4'd3;
password4<=4'd4;
led<=1;
warnning<=0;
beep<=0;
w_cnt=2'b10;
right=0;
delay_beep<=0;
cnt_beep<=0;
clk_beep=0;
end
/**********密**码**核**实********/
always@(posedge clk)
begin
if(check==1) confirm<=1;
else if(clear_flag==1) confirm<=0;
else if(state_lock==0) confirm<=0;
else confirm<=confirm;
end
assign right0[0]=(password1==preword1);
assign right0[1]=(password2==preword2);
assign right0[2]=(password3==preword3);
assign right0[3]=(password4==preword4);
always@(posedge clk)
if(state_lock==1) begin
if(confirm==1) begin
if(right0==4'b1111)begin
led<=0; //输入密码正确指示灯亮
right<=1;
end
else begin
led<=1;
right<=0;
end
end
else if(reset_state==1)begin
led<=0;
right<=1;
end
else if((reset_state==0)&&(clear_flag==1))begin
led<=1;
right<=0;
end
else begin
led<=led;
right<=right;
end
end
else begin
led<=1;
right<=0;
end
//*****密码确认次数控制**********//
always@(posedge confirm)
begin
if(state_lock==1) begin
if(right==0) begin
if(w_cnt==2'b11) w_cnt<=2'b00;
else w_cnt<=w_cnt+2'b1;
end
else w_cnt<=2'b00;
end
else w_cnt<=2'b00;
end
//*********输入三次错误*********//
always@(posedge clk) //当第四次输入,触发警报
if(state_lock==1) begin
if(w_cnt==2'b11) warnning<=1;
else warnning<=0;
end
else warnning<=0;
always@(posedge clk) //驱动蜂鸣器
if(warnning==1) begin
if(cnt_beep<=5_00)
cnt_beep<=cnt_beep+24'd1;
else begin
cnt_beep<=24'd0;
clk_beep<=~clk_beep;
end
end
else cnt_beep<=24'd0;
always@(posedge clk_beep) //蜂鸣持续时间
if(delay_beep<=16'd800)
begin
beep=~beep;
delay_beep<=delay_beep+16'd1;
end
else begin
if(w_cnt==2'b11)
delay_beep<=16'd8001;
else delay_beep<=16'd0;
end
/*************密**码**接**收***************/
always@(posedge clk)
if(state_lock==1) begin
case(reset_state)
0:if(clear_flag==1) begin
preword1<=4'd0;
preword2<=4'd0;
preword3<=4'd0;
preword4<=4'd0;
end
else begin
case(rece_cnt)
0:begin
preword1<=4'd0;
preword2<=4'd0;
preword3<=4'd0;
preword4<=4'd0;
end
1:preword1<=number_key;
2:preword2<=number_key;
3:preword3<=number_key;
4:preword4<=number_key;
default:begin
preword1<=preword1;
preword2<=preword2;
preword3<=preword3;
preword4<=preword4;
end
endcase
end
1:begin
preword1<=password1;
preword2<=password2;
preword3<=password3;
preword4<=password4;
end
endcase
end
/**********更**改**密**码*****************/
always@(posedge clk)
if(right==1) begin //输入密码正确才可进入修改密码状态
if(reset==1) reset_state<=1;
else if(lock==1) reset_state<=0;
else reset_state<=reset_state;
end
else reset_state<=0;
always@(posedge clk)
if(state_lock==1) begin
case(reset_state)
1: if(clear_flag==1) begin
password1<=4'd0;
password2<=4'd0;
password3<=4'd0;
password4<=4'd0;
end
else begin
case(rece_cnt)
0: begin
password1<=password1;
password2<=password2;
password3<=password3;
password4<=password4;
end
1:password1<=number_key;
2:password2<=number_key;
3:password3<=number_key;
4:password4<=number_key;
default:
begin
password1<=password1;
password2<=password2;
password3<=password3;
password4<=password4;
end
endcase
end
0:begin
password1<=password1;
password2<=password2;
password3<=password3;
password4<=password4;
end
endcase
end
always@(posedge clk)
if(state_lock==1) begin
disp_in1<=preword1;
disp_in2<=preword2;
disp_in3<=preword3;
disp_in4<=preword4;
end
endmodule
4、显示模块
控制模块完成了工程的主要内容,显示模块只需要将数值显示出来即可
首先,还是先定义接口:
input clk;
input state_lock;
input [2:0] rece_cnt;
input [3:0] disp_in1;
input [3:0] disp_in2;
input [3:0] disp_in3;
input [3:0] disp_in4;
output reg [7:0] dxuan;
output reg [5:0] wxuan;
接着明确该模块内的主要任务:
1、能显示输入的数字
2、最新输入的数字总是显示在最右侧
基于这两点,对程序大体部分进行划分。首先是做一个段数码管的显示部分,即八段数码管能显示正确的数值。对数字0-9进行八段数码管的定义:
//*************数码管数值定义******************//
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
parameter disp10=8'b1011_1111;
根据输入的值不同,让段数码管显示不同的数值:
always@(posedge clk)
begin
case(cnt_symin)
4'd1:dxuan<=disp1;
4'd2:dxuan<=disp2;
4'd3:dxuan<=disp3;
4'd4:dxuan<=disp4;
4'd5:dxuan<=disp5;
4'd6:dxuan<=disp6;
4'd7:dxuan<=disp7;
4'd8:dxuan<=disp8;
4'd9:dxuan<=disp9;
4'd0:dxuan<=disp0;
4'd10:dxuan<=disp10;
default:dxuan<=disp0;
endcase
end
其中,cnt_symin变量就需要根据不同的情况进行自动变更,利用case语句根据输入密码的位数来切换数码管显示的数字(即使输最新输入的密码显示在最右侧):
always@(posedge clk)
if(state_lock==1) begin //密码锁开关打开
case(rece_cnt)
3'd0:begin
case(disp_state)
2'd0:begin wxuan<=4'b0111; cnt_symin<=4'd0;end
2'd1:begin wxuan<=4'b1011; cnt_symin<=4'd0;end
2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
endcase
end
3'd1:begin
case(disp_state)
2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in1;end
2'd1:begin wxuan<=4'b1011; cnt_symin<=4'd0;end
2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
endcase
end
3'd2:begin
case(disp_state)
2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in2;end
2'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in1;end
2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
endcase
end
3'd3:begin
case(disp_state)
3'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in3;end
3'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in2;end
3'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in1;end
3'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
endcase
end
3'd4:begin
case(disp_state)
3'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in4;end
3'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in3;end
3'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in2;end
3'd3:begin wxuan<=4'b1110; cnt_symin<=disp_in1;end
endcase
end
default:begin
case(disp_state)
2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in4;end
2'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in3;end
2'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in2;end
2'd3:begin wxuan<=4'b1110; cnt_symin<=disp_in1;end
endcase
end
endcase
end
else wxuan<=4'b1111;
由于采用动态数码管的显示,所以需要对位数码管进行不断的切换,需要做一个分频模块对每位数码管循环显示:
/*********************分频*****************/
always@(posedge clk) //位数码管切换控制
begin
if(count<28'd20_0000)
count<=count+28'd1;
else begin
count<=28'd0;
if(disp_state<=2'd3) disp_state<=disp_state+1;
else disp_state<=0;
end
end
该模块完整程序:
module display(clk,state_lock,rece_cnt,
disp_in1,disp_in2,disp_in3,disp_in4,
dxuan, wxuan);
input clk;
input state_lock;
input [2:0] rece_cnt;
input [3:0] disp_in1;
input [3:0] disp_in2;
input [3:0] disp_in3;
input [3:0] disp_in4;
output reg [7:0] dxuan;
output reg [5:0] wxuan;
reg [27:0] count; //位数码管分频计数器
reg [1:0] disp_state; //位数码管切换状态
reg [3:0] cnt_symin;
//*************数码管数值定义******************//
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
parameter disp10=8'b1011_1111;
/*********************分频*****************/
always@(posedge clk) //位数码管切换控制
begin
if(count<28'd20_0000)
count<=count+28'd1;
else begin
count<=28'd0;
if(disp_state<=2'd3) disp_state<=disp_state+1;
else disp_state<=0;
end
end
always@(posedge clk)
if(state_lock==1) begin //密码锁开关打开
case(rece_cnt)
3'd0:begin
case(disp_state)
2'd0:begin wxuan<=4'b0111; cnt_symin<=4'd0;end
2'd1:begin wxuan<=4'b1011; cnt_symin<=4'd0;end
2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
endcase
end
3'd1:begin
case(disp_state)
2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in1;end
2'd1:begin wxuan<=4'b1011; cnt_symin<=4'd0;end
2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
endcase
end
3'd2:begin
case(disp_state)
2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in2;end
2'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in1;end
2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
endcase
end
3'd3:begin
case(disp_state)
3'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in3;end
3'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in2;end
3'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in1;end
3'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
endcase
end
3'd4:begin
case(disp_state)
3'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in4;end
3'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in3;end
3'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in2;end
3'd3:begin wxuan<=4'b1110; cnt_symin<=disp_in1;end
endcase
end
default:begin
case(disp_state)
2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in4;end
2'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in3;end
2'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in2;end
2'd3:begin wxuan<=4'b1110; cnt_symin<=disp_in1;end
endcase
end
endcase
end
else wxuan<=4'b1111;
always@(posedge clk)
begin
case(cnt_symin)
4'd1:dxuan<=disp1;
4'd2:dxuan<=disp2;
4'd3:dxuan<=disp3;
4'd4:dxuan<=disp4;
4'd5:dxuan<=disp5;
4'd6:dxuan<=disp6;
4'd7:dxuan<=disp7;
4'd8:dxuan<=disp8;
4'd9:dxuan<=disp9;
4'd0:dxuan<=disp0;
4'd10:dxuan<=disp10;
default:dxuan<=disp0;
endcase
end
endmodule
总结
在做这个工程的时候,出现了各种各样的问题。解决时间最长的是矩阵键盘的模块,由于矩阵键盘的错误导致后续模块都无法正常工作。
现在虽然能实现大多数功能,但仍然不能保证他的功能都是完整无错的。逻辑很复杂,思维难免出现漏洞,所以倘若发现有不正确的地方,请不吝告知,好及时更改。
【注】个人学习笔记,请不吝赐教!