1. 程式人生 > >數碼顯示型計時器的HDL設計與FPGA板級除錯

數碼顯示型計時器的HDL設計與FPGA板級除錯

在這裡插入圖片描述
作者:毛蘢瑋 / Saint
掘金:https://juejin.im/user/5aa1f89b6fb9a028bb18966a
微博:https://weibo.com/5458277467/profile?topnav=1&wvr=6&is_all=1
GitHub:github.com/saint-000
CSDN: https://me.csdn.net/qq_40531974

數碼顯示型計時器的HDL設計與FPGA板級除錯

摘 要

數字鐘是典型的電子電路的應用,而基於FPGA的數字鐘電路具有更大的靈活性和通用性。本文介紹了基於FPGA開發板的數字鐘設計的基本構想,所提供的功能,基本的模組和控制邏輯。本時鐘系統主晶片採用xc3s200 PQ208-4c,具有顯示時間及計數功能。時間採用24小時迴圈計數,通過按鍵控制數字鐘的開啟和關閉。
**關鍵詞:**FPGA;模組;迴圈計數。

1引言
VHDL結合FPGA可以方便地,可重複利用地實現各種設計,本文主要基於之前的設計原理,設計數字鐘功能所需要的模組和功能邏輯,並在FPGA(開發板;Spartan-3系列xc3s200 )中實現。

2 設計概述
設計一個數字時鐘,具有時分、秒計數顯示。鑑於所提供的功能,電路應當包括以下三大模組:控制模組,分頻模組,計時模組和顯示模組。

3 設計原理

3.1各模組介紹
控制模組包括了開和關兩個按鍵,start端定義管腳約束為P113-S18;reset端定義管腳約束為P111-S17
在這裡插入圖片描述

分頻模組主要是給需要的模組提供特定頻率的時鐘訊號;在實驗中將時鐘分為兩個頻率 bclk1<=count(24);–用於計時
bclk2<=count(14);–用於掃描數碼管
在這裡插入圖片描述


模組程式碼如下:

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
use IEEE.STD_LOGIC_ARITH.all;
entity div_freq is
	port(
		clk,clr : in STD_LOGIC;
		bclk1,bclk2 : out STD_LOGIC
		);
end div_freq;
--}} End of automatically maintained section
architecture behav of div_freq is
	signal count:std_logic_vector(24 downto 0):="0000000000000000000000000";
	signal q : std_logic:='0';
begin
	process(clk,clr)
	begin
		if (clr='0') then 
			count<="0000000000000000000000000";
		elsif(clk'event and clk='1') then	  --上升沿來臨時
			if (count="1111111111111111111111111")then 
				count<="0000000000000000000000000";
			else 
				count<=count+1;
			end if;
		end if;
	end process;
	bclk1<=count(24);--用於計時
	bclk2<=count(14);--用於掃描數碼管
	
	
	-- enter your statements here --	
end behav;

計時模組包括了秒、分、時計數模組,通過不同數位的計數器實現,並提供給顯示模組顯示輸出;
在這裡插入圖片描述
程式碼如下:
U4:秒的個位

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
use IEEE.STD_LOGIC_ARITH.all;
entity cnt10 is
	 port(
		 reset : in STD_LOGIC;
		 en : in STD_LOGIC;
		 clk : in STD_LOGIC;
		 carry : out STD_LOGIC;
		 q : out STD_LOGIC_VECTOR(3 downto 0)
	     );
end cnt10;
--}} End of automatically maintained section
architecture rtl of cnt10 is
signal qs:std_logic_vector(3 downto 0):="0000";
signal ca:std_logic:='0';
begin
	process (clk)
	begin 
		if (rising_edge(clk)) then 
			if (reset='1')then 
				qs<="0000";
			elsif (en='1') then 
				if (qs="1001")then 		   --計數到9,共有10個時鐘上升沿
					qs<="0000";
					ca<='0';
			
				else 
					qs<=qs+1;
					ca<='0';
				end if ;
			end if ;
		end if ;
	q<=qs;
	end process;
	process (ca)
	begin 	
		carry<=ca and en ;
	end process ;
	 -- enter your statements here --
