课程名称:数字逻辑与数字系统设计
任课教师:李琼
作业题目:4位电子密码锁
完成人:HIT的柯小信
报告日期:2020年 12月 13日
设计要求
- 计一个开锁密码至少为4位数字(或更多)的密码锁。
- 当开锁按扭开关(可设置8位或更多,其中只有4位有效,其余位为虚设)的输入代码等于所设密码时启动开锁控制电路,并且用绿灯亮、红灯灭表示开锁状态
- 从第一个按扭触动后的5秒内若未能将锁打开,则电路自动复位并发出报警信号,同时用绿灯灭、红灯亮表示关锁状态。
- (附加功能)通过十进制输入、重置密码,并通过七段数码管显示数码及倒计时。
工作原理及系统方框图
- 十进制数字的输入:由于开发板按键资源数量有限,无法实现数字0-9与按键的一一对应,只能采用“按一下,增加1”的方式实现数字的输入。这里的实现方式是利用mod10计数器控制输入数字,借助拨码按键模拟时钟上升沿,计数器每接收到一个上升沿(即拨码按键拨动了一下),就让计数器+1,数字9加一后变为数字0,这样就完成了十进制0~9数码的输入。
- 电路不同状态的控制:由于密码锁需要实现“开锁”和“密码重置”两个主要功能,电路需要定义两种状态,一是开锁状态,二是重置密码状态。通过两个拨码按键来控制这两种状态。当其中一个拨码按键输出高电平时,让电路处于开锁状态,此时可以通过输入四位十进制数码来检测密码,当输入正确时开锁成功,对应指示灯亮,否则熄灭。当重置密码对应的拨码按键输出高电平时,如果同时锁处于打开状态,就允许通过修改数码来重置密码,否则修改数码无效。
- 七段数码管的显示:单独编写一个数码管模块,将输入的数字(8421BCD码)译成七段数码管的驱动信号(7位)数据输出到数码管位选端口,另用一个复位信号控制数码管输出置0。4位数码需要动态显示时,不断扫描四个端口,改变位选信号完成4位数码的动态显示。
- 开锁倒计时:通过一个计数器不断记录系统时钟上升沿数量,这里定义每250000000个上升沿对应一秒钟。当记录到0个上升沿时,倒计时剩余5秒;当记录到250000000个上升沿时,倒计时剩余4秒;当记录到500000000个上升沿时,倒计时剩余3秒;当记录到750000000个上升沿时,倒计时剩余2秒……当记录到1250000000个上升沿时,倒计时剩余0秒,电路需要复位。
各部分模块具体功能及设计思路
1. 数码管显示模块
输入数字对应的8421BCD,据此译出对应的7段数码管驱动信号并输出到位选端口。

2. 模10计数器
当输入信号start为1时开开始计数,根据时钟上升沿来完成计数加一,同时通过rst信号来控制计数器复位(置0)。
 ## 3. 输入控制模块
对于不同的输入信号,需先判断倒计时是否为零。如果倒计时为零则复位信号输出高电平,否则复位信号输出低电平并令倒计时计数不断增加。当电路不处于复位状态时,检测“开锁按键”和“重置密码”两个开关是否输出高电平。如果“开锁按键”未输出高电平,则令倒计时为5秒;否则的话,令倒计时不断减少,并判断输入的4个数码是否等于当前存储的密码。如果“重置密码”按键输出高电平,需要同时判断电路的开锁状态是否为真(即锁是否成功打开),只有当电路已经开锁成功时才能修改密码。修改密码的电路只需将输入的四个数码同时赋值给电路中保存的四位密码即可。
 ## 4. 数码管动态显示模块
从《0-EGo1资料文档-v1.1》[1]中可以看到说明,实验开发板的8个数码管分成左右两部分,分别用使用不同的段选信号控制,这样的架构恰好可以满足本实验的要求。令右侧的四个数码管用于显示4位数码,而左侧4个数码管只使用最左边一个,用于显示倒计时。左侧4个数码管的位选信号只需始终选中最左侧的数码管即可,左1数码管显示的数值就是倒计时的剩余秒数。而右侧4个数码管需要不断扫描4个位选端口,每个数码管位选有效时同时输入对应位置数码的段选信号即可。

