本文篇幅可能有些长,请耐心阅读
本数字密码锁采用矩阵键盘作为十六位按键输入,其中,十位数字键,六位功能按键。密码锁具有清零,密码重置,错误警报等多项功能,采用数码管动态显示的方法显示信息。
1、FPGA开发板一块(需要有至少四位数码管)
2、矩阵键盘一块
3、下载线,电源线等以及杜邦线若干
六位功能按键:
首先,作为一个功能较多的工程而言,其内在逻辑必然十分复杂。所以在建立工程时首先考虑进行模块划分,将整个工程分为几个模块,尽量降低每个模块之间的耦合性,提高模块的独立性,这样才会使逻辑更加清晰,程序的可读性更好,以及更加便于修改。
本密码锁具体分为四个模块:
1、矩阵键盘扫描模块:要求将矩阵键盘的十六个值分别进行输出
2、按键功能模块:对于矩阵键盘输出的十六个值进行划分,分别为十位数字键以及六位功能按键并对下一个模块进行输出
3、中心处理模块:对于各个按键值接受以后,按照各个按键功能进行相应的信号处理,最后将其结果输出给下一个模块
4、结果显示模块:接收上一个模块的结果,并对结果进行数码管的显示
按照划分的几个模块进行顶层模块的编写,顶层模块的编写放到最后,需要根据各个模块的信号接口进行连接。
module top_lock(clk,dxuan,wxuan,led,beep,row,col);
input clk;
input [3:0] row;
output beep;
output [3:0] col;
output led;
output [5:0] wxuan;
output [7:0] dxuan;
wire [4:0] key_out;
wire [8:0] number_key;
wire [3:0] row;
wire [3:0] cow;
wire lock;
wire check;
wire reset;
wire clear_flag;
wire state_lock;
wire [3:0]rece_cnt;
wire rece_flag;
wire [3:0] disp_in1;
wire [3:0] disp_in2;
wire [3:0] disp_in3;
wire [3:0] disp_in4;
key_board key_board1(
.clk (clk ),
.row (row ),
.col (col ),
.key_out (key_out)
);
key_function key_function1(
.clk ( clk ),
.key_out (key_out),
.state_lock (state_lock),
.clear_flag (clear_flag),
.number_key (number_key),
.lock (lock),
.reset (reset),
.check (check),
.rece_cnt (rece_cnt),
.rece_flag (rece_flag)
);
lock_ctrl lock_ctrl1(
.clk (clk),
.check (check),
.state_lock (state_lock),
.clear_flag (clear_flag),
.lock (lock),
.reset (reset),
.rece_cnt (rece_cnt),
.rece_flag (rece_flag),
.number_key (number_key),
.led (led),
.beep (beep),
.disp_in1 (disp_in1),
.disp_in2 (disp_in2),
.disp_in3 (disp_in3),
.disp_in4 (disp_in4)
);
display display1(
.clk (clk),
.rece_cnt (rece_cnt),
.disp_in1 (disp_in1),
.disp_in2 (disp_in2),
.disp_in3 (disp_in3),
.disp_in4 (disp_in4)
.state_lock (state_lock),
.dxuan (dxuan),
.wxuan (wxuan)
);
endmodule
关于矩阵键盘的原理,在上一篇中详细写过,在这里直接调用。上一篇的地址:
https://blog.csdn.net/zsisinterested/article/details/117195234
module key_board(
input clk,
input [3:0] row, // 矩阵键盘 行
output reg [3:0] col, // 矩阵键盘 列
output reg [3:0] key_out
);
reg [19:0] cnt; //计数器
reg key_pressed_flag; // 键盘按下标志
reg [3:0] col_val, row_val; // 列行值寄存器
reg key_clk; //分频脉冲
//***************分频*******************//
always @ (posedge clk)
begin
if(cnt<=20'd1048_500) //分频21ms
cnt <= cnt + 1'b1;
else begin
cnt<=0;
key_clk<=~key_clk;
end
end
//*******************状态机定义*********************//
parameter IDLE = 6'b000_001; // 没有按键按下
parameter SCAN_COL0 = 6'b000_010; // 扫描第0列
parameter SCAN_COL1 = 6'b000_100; // 扫描第1列
parameter SCAN_COL2 = 6'b001_000; // 扫描第2列
parameter SCAN_COL3 = 6'b010_000; // 扫描第3列
parameter KEY_PRESSED = 6'b100_000; // 有按键按下
reg [5:0] current_state, next_state; // 现态、次态
always @ (posedge key_clk)
current_state <= next_state;
//************状态机切换************************//
always @ *
case (current_state)
IDLE : // 没有按键按下
if (row != 4'hF)
next_state = SCAN_COL0;
else
next_state = IDLE;
SCAN_COL0 : // 扫描第0列
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = SCAN_COL1;
SCAN_COL1 : // 扫描第1列
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = SCAN_COL2;
SCAN_COL2 : // 扫描第2列
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = SCAN_COL3;
SCAN_COL3 : // 扫描第3列
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = IDLE;
KEY_PRESSED : // 有按键按下
if (row != 4'hF)
next_state = KEY_PRESSED;
else
next_state = IDLE;
endcase
//***************赋列值***********************//
always @ (posedge key_clk)
case (next_state)
IDLE : // 没有按键按下
begin
col <= 4'h0;
key_pressed_flag <= 0; // 清键盘按下标志
end
SCAN_COL0 : // 扫描第0列
col <= 4'b1110;
SCAN_COL1 : // 扫描第1列
col <= 4'b1101;
SCAN_COL2 : // 扫描第2列
col <= 4'b1011;
SCAN_COL3 : // 扫描第3列
col <= 4'b0111;
KEY_PRESSED : // 有按键按下
begin
col_val <= col; // 锁存列值
row_val <= row; // 锁存行值
key_pressed_flag <= 1; // 置键盘按下标志
end
endcase
//********************键值输出*************************//
always @ (posedge key_clk)
if (key_pressed_flag)
case ({col_val, row_val})
8'b1110_1110 : key_out <= 4'h0;
8'b1110_1101 : key_out <= 4'h4;
8'b1110_1011 : key_out <= 4'h8;
8'b1110_0111 : key_out <= 4'hC;
8'b1101_1110 : key_out <= 4'h1;
8'b1101_1101 : key_out <= 4'h5;
8'b1101_1011 : key_out <= 4'h9;
8'b1101_0111 : key_out <= 4'hD;
8'b1011_1110 : key_out <= 4'h2;
8'b1011_1101 : key_out <= 4'h6;
8'b1011_1011 : key_out <= 4'hA;
8'b1011_0111 : key_out <= 4'hE;
8'b0111_1110 : key_out <= 4'h3;
8'b0111_1101 : key_out <= 4'h7;
8'b0111_1011 : key_out <= 4'hB;
8'b0111_0111 : key_out <= 4'hF;
endcase
endmodule
首先是根据模块功能来确定该模块的输入输出口。本模块用于对按键的功能进行划分,那么输入一定是来自矩阵键盘模块。
输入:
input clk;
input [4:0] key_out;
对十六位输入进行功能的划分,最终的输出为六位功能键以及一位数字键。
输出:
output reg [3:0] number_key; //密码数字(可在0-9切换)
output reg state_lock; //密码锁状态(开机或关机,包括了on和off)
output reg lock; //密码锁定
output reg reset; //密码重置
output reg check; //密码确认
output reg clear_flag; //清除输入
除了正常的功能输出外,还需要输出其他信号辅助其他模块进行处理:
output reg [3:0] rece_cnt; //接收密码位数
output reg rece_flag; //接收标志
完成了接口上的定义以后,接着就是定义在该模块中用到的各种变量。待变量定义结束,开始做逻辑上的编写。对于接收到的十六位按键数值,用case语句将数值进行划分,按照键盘的排列顺序将左上角的九位按键分别定义为数值按键,最后一列第二行的按键定义为数值位0的按键,其余按键为功能按键。
当按键被松开,即键值输出不在复制范围,就将所有的功能按键复位,即使用功能键的上升沿进行触发后续信号
always@(posedge clk)
begin
case(key_out)
5'd 0:off<=1;
5'd 1: on<=1;
5'd 2:begin number_key<=4'd0;rece_flag<=1; end
5'd 3:clear<=1;
5'd 4:check<=1;
5'd 5:begin number_key<=4'd9;rece_flag<=1; end
5'd 6:begin number_key<=4'd8;rece_flag<=1; end
5'd 7:begin number_key<=4'd7;rece_flag<=1; end
5'd 8:lock<=1;
5'd 9:begin number_key<=4'd6;rece_flag<=1; end
5'd10:begin number_key<=4'd5;rece_flag<=1; end
5'd11:begin number_key<=4'd4;rece_flag<=1; end
5'd12:reset<=1;
5'd13:begin number_key<=4'd3;rece_flag<=1; end
5'd14:begin number_key<=4'd2;rece_flag<=1; end
5'd15:begin number_key<=4'd1;rece_flag<=1; end
default:begin
rece_flag=0;
if(clear_flag==1) number_key<=4'd0;
else number_key<=number_key;
off<=0;
on<=0;
clear<=0;
check<=0;
lock<=1;
reset<=0;
end
endcase
end
键值按功能划分共可分出七个变量,其中包括六个功能变量以及一个数值变量。但是可以看出来几个变量中有些是相关的,比如on与off,当on有效时off就无效,所以我们考虑用一个变量来代替几个互相关联的变量:
on与off用state_lock来代替,state_lock=1时密码锁打开,否则关闭
//开关控制
always@(posedge clk)
begin
if(on==1) state_lock<=1;
else if(off==1) state_lock<=0;
else state_lock<=state_lock;
end
清零信号的控制:
由于清零信号收到清零按键clear以及开关机的控制,所以需要对清零信号进行进一步的设置
//清零标志控制
always@(posedge clk)
begin
if(state_lock==0) //当关机时,清零
clear_flag<=1;
else if(clear==1)
clear_flag<=1; //按下清零按键时,清零
else
clear_flag<=0; //其余时间不清零
end
密码锁存在一个隐性要求就是,当输入四位数字以后再输入第五位数字时密码不在发生变化,因此要规定输入数字的位数(输入数字位数也会受到清零信号以及开关机的制约)
//***********输入密码位数变换控制*************//
always@(posedge rece_flag or posedge clear_flag)
begin
if(clear_flag==1) rece_cnt<=4'd0;
else begin
if(state_lock==1) begin
if(rece_cnt==4'd5) rece_cnt<=rece_cnt;
else if(rece_cnt==4'd4) rece_cnt<=4'd5;
else rece_cnt<=rece_cnt+4'd1;
end
else rece_cnt<=4'd0;
end
end
下面为该模块完整代码:
module key_function(clk,key_out,state_lock,number_key,
lock,reset,rece_flag,
check,rece_cnt,clear_flag);
input clk;
input [4:0] key_out;
output reg [3:0] number_key;
output reg state_lock;
output reg clear_flag;
output reg lock;
output reg reset;
output reg check;
output reg [3:0] rece_cnt;
output reg rece_flag;
reg on;
reg off;
reg clear;
initial
begin
rece_cnt=4'd0;
end
always@(posedge clk)
begin
case(key_out)
5'd 0:off<=1;
5'd 1: on<=1;
5'd 2:begin number_key<=4'd0;rece_flag<=1; end
5'd 3:clear<=1;
5'd 4:confirm<=1;
5'd 5:begin number_key<=4'd9;rece_flag<=1; end
5'd 6:begin number_key<=4'd8;rece_flag<=1; end
5'd 7:begin number_key<=4'd7;rece_flag<=1; end
5'd 8:c_lock<=1;
5'd 9:begin number_key<=4'd6;rece_flag<=1; end
5'd10:begin number_key<=4'd5;rece_flag<=1; end
5'd11:begin number_key<=4'd4;rece_flag<=1; end
5'd12:reset<=1;
5'd13:begin number_key<=4'd3;rece_flag<=1; end
5'd14:begin number_key<=4'd2;rece_flag<=1; end
5'd15:begin number_key<=4'd1;rece_flag<=1; end
default:begin
rece_flag=0;
if(clear_flag==1) number_key<=4'd0;
else number_key<=number_key;
off<=0;
on<=0;
clear<=0;
confirm<=0;
c_lock<=1;
reset<=0;
end
endcase
end
//开关控制
always@(posedge clk)
begin
if(on==1) state_lock<=1;
else if(off==1) state_lock<=0;
else state_lock<=state_lock;
end
//清零标志控制
always@(posedge clk)
begin
if(state_lock==0)
clear_flag<=1;
else if(clear==1)
clear_flag<=1;
else
clear_flag<=0;
end
//***********输入密码位数变换控制*************//
always@(posedge rece_flag or posedge clear_flag)
begin
if(clear_flag==1) begin
rece_cnt<=4'd0;
end
else begin
if(state_lock==1) begin
if(rece_cnt==4'd5) rece_cnt<=rece_cnt;
else if(rece_cnt==4'd4) rece_cnt<=4'd5;
else rece_cnt<=rece_cnt+4'd1;
end
else rece_cnt<=4'd0;
end
end
endmodule
由于该工程涉及到的逻辑信号太多了,脑子一时转不过来就可能会出现很多的BUG,所以一旦发现有错误会及时更改,也请各位不吝赐教!
剩余模块在下一篇中写出!
地址:数字密码锁(下)