华南师范大学实验报告
学生姓名: 彭小澎
学 号: 20103100202_
专 业: 通信工程
年级班级: 11级电通6C
课程名称: 可编程数字系统
实验项目: VHDL设计一密码锁
试验时间: 2013年05月18日
指导老师: 冯寿廷
一、课程设计目的
熟悉EDA工具,掌握用VHDL语言进行数字系统设计的基本方法和流程,提高工程设计实践能力。
二、设计任务
设计一密码锁,用VHDL语言描述,用QuartusII工具编译和综合,并在实验板上实现,撰写实验报告。
数字锁即电子密码锁,锁内有预置密码,如果输入代码与锁内密码一致,锁被打开;否则,应封闭开锁电路,若多次输入错误密码,应发出报警信号。
三、功能要求与技术指标
本设计设计一个4 位数字锁,并验证其操作。
1、基本功能:
(1 )开锁密码为 4 位十进制数,通过按钮输入密码,输入的密码在4个数码管上显示,若与锁内预置的密码一致,输出开锁信号(以点亮一个LED灯表示开锁)。
(2)按钮开关输入须消抖处理。
2、扩展功能:
(1)用户可以设置锁内的密码;
(2)若输入密码三次不正确,输出报警信号,报警信号可以通过闪烁LED或某个数码管上小数点指示。
(3)设置一个复位按键,忘记密码后可通过该复位按键恢复出厂原始密码,如原始密码为“1234”;
(4)其它实用功能。
四、设计原理
在数字电路设计中,自顶向下设计方法的主要思想是对数字系统进行划分,将复杂的设计原理简化为相对简单的模块设计,不同的模块用来完成数字系统中某一部分的具体功能。此密码锁的设计可以分为以下几个模块:
密码锁原理框图
五、系统分析
1、根据原理框图:系统大致可分为时钟模块,显示模块,控制模块。
时钟模块:密码锁的工作时钟由外部晶振提供,时钟频率为50Mhz。对CLK进行分频输出三路时钟CLK1Khz,CLK1,CLK2,频率分别为1Khz,5hz,1hz。由此,时钟分频模块原理框图如下:
CLK
显示模块: 数码管动态扫描模块以及数码管显示模块的时钟频率分别由CLK1Khz和CLK提供。数码管动态扫描模块的原理是利用人体的视觉暂留特性,只要每秒扫描次数大于25次,就感觉数码管一直在亮。数码管显示模块由四个数码管分别显示四位十进制密码。四个数码管为共阳极连接,其中一段输入低电平则点亮,否则不亮;且由各自位选信号控制,被选通数码管则显示数据,其余关闭。
控制模块: 根据密码锁的功能可划分为以下五个模块。
按键消抖模块:消抖是为了避免在按键按下或是抬起时电平剧烈抖动带来的影响。一般来说,软件消抖的方法是不断检测按键值,直到按键值稳定。实现方法:假设未按键时输入1,按键后输入为0,抖动时不定。可以做以下检测:检测到按键输入为0之后,开始计数,延时10ms,再次检测,如果按键还为0,那么就认为有按键输入。延时的10ms恰好避开了抖动期。
数码管移位模块:当CLK时钟上升沿到来时,设置按键S3对数码管进行移位控制。通过闪烁的方式显示当前其控制的数码管位置。
数码管计数模块:当CLK时钟上升沿到来时,设置按键S2对数码管进行加数控制。
密码检验模块:该模块的作用是将输入的密码跟程序中预置的密码进行对比,如果一样,则密码锁开锁。在这里值得注意的是设“1111”为初始密码,如果修改密码后但是又忘记了密码,重新下载或者按复位键(rst)之后只要输入的密码为“1111”则都可通过密码锁。此模块是整个设计的核心 ,它实现密码锁的逻辑功能。在任何时候按动密码初始化按键内密码设置为程序初始化密码值 “1111”代码为 4 位二进制数 ,当输入代码的位数和位值与锁内给定的密码一致 ,且按规定程序开锁时 ,方可开锁 ,并点亮开锁指示灯且数码管显示“PASS”字样。否则 ,系统进入 “错误”状态 ,并闪烁LED发出报警信号,且数码管显示“FFFF”字样。
密码设置模块:为防止任意进行密码修改 ,必须在正确输入密码后 ,才能重新设置密码。输入正确密码后 ,锁打开 ,同时 ,密码修改控制信号M置高电平 ,就可直接进行修改密码的操作。修改密码实质就是用输入的新密码去取代原来的旧密码, 按确定键, 存储新密码。此功能设置方便 ,保密性好。
六、程序设计
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_unsigned.all;
entity mimasuo is
port(clk:in std_logic;
start,num,keyr,set,rst:in std_logic;
display:out std_logic_vector(7 downto 0);
seg:out std_logic_vector(3 downto 0);
led:out std_logic);
end mimasuo;
architecture lock of mimasuo is
signal cnt2:integer range 0 to 50000;
signal cnt3:integer range 1 to 10000000:=1;
signal cnt4:integer range 1 to 50000000:=1;
signal clk1khz:std_logic;
signal clk1,clk2:std_logic;
signal k:integer range 0 to 3:=0;
signal m:integer range 0 to 1;
signal count1:integer range 0 to 1:=0;
signal count2:integer range 0 to 3;
signal count3:integer range 0 to 100:=0;
signal count4:integer range 0 to 1:=0;
signal numcnt,keyrcnt,startcnt,setcnt,rstcnt:integer range 0 to 50000000;
signal numfilt,keyrfilt,startfilt,setfilt,rstfilt:std_logic;
signal a,b,c,d:integer range 0 to 9;
signal a1,b1,c1,d1:integer range 0 to 9:=1;
begin
-------------------------------------------------------分频模块
process(clk)
begin
if rising_edge(clk) then
if cnt2=50000 then
cnt2<=0;
clk1khz<='1';
else
cnt2<=cnt2+1;
clk1khz<='0';
end if;
end if;
end process;
process(clk)
begin
if rising_edge(clk)then ----产生周期为0.2s的时钟信号
if cnt3=10000000 then
cnt3<=1;
else cnt3<=cnt3+1;
end if;
if cnt3<5000000 then
clk1<='1';
else clk1<='0';
end if;
end if;
end process;
process(clk)
begin
if rising_edge(clk)then ----产生周期为1s的时钟信号
if cnt4=50000000 then
cnt4<=1;
else cnt4<=cnt4+1;
end if;
if cnt4<25000000 then
clk2<='1';
else clk2<='0';
end if;
end if;
end process;
------------------------------------------------------动态扫描
process(clk1khz)
begin
if rising_edge(clk1khz) then
if k=3 then
k<=0;
else
k<=k+1;
end if;
end if;
if m=1 then
case k is
when 0=>seg<="0111"; --'0'
when 1=>seg<="1011"; --'1'
when 2=>seg<="1101"; --'2'
when 3=>seg<="1110"; --'3'
end case;
else if m=0 and count3=3 then
case k is
when 0=>seg<="0111"; --'0'
when 1=>seg<="1011"; --'1'
when 2=>seg<="1101"; --'2'
when 3=>seg<="1110"; --'3'
end case;
else
case count2 is
when 0=>
case k is
when 0=>if clk2='1'then seg<="1111";
else
seg<="0111"; --'0'
end if;
when 1=>seg<="1011"; --'1'
when 2=>seg<="1101"; --'2'
when 3=>seg<="1110"; --'3'
end case;
when 1=>
case k is
when 1=>if clk2='1'then seg<="1111";
else
seg<="1011"; --'0'
end if;
when 0=>seg<="0111"; --'1'
when 2=>seg<="1101"; --'2'
when 3=>seg<="1110"; --'3'
end case;
when 2=>
case k is
when 2=>if clk2='1'then seg<="1111";
else
seg<="1101"; --'0'
end if;
when 1=>seg<="1011"; --'1'
when 0=>seg<="0111"; --'2'
when 3=>seg<="1110"; --'3'
end case;
when 3=>
case k is
when 3=>if clk2='1'then seg<="1111";
else
seg<="1110"; --'0'
end if;
when 1=>seg<="1011"; --'1'
when 2=>seg<="1101"; --'2'
when 0=>seg<="0111"; --'3'
end case;
end case;
end if;
end if;
end process;
------------------------------------------------------移位
process (clk, keyrfilt)
begin
if rising_edge(clk) then
if (keyrfilt = '1') then
if count2 = 3 then
count2 <= 0;
else
count2 <= count2+ 1;
end if;
end if;
end if;
end process;
------------------------------------------------------- 计数
process(clk,numfilt)
begin
if rising_edge(clk) then
if numfilt='1' then
if count2=0 then
if a=9 then
a<=0;
else a<=a+1;
end if;
end if;
if count2=1 then
if b=9 then
b<=0;
else b<=b+1;
end if;
end if;
if count2=2 then
if c=9 then
c<=0;
else c<=c+1;
end if;
end if;
if count2=3 then
if d=9 then
d<=0;
else d<=d+1;
end if;
end if;
end if;
end if;
end process;
------------------------------------------------ 显示
process(clk)
begin
if m=1 then ----开锁时且按一下start ,显示PASS
if count1=1 then
if k=0 then
display<="10001100";
end if;
if k=1 then
display<="10001000";
end if;
if k=2 then
display<="10010010";
end if;
if k=3 then
display<="10010010";
end if;
else -----显示PASS后,
if k=0 then 再按一下start,
case a is --hgfedcba 显示原先密码
when 0 => display <= "11000000"; -- 0
when 1 => display <= "11111001"; -- 1
when 2 => display <= "10100100"; -- 2
when 3 => display <= "10110000"; -- 3
when 4 => display <= "10011001"; -- 4
when 5 => display <= "10010010"; -- 5
when 6 => display <= "10000010"; -- 6
when 7 => display <= "11111000"; -- 7
when 8 => display <= "10000000"; -- 8
when 9 => display <= "10010000"; -- 9
end case;
end if;
if k=1 then
case b is --hgfedcba
when 0 => display <= "11000000"; -- 0
when 1 => display <= "11111001"; -- 1
when 2 => display <= "10100100"; -- 2
when 3 => display <= "10110000"; -- 3
when 4 => display <= "10011001"; -- 4
when 5 => display <= "10010010"; -- 5
when 6 => display <= "10000010"; -- 6
when 7 => display <= "11111000"; -- 7
when 8 => display <= "10000000"; -- 8
when 9 => display <= "10010000"; -- 9
end case;
end if;
if k=2 then
case c is --hgfedcba
when 0 => display <= "11000000"; -- 0
when 1 => display <= "11111001"; -- 1
when 2 => display <= "10100100"; -- 2
when 3 => display <= "10110000"; -- 3
when 4 => display <= "10011001"; -- 4
when 5 => display <= "10010010"; -- 5
when 6 => display <= "10000010"; -- 6
when 7 => display <= "11111000"; -- 7
when 8 => display <= "10000000"; -- 8
when 9 => display <= "10010000"; -- 9
end case;
end if;
if k=3 then
case d is --hgfedcba
when 0 => display <= "11000000"; -- 0
when 1 => display <= "11111001"; -- 1
when 2 => display <= "10100100"; -- 2
when 3 => display <= "10110000"; -- 3
when 4 => display <= "10011001"; -- 4
when 5 => display <= "10010010"; -- 5
when 6 => display <= "10000010"; -- 6
when 7 => display <= "11111000"; -- 7
when 8 => display <= "10000000"; -- 8
when 9 => display <= "10010000"; -- 9
end case;
end if;
end if;
else if m=0 and count3=3 then ----连续错误三次,则显示FFFF
if k=0 then
display<="10001110";
end if;
if k=1 then
display<="10001110";
end if;
if k=2 then
display<="10001110";
end if;
if k=3 then
display<="10001110";
end if;
else
if k=0 then
case a is --hgfedcba
when 0 => display <= "11000000"; -- 0
when 1 => display <= "11111001"; -- 1
when 2 => display <= "10100100"; -- 2
when 3 => display <= "10110000"; -- 3
when 4 => display <= "10011001"; -- 4
when 5 => display <= "10010010"; -- 5
when 6 => display <= "10000010"; -- 6
when 7 => display <= "11111000"; -- 7
when 8 => display <= "10000000"; -- 8
when 9 => display <= "10010000"; -- 9
end case;
end if;
if k=1 then
case b is --hgfedcba
when 0 => display <= "11000000"; -- 0
when 1 => display <= "11111001"; -- 1
when 2 => display <= "10100100"; -- 2
when 3 => display <= "10110000"; -- 3
when 4 => display <= "10011001"; -- 4
when 5 => display <= "10010010"; -- 5
when 6 => display <= "10000010"; -- 6
when 7 => display <= "11111000"; -- 7
when 8 => display <= "10000000"; -- 8
when 9 => display <= "10010000"; -- 9
end case;
end if;
if k=2 then
case c is --hgfedcba
when 0 => display <= "11000000"; -- 0
when 1 => display <= "11111001"; -- 1
when 2 => display <= "10100100"; -- 2
when 3 => display <= "10110000"; -- 3
when 4 => display <= "10011001"; -- 4
when 5 => display <= "10010010"; -- 5
when 6 => display <= "10000010"; -- 6
when 7 => display <= "11111000"; -- 7
when 8 => display <= "10000000"; -- 8
when 9 => display <= "10010000"; -- 9
end case;
end if;
if k=3 then
case d is --hgfedcba
when 0 => display <= "11000000"; -- 0
when 1 => display <= "11111001"; -- 1
when 2 => display <= "10100100"; -- 2
when 3 => display <= "10110000"; -- 3
when 4 => display <= "10011001"; -- 4
when 5 => display <= "10010010"; -- 5
when 6 => display <= "10000010"; -- 6
when 7 => display <= "11111000"; -- 7
when 8 => display <= "10000000"; -- 8
when 9 => display <= "10010000"; -- 9
end case;
end if;
end if;
end if;
end process;
----------------------------------------------------------设置密码
process(clk,setfilt)
begin
if rising_edge(clk)then
if m=1 then ---密码正确前提下,才可按下set,修改密码
if setfilt='1' then
a1<=a;
b1<=b;
c1<=c;
d1<=d;
end if;
end if;
if rstfilt='1' then ----按下rst,恢复出厂密码“1111”
a1<=1;
b1<=1;
c1<=1;
d1<=1;
end if;
end if;
end process;
-----------------------------------------------------密码校验
process(clk,startfilt)
begin
if rising_edge(clk) then
if startfilt = '1' then ----输入密码“abcd”后,按下start,
与设置的密码“a1b1c1d1”比较
if(a=a1 and b=b1 and c=c1 and d=d1)then
led<='1'; ----若密码正确,则点亮led
count3<=0;
m<=1;
if count1=1 then
count1<=0;
else count1<=count1+1;
end if;
else count3<=count3+1; ----否则,led不亮
led<='0';
m<=0;
end if;
end if;
if (count3>2 and m=0) then
led<=clk1; ---若连续错误三次,则发出led闪烁警报 end if;
end if;
end process;
------------------------------------------------- 按键消抖
process (num)
constant N :integer := 5000000;
begin
if clk'event and clk = '1' then
if num = '0' then
if numcnt /= N then
numcnt <= numcnt + 1;
end if;
if numcnt = N-1 then
numfilt <= '1';
else
numfilt <= '0';
end if;
else
numcnt <= 0;
end if;
end if;
end process;
process (keyr)
constant N :integer := 5000000;
begin
if clk'event and clk = '1' then
if keyr = '0' then
if keyrcnt /= N then
keyrcnt <= keyrcnt + 1;
end if;
if keyrcnt = N-1 then
keyrfilt <= '1';
else
keyrfilt <= '0';
end if;
else
keyrcnt <= 0;
end if;
end if;
end process;
process (start)
constant N :integer := 5000000;
begin
if clk'event and clk = '1' then
if start = '0' then
if startcnt /= N then
startcnt <= startcnt + 1;
end if;
if startcnt = N-1 then
startfilt <= '1';
else
startfilt <= '0';
end if;
else
startcnt <= 0;
end if;
end if;
end process;
process (set)
constant N :integer := 5000000;
begin
if clk'event and clk = '1' then
if set = '0' then
if setcnt /= N then
setcnt <= setcnt + 1;
end if;
if setcnt = N-1 then
setfilt <= '1';
else
setfilt <= '0';
end if;
else
setcnt <= 0;
end if;
end if;
end process;
process (rst)
constant N :integer := 5000000;
begin
if clk'event and clk = '1' then
if rst = '0' then
if rstcnt /= N then
rstcnt <= rstcnt + 1;
end if;
if rstcnt = N-1 then
rstfilt <= '1';
else
rstfilt <= '0';
end if;
else
rstcnt <= 0;
end if;
end if;
end process;
end lock;
七、仿真/实验结果
软件仿真:编译成功后进行仿真。首先建立波形文件。波形文件 lock. scf 建好并存盘后 ,选择菜单 “new”→ “Vector Waveform File”,启动仿真操作 ,结束后观察仿真波形。本设计中 ,仿真波形如图所示。当给初始密码输入信号 LC 一个低电平时 ,就将程序预先设定的密码( “0000” )装入 lock 中 。按下 reset 后,系统复位 ,处于输入密码状态。输入的开锁密码串行顺序装入 ,。密码输入完毕后 ,比较输入的密码 是否等于预先设定的密码,若相等 ,锁开启。如图所示
实验结果:在不断调试并改正错误之后得到能正确运行的程序,运行结果如下所示。
密码初始状态
密码正确状态
密码错误三次 报警状态
设置密码状态
八、结论和分析
1、四位十进制数字密码锁设计基于VHDL语言,首先得弄懂设计原理,再通过对VHDL语言的学习之后编程描述理论中的电路,调试程序过程中不断发现并改正错误,直到程序完全没有错误并能够成功调试得到自己设想中的结果。结果用四位八段数码管显示,必须弄清楚管脚的分配,理解数码管扫频和显示的原理,分清楚移位位置,实现译码后显示最后结果的功能。
2、这次密码锁的设计过程表明,用VHDL可以快速、灵活地设计出符合要求的密码锁控制器,而且操作简单。此密码锁可以实现密码输入、密码校验、密码设置和更改等功能。设计过程能够在设计完成后在QuartusⅡ环境下进行电路的模拟仿真,反馈结果可以验证程序设计的可行性与可靠性。
3、本密码锁控制器设置的是4位密码,在系统复位后,输入一个完整的密码串,输入完后,系统会进行比对,如果发现密码吻合,则开灯,否则要求用户继续输入,如果输入的密码串三次都是错误的,则系统报警,直到输入正确的密码,报警灯停止闪烁。这样的设计可以很好的满足人们的日常需求。同时,密码锁还具有密码修改功能,方便操作,使得密码锁的使用更加安全、便捷。
九、使用说明
1、接通密码锁电源,通过num键(s2)和keyr键(s3),键入初始密码“1111”,按下start键(s6)确定,此时led灯亮表示密码锁开锁,且显示“PASS”的提示字样。
2、再次按下start键(s6),退出“PASS”界面,此时可键入任意四位十进制数(a1b1c1d1),再按下set键(s4),成功将其设置为新密码。
3、重新键入一个四位十进制数(abcd),按下start键(s6),进行密码校验。
4、若abcd=a1b1c1d1,则密码正确,密码锁开锁,led灯点亮,且显示“PASS”的提示字样。否则,密码错误,led灯不亮。
5、若错误次数超过3次(包含3次),则系统报警,led灯持续闪烁,且显示“FFFF”的警报提示字样。
6、再在按下start键(s6),重新键入密码。只有当键入正确密码之后方可解除警报,且led灯点亮,密码锁开锁。
7、若忘记密码,则可按下rst键(s5)将密码恢复为初始密码。
注:只有在开锁前提下,方可进行密码重置操作。