end rtl;

U5:秒的十位
實體大致一樣的,此處不再列舉

	process (clk)
	begin 
		if (rising_edge(clk)) then 
			if (reset='1')then 
				qs<="0000";
			elsif (en='1') then 
				if (qs="0110")then 		--計數到6,清零,不產生進位
					qs<="0000";
					ca<='0';
				
				else 
					qs<=qs+1;
					ca<='0';
				end if ;
			end if ;
		end if ;
	q<=qs;
	end process;
	process(ca,en)
	begin 
		carry<= ca and en ;
	end process;
end rtl;

U6,U8都為十位計數器,U7,U9都為6位計數器;此處不再一一列舉

掃描按鍵顯示:
程式碼如下:

library IEEE;  
use IEEE.STD_LOGIC_1164.ALL; 
use IEEE.STD_LOGIC_ARITH.ALL;  
use IEEE.STD_LOGIC_UNSIGNED.ALL; 
ENTITY KEYSCAN IS 
	PORT (  
		clk : IN std_logic; 
		rst : IN std_logic;
		en : out std_logic;
		row : OUT std_logic_vector(3 DOWNTO 0); -- 行線 
		column  : IN std_logic_vector(3 DOWNTO 0);  -- 列線
		dataout : OUT std_logic_vector(7 DOWNTO 0)--數碼管顯示資料 
		);	
END KEYSCAN;  
ARCHITECTURE arch OF KEYSCAN IS 
	SIGNAL div_cnt : std_logic_vector(24 downto 0):="0000000000000000000000000";  
	SIGNAL scan_key : std_logic_vector(3 DOWNTO 0):="0000"; --掃描碼暫存器
	SIGNAL key_code : std_logic_vector(3 DOWNTO 0):="0000"; 
	SIGNAL dataout_tmp  : std_logic_vector(7 DOWNTO 0):="00000000";