调试过程
1. 端口功能说明
端口 |
类型 |
位宽 |
功能 |
|
|
|
时钟信号 |
|
|
|
重置密码命令信号 |
|
|
|
开锁命令信号 |
|
|
|
输入四个数码 |
|
|
|
右侧 |
|
|
|
右侧 |
|
|
|
左侧 |
|
|
|
左 |
|
|
|
右 |
|
|
|
左 |
2. 引脚约束列表
端口 |
方向 |
管脚号 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3. 上板调试
(1) 电路初态
(2) 设置密码
(3) 尝试开锁,倒计时结束开锁失败,电路复位,指示灯亮
(4) 开锁成功,指示灯亮
设计结论
基于FPGA的电子密码锁已经是现代生活中经常用到的工具之一,用于各类保险柜、房门、防盗门等等。用电子密码锁代替传统的机械式密码锁,克服了机械式密码锁密码量少、安全性能差的缺点。由于采用的是可编程逻辑器件FPGA,使得系统有相当大的灵活性,随时可以进行硬件升级、扩展。而且由于硬件可升级,还可随时增加密码位数或增加新的功能,使得密码锁有更高的安全性、可靠性和方便性。
课题主要解决系统硬件和软件两方面的问题。硬件方面要解决FPGA可编程器件与其外围电路的接口设计的问题;软件方面主要问题是利用Verilog HDL语言完成基于FPGA的电子密码锁的编程问题。除此之外,程序还要完成基本的密码开锁功能。本设计是由FPGA可编程逻辑器件编程实现的控制电路,具体有按键指示、密码有效指示、控制开锁、控制复位、倒计时显示等功能。它具有安全可靠、连接方便、简单易用、结构紧凑、系统可扩展性好等特点。
心得与总结
比起C++、Java等高级程序设计语言,Verilog HDL硬件描述语言的使用要复杂一些,利用FPGA开发板开发也不同于利用实验箱进行接线,整个设计流程还是比较繁琐,系统架构要求也比较高。通过这样一个电子密码锁的设计,基本掌握了Verilog HDL硬件描述语言,掌握了小型硬件系统的设计方法,尤其是学会了FPGA开发板上七段数码管的使用。本项目设计的电子密码锁同时存在一些优点与不足。
优点:密码可以通过十进制数字输入,且能显示在七段数码管上,并允许用户自行修改密码,且修改密码时加了一道“控制门”,即只有当解锁成功时才能修改密码。
不足:由于板载资源有限,无法实现十进制0-9数码与按键的一一对应,只能通过不断加一的方式输入数码,输入过程比较繁琐。
参考文献
[1]依元素科技.EGO1_UserManual_v1.2[OL],2017
[2]熊军洲.基于FPGA的电子密码锁控制电路设计[J].石家庄职业技术学院学报,2018,30(06):11-15.
[3]汪国强,李尚甫,王飞,谢丽丽,王钦,孙柏.基于FPGA的四位电子密码锁设计与实现[J].无线电通信技术,2016,42(04):95-98.
附录(工程代码)
//文件名:main.v
//
// Company:
// Engineer:
//
// Create Date: 2020/12/13 20:42:07
// Design Name:
// Module Name: main
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module main(clk,clk0,clk1,clk2,clk3,rst,setpw,judge,pos,led,pos_countdown,led_countdown,staus,g_rst);
input clk;//系统时钟
input clk0,clk1,clk2,clk3;//右侧4个拨码按键模拟的时钟信号,用于给数码+1
input rst;//左1拨码按键的复位信号
input setpw;//左数第3个拨码按键,设置密码状态
input judge;//左数第2个拨码开关,输入密码模式
output reg [3:0] pos;//右侧右侧4个数码管位选
output [6:0] led;//右侧4个数码管段选,表示当前数码管显示的数字字形
output [6:0] led_countdown;//左侧4个数码管段选,显示倒计时数码
output reg pos_countdown;//左1数码管位选,用于显示倒计时
output reg staus;//右1LED,表示开锁状态
output reg g_rst; //左1LED,表示复位状态
integer clock_led,clock_countdown;//计算上升沿数量,用于分频
wire [3:0] q0,q1,q2,q3;//存储当前输入的四个数码
reg [3:0] pw0,pw1,pw2,pw3;//存储当前保存的四位密码
reg [3:0] num;//存储当前位选下数码管显示的数字
reg timeout;//分频给数码管动态显示
reg [3:0] countdown;//倒计时,这里设为5s倒计时
reg input_start,input_rst;
initial
begin
countdown=4'b0101; //初始时设倒计时为5s
pos=4'b0001; //右侧四个数码管位选先选到最右侧一个数码管
timeout=0;
clock_led=0;//分频,使数码管能动态显示
clock_countdown=0; //计数,控制倒计时
pos_countdown=1; //左侧4个数码管段选始终选到最左侧一个
staus=1; //开锁状态先打开
g_rst = 0; //先不处于复位状态
end
//右侧四个拨码按键的输入,通过模10计数器输入十进制0~9数码
mod10counter u0(clk0,input_rst,input_start,q0);
mod10counter u1(clk1,input_rst,input_start,q1);
mod10counter u2(clk2,input_rst,input_start,q2);
mod10counter u3(clk3,input_rst,input_start,q3);
//右侧四个数码管显示4位密码,led为右侧数码管段选
smg s0(clk,num,input_rst,led);
//左1数码管显示倒计时,led_countdown为左侧数码管段选
smg s1(clk,countdown,0,led_countdown);
always @(posedge clk) //输入控制模块
begin
if(countdown==4'b0000)//5s倒计时结束,电路复位
begin
input_start<=0; //停止拨码按键输入
input_rst<=1; //数码管复位信号置1,令所有数码管显示0
g_rst <= 1; //复位信号置1
end
else
begin
g_rst <= 0; //正常情况下复位信号置0
input_start<=setpw|judge; //要么处于重置密码状态,要么处于输入密码状态,都是处于输入状态
input_rst<=rst|((~judge)&(~setpw)); //电路总复位信号为1或同时打开输入和重置密码状态,令电路输入复位
end
//分频
if(clock_led==200000)
begin
timeout<=1;
clock_led<=0;
end
else
begin
timeout<=0;
clock_led<=clock_led+1;
end
if(judge&&countdown!=4'b0000&&~staus) //处于开锁状态
begin
if(clock_countdown!=1250000000)
begin
clock_countdown<=clock_countdown+1;
end
case(clock_countdown)
1250000000:
countdown<=4'b0000;
1000000000:
countdown<=4'b0001;
750000000:
countdown<=4'b0010;
500000000:
countdown<=4'b0011;
250000000:
countdown<=4'b0100;
0:
countdown<=4'b0101;
endcase
end
else if(~judge) //未处于开锁状态,倒计时置5
begin
countdown<=4'b0101;
clock_countdown<=0;
end
//重置密码模块
if(setpw && staus)//只有当锁打开时才能重置密码
begin
pw0<=q0;
pw1<=q1;
pw2<=q2;
pw3<=q3;
end
end
//扫描显示右侧4个七段数码管
always @(posedge timeout)
begin
if (pos==4'b0001)
begin
pos<=4'b0010;
num<=q1;
end
else if(pos==4'b0010)
begin
pos<=4'b0100;
num<=q2;
end
else if(pos==4'b0100)
begin
pos<=4'b1000;
num<=q3;
end
else if(pos==4'b1000)
begin
pos<=4'b0001;
num<=q0;
end
end
//判断开锁模块
always @(*)
begin
if(judge)
begin
if(pw0==q0&&pw1==q1&&pw2==q2&&pw3==q3&&countdown!=4'b0000)
staus<=1;
else
staus<=0;
end
end
endmodule
//文件名:smg.v
module smg(clk,num,rst,out);
input clk;//时钟信号
input [3:0] num;//输入十进制数码的8421BCD码
input rst;//复位信号(置0)
output reg [6:0] out;//输出七段数码管对应的7位信号
always @(posedge clk or posedge rst)
begin
if(rst)
out<=7'b1111110;//收到复位信号,数码管输出置0
else
begin
case(num) //将0-9九个数译成对应的七段数码管取值
4'b0000:
out <= 7'b1111110;
4'b0001:
out <= 7'b0110000;
4'b0010:
out <= 7'b1101101;
4'b0011:
out <= 7'b1111001;
4'b0100:
out <= 7'b0110011;
4'b0101:
out <= 7'b1011011;
4'b0110:
out <= 7'b1011111;
4'b0111:
out <= 7'b1110000;
4'b1000:
out <= 7'b1111111;
4'b1001:
out <= 7'b1111011;
default:
out <= 7'b1111110;
endcase
end
end
endmodule
//文件名:mod10counter.v
module mod10counter(clk,rst,start,out);
//mod10计数器输入十进制0-9数码
input clk;//时钟信号,上升沿时输出+1
input rst;//复位信号(置0)
input start;//开始计数
output reg [3:0] out;//计数器输出(8421BCD)
always @(posedge clk or posedge rst) //遇到上升沿或复位信号
begin
if (rst)
out <= 4'b0000;//set q=4'b0000
else if(start) //start valid
begin
if(out==4'b1001)
out <=4'b0000;//计数9时,次态为0
else
out<=out+4'b0001;//计数0-8时,输出加1
end
end
endmodule
#文件名:con_lock.xdc
#引脚约束
set_property IOSTANDARD LVCMOS33 [get_ports {led[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_countdown[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_countdown[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_countdown[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_countdown[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_countdown[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_countdown[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led_countdown[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pos[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pos[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pos[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {pos[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports clk0]
set_property IOSTANDARD LVCMOS33 [get_ports clk1]
set_property IOSTANDARD LVCMOS33 [get_ports clk2]
set_property IOSTANDARD LVCMOS33 [get_ports clk3]
set_property IOSTANDARD LVCMOS33 [get_ports judge]
set_property IOSTANDARD LVCMOS33 [get_ports pos_countdown]
set_property IOSTANDARD LVCMOS33 [get_ports rst]
set_property IOSTANDARD LVCMOS33 [get_ports setpw]
set_property IOSTANDARD LVCMOS33 [get_ports staus]
set_property IOSTANDARD LVCMOS33 [get_ports g_rst]
#右1LED,灯亮表示锁处于打开状态
set_property PACKAGE_PIN K2 [get_ports staus]
#左1LED,灯亮表示倒计时结束,电路复位
set_property PACKAGE_PIN F6 [get_ports g_rst]
#左数第3个拨码开关,拨上表示处于设置密码模式
set_property PACKAGE_PIN P3 [get_ports setpw]
#左数第1个拨码开关,用于电路复位
set_property PACKAGE_PIN P5 [get_ports rst]
#左数第2个拨码开关,拨上表示处于输入密码模式
set_property PACKAGE_PIN P4 [get_ports judge]
#左1数码管位选,用于显示倒计时
set_property PACKAGE_PIN G2 [get_ports pos_countdown]
#右侧四个拨码开关,模拟上升沿用于给对应数码+1
set_property PACKAGE_PIN R2 [get_ports clk3]
set_property PACKAGE_PIN M4 [get_ports clk2]
set_property PACKAGE_PIN N4 [get_ports clk1]
set_property PACKAGE_PIN R1 [get_ports clk0]
#系统时钟
set_property PACKAGE_PIN P17 [get_ports clk]
#数码管位选(右侧4个,用于4位密码显示)
set_property PACKAGE_PIN G6 [get_ports {pos[0]}]
set_property PACKAGE_PIN E1 [get_ports {pos[1]}]
set_property PACKAGE_PIN F1 [get_ports {pos[2]}]
set_property PACKAGE_PIN G1 [get_ports {pos[3]}]
#倒计时显示数码管(左侧4个)段选
set_property PACKAGE_PIN B4 [get_ports {led_countdown[6]}]
set_property PACKAGE_PIN A4 [get_ports {led_countdown[5]}]
set_property PACKAGE_PIN A3 [get_ports {led_countdown[4]}]
set_property PACKAGE_PIN B1 [get_ports {led_countdown[3]}]
set_property PACKAGE_PIN A1 [get_ports {led_countdown[2]}]
set_property PACKAGE_PIN B3 [get_ports {led_countdown[1]}]
set_property PACKAGE_PIN B2 [get_ports {led_countdown[0]}]
#4位密码数码管(右侧4个)段选
set_property PACKAGE_PIN D4 [get_ports {led[6]}]
set_property PACKAGE_PIN E3 [get_ports {led[5]}]
set_property PACKAGE_PIN D3 [get_ports {led[4]}]
set_property PACKAGE_PIN F4 [get_ports {led[3]}]
set_property PACKAGE_PIN F3 [get_ports {led[2]}]
set_property PACKAGE_PIN E2 [get_ports {led[1]}]
set_property PACKAGE_PIN D2 [get_ports {led[0]}]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk0_IBUF]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk1_IBUF]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk2_IBUF]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk3_IBUF]