1. 程式人生 > 其它 >基於VHDL語言的數字電子鐘設計

基於VHDL語言的數字電子鐘設計

這是在2021年10月底完成的一次VHDL課程設計,全程自己設計組裝完成,現作為記錄存檔釋出,大家也可以借鑑本文來完成自己的課程設計。(建議使用電腦閱讀,本文有修改)

基於VHDL語言的數字電子鐘設計

【內容摘要】 數字電子鐘是一種用數字顯示秒、分、時的記時裝置,該數字電子鐘的功能和特點有:時鐘源產生1Hz時鐘脈衝,用以提供“秒”的計數;設計兩個六十進位制的計數器對“分”、“秒”訊號計數,二十四進位制計數器對“時”訊號計數;通過“時”、“分”校正電路進行時間的校正;通過組合邏輯電路實現報時功能和鬧鈴功能,由蜂鳴器發出提醒。本次課程設計使用Quartus II軟體進行設計,所採用的硬體語言是VHDL

【關鍵詞】電子鐘 計數器 數位電子技術 EDA VHDL

目錄

1 課程設計和方案設計

1.1 EDA課程設計的目的與意義

電子技術是一門實踐性很強的課程,加強工程訓練,特別是技能的培養,對於培養工程人員的素質和能力具有十分重要的作用。在電子資訊類本科教學中,電子技術課程設計是一個重要的實踐環節,它包括選擇課題、電子電路設計、組裝、除錯和編寫總結報告等實踐內容。

通過 EDA 課程設計,要實現以下兩個目標:第一,讓學生初步掌握 EDA 電子線路的試驗、設計方法。即學生根據設計要求和效能引數,查閱文獻資料,收集、分析類似電路的效能,並通過組裝除錯等實踐活動,使電路達到效能指標;第二,課程設計為後續的畢業設計打好基礎。畢業設計是系統的工程設計實踐,而課程設計的著眼點是讓學生開始從理論學習的軌道上逐漸引向實際運用,從已學過的定性分析、定量計算的方法,逐步掌握工程設計的步驟和方法,瞭解科學實驗的程式和實施方法,同時,課程設計報告的書寫,為今後從事技術工作撰寫科技報告和技術資料打下基礎。

1.2 本次課程設計的學習目標

1)掌握電子鐘的設計、組裝與除錯方法。

2)熟悉Quartus II軟體的使用方法。

3)提高動手能力,學會將理論知識與實踐相結合,充分發揮個人與團隊協作的能力。

1.3 設計任務與要求

1)時鐘顯示功能,能夠以十進位制顯示“時”、“分”、“秒”。

2)具有校準時、分、秒的功能。

3)整點自動報時,在整點時,便自動發出鳴叫。

4)具有鬧鈴功能,可設定鬧鈴時間,到時便發出鳴叫。

2 基本實現思路及系統框圖

2.1 基本實現思路

數字電子鐘的計時功能由三個計數器實現,其中分、秒計時分別為60進位制計數,小時計時為24進位制計數器,且採用進位級聯方式連線。

數字電子鐘的校時功能由三個按鍵實現,秒校時是按下置零,分校時和時校時是通過長按按鍵以加快時鐘頻率來實現。

數字電子鐘的整點報時功能需要檢測時間是否到達整點,若是,則揚聲器工作,否則,揚聲器不工作。

數字電子鐘的鬧鈴功能需要三個按鍵和另外三個計數器實現,即分、秒計時分別為60進位制計數,小時計時為24進位制計數器,但並不採用級聯方式連線。三個按鍵連線計數器,設定時間是通過長按按鍵來實現。鬧鈴的檢測實現思路與整點檢測的類似。

數字電子鐘的數碼管需要顯示正常時間或鬧鈴時間,為此可新增一個按鍵用以切換正常介面和鬧鈴介面。按鍵可作為二選一選擇器的位選訊號,數碼管通過選擇器來決定顯示哪個時間,同時又不影響時間的計時。