BEGIN  
	PROCESS(clk,rst) 
	BEGIN  
		IF (NOT rst = '1') THEN  
			div_cnt <= "0000000000000000000000000"; 
		ELSIF(clk'EVENT AND clk = '1')THEN 
			div_cnt <= div_cnt + 1; 
		END IF; 
	END PROCESS;  
	PROCESS(div_cnt(20 downto 19)) 
	BEGIN  
		CASE div_cnt(20 downto 19) IS 
			WHEN "00"=> scan_key<="1110"; 
			WHEN "01"=> scan_key<="1101"; 
			WHEN "10"=> scan_key<="1011";
			WHEN "11"=> scan_key<="0111";
			WHEN OTHERS=>NULL;
		END CASE; 
	END PROCESS;
	PROCESS(clk,rst)
	BEGIN
		IF (NOT rst = '1') THEN
			key_code <= "0000";
		elsif(rising_edge(clk))then
			CASE scan_key IS  --檢測何處有鍵按下
				when"1110"=>
					case column is
						when "1110"=> key_code <="0000";
						when "1101"=> key_code <="0001";
						when "1011"=> key_code <="0010";
						when "0111"=> key_code <="0011";
						when others=>null;
				end case;
				when "1101"=>
					case column is
						when "1110"=> key_code <="0100"; 
						when "1101"=> key_code <="0101";
						when "1011"=> key_code <="0110";
						when "0111"=> key_code <="0111";
						when others=>null; 
				end case;
				when "1011"=>
					case column is
						when "1110"=> key_code <="1000"; 
						when "1101"=> key_code <="1001";
						when "1011"=> key_code <="1010";
						when "0111"=> key_code <="1011";
						when others=>null; 
				end case;
				when "0111"=>
					case column is
						when "1110"=> key_code <="1100"; 
						when "1101"=> key_code <="1101";
						when "1011"=> key_code <="1110";
						when "0111"=> key_code <="1111";
						when others=>null; 
				end case;
				when others => key_code <="1111";
			end case;
		end if;	
	end process;
	process(key_code)--顯示鍵值
	begin 
		case key_code is 
			when "0000"=> dataout_tmp <="11000000";
			when "0001"=> dataout_tmp <="11111001";
			when "0010"=> dataout_tmp <="10100100";
			when "0011"=> dataout_tmp <="10110000";
			when "0100"=> dataout_tmp <="10011001";
			when "0101"=> dataout_tmp <="10010010";
			when "0110"=> dataout_tmp <="10000010";
			when "0111"=> dataout_tmp <="11111000";
			when "1000"=> dataout_tmp <="10000000";
			when "1001"=> dataout_tmp <="10010000";
			when "1010"=> dataout_tmp <="10001000";
			when "1011"=> dataout_tmp <="10000011";
			when "1100"=> dataout_tmp <="11000110";
			when "1101"=> dataout_tmp <="10100001";
			when "1110"=> dataout_tmp <="10000110";
			when "1111"=> dataout_tmp <="10001110";
			when others=> null;
		end case;
	end process;
	row <= scan_key;  
	dataout <= dataout_tmp;
	en <='1';
end arch; 

在這裡插入圖片描述

顯示模組包括數碼管的位選和段選,通過資料選擇器實現位選。
位選程式碼如下:

library IEEE;
use IEEE.STD_LOGIC_1164.all; 
use IEEE.STD_LOGIC_unsigned.all;
use IEEE.STD_LOGIC_arith.all;

entity \select\ is
	 port(
		 sec1 :  in STD_LOGIC_VECTOR(3 downto 0);
		 sec2 :  in STD_LOGIC_VECTOR(3 downto 0);
		 min1 :  in STD_LOGIC_VECTOR(3 downto 0);
		 min2 :  in STD_LOGIC_VECTOR(3 downto 0);
		 hour1 : in STD_LOGIC_VECTOR(3 downto 0);
		 hour2 : in STD_LOGIC_VECTOR(3 downto 0);
		 sel  :  in STD_LOGIC_VECTOR(3 downto 0);
		 cout : out STD_LOGIC_VECTOR(3 downto 0);
		 com  : out STD_LOGIC_VECTOR(5 downto 0)
	     );
end \select\;
--}} End of automatically maintained section
architecture rtl of \select\ is
begin
	process(sel)
	begin
		case sel is 
			when "0000"=>cout<=sec1;com<="000001";--選中最低位的數碼管有效
			when "0001"=>cout<=sec2;com<="000010";
			when "0010"=>cout<=min1;com<="000100";
			when "0011"=>cout<=min2;com<="001000";
			when "0100"=>cout<=hour1;com<="010000";
			when "0101"=>cout<=hour2;com<="100000";
			when others=>null;
		end case ;
	end process;
	 -- enter your statements here --

end rtl;

在這裡插入圖片描述

段選程式碼如下:

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
use IEEE.STD_LOGIC_ARITH.all;
entity decorder4_8 is
	port(
		A : in STD_LOGIC_VECTOR(3 downto 0);
		
		Y : out STD_LOGIC_VECTOR(7 downto 0)
		);
end decorder4_8;

--}} End of automatically maintained section
architecture rtl of decorder4_8 is
begin
	process(A)
	begin
		case(A) is						   --hp,g,f,e,d,c,b,a,“1”有效
			when "0000"=>Y<="00111111";	   --顯示數字0
			when "0001"=>Y<="00000110";	   --顯示數字1
			when "0010"=>Y<="01011011";	   --顯示數字2
			when "0011"=>Y<="01001111";	   --顯示數字3
			when "0100"=>Y<="01100110";	   --顯示數字4
			when "0101"=>Y<="01101101";	   --顯示數字5
			when "0110"=>Y<="01111101";	   --顯示數字6
			when "0111"=>Y<="00000111";	   --顯示數字7
			when "1000"=>Y<="01111111";	   --顯示數字8
			when "1001"=>Y<="01101111";	   --顯示數字9
			when others=>Y<="10000000";
		end case;
	end process;	
	-- enter your statements here --	
