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退出修改密碼模式,密碼修改成功后只能使用新密碼開鎖,舊密碼失效,
后記
個人能力有限,代碼難免有錯誤、不足和冗余歡迎大家指正,在自己能力范圍內會盡量修改,希望能和大家一起討論,交流學習,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/401698.html
標籤:其他
