资源描述
数字电子密码锁
一、 设计目的与要求:
目的:掌握矩阵式键盘的工作原理、设计方法;掌握译码器的应用;掌握状态机的设计方法,掌握数码管的控制。
要求:设计一个数字电子密码锁,密码为3 位,密码锁由键盘控制,显示采用数码管及LED灯。
功能:
1、 密码输入:每按下一个数字键,要求在数码管上显示,并依次左移;输入错误时,按退格键,清除前一个输入的数字;输入完毕,按确认键。
2、 开锁:按开锁键,检查输入的密码是否正确,正确才开锁,成功。
3、 上锁:按上锁键,可以直接上锁或者设定新密码上锁。
4、 密码修改:按下修改键,必须先核对原密码,正确后可以输入3位数字,设为新的密码。
5、 报警:开锁输入密码,连续错误三次,则禁止输入密码,并发出报警信号,用红灯一直亮表示。
6、 解除报警:按复位键,解除报警状态,实际应用中复位键设置在其他部位,不可设置在键盘上。
7、 万能密码:电锁维护者使用,在程序中暂时统一设置为999。
8、 其他要求:输入键盘为矩阵式,不使用直接式;条件允许时可以使用语音模块代替指示灯的表示。
二、 电路组成:
为达到以上功能,可将数字电子密码锁分为以下几个模块:
1、键盘接口电路:
键盘矩阵、键盘扫描、键盘消抖、键盘译码及按键存储。
2、密码锁控制电路:
数字按键输入、存储及清除;
功能按键的设计:退格键、确认键、开锁键、上锁键、修改键、解除报警键;
密码清除、修改与存储。
密码锁的上锁与开锁。
3、输出显示电路
BCD译码、动态扫描电路、指示灯电路(或者为语音控制电路)。
三、功能电路的设计:
1、键盘接口电路:图1
(1)矩阵式键盘工作原理:
矩阵式键盘是一种常见的输入装置,在计算机、电话、手机、微波炉等各工电子产品中被广泛应用。如图2所示为一4×4 矩阵式键盘。
矩阵式键盘以行、列形式排列,图中为4 行4 列,键盘上的每一个按键盘其实是一个开关电路,当某键被按下时,该按键所对应的位置就呈现逻辑0的状态,键盘的扫描可以以行扫或列扫方式进行,图中为行扫方式,KEYR3—KEYR0 为扫描信号,其中的某一位为0即扫描其中的一行,具体见表1。
表1 键盘扫描与其对应的键值的关系
KEYR3..0
KEYC3..0
对应的按键
KEYR3..0
KEYC3..0
对应的按键
0111
0111
7
1101
0111
1
1011
8
1011
2
1101
9
1101
3
1110
开锁键
1110
密码修改键
1011
0111
4
1110
0111
0
1011
5
1011
#
1101
6
1101
退格键
1110
上锁键
1110
确认键
键盘扫描信号KEYR3与第一行相连,KEYR2与第二行相连,依此类推。很显然,扫描信号的变化顺序为:0111、1011、1101、1110,周而复始。在扫描的过程中,当有键按下时,对应的键位就为逻辑0 状态,从而从KEYC3..0 读出的键值相应列为0,具体情况如表1 所示。若从KEYC3..0 读出的值全为1 时,表示没有键被按下,则不进行按键的处理。如果的键被按下,则将KEYC3..0读出的送至键盘译码电路进行译码。
(2)时钟产生电路
在一个系统的设计中,往往需要多种时钟信号,最方便的方法是利用一个自由计数器来产生各种需要的频率。本电路需要:系统主时钟CLK、扫描状态产生时钟clkouta和动态扫描时钟clkoutb,其中系统主时钟CLK为输入时钟,由20MHZ晶振产生。
程序清单:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY clkdiv IS
PORT (CLK: IN STD_LOGIC ;
clkouta,clkoutb: OUT STD_LOGIC);
END clkdiV ;
ARCHITECTURE rtl OF clkdiV IS
SIGNAL CLKA:STD_LOGIC;
Begin
PROCESS (CLK)
VARIABLE S:INTEGER RANGE 200 DOWNTO 0;
Begin
IF CLK'Event AND CLK='1' then
IF S<200 THEN
S:= S+1;
CLKA<='0';
ELSE S:=0;
CLKA<='1';
END IF;
END IF;
END PROCESS;
PROCESS (CLKA)
VARIABLE S:INTEGER RANGE 100 DOWNTO 0;
Begin
IF CLKA'Event AND CLKA='1' then
IF S<10 THEN
S:= S+1;
CLKOUTa<='0';
clkoutb<='0';
ELSIF S<20 THEN
S:=S+1;
CLKOUTa<='1';
CLKOUTb<='0';
ELSIF S<50 THEN
S:=S+1;
CLKOUTa<='1';
CLKOUTb<='1';
ELSIF S<70 THEN
S:=S+1;
CLKOUTa<='1';
CLKOUTb<='0';
ELSIF S<100 THEN
S:=S+1;
CLKOUTa<='0';
CLKOUTb<='0';
ELSE S:=0;
END IF;
END IF;
END PROCESS;
end rtl;
说明:CLK时钟经过两次分频,得到扫描状态产生时钟clkouta和动态扫描时钟clkoutb均是1KHZ,但是扫描状态产生时钟clkouta的高电平部分完全包含动态扫描时钟clkoutb的高电平部分,目的在于先使用时钟clkouta的上升沿产生扫描状态的变化,再使用时钟clkoutb的上升沿产生键盘扫描信号。
仿真波形:
(3) 键盘扫描电路
目标:提供键盘扫描信号SCAN_OUT,即表1 中的KEYR3..0,变化顺序依次为0111、1011、1101、1110. . . .依次重复出现。
程序清单:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY scan IS
PORT (CLKA,CLKB: IN STD_LOGIC ;
SCAN_STATE: OUT STD_LOGIC_VECTOR(1 DOWNTO 0);
SCAN_OUT: OUT STD_LOGIC_VECTOR (3 downto 0));
END scan ;
ARCHITECTURE rtl OF scan IS
SIGNAL S:STD_LOGIC_VECTOR(1 DOWNTO 0);
Begin
PROCESS (CLKA)
Begin
IF CLKA'Event AND CLKA='1' then
S <= S+1;
END IF;
END PROCESS;
PROCESS (CLKB,S)
Begin
IF CLKB'Event AND CLKB='1' then
CASE S IS
WHEN "00"=>SCAN_OUT<= "1110";
WHEN "01"=>SCAN_OUT<="1101";
WHEN "10"=>SCAN_OUT<="1011";
WHEN "11"=>SCAN_OUT<="0111";
WHEN OTHERS=>SCAN_OUT<="1111";
END CASE;
SCAN_STATE<=S;
END IF;
END PROCESS;
END rtl ;
说明:程序中,S信号是用来产生扫描信号的四个状态SCAN_STATE :00、01、10、11,再利用动态扫描时钟的上升沿产生扫描信号:0111、1011、1101、1110。
仿真波形:
(4)键盘消抖电路
因为按键大多是机械式开关,在开关切换的瞬间会在接解点出现来回弹跳的现象,其现象可用图3表示。由图可见,虽然只是按了一次键,实际产生的按键信号却不只跳动一次,将会造成误码判,认为是按了两次键。如果调整取样时钟周期,可以发现抖现象得到了改善。
通过时序图可见:在按键过程中可能有三种情况发生,①抖动发生在时钟的某个上升沿,此时的输入信号为低电平,则输出为低电平;②抖动发生在时钟的某个上升沿,但此时的输入信号抖动为高电平,由于按键(低电平)时间通常在200ms以上,则在下一个上升沿输出为低电平;③抖动发生在时钟信号的两个相邻上升沿之间,则在第二个上升沿输出高电平。释放键盘时的情况类似,可见,只要时钟周期大于抖动时间而远小于按键时间,去抖电路即可正确识读按键次数。一般键盘在被按下和释放时会造成持续时间不大于10ms的信号抖动,而按键时间通常在200ms以上,所以可以使用较低频率的时钟信号作为消抖时钟。
Ø 程序清单:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY debouncing IS
PORT
(clkSCAN : IN STD_LOGIC;
d_in:IN STD_LOGIC_VECTOR(3 DOWNTO 0);
d_out: OUT STD_LOGIC_VECTOR(3 DOWNTO 0));
END debouncing ;
ARCHITECTURE a OF debouncing IS
signal TEMPA,TEMPB,TEMPC,TEMPD,TEMPE: STD_LOGIC_VECTOR(3 DOWNTO 0);
signal COUNT: STD_LOGIC_VECTOR(3 DOWNTO 0);
BEGIN
process (clkSCAN)
begin
if clkSCAN'event and clkSCAN='0' then
TEMPA<=D_IN;
IF TEMPE=D_IN THEN
IF COUNT>="1000"THEN
COUNT<="1000";
D_OUT<=D_IN;
ELSE
COUNT<=COUNT+1;
D_OUT<="1111";
END IF;
ELSE
COUNT<="0000";
D_OUT<="1111";
END IF;
END IF;
end process ;
process (clkSCAN)
begin
if clkSCAN'event and clkSCAN='1' then
TEMPB<=TEMPA;
TEMPC<=TEMPB;
TEMPD<=TEMPC;
TEMPE<=TEMPD;
END IF;
END PROCESS;
END a;
说明:将d_in信号去抖后送给D_OUT,其中d_in信号的变化规律是四个扫描时钟周期内仅有一个时钟周期为输入按键值,其余时间保持为“1111”。
仿真波形:
(5)键盘译码电路
从前面的键盘扫描电路的输出可以看出,扫描得到的信号规律性不强,例如数字键主要用来输入数字,但键盘扫描输出无法拿来直接使用,必须对其进行译码才能使用,如表1所示。
Ø 程序清单:
LIBRARY ieee;
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY KEYBOARD_DEC IS
PORT (
clkSCAN : IN STD_LOGIC ;
S: IN STD_LOGIC_VECTOR (1 downto 0);
d_IN: IN STD_LOGIC_VECTOR (3 downto 0); --key code after debounce
out_numb : OUT STD_LOGIC_VECTOR(3 downto 0));
END KEYBOARD_DEC ;
--******************************************************
ARCHITECTURE a OF KEYBOARD_DEC IS
signal Z: std_logic_VECTOR(5 downto 0) ; --SCAN CODE
BEGIN
Z<= S & d_IN;
PROCESS(clkSCAN)
begin
IF CLKSCAN'EVENT AND CLKSCAN = '0' THEN
case Z is
when "110111" =>out_numb<= "0000" ;--0
when "100111" =>out_numb<= "0001" ;--1
when "101011" =>out_numb<= "0010" ;--2
when "101101" =>out_numb<= "0011" ;--3
when "010111" =>out_numb<= "0100" ;--4
when "011011" =>out_numb<= "0101" ;--5
when "011101" =>out_numb<= "0110" ;--6
when "000111" =>out_numb<= "0111" ;--7
when "001011" =>out_numb<= "1000" ;--8
when "001101" =>out_numb<= "1001" ;--9
when "111101" =>out_numb<= "1010" ;--BACKSPACE
when "111110" =>out_numb<= "1011" ;--ENTER
when "101110" =>out_numb<= "1100" ;--MODIFY NUM
when "011110" =>out_numb<= "1101" ;--LOCK;
when "001110" =>out_numb<= "1110" ;--UNLOCK;
when others =>out_numb<= "1111" ;
end case ;
END IF ;
end process ;
END a;
说明:键盘译码电路根据键盘输入数据d_IN和扫描状态S进行译码out_numb,数字键为:0、1、2、3、4、5、6、7、8、9,功能键为:退格键、确认键、修改键、上锁键和解锁键,out_numb=“1111”表示无任何操作。
仿真波形:
(6)按键存储电路
键盘译码后,可以看出,译码值不等于“1111”时表示有按键动作发生,当译码值等于长时间“1111”时,表示无按键动作发生,所以可以根据译码值产生按键动作发生的标示信号和按键的译码值。
Ø 程序清单:
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY num_or_fun_DEC IS
PORT (
clkSCAN : IN STD_LOGIC ;
in_num: IN STD_LOGIC_VECTOR (3 downto 0);
change_clk: out STD_LOGIC;
out_num : OUT STD_LOGIC_VECTOR(3 downto 0));
END num_or_fun_DEC ;
--******************************************************
ARCHITECTURE a OF num_or_fun_DEC IS
signal temp_num:STD_LOGIC_VECTOR(3 downto 0);
BEGIN
PROCESS(clkSCAN)
variable count,counta:std_logic_vector(2 downto 0);
begin
IF CLKSCAN'EVENT AND CLKSCAN = '1' THEN
if in_num="1111"then
if count<7 then
count:=count+1;
else
change_clk<='0';
count:="111";
counta:="000";
end if;
else
temp_num<=in_num;
count:="000";
if(counta<3)then
counta:= counta+1;
else
counta:="011";
change_clk<='1';
end if;
end if;
END IF ;
end process ;
out_num<=temp_num;
END a;
说明:当按键译码值in_num不等于“1111”时,按键发生标示信号change_clk='1',按键译码输出值out_num等于按键译码值。
仿真波形:
2、电锁控制电路设计
电锁控制电路是整个电路的控制中心,主要完成如下功能:
1、 数字按键输入部分
Ø 如果输入数字键,第一个数字会从显示器的最右端开始显示,此后每新按一个数字时,显示
器上的数字必须往左移动一位。
Ø 若想要更改输入的数字,可按退格键来清除前一个输入的数字,或按清除键清除输入的所有
数字,再重新输入3位数字。
Ø 既然设计的是三位电子密码锁,当输入的数字键超过3 位时,电路不应理会。
2、 功能键输入部分
Ø 退格键:清除前一个输入的数字,如果数字全部清除,则回到前一个状态(例如上锁或者解锁等)。
Ø 密码核对:在密码更改,开锁之前必须先核对密码。
Ø 密码修改:按下此键先输入原密码,核对后将新输入的数字设定为新的密码。
Ø 上锁:按上锁键可以直接上锁。
Ø 解锁:检查输入的密码是否正确,正确才开锁。
Ø 万用密码:电锁维护者使用。
程序清单:三位电子密码锁
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL ;
USE IEEE.STD_LOGIC_UNSIGNED.ALL ;
ENTITY control IS
PORT (reset,d_clk:in std_logic;
d_in: IN STD_LOGIC_VECTOR (3 downto 0);
led_alarm:out std_logic;
led_flag,leda,ledb,ledc: inout STD_LOGIC_VECTOR (3 downto 0)
);
END control ;
--******************************************************
ARCHITECTURE a OF control IS
TYPE STATE_TYPE IS (lock_idle,lock,unlock_idle,unlock,modify,new_num,alarm,num_one,
num_two,num_three);
SIGNAL state: STATE_TYPE;
signal lock_rega,lock_regb,lock_regc:std_logic_vector(3 downto 0);
signal locked,unlock_modify_flag:std_logic;
BEGIN
PROCESS (reset,state,d_in,d_clk)
variable alarmcnt:std_logic_vector(1 downto 0);
BEGIN
if reset='0' then
state<=lock_idle;
leda<="1111";
ledb<="1111";
ledc<="1111";
alarmcnt:="00";
locked<='0';
led_flag<="0000";
unlock_modify_flag<='0';
lock_rega<="0001";
lock_regb<="0001";
lock_regc<="0001";
elsif(d_clk'event and d_clk='1') then
if locked='0'then
CASE state IS
WHEN lock_idle =>IF d_in="1110"then--unlock
state<=unlock;
led_flag<="1000";
unlock_modify_flag<='0';
leda<="0000";
ledb<="0000";
ledc<="0000";
ELSif d_in="1100"then--modify
state<=modify;
led_flag<="0010";
unlock_modify_flag<='1';
leda<="0000";
ledb<="0000";
ledc<="0000";
ELSE
led_flag<="0000";
leda<="1111";
ledb<="1111";
ledc<="1111";
END IF;
WHEN unlock =>if d_in="1010"then--lock_idle
state<=lock_IDLE;
led_flag<="0000";
leda<="1111";
ledb<="1111";
ledc<="1111";
ELSif d_in<"1010"then--num_one
state<=num_one;
leda<= d_in;
END IF;
WHEN num_one=>IF d_in="1010"then--backspace
leda<="0000";
ledb<="0000";
ledc<="0000";
case led_flag is
when"1000"=>state<=unlock;
when"0100"=>state<=lock;
when"0010"=>state<=modify;
when"0001"=>state<=new_num;
when others=>null;
end case;
ELSif d_in<"1010"then--num_two
state<=num_two;
leda<=d_in;
ledb<=leda;
END IF;
WHEN num_two=>IF d_in="1010"then--num_one
state<=num_one;
leda<=ledb;
ledb<="0000";
ELSif d_in<"1010"then--num_three
state<=num_three;
leda<=d_in;
ledb<=leda;
ledc<=ledb;
END IF;
WHEN num_three=>IF d_in="1010"then--_num_two
state<=num_two;
leda<=ledb;
ledb<=ledc;
ledc<="0000";
ELSif d_in="1011"then--enter
if led_flag="1000"or led_flag="0010"then
if (leda=lock_rega and ledb=lock_regb and ledc=lock_regc)
or(ledc="1000"and ledb="0001"and leda="0101")then
if unlock_modify_flag='0' then
state<=unlock_idle;
led_flag<="1111";
elsif unlock_modify_flag='1' then
state<=new_num;
led_flag<="0001";
end if;
leda<="0000";
ledb<="0000";
ledc<="0000";
alarmcnt:="00";
led_alarm<='0';
else
state<=lock_idle;
led_flag<="0000";
led_alarm<='1';
leda<="1111";
ledb<="1111";
ledc<="1111";
if alarmcnt<3 then
alarmcnt:=alarmcnt+1;
locked<='0';
else
alarmcnt:="11";
locked<='1';
end if;
end if;
elsif led_flag="0001"then
lock_rega<=leda;
lock_regb<=ledb;
lock_regc<=ledc;
state <=lock_idle;
led_flag<="0000";
leda<="1111";
ledb<="1111";
ledc<="1111";
END IF;
end if;
WHEN unlock_idle =>IF d_in="1101"THEN --lock
state <=lock;
led_flag<="0100";
ELSif d_in="1100"THEN --modify
state <=modify;
led_flag<="0010";
unlock_modify_flag<='1';
END IF;
WHEN lock =>IF d_in="1010"then--unlock_idle
state<=unlock_IDLE;
led_flag<="1111";
elsIF d_in="1011"then--lock_idle
state<=lock_idle;
led_flag<="0000";
leda<="1111";
ledb<="1111";
ledc<="1111";
ELSif d_in="1100"then--modify
state<=modify;
led_flag<="0010";
unlock_modify_flag<='1';
leda<="0000";
ledb<="0000";
ledc<="0000";
END IF;
WHEN modify=>IF d_in="1010"or d_in="1011"then--lock_idle
state<=lock_idle;
led_flag<="0000";
leda<="1111";
ledb<="1111";
ledc<="1111";
ELSif d_in<"1010"then--num_one
state<=num_one;
leda<=d_in;
ledb<="0000";
ledc<="0000";
END IF;
WHEN new_num=>IF d_in="1011"then--lock_idle
state<=lock_idle;
led_flag<="0000";
leda<="1111";
ledb<="1111";
ledc<="1111";
ELSif d_in<"1010"then--num_one
state<=num_one;
leda<=d_in;
ledb<="0000";
ledc<="0000";
END IF;
WHEN others=>null;
END CASE;
else
state<=lock_idle;
end if;
END IF;
END PROCESS;
END a;
说明:采用状态机的设计方法进行设计,程序流程如图。
仿真波形:
3、整体电路如图
展开阅读全文