2.2 電路整體控制邏輯

圖2-1 數字電子鐘整體邏輯框圖

電路的整體邏輯框圖如上圖所示,這裡需要做些說明:

一個具有計時、校時、報時、顯示等基本功能的數字鐘主要由分頻器、計數器、顯示器、校時、報時、鬧鈴等模組組成。
原始時鐘訊號經過分頻器得到秒脈衝,秒脈衝送入計數器計數,計數結果通過“時”、“分”、“秒”譯碼器譯碼,並通過顯示器顯示時間。

按鍵A用來切換介面,可切換正常介面和鬧鈴介面。按鍵B、C、D用來調整時、分、秒,當位於正常介面時是用於校時的,當位於鬧鈴介面時是用於調整鬧鈴的。通過一個組合邏輯模組,便可以分辨按鍵在不同介面下的不同功能(限於篇幅,該模組在圖中並未畫出)。因此,這三個按鍵具有複用的功能。

報時模組接收來自上面三個正常時間計數器的輸出,鬧鈴模組接收來自下面三個鬧鈴時間計數器的輸出,它們均會持續檢測是否滿足響鈴要求。

3 模組電路設計和模擬

3.1 分頻器模組

分頻器的作用是將原始時鐘頻率分為兩種頻率,前者供給正常時鐘的時鐘源,後者供給設定鬧鈴和校時的時鐘源。

3.1.1 程式碼實現

實現原理是每隔N個原始時鐘週期,輸出的時鐘訊號將翻轉一次,這樣就得到新的時鐘頻率。完整程式碼如下:

【程式碼3-1 分頻器】
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- 分頻器

entity diver is
	port(
		clk:	in std_logic;	-- 原始時鐘頻率
		clk0:	out std_logic;	-- 正常計時分頻
		clk1:	out std_logic	-- 設定鬧鈴和校時分頻
	);
end diver;
 
architecture a of diver is
	signal m_clk0: std_logic;
	signal m_clk1: std_logic;
	constant c_cnt_0: integer:= 10;
	constant c_cnt_1: integer:= 50;