end rtl;

3.2系統方案
在這裡插入圖片描述
採用同步電路,匯流排結構,主要功能集中在模組內部,模組較為獨立,模組間連線簡單,易於擴充套件,本次設計採用此方案

3.3時鐘系統整體介紹
由分頻器從33.8688MHZ晶振中得到1HZ訊號給計數器提供標準時鍾,用於計時,針對時,分,秒分別設計一組6位和十位計數器對應具體的十位和個位,記滿進位。在通過資料選擇器對掃描數碼管位選,此處用的是bclk2,即分頻器分出的另一時鐘訊號,用來掃描數碼管,顯示是要考慮段選顯示,用四八譯碼器實現。最後輸出至SG段選和COM位選。
在這裡插入圖片描述

3.4頂層程式碼:

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
use IEEE.STD_LOGIC_ARITH.all;
entity add_all is
	port(
		CLK : in STD_LOGIC;
		RESET : in STD_LOGIC;
		START : in STD_LOGIC;
		SG : out STD_LOGIC_VECTOR(7 downto 0):="00000000";
		COM : out STD_LOGIC_VECTOR(5 downto 0):="000000" 		
		);
end add_all;
--}} End of automatically maintained section
architecture rtl of add_all is
	component div_freq
		port (clk:in std_logic;
			bclk: out std_logic );
	end component ;
	component cnt10
		port (
			reset : in STD_LOGIC;
			en : in STD_LOGIC;
			clk : in STD_LOGIC;
			carry : out STD_LOGIC;
			q : out STD_LOGIC_VECTOR(3 downto 0)
			);
	end component ;
	component cnt_6
		port (
			clk : in STD_LOGIC;
			en : in STD_LOGIC;
			reset : in STD_LOGIC;
			carry : out STD_LOGIC;
			q : out STD_LOGIC_VECTOR(3 downto 0)
			);
	end component ;
	component decorder4_8
		port (
			A : in STD_LOGIC_VECTOR(3 downto 0);
			Y : out STD_LOGIC_VECTOR(7 downto 0)
			);
	end component ;	
	signal clk_temp:std_logic ;
	signal en1,en2,en3,en4,en5,en6:std_logic ;
	signal q1,q2,q3,q4,q5,q6:std_logic_vector(3 downto 0) ;
	signal qs:std_logic_vector(3 downto 0):="0000";
begin				   	
	u1:div_freq 
	port map (clk=>CLK,bclk=>clk_temp);
	process(clk_temp,qs) 
	begin 
		if (rising_edge(clk_temp)) then	
			if qs="0101" then 	 --計數到5,有6個時鐘上升沿。對div_2k再進行6分頻
				qs<="0000";
			else
				qs<=qs+1;
			end if ;
		end if ;
		case qs is 
			when "0000"=>COM<="000001";	--com端最低位為高電平,選通此位數碼管
			when "0001"=>COM<="000010";
			when "0010"=>COM<="000100";
			when "0011"=>COM<="001000";
			when "0100"=>COM<="010000";
			when "0101"=>COM<="100000";
			when others => null ;
		end case ;	
	end process ;
	c1:cnt10
	port map (reset=>RESET,en=>START,clk=>clk_temp,	 --最低位
		carry=>en1,q=>q1); 
	c2:cnt10
	port map (reset=>RESET,en=>en1,clk=>clk_temp,	 --秒
		carry=>en2,q=>q2);
	c3:cnt10
	port map (reset=>RESET,en=>en2,clk=>clk_temp,	 --分,個位
		carry=>en3,q=>q3);
	c4:cnt_6
	port map (reset=>RESET,en=>en3,clk=>clk_temp,	 --分,十位
		carry=>en4,q=>q4);
	c5:cnt10
	port map (reset=>RESET,en=>en4,clk=>clk_temp,	 --時,個位
		carry=>en5,q=>q5);
	c6:cnt_6
	port map (reset=>RESET,en=>en5,clk=>clk_temp,	 --時,十位
		carry=>en6,q=>q6);
	d1:decorder4_8
	port map (A=>q1,Y=>SG);							 --將計數器產生的四位二進位制數傳到譯碼器譯碼輸出
	d2:decorder4_8
	port map (A=>q2,Y=>SG);
	d3:decorder4_8
	port map (A=>q3,Y=>SG);
	d4:decorder4_8
	port map (A=>q4,Y=>SG);
	d5:decorder4_8
	port map (A=>q5,Y=>SG);
	d6:decorder4_8
	port map (A=>q6,Y=>SG);
	
