EDA课程设计报告——数字密码锁
前言
大二上做的一个课程设计,一开始选这个题的时候看到网上有挺多代码的还以为可以参考参考,不过自己写的时候才发现许多的代码跟自己的题目没太大关系,然后自己写,就开始了快半个月的折磨(哈哈哈~),因为临近又期末考试,搞得我饭吃不下觉睡不好,操作系统的课设也没做几个(抱怨一下下呜呜呜)。现在想着把自己的设计给学弟学妹们参考一下,别像我一样被折磨。虽然我的设计与题目要求的还是有点出入,不过还是拿的优秀。具体使用说明放在后面。
一、设计题目说明
题目十:数字密码锁设计(平台实现)★★
完成一简易密码锁的设计,实现6位密码的设定与开锁。
1)使用6个按键进行密码输入,K0-K5,分别代表数字键0-5,用右边6个数码管显示;
2)密码初始值为555555;开锁方式:xxxxxx(x代表密码数字,位数等于6位);上电后,初始显示:"PP------";输入一个数字就在最右数码管显示,前面的数字均左移一个数码管。输入正确显示“–OPEN–”,输入错误显示“–EEEE–”。
3)设计一个重新输入按钮K6,在输入未全或者错误(没达到3次)时,恢复输入,按下后显示“PP------”
4)工作时钟1khz;连续3次输错密码则锁死,只有重启电路;连续2次错误点亮警报灯。
5 ) 用按键k7设置密码,设定方式:旧密码,输入两次,输入前显示为“OP------”,正确后提示输入新密码:“NP------”,连续输入2次。以上出错均显示“–EEEE–”,可按K7恢复设置,或者K6。
二、设计题目与要求
- 学会在Quartus Ⅱ环境中运用VHDL语言设计方法构建具有一定逻辑功能的模块,并能运用图形设计方法完成顶层原理图的设计。
- 掌握数字密码锁的主要功能与在电路板中的实现。
- 运用Quartus Ⅱ软件中的仿真功能对所设计的数字密码锁的各个模块及顶层电路的功能进行仿真分析。
三、总体设计
3.1基本原理
- 设计输入
本次大作业使用文本输入的方式,采用VHDL语言编写各个模块:消抖模块、动态扫描显示模块、密码比较模块、密码锁控制模块、计数模块、译码模块、密码寄存器模块。将每个模块生成元件加入元件库,最后将电路得到设计模块采用原理图的方式输入。这种输入方式直观,且便于电路的观察与修改。 - 设计处理
本次大作业使用文本输入的方式,采用VHDL语言编写各个模块:消抖模块、动态扫描显示模块、密码比较模块、密码锁控制模块、计数模块、译码模块、密码寄存器模块。将每个模块生成元件加入元件库,最后将电路得到设计模块采用原理图的方式输入。这种输入方式直观,且便于电路的观察与修改。 - 设计验证
设计验证即时序仿真和功能仿真。器件编程,板载测试
器件编程是指将设计处理中产生的编程数据下载到具体的可编程器件中。如果之前的步骤都满足设计的要求,就可以将适配器产生的配置或下载文件通过CPLD/FPGA编程器或下载电缆载入目标芯片CPLD或FPGA中。
本次使用芯片为:FPGA10K20TC144-4
开发环境:Quartus II 9.0 sp2 Web Edition
3.2总体结构分析与设计
根据层次化设计理论,该设计问题自顶向下可以分为消抖模块、动态扫描显示模块、密码比较模块、密码锁控制模块、计数模块、译码模块、密码寄存器模块。
四、实验与仿真
4.1消抖模块
因为按下FPGA的按键时,弹簧片最低震动频率不会低于50 HZ,所以,两次下降沿出现的时间差不会超过20ms,也就是说,当我们检测到一次下降沿/上升沿之后,再在20ms内检测是否有下降沿/上升沿产生,如果有,则重新检测,如果没有,则说明按键稳定。(一般而言,按钮被释放比按钮被按下去的反弹要少很多)
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity E_shaking is
port(clk:in std_logic; --时钟频率,基准频率为1000HZ,对应实验板pin128
DIN:in std_logic; --按键
Dout:out std_logic); --输出,若为1,表示按键按下,若为0,表示按键未按下
end entity;
architecture one of E_shaking is
signal count:integer range 0 to 20; --计数
begin
process(clk)
begin
if(rising_edge(clk)) then --上升沿
if(DIN='0') then --按键初始为1
count<=count+1;
if(count=20) then --对应现实20ms
Dout<='1'; --判断是否有一段持续20ms的'0'状态,若有即代表按下
else
Dout<='0'; --若无代表没按下
end if;
else
count<=0;
end if;
end if;
end process;
end one;
4.2 密码比较模块
将输入的两个24位二进制代码比较,相同输出1,不同输出0。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity compare is
port(acc : in std_logic_vector(23 downto 0);
old : in std_logic_vector(23 downto 0);
yes : out std_logic);
end compare;
architecture beha of compare is
begin
yes <= '1' when acc = old else '0';
end beha;
4.3译码模块
将BCD码译为七段数码管显示
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity decode is
port(datain:in std_logic_vector(3 downto 0);
dataout:out std_logic_vector(6 downto 0));
end entity;
architecture beha of decode is
begin
process(datain)
begin
case datain is
when "0000" => dataout <= "0111111";-- 0
when "0001" => dataout <= "0000110";-- 1
when "0010" => dataout <= "1011011";-- 2
when "0011" => dataout <= "1001111";-- 3
when "0100" => dataout <= "1100110";-- 4
when "0101" => dataout <= "1101101";-- 5
when "0110" => dataout <= "1110011";-- P
when "0111" => dataout <= "1111001";-- E
when "1000" => dataout <= "0110111";-- N
when others => dataout <= "1000000";-- "-"
end case;
end process;
end beha;
4.4密码寄存模块
Set端为使能端,控制是否修改密码,高电平有效。clk为工作时钟基准1000HZ,acc为当前输入的密码,old为旧密码输出给控制器,default_code初始设置为初始密码。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity code_register is
port(set,clk : in std_logic;
acc : in std_logic_vector(23 downto 0);
old : out std_logic_vector(23 downto 0));
end code_register;
architecture beha of code_register is
signal code : std_logic_vector(23 downto 0);
signal default_code : std_logic_vector(23 downto 0):="010101010101010101010101";
signal Q,isold : std_logic := '1';
begin
process(clk)
begin
if(rising_edge(clk))then
Q<=set;
if(Q='0' and set = '1')then
code<=acc;
isold<='0';
end if;
if(isold = '0')then
default_code<=code;
old<=code;
else
old<=default_code;
end if;
end if;
end process;
end beha;
4.5计数模块
cnt为上一模块的密码输入,每输入一次给一高电平,结构体内以cnt为敏感信号量。yes是密码正确时给高电平,将错误次数清零。当密码错误次数达2时给alarm高电平输出到display模块显示警报灯,达3次时deadlock输出为1,display模块显示“EEEEEE”,死锁,此时所有按键失效,只有重新上电。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity counter is
port(cnt : in std_logic;
yes : in std_logic;
alarm : buffer std_logic;
deadlock : out std_logic );
end counter;
architecture beha of counter is
signal change : integer range 0 to 3 :=0; --记录错误次数
begin
process(cnt)
begin
if(rising_edge(cnt))then
change <= change+1;
end if;
if(yes = '1')then
change<=0;
end if;
if(change = 2)then
alarm<='1';
deadlock<='0';
elsif(change = 3)then
deadlock<='1';
else
deadlock<='0';
alarm<='0';
end if;
end process;
end beha;
4.6控制模块
clk为工作时钟1000HZ,K0-K7为消抖后的按键输入,old是来自code_register模块的旧密码输入,acc是当前输入的密码,达到六位时送入比较器比较。recode信号是否修改寄存器所存储的密码。C是计数信号,se代表在修改密码时的三次密码比较只要有一次出错就赋值为‘1’给display模块显示“-EEEE-”,rs代表两次新密码都正确时改为‘1’。B[2…0]当前输入密码的位数。
在重置密码模式中,要输入两次旧密码,两次新密码,每当Bi=”110”时按下一次重新输入按钮K6,统计密码输入次数的integer变量count加1,当count=1时将data值赋给寄存器R1,并比较R1与寄存器模块里面存储的密码;当count=2时将data值赋给寄存器R2,并比较R1与R2;当count=3时将data值赋给寄存器R3,将RS赋值为‘1’;当count=4时将data值赋给寄存器R4,并比较R3与R4。
上面3次比较只要有一次出现错误则将密码输入和控制器模块的阻塞信号量blocking赋值为’1’,将信号量SE赋值为‘1’,此时K0~K6按键全部失效,只有按下K7退出设计密码模式将blocking赋值为’0’,才能再次设计密码。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity contral is
port (clk,K0,K1,K2,K3,K4,K5,K6,K7 : in std_logic;
acc : buffer std_logic_vector(23 downto 0);--当前输入的密码达到六位时输出。
reset : buffer std_logic := '1'; --两次新密码和两次旧密码都正确时改为‘0’。
recode : out std_logic := '0';--是否修改寄存器所存储的密码
c : out std_logic;
se,rs : out std_logic:='0';--se代表在修改密码时的三次密码比较只要有一次出错就赋值为‘1’,rs代表两次新密码都正确时改为‘1’。
B : buffer std_logic_vector(2 downto 0);--当前输入密码的位数。
old : in std_logic_vector(23 downto 0));--旧密码,从寄存器中来。
end contral;
architecture beha of contral is
signal key0,key1,key2,key3,key4,key5,key6,key7 : std_logic;
signal cnt : integer range 0 to 4 :=0;
signal r1,r2,r3,r4 : std_logic_vector(23 downto 0);
signal lock : std_logic:='0';
begin
process(clk)
begin
if(rising_edge(clk))then
key0<=K0;key1<=K1;key2<=K2;key3<=K3;key4<=K4;key5<=K5;key6<=K6;key7<=K7;
if(lock = '0')then
if(key0 = '0' and K0 = '1')then
B<=B+1;
acc(23 downto 4)<=acc(19 downto 0);
acc(3 downto 0)<="0000";
end if;
if(key1 = '0' and K1 = '1')then
B<=B+1;
acc(23 downto 4)<=acc(19 downto 0);
acc(3 downto 0)<="0001";
end if;
if(key2 = '0' and K2 = '1')then
B<=B+1;
acc(23 downto 4)<=acc(19 downto 0);
acc(3 downto 0)<="0010";
end if;
if(key3 = '0' and K3 = '1')then
B<=B+1;
acc(23 downto 4)<=acc(19 downto 0);
acc(3 downto 0)<="0011";
end if;
if(key4 = '0' and K4 = '1')then
B<=B+1;
acc(23 downto 4)<=acc(19 downto 0);
acc(3 downto 0)<="0100";
end if;
if(key5 = '0' and K5 = '1')then
B<=B+1;
acc(23 downto 4)<=acc(19 downto 0);
acc(3 downto 0)<="0101";
end if;
if(key6 = '0' and K6 = '1')then
B<="000";
if(reset = '1')then
cnt<=cnt+1;
if(cnt = 4)then
cnt<=0;
else
recode<='0';
end if;
else
recode<='0';
end if;
end if;
end if;
if(key7 = '0' and K7 = '1')then
recode<='0';
lock<='0';
cnt<=0;
c<='0';
B<="000";
reset<=not reset;
end if;
if(B = "000" and lock = '0')then
if(cnt = 1)then
r1<=acc;
rs<='0';
if(acc = old)then--第一次比较
se<='0';
rs<='1';
else
se<='1';
lock<='1';
end if;
elsif(cnt = 2)then
r2<=acc;
if(r1 = acc)then --第二次比较
se<='0';
rs<='1';
c<='1';
else
se<='1';
lock<='1';
end if;
elsif(cnt = 3)then
r3<=acc;
se<='0';
rs<='1';
elsif(cnt = 4)then
rs<='1';
r4<=acc;
if(r3 = acc)then--第三次比较
recode<='1';
se<='0';
rs<='1';
else
recode<='0';
se<='1';
rs<='0';
end if;
else
se<='0';
rs<='0';
end if;
end if;
end if;
end process;
end beha;
4.7动态扫描显示模块
clk为工作时钟基准为1000HZ,cmp为比较模块送来的密码验证,modifycode_mode密码修改模式,se与rs的作用同控制模块,K0-K7为消抖后的按键输入与控制模块的输入同步,用于显示每次按键的输入数值,sel为片选信号,共有8个,低电平有效,8个数码区的片选全部赋值为0了,这样8个数码管就全部有效,同时显示(片选的意思是选择哪一个数码管有效)。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity display is
port(clk,cmp,modifycode_mode,deadlock,se,rs : in std_logic;
K0,K1,K2,K3,K4,K5,K6,K7: in std_logic;
B : in std_logic_vector(2 downto 0);
c : in std_logic;
cnt : out std_logic;
sel : buffer std_logic_vector(7 downto 0);
BCD : buffer std_logic_vector(3 downto 0));
end display;
architecture beha of display is
signal code1,code2,code3,code4,code5,code6:std_logic_vector(3 downto 0):="1001";
signal code : std_logic_vector(3 downto 0);
signal cnt8 : integer range 0 to 7;
signal input,test,input1,input2 : std_logic:='0';
begin
process(clk)
begin
if(rising_edge(clk)) then
test <= K0 or K1 or K2 or K3 or K4 or K5 or K6 or K7;
end if;
end process;
process(test,clk)
begin
if(rising_edge(test))then
code6(3 downto 0)<=code5(3 downto 0);
code5(3 downto 0)<=code4(3 downto 0);
code4(3 downto 0)<=code3(3 downto 0);
code3(3 downto 0)<=code2(3 downto 0);
code2(3 downto 0)<=code1(3 downto 0);
code1(3 downto 0)<=code(3 downto 0);
end if;
if(K6 = '1' or k7 = '1')then
code6(3 downto 0)<="1001";
code5(3 downto 0)<="1001";
code4(3 downto 0)<="1001";
code3(3 downto 0)<="1001";
code2(3 downto 0)<="1001";
code1(3 downto 0)<="1001";
end if;
if(B = "111")then
code6(3 downto 0)<="1001";
code5(3 downto 0)<="1001";
code4(3 downto 0)<="1001";
code3(3 downto 0)<="1001";
code2(3 downto 0)<="1001";
code1(3 downto 0)<="1001";
end if;
end process;
process(clk)
begin
if(K0 = '1') then code <="0000";
elsif(K1 = '1')then code <="0001";
elsif(K2 = '1')then code <="0010";
elsif(K3 = '1')then code <="0011";
elsif(K4 = '1')then code <="0100";
elsif(K5 = '1')then code <="0101";
end if;
end process;
process(clk)
begin
if(rising_edge(clk))then
if(cnt8 = 7)then
cnt8<=0;
else
cnt8<=cnt8+1;
end if;
if(deadlock = '1')then
case cnt8 is
when 0 => sel <="11111110";BCD<="0111";
when 1 => sel <="11111101";BCD<="0111";
when 2 => sel <="11111011";BCD<="0111";
when 3 => sel <="11110111";BCD<="0111";
when 4 => sel <="11101111";BCD<="0111";
when 5 => sel <="11011111";BCD<="0111";
when 6 => sel <="10111111";BCD<="0111";
when 7 => sel <="01111111";BCD<="0111";
end case;
else
if(modifycode_mode = '0')then
if(B = "110")then
if(cmp = '1')then
cnt<='0';
case cnt8 is
when 0 => sel <="11111110";BCD<="1001";
when 1 => sel <="11111101";BCD<="1001";
when 2 => sel <="11111011";BCD<="1000";
when 3 => sel <="11110111";BCD<="0111";
when 4 => sel <="11101111";BCD<="0110";
when 5 => sel <="11011111";BCD<="0000";
when 6 => sel <="10111111";BCD<="1001";
when 7 => sel <="01111111";BCD<="1001";
end case;
else
cnt<='1';
case cnt8 is
when 0 => sel <="11111110";BCD<="1001";
when 1 => sel <="11111101";BCD<="1001";
when 2 => sel <="11111011";BCD<="0111";
when 3 => sel <="11110111";BCD<="0111";
when 4 => sel <="11101111";BCD<="0111";
when 5 => sel <="11011111";BCD<="0111";
when 6 => sel <="10111111";BCD<="1001";
when 7 => sel <="01111111";BCD<="1001";
end case;
end if;
else
cnt<='0';
case cnt8 is
when 0 => sel <="11111110";BCD<=code1;
when 1 => sel <="11111101";BCD<=code2;
when 2 => sel <="11111011";BCD<=code3;
when 3 => sel <="11110111";BCD<=code4;
when 4 => sel <="11101111";BCD<=code5;
when 5 => sel <="11011111";BCD<=code6;
when 6 => sel <="10111111";BCD<="0110";
when 7 => sel <="01111111";BCD<="0110";
end case;
end if;
else
cnt<='0';
if(se = '1')then
case cnt8 is
when 0 => sel <="11111110";BCD<="1001";
when 1 => sel <="11111101";BCD<="1001";
when 2 => sel <="11111011";BCD<="0111";
when 3 => sel <="11110111";BCD<="0111";
when 4 => sel <="11101111";BCD<="0111";
when 5 => sel <="11011111";BCD<="0111";
when 6 => sel <="10111111";BCD<="1001";
when 7 => sel <="01111111";BCD<="1001";
end case;
else
if(c='0')then
case cnt8 is
when 0 => sel <="11111110";BCD<=code1;
when 1 => sel <="11111101";BCD<=code2;
when 2 => sel <="11111011";BCD<=code3;
when 3 => sel <="11110111";BCD<=code4;
when 4 => sel <="11101111";BCD<=code5;
when 5 => sel <="11011111";BCD<=code6;
when 6 => sel <="10111111";BCD<="0110";
when 7 => sel <="01111111";BCD<="0000";
end case;
elsif(B = "110" and c='1')then
case cnt8 is
when 0 => sel <="11111110";BCD<=code1;
when 1 => sel <="11111101";BCD<=code2;
when 2 => sel <="11111011";BCD<=code3;
when 3 => sel <="11110111";BCD<=code4;
when 4 => sel <="11101111";BCD<=code5;
when 5 => sel <="11011111";BCD<=code6;
when 6 => sel <="10111111";BCD<="0110";
when 7 => sel <="01111111";BCD<="1000";
end case;
else
if(rs = '0')then
case cnt8 is
when 0 => sel <="11111110";BCD<=code1;
when 1 => sel <="11111101";BCD<=code2;
when 2 => sel <="11111011";BCD<=code3;
when 3 => sel <="11110111";BCD<=code4;
when 4 => sel <="11101111";BCD<=code5;
when 5 => sel <="11011111";BCD<=code6;
when 6 => sel <="10111111";BCD<="0110";
when 7 => sel <="01111111";BCD<="0000";
end case;
elsif(rs = '1')then
case cnt8 is
when 0 => sel <="11111110";BCD<=code1;
when 1 => sel <="11111101";BCD<=code2;
when 2 => sel <="11111011";BCD<=code3;
when 3 => sel <="11110111";BCD<=code4;
when 4 => sel <="11101111";BCD<=code5;
when 5 => sel <="11011111";BCD<=code6;
when 6 => sel <="10111111";BCD<="0110";
when 7 => sel <="01111111";BCD<="1000";
end case;
end if;
end if;
end if;
end if;
end if;
end if;
end process;
end beha;
五、设计结论与分析
系统测试结论:基本实现了题目要求的功能,不过方式有些许出入。
优点:完成了基本功能和补充内容中的功能。
不足:没有在修改密码后自动跳回上锁界面。
可扩展性分析 :
-
可以设置任意位的密码(不超过6位);
-
可以设置管理员模式,在用户忘记密码是可进入管理员模式修改密码或解除死锁。
六、小结与心得体会
-
要养成看API或者说明文档的习惯;
-
在最后调试的时候数码管显示一直有问题,在同学的帮助下发现是0/1表示的问题;
-
动态扫描就是数码管不断变化,当变化速度超过人眼的识别范围,看起来就像数码管一直显示。需要一个七位段选信号和一个八位片选信号,然后这个信号对应比较麻烦。
-
板载实验中,数码管数字跳的太快或者太慢的问题,在数码管后边有频率说明,但是没有直观感受,切换引脚多试几次就明白了;
-
不可以使用在进程中对信号量的赋值作为判断条件,因为进程中的所有信号量只有在进程结束时才会被赋值。
-
多时钟问题。
在写代码过程,在控制模块的进程使用了多个时钟信号来满足不同的功能。
例如:在主进程中同时使用了判断条件(rising_edge(clk))和(rising_edge(K))。这个错误主要是由于一个process不可以由两个时钟触发,会产生竞争冒险。
解决方法:利用信号的值在进程结束以后才会更新的特点,利用寄存器来判断信号是否出现上升沿。
七、使用方法
- 密码锁默认密码为555555。
- 输入密码未3达六位是可按K6重新输入。
- 密码错误两次警报灯亮起,错误三次进入死锁。死锁后只有重新上电才会恢复。
- 开锁后按任意键自动上锁。
- 按下K7进入修改密码模式,每输入一次密码按K6确认。前两次为旧密码输入,后两次为新密码输入,输入四次后按下K7退出修改密码模式,密码修改成功后只能使用新密码开锁,旧密码失效。
后记
个人能力有限,代码难免有错误、不足和冗余欢迎大家指正。在自己能力范围内会尽量修改,希望能和大家一起讨论,交流学习。