begin
	clk0 <= m_clk0;
	clk1 <= m_clk1;
	process(clk)
		variable cnt_0: integer range 0 to c_cnt_0;	
		variable cnt_1: integer range 0 to c_cnt_1; 	
	begin
		if (clk'event and clk = '1') then
			cnt_0:= cnt_0 + 1;
			cnt_1:= cnt_1 + 1;
			if (cnt_0 = c_cnt_0) then
				m_clk0 <= not m_clk0;
				cnt_0:= 0;
			end if;
			if (cnt_1 = c_cnt_1) then
				m_clk1 <= not m_clk1;
				cnt_1:= 0;
			end if;
		end if;
	end process;
end a;

3.1.2 模擬結果

使用軟體自帶模擬結果如下:

圖3-1 分頻器模組的模擬結果

可見,原始時鐘在經過分頻後產生了兩種不同的時鐘頻率訊號,說明達到預期效果,模擬成功。不過分頻因子有待後續除錯階段作進一步調整。

3.2 計數器模組

時鐘和鬧鈴的計數器,均由一個24進位制計數器和兩個60進位制計數器組成。因此,時鐘和鬧鈴計數器放在本節內容一併說明。

3.2.1 程式碼實現

計數器為含非同步清零和同步使能的加法計數器,輸入端有三個:輸入時鐘訊號clk、非同步清零端rst、同步使能端en;輸出端為個位和十位的二進位制數q0q1,以及進位訊號cout

以下是秒/分計數器的完整程式碼:

【程式碼3-2 秒/分計數器】
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- 秒計時器

entity count_sec is
	port(
		clk, rst, en: in std_logic;				
		q0, q1: buffer std_logic_vector(3 downto 0); 
		cout: out std_logic
	);
end count_sec;

architecture a of count_sec is
	signal m_clk: std_logic;
	signal num0, num1: std_logic_vector(3 downto 0);
begin
	cout <= '1' when (num0 = "1001" and num1 = "0101" and en = '1') else '0';
	process (rst, clk)
	begin
		m_clk <= clk;
		if (rst = '0') then
			num0 <= "0000";
			num1 <= "0000";
		elsif (m_clk'event and m_clk = '0') then
			if (en = '1') then
				if (num0 = "1001") then
					num0 <= "0000";
					if (num1 = "0101") then
						num1 <= "0000";
					else
						num1 <= num1 + 1;
					end if;
				else
					num0 <= num0 + 1;
				end if;
			end if;
		end if;
	end process;
	q0 <= num0;
	q1 <= num1;
end a;

以下是時計數器的完整程式碼:

【程式碼3-3 時計數器】
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- 時計時器

entity count_hour is
	port(
		clk, rst, en: in std_logic;				
		q0, q1: buffer std_logic_vector(3 downto 0); 
		cout: out std_logic
	);
end count_hour;

architecture a of count_hour is
	signal m_clk: std_logic;
	signal num0, num1: std_logic_vector(3 downto 0);
begin
	cout <= '1' when (num0 = "0011" and num1 = "0010" and en = '1') else '0';
	process (rst, clk)
	begin
		m_clk <= clk;
		if (rst = '1') then
			num0 <= "0000";
			num1 <= "0000";
		elsif (m_clk'event and m_clk = '0') then
			if (en = '1') then
				if ((num0 = "0011") and (num1 = "0010")) then
					num0 <= "0000";
					num1 <= "0000";
				elsif (num0 = "1001") then
					num0 <= "0000";
					num1 <= num1 + 1;
				else
					num0 <= num0 + 1;
				end if;
			end if;
		end if;
	end process;
	q0 <= num0;
	q1 <= num1;
end a;

3.2.2 模擬結果

下面是秒計數器和分計數器(60進位制計數器)的模擬結果:

圖3-2 秒計數器和分計數器的模擬結果

下面是時計數器(24進位制計數器)的模擬結果:

圖3-3 時計數器的模擬結果

可見,兩者均實現了預期效果,模擬成功。

3.3 校時模組

校時功能的具體實現是,當長按下調整按鍵時,時鐘頻率(即計數頻率)加快,待時鐘跳轉到想要的時間時,鬆開按鍵,這樣就完成了一次校時。

校時功能的實現原理十分巧妙,是通過一個二選一選擇器來實現的,位選訊號s連線到調整按鍵,兩路訊號ab分別是來自低階計數器的進位訊號和較快頻率的時鐘訊號,而輸出端則連到計數器的進位訊號。

這樣,當不按按鍵時,選擇器輸出a訊號,此時計數器以正常頻率計數;當按下按鍵時,選擇器輸出b訊號,此時計數器以較快頻率計數,以便使用者進行校時。

3.3.1 程式碼實現

校時模組的本質即是二選一選擇器,完整程式碼如下:

【程式碼3-4 校時模組】

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY mux21a IS
	PORT ( a, b, s: IN  STD_LOGIC;
			y : OUT STD_LOGIC  );
END ENTITY mux21a;

ARCHITECTURE one OF mux21a IS
BEGIN
	PROCESS (a,b,s)
	BEGIN
		IF s = '0'  THEN  y <= a ; ELSE y <= b ;
		END IF;
	END PROCESS;
END ARCHITECTURE one ;

3.3.2 模擬結果

使用軟體自帶模擬結果如下:

圖3-4 校時模組的模擬結果

可見其實現了預期效果,模擬成功。

3.4 報時模組

報時模組的具體實現是,當檢測到分達到59,秒達到53、55、57、59時,蜂鳴器各響一次。

3.4.1 程式碼實現

輸入端是來自時鐘計數器的分和秒,輸出端連線到蜂鳴器。程式碼的實現只需檢測是否符合分達到59,秒達到53、55、57、59的情況,若是,則蜂鳴器訊號為高電平,否則為低電平。完整程式碼如下:

【程式碼3-5 報時模組】
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

entity baoshi is
	port ( min0 ,min1 , sec0, sec1 :in std_logic_vector ( 3 downto 0 );
			speak: out std_logic );
end baoshi;

architecture cml of baoshi is
begin
	speak <= '1'	-- min1=5, min0=9, sec1=5  and  sec0=3, sec0=5, sec0=7, sec0=9
			when (min1="0101" and min0 ="1001" and sec1="0101")and ( sec0="0011" or sec0 ="0101" or sec0 ="0111" or sec0="1001") 
	else '0';
end cml;

3.4.2 模擬結果

使用軟體自帶模擬結果如下:

圖3-5 報時模組的模擬結果

可見其實現了預期效果,模擬成功。

3.5 模式切換模組(按鍵複用模組)

之前已經提到,我們所設計的系統中有三個按鍵是用來調整時、分、秒的,由於系統有兩種模式,若每種模式均配備三個按鍵就顯得有些冗餘了(這樣就一共需要六個按鍵了)。

為了減少按鍵的個數,我們希望這三個按鍵可以在不同模式下設定正常時間和鬧鈴時間,此所謂“按鍵複用”。而且這樣做還有一個好處是,不必擔心在正常顯示時間的狀態下誤觸了設定鬧鈴的按鍵。為了實現以上功能,需要設計一個邏輯電路用來辨別是調整正常時間還是調整鬧鈴時間。

3.5.1 程式碼實現

輸入端是來自模式切換按鍵key和調整時間按鍵的訊號s,輸出端有兩個,一個連線到正常時間的部分s1,另一個連線到鬧鈴時間的部分s2。實現思路是:當s為高電平時,s2通過而s1遮蔽,此時按下key則是調整s2;當s為低電平時,s1通過而s2遮蔽,此時按下key則是調整s1。通過邏輯表,可以很容易得出程式碼如下:

【程式碼3-6 模式切換模組】
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

entity switch is
	port(
		key, s: in std_logic;
		s1, s2: out std_logic
	);
end switch;

architecture a of switch is
begin
	s1 <= key and (not s);
	s2 <= key and s;
end a;

3.5.2 模擬結果

使用軟體自帶模擬結果如下:

圖3-6 模式切換模組的模擬結果

可見其實現了預期效果,模擬成功。

3.6 模式狀態維持模組

由於我們希望按下切換按鍵時,模式狀態就會切換且維持不變,所以我們使用了T觸發器來對按鍵訊號進行處理。當來自按鍵的輸入訊號為高電平時,輸出訊號q就會翻轉一次訊號;當來自按鍵的輸入訊號為低電平時,輸出訊號q狀態不變。

3.6.1 程式碼實現

輸入端有時鐘clk和訊號t,輸出端為訊號q。當時鐘的上升沿來臨時,若t為高電平,則訊號q翻轉一次;否則,訊號q維持不變。完整程式碼如下:

【程式碼3-7 模式狀態維持模組】
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
Use IEEE.STD_LOGIC_ARITH.ALL;
Use IEEE.STD_LOGIC_UNSIGNED.ALL;

ENTITY trigger IS
	port (t, clk: in std_logic;
			q: out std_logic);
end entity;

architecture bhv of trigger is
	signal temp: std_logic;
begin
	process(clk, t)
	begin
		if clk'event and clk='1' then
			if t='1' then
				temp <= not temp;
			else 
				temp <= temp;
			end if;
		end if;
		q <= temp;
	end process;
end bhv;

3.6.2 模擬結果

使用軟體自帶模擬結果如下:

圖3-7 模式狀態維持模組的模擬結果

可見其實現了預期效果,模擬成功。

3.7 鬧鈴檢測模組

鬧鈴檢測模組的具體實現是,當檢測到時、分、秒均達到預設時間時,蜂鳴器響一次。

3.7.1 程式碼實現

輸入端分別是來自時鐘的時、分計數器(min/hour)和來自鬧鈴的時、分計數器(amin/ahour),輸出端則連線到蜂鳴器。當檢測到時、分、秒均達到預設時間時,蜂鳴器響一次。完整程式碼如下:

【程式碼3-8 鬧鈴檢測模組】
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

entity alarm is
	port(
		--sec0, sec1: in std_logic_vector(3 downto 0); 
		min0, min1: in std_logic_vector(3 downto 0); 
		hour0, hour1: in std_logic_vector(3 downto 0); 
		--asec0, asec1: in std_logic_vector(3 downto 0); 
		amin0, amin1: in std_logic_vector(3 downto 0); 
		ahour0, ahour1: in std_logic_vector(3 downto 0); 
		speak: out std_logic
	);
end alarm;

architecture a of alarm is
begin
	speak <= '1' when(min0=amin0 and min1=amin1 
						and hour0=ahour0 and hour1=ahour1)
		else '0';
end a;

3.7.2 模擬結果

假設鬧鈴設定為23:57:00,現實時間從23:57:00過渡到23:58:00,使用軟體自帶模擬結果如下:

圖3-8 鬧鈴檢測模組的模擬結果

可見,當現實時間到了23:57:00時,speak為高電平,此時蜂鳴器工作;當現實時間到了23:58:00時,speak為低電平,此時蜂鳴器不工作,說明實現了預期效果,模擬成功。

3.8 數碼管顯示切換模組

該模組的作用是,通過二選一選擇器,數碼管可以切換顯示正常時間和鬧鈴時間。

3.8.1 程式碼實現

這是一個二選一選擇器,輸入訊號有三個,位選訊號來自切換模式按鍵,其餘兩個為a和b,分別來自正常計數器和鬧鈴計數器的輸出q0和q1。完整程式碼如下:

【程式碼3-9 數碼管顯示切換模組】
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;

ENTITY mmux21a IS
	PORT ( a, b: in std_LOGIC_vector (3 downto 0);
			s: IN  STD_LOGIC;
			y : OUT std_LOGIC_vector (3 downto 0)  
		);
END ENTITY mmux21a;

ARCHITECTURE one OF mmux21a IS
BEGIN
	PROCESS (a,b,s)
	BEGIN
		IF s = '0'  THEN  y <= a ; ELSE y <= b ;
		END IF;
	END PROCESS;
END ARCHITECTURE one ;

3.8.2 模擬結果

使用軟體自帶模擬結果如下:

圖3-9 數碼管顯示切換模組的模擬結果

可見其實現了預期效果,模擬成功。

4 頂層電路設計、模擬和除錯

4.1 頂層電路設計

在各個模組都設計完成、模擬通過、達到預期效果以後,在頂層檔案將模組例項化,接著以一定的方式連線起來。如下程式碼定義了整個系統的輸入和輸出的埠:

【程式碼4-1 頂層電路輸入輸出埠定義】
entity clock_top_1 is
	port(
		clk: in std_LOGIC;  --時鐘源
		key_sec, key_min, key_hour: in std_LOGIC;  --校時設定/鬧鈴設定按鍵
		sec0, sec1: buffer std_logic_vector(3 downto 0); --顯示時
		min0, min1: buffer std_logic_vector(3 downto 0); --顯示分
		hour0, hour1: buffer std_logic_vector(3 downto 0); --顯示秒
		key_shift: in std_LOGIC; --切換功能
		speak: out std_LOGIC --整點響鈴/鬧鈴響鈴
	);
end clock_top_1;

4.1.1 時鐘源部分

建立兩個訊號,分別提供正常時鐘頻率(normal_clk)和較快時鐘頻率(fast_clk,用於校時和設定鬧鈴)。摘錄程式碼如下:

【程式碼4-2 分頻器例項化】
signal normal_clk, fast_clk : std_LOGIC;

--------分頻---------
U1: diver port map (clk=>clk, clk0=>normal_clk, clk1=>fast_clk);

4.1.2 正常時間計數部分

定義訊號,用於數碼管的顯示訊號(sec0_n, sec1_n, min0_n, min1_n, hour0_n, hour1_n)(但並不是真的顯示,因為還要通過模式切換模組來判斷顯示哪個介面);定義兩個進位訊號(min_clk, hour_clk),用於提供下一級計數器的時鐘;三個計數器採取級聯方式,進位訊號作為下一級計數器的時鐘源。

注意,除了秒計數器的rst端外,其他計數器都接入了低電平(復位端為高電平有效),這是因為秒計數器的校時方式只有復位方式。摘錄程式碼如下:

【程式碼4-3 正常時間計數部分例項化】
signal min_clk, hour_clk : std_LOGIC;
signal sec0_n, sec1_n, min0_n, min1_n, hour0_n, hour1_n: std_logic_vector(3 downto 0);

--------正常時間計數器---------
U2: count_sec port map (clk=>normal_clk, rst=>key_sec_n, en=>'1', q0=>sec0_n, q1=>sec1_n, cout=>sec_cout);
U3: count_min port map (clk=>min_clk, rst=>'0', en=>'1', q0=>min0_n, q1=>min1_n, cout=>min_cout);
U4: count_hour port map (clk=>hour_clk, rst=>'0', en=>'1', q0=>hour0_n, q1=>hour1_n);

4.1.3 校時部分

位選訊號s連線到調整按鍵(key_min_nkey_hour_n),兩路訊號a和b分別是來自低階計數器的進位訊號(sec_coutmin_cout)和較快頻率的時鐘訊號(fast_clk),而輸出端則連到計數器的進位訊號(min_clkhour_clk)。摘錄程式碼如下:

【程式碼4-4 校時部分例項化】
signal key_sec_n, key_min_n, key_hour_n: std_LOGIC;

--------校時---------
U5: mux21a port map (a=>sec_cout, b=>fast_clk, s=>key_min_n, y=>min_clk);
U6: mux21a port map (a=>min_cout, b=>fast_clk, s=>key_hour_n, y=>hour_clk);

4.1.4 鬧鈴設定部分

定義了三個按鍵訊號(key_sec_a, key_min_a, key_hour_a),用於設定鬧鈴時間;定義了用於數碼管的顯示訊號(min0_a, min1_a, hour0_a, hour1_a)。

該部分與正常時間計數部分的接法類似,所不同的是,三個計數器的rst端均為低電平,且en端均接了按鍵訊號,在高電平狀態下就會計數。摘錄程式碼如下:

【程式碼4-5 鬧鈴設定部分例項化】
signal sec0_a, sec1_a, min0_a, min1_a, hour0_a, hour1_a: std_logic_vector(3 downto 0);
signal key_sec_a, key_min_a, key_hour_a: std_LOGIC;

--------鬧鐘計數器---------
U17: count_sec port map (clk=>fast_clk, rst=>'0', en=>key_sec_a, q0=>sec0_a, q1=>sec1_a);
U18: count_min port map (clk=>fast_clk, rst=>'0', en=>key_min_a, q0=>min0_a, q1=>min1_a);
U19: count_hour port map (clk=>fast_clk, rst=>'0', en=>key_hour_a, q0=>hour0_a, q1=>hour1_a);

4.1.5 模式切換部分

T觸發器是對模式狀態起保持作用,其例項化如下:

【程式碼4-6 T觸發器例項化】
signal key_shift1: std_LOGIC;

--------模式狀態保持--------
U21: trigger port map (clk=>clk, t=>key_shift, q=>key_shift1);

輸入端是模式切換按鍵key(key_shift1)和調整時間按鍵的訊號s(key_sec, key_min, key_hour),輸出端有兩個,一個連線到正常時間的部分s1(key_sec_n, key_min_n, key_hour_n),另一個連線到鬧鈴時間的部分s2(key_sec_a, key_min_a, key_hour_a)。摘錄程式碼如下:

【程式碼4-7 模式切換部分例項化】
--------模式切換(三按鍵複用)---------
U8: switch port map (key=>key_sec, s=>key_shift1, s1=>key_sec_n,s2=>key_sec_a);
U9: switch port map (key=>key_min, s=>key_shift1, s1=>key_min_n,s2=>key_min_a);
U10: switch port map (key=>key_hour, s=>key_shift1, s1=>key_hour_n, s2=>key_hour_a);

4.1.6 數碼管顯示切換部分

該部分對鬧鈴和正常時間部分的時、分、秒輸出進行分辨後,選擇顯示兩者之一的輸出。摘錄程式碼如下:

【程式碼4-8 數碼管顯示切換部分例項化】
--------數碼管顯示切換(二選一)---------
U11: mmux21a port map (s=>key_shift1, a=>sec0_n, b=>sec0_a, y=>sec0);
U12: mmux21a port map (s=>key_shift1, a=>sec1_n, b=>sec1_a, y=>sec1);
U13: mmux21a port map (s=>key_shift1, a=>min0_n, b=>min0_a, y=>min0);
U14: mmux21a port map (s=>key_shift1, a=>min1_n, b=>min1_a, y=>min1);
U15: mmux21a port map (s=>key_shift1, a=>hour0_n, b=>hour0_a, y=>hour0);
U16: mmux21a port map (s=>key_shift1, a=>hour1_n, b=>hour1_a, y=>hour1);

4.1.7 整點檢測和鬧鈴檢測部分

由於蜂鳴器的輸入來源有兩個,一是來自整點報時,二是來自鬧鈴,因此為解決兩者衝突的矛盾,引入了或門。摘錄程式碼如下:

【程式碼4-9 揚聲器選擇程式碼】
signal speak_a, speak_zd: std_LOGIC; --鬧鈴(a)/整點(zd)

--------揚聲器--------
speak <= speak_a or speak_zd;

整點檢測和鬧鈴檢測的例項化程式碼如下:

【程式碼4-10 整點檢測和鬧鈴檢測的例項化】
--------整點檢測---------
U7: baoshi port map (min0=>min0, min1=>min1, sec0=>sec0, sec1=>sec1, speak=>speak_zd);
--------鬧鈴檢測---------
U20: alarm port map (min0=>min0_n, min1=>min1_n, hour0=>hour0_n, hour1=>hour1_n,
	amin0=>min0_a, amin1=>min1_a, ahour0=>hour0_a, ahour1=>hour1_a,
	speak=>speak_a );

至此,已完成所有模組的例項化和連線。

4.2 頂層電路模擬

使用軟體模擬結果如下:

圖4-1 頂層電路的模擬結果

從左到右:

(1)在圖中的第一部分測試的是校時模組。在正常時間介面顯示時,按下時校時按鍵和分校時按鍵時,分、時的計數頻率加快了。鬆開按鍵後計數器正常計數。同時,剛開始時由於是整點,所以speak為高電平。

(2)在圖中的第二部分測試的是鬧鈴模組。按下切換按鍵(key_shift)然後鬆開,數碼管會切換顯示鬧鈴的時間,按下時校時按鍵和分校時按鍵時,分、時計數器開始計數。鬆開校時按鍵,計數器停止計數。

(3)在圖中的第三部分,再次按下切換按鍵,發現數碼管又切換為正常時間,而且之前調鬧鈴的時候沒有影響到正常時間的計數。

(4)在圖中的最後一部分,再次按下切換按鍵進入鬧鈴模式,發現數碼管顯示的是之前已調好的鬧鈴時間。

總體來看,模擬結果基本符合預期。

4.3 頂層電路除錯

4.3.1 管腳分配

引腳鎖定如下圖:

圖4-2 引腳分配圖

4.3.2 除錯結果

將程式下載、進行硬體測試,下圖是除錯過程中的實驗現象:

圖4-3 硬體除錯過程中的照片

通過一系列的測試,能正常計時、調整時間、報時響鈴、以及鬧鈴響鈴,實驗現象基本符合預期。

5 總結

5.1 缺陷與不足

這次的課設存在以下幾個缺陷與不足:

(1)秒錶功能未實現,數字鐘的功能未完善;

(2)調整時間的設計方法還不太好,我們的方案是按下按鍵,計數頻率就會加快,而後來經過老師提醒,其實還可以將按鍵直接接在下一級的計數器上,這樣按下一次按鍵就能調整時間,顯然這個方案更加簡潔;

(3)鬧鈴的響聲到點只有一次,且聲音很小。

而且我們沒想到的是(或者說沒有注意到),實驗室板子的按鍵原本就有狀態保持的功能,而我們卻為了實現這個功能,多此一舉地專門設計了一個模組,反而影響了最後的實驗效果。可惜礙於時間關係就無法使其臻於完美了。總之,雖然主要功能實現了,但這次課設設計我們還不是特別滿意。

5.2 心得體會

通過此次數字類比電路課程設計,從最開始的選題,到最後的模擬結束,我們更深刻地掌握了數字鐘設計的原理與過程。儘管課題所給出的各個題目相對簡單,但一開始難免有些無從下手,畢竟課程設計不同於實驗課,程式碼與邏輯圖都需要我們自己思考並動手設計實踐的。平常我們所學的基礎課程與實驗,都在此次實踐當中將理論與實踐相結合,它不但能鞏固我們已經所學的理論知識,而且能提高我們的電子電路的設計水平,還能加強我們綜合分析問題與解決問題的能力,進一步培養我們的實驗技能和動手能力,啟發我們的創新意識和創新思維。

我們在著手設計時,著重邏輯,將整個系統根據不同功能化分為多個模組,之後逐個攻破,最後再將其整合,這也是從此次課設所學習到的一種重要方法。所以之後靜下心來,通過查詢課本與課外資料,再加上指導老師所給的提示內容,我們仔細分析題目並思考後,大家互相協助,各抒己見,很快地就完成了課題。

經過這幾周的 VHDL 課設,我們最終做出了數字鐘電路,雖然過程比較艱辛,但這其中的興奮自然是無法用言語表達的。在整個電路設計的過程中,我逐漸加強了對數位電路和 EDA 設計的瞭解和運用能力,對課本知識以及以前學過的知識有了一個更好的總結與理解。通過這次設計,我深刻體會到理論與實踐的區別,瞭解了理論知識和實踐相結合的重要意義,只有當我們親自動手,將理論付之於實現,才能夠將理論知識內化成自己的一部分。在這個過程中,我的動手能力和理論知識的掌握程度有了很大的提高,這將為自己今後的學習和工作打好了堅實的基礎。最後,感謝同學和老師的點撥和幫助!

附錄

模組清單

模組名稱 功能 個數
diver 分頻器 1
count_hour 時計數器 2
count_min 分計數器 2
count_hour 時計數器 2
mux21a 二選一選擇器(校時模組用) 2
mmux21a 二選一選擇器(數碼管顯示用) 6
baoshi 整點檢測 1
alarm 鬧鈴檢測 1
switch 模式切換 3
trigger T觸發器 1

模組總數:21

參考文獻

[1] 閻石主編.數位電子技術基礎[M].6版.北京:高等教育出版社,2016.

[2] 閻石,王紅編.數位電子技術基礎學習輔導與習題解答[M].6版.北京:高等教育出版社,2016.

[3] 潘鬆,黃繼業編.EDA技術實用教程:VHDL版[M].6版.北京:科學出版社,2018.

[4] 江國強編.現代數位電路與系統設計:VHDL版[M]. 6版.北京:電子工業出版社,2018.

[5] 劉炳海編.從零開始學電子電路設計[M].北京:化學工業出版社,2019.