end rtl;

3.5 管腳約束

在這裡插入圖片描述
3.6代表性波形模擬
正常計數時,當秒計數器計數到59時,再來一個時鐘脈衝,則秒計數器清零,而進位則作為分計數器的計數脈衝,使時計數器加一。(00:00:59=>00:01:00)
在這裡插入圖片描述
當分計數器計數到59時,再來一個時鐘脈衝,則分計數器清零,而進位則作為時計數器的計數脈衝,使時計數器加一。(00:59:59=>01:00:00)
在這裡插入圖片描述
3.7實驗截圖:
在這裡插入圖片描述
4 總結
4.1實驗改進:
在實驗中,發現忽略了時計數模組是二十四進位制的,所以對實驗的改進如下:
時鐘時計數子模組:
時計數子模組是由一個24進位制計數器組成,正常計數時,當時計數器計數到23時,再來一個脈衝,則時計數器清零,重新開始新一輪的計數。
時的計時電路可以用如下方式實現:
程式碼如下:

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
use IEEE.STD_LOGIC_ARITH.all;
entity clock24 is
	 port(
		 clk : in STD_LOGIC;  --時鐘
		 en : in STD_LOGIC;	  --使能端
		 reset : in STD_LOGIC; --復位
		 load : in STD_LOGIC;  --置數
		 d : in STD_LOGIC;	--輸入端
		 carry : out STD_LOGIC;	--進位
		QH: BUFFER STD LOGIC VECTOR(3 DOWNTO 0);
 		QL: BUFFER STD LOGIC VECTOR(3 DOWNTO 0)
	     );
end  clock24;
--}} End of automatically maintained section
architecture rtl of clock24 is
	process (clk,reset)
	begin  
		if(reset='0')then
			QH<="0000";
			QL<="0000";		   --非同步復位
		elsif (rising_edge(clk)) then 
			if (load='1')then 
			QH<=D(7DOWNTO 4);
			QL<=D(3DOWNTO 0);	
		elsif(en='1')then
		if(QL=9 or (QH=2 and QL=3))then
			QL<=0000;
			if(QH=2)then
			   QH<="0000";
			else
				QH<=QH+1;
			end if
			else
				QL<=QL+1;
		end if;
		end if;
			
	end process;	
end rtl;

4.2實驗心得:
(1)通過這次電子設計,我更加明白了團隊合作的重要性。僅僅是做一個數字鍾,工作量就相當大,不是一個人所能獨立完成的。
(2)我對FPGA的設計和模擬產生了更深刻的認識和理解,也對之前學到的知識和做過的實驗有了更深刻的認識。熟悉編譯環境、實用軟體,也通過軟體鞏固了自己的知識。
(3)在實驗除錯過程會遇到各種問題,我們耐心解決,在解決問題時也用到了排除法,比如在ise軟體下下載bit檔案時發現軟體視窗沒有彈出如下頁面:
在這裡插入圖片描述
後來依次排查FPGA開發板,JTAG聯結器和杜邦線,一一排除問題後,發現原來的聯結器的線接觸不良。
在這裡插入圖片描述
排除問題後匯入BIT流檔案,測試FPGA。