前言
數字系統設計的大作業來了,用VHDL語言在實驗板上實現一個出租車計價設計,有一些難度,大概花了幾天時間,不過好在最后搞出來了,同時總結一下遇到的問題,為了排版整潔,源代碼就放在最后放出了。
溫馨提示一下,一定要看老師發的關于實驗板的說明文檔和教材,很多東西說明文檔里都有的!
本博客原創,轉載請注明!!!
本源代碼原創,轉載和使用請注明!!!
白嫖容易,希望同學能獨立完成!!!
如果有一些童鞋毫無思路可以參考一下我的實現思路,但是最后一定要自己去實踐、去完成本次實驗設計!!!
可能看懂代碼花的時間比自己敲用的時間還長
實驗板:FLEX10k系列的EPF10K20TC144-4
軟件:QuartusII 9
如果對QuartusII 9下載程序過程不熟悉的童鞋,可以看一下我之前的博客:數字系統設計學習之QuartusII9下載程序
廢話不多說,開始吧!
本文原創,創作不易,轉載請注明!!!
本文鏈接
個人博客:https://ronglin.fun/?p=262
PDF鏈接:見博客網站
CSDN: https://blog.csdn.net/RongLin02/article/details/116535972
設計
題目需求
出租車計價器設計(平臺實現)★★
完成簡易出租車計價器設計,選做停車等待計價功能。
基本功能:
1)起步8元/3公里,此后2元/公里;
2)里程指示信號為每前進50米一個高電平脈沖,上升沿有效;顯示行駛公里數,精確到0.1公里。(模擬時速40KM/h)
4)前進里程開始之前顯示價錢,精確到0.1元;
5)用兩個按鍵分別表示開始行程和結束行程。
選做功能:
1) 增加一個停車等待/恢復行程按鈕,用2個數碼管顯示等待時間,精確到0.1分鐘。
2) 等候費1元/分鐘,計價精度為0.1元。
題目分析
根據實驗需求,整個程序的運行過程可以分為三個狀態,分別是停止狀態、運行狀態和等待狀態,停止狀態用來顯示本次行駛的相關數據;運行狀態,模擬車輛運行,里程指示以時鐘脈沖為標志,即來一次時鐘上升沿,行駛里程數增加一次。同時根據計費準則計費,計費規則為,前3公里8元錢,此后2元/公里;等待狀態下,以時鐘信號為標志增加等待時間,顯示等待時間,同時根據等待時間計費,計費規則為1分鐘1元錢,即6秒1毛錢。
分析完題目就可以開始畫思維圖了:
思路設計如上圖,一共設計了三個狀態分別是停止狀態、運行狀態和等待狀態,停止狀態不做任何計算,只是顯示本次行駛的“等待時間”、“行駛公里數”和“費用”,當用戶按下開始按鈕之后,程序切換到運行狀態,當cp到來時增加公里數,同時費用增加,當用戶按下等待按鈕時,程序切換到等待狀態,當cp到來時增加等待時間,計費累計。一目了然。
之后開始頭禿敲代碼,為了排版整潔,源代碼放在博客最后。
測試
因為用到了數碼管,輸出的最后結果是數碼管的譯碼值,不直觀,所以仿真圖就不放出來了,直接看板載結果。
板載測試
計費規則:
等待時間換算,1元/分鐘(每6秒1毛錢)
前3公里8元錢,
此后2元/公里;
測試結果1
計算過程:
等待時間 花費:42秒/6 = 0.7元
前3公里 花費:8元
后2.8公里 花費: 2.8*2=5.6元
一共花費 0.7 + 8 + 5.6 = 14.3元
測試結果2
計算過程:
等待時間 花費:1分12秒 = 72秒,72秒/6 = 1.2元
前3公里 花費:8元
后0.5公里 花費: 0.5*2=1元
一共花費1.2 + 8 + 1 = 10.2元
設計結論與分析
總述
本次設計采取的主要是有限狀態機的思想,雖說是狀態機,但和書上的比卻不是正宗的VHDL模式的狀態機,而是類比了嵌入式STM32類單片機的狀態機設計思想,非要說的話,算是Moore類狀態機,用一個process控制狀態切換,這個進程主要監聽兩個按鈕的變化,根據用戶的輸入改變狀態;第二個process就是每一個狀態下,當時鐘信號到來時應該執行什么操作;還有一(兩)個process是用來控制數碼管,讓其顯示自己需要的內容。最開始設計數碼管的顯示,也是根據當前的狀態輸出值,但最后經過比較 不同狀態需要輸出的內容時,由于數碼管數量有限,不同狀態下能輸出的值的數量有限,最后統一,不論哪種狀態,都只顯示“等待時間”、“行駛公里數”、“價錢”。其中還有一個小瑕疵就是,本來想用1Hz作為標準時鐘,但是1Hz的頻率太慢,不利于調試,最后還是調高了頻率。
容錯測試
我的測試重點主要在于用戶的錯誤操作,對于基礎功能,經過多次計算和測試是沒有問題的,所以重點放在了錯誤操作上:
1.在停止狀態,點擊等待狀態的按鍵。
在停止狀態,如果啟動等待狀態,是沒有任何反應的,數碼管仍然顯示“等待時間”、“行駛公里數”、“價錢”且數值不變。符合日常生活。
2.在等待狀態,點擊停止狀態的按鍵。
點擊之后,等待時間不再讀秒,立刻停止,此時再次撥動等待狀態的按鍵,數碼管沒有任何反應。符合日常邏輯。
3.在等待狀態,點擊開始狀態的按鍵。
點擊開始狀態的按鍵,沒有任何反應。
4. 在等待狀態的按鍵未關閉的狀態下,直接點擊開始
會直接進入等待狀態,“等待時間”讀秒,同時計費,不論是否超過了3公里。符合設計邏輯。
優點
由于數碼管的限制,行駛公里數只能顯示兩位,所以最高只能顯示9.9公里,但是這只是顯示問題,公里數只會顯示個位和小數位,費用不會受到影響,等待時間同理,等待時間只顯示分鐘和秒數,最高只能顯示到9分59秒,如果超過了10分鐘,只會從0開始讀秒,但是費用不會受到影響。不過因為數碼管數量限制,只能最高顯示99.9元,如果超過99.9元,費用就會清空。
缺點
最大的問題還是在于頻率的問題,因為1Hz太慢,不利于調試查看最后結果,就稍微加快了一下頻率,用的100Hz時鐘,然后開關是110,最后的時鐘頻率就是100/(2^6) = 1.5625Hz,如果非要改進的話,需要再定義一個分頻器。按照目前的時鐘頻率,模擬的出租車時速為562.5km/h。起飛 盡管如此,需要看到3公里之后的價格情況仍需等待一段時間,故我特意加快了時鐘頻率(車速)來更方便的調試。
可擴展性
我的設計思路基于有限狀態機,最初定義了4個狀態,停止,行駛3公里內,行駛超3公里,等待。但經過分析,最后確定的狀態只有,停止和行駛兩種狀態,為了使代碼更整潔,等待狀態融進了行駛狀態中,但是基本的狀態框架已經設計好,可以在其上增加其他狀態,或者將我調快的時鐘頻率調回成符合日常的車速,這些改動不需要對代碼代碼有很大改動,這是狀態機設計思路的好處,同時代碼的可讀性也更強。
問題與總結
重點來了,主要是遇到的問題的一些解決方法,非要說的話,也不算是解決方案,而且避開這些問題,因為理論部分實在是不扎實,很多東西并不清楚是什么原因。
遇到的問題
邏輯單元不足
遇到的第一個問題就是,邏輯單元不夠用。這個錯誤主要是因為代碼太復雜,綜合的時候需要大量的邏輯單元,而手上的芯片只有1152個邏輯單元,所以編譯的時候都通不過。落實到源代碼中就是定義了一個整數范圍為0-99999的信號量,當這個信號量參與運算尤其是乘除模的時候,需要耗費大量的邏輯單元,最后導致不可用。
解決方法:
首先嘗試的的一個方法就是把這個整型量換算為二進制數,最后需要用數碼管顯示的時候再用函數轉化。最開始這個方法是可以解決問題的,但隨著功能的增加,需要對這個信號量進行大量的運算,結果還是超了。
最后解決方法是,定義多個信號量,將上邊的整型按位拆分,例如最大需要999,就定義3個長度為4的位向量(0000-1010),最后顯示的時候,依次處理,不過這種方法的難度在于,每一次運算都要做進位運算,當個位滿10的時候就要進位到十位,每一位都要判斷,雖然代碼麻煩一些,但是可省下很多邏輯單元。
信號賦值延遲問題
這個問題應該算是VHDL語言的特性了,給一個信號量賦值的時候,不會有效,整個進程執行到最后一條語句時進程接下來掛起時,數據才發生帶入,當設計很簡單的電路時,延遲一個時鐘周期貌似沒什么問題,但是設計很難的項目時,必須要嚴謹的對待每一個時鐘周期。
解決方法:
我用的方法就是,在process中定義一個變量,初始值為信號量的值,在進程中對信號的賦值是立刻實現的,然后對這個變量進行操作,就行了,最后還要記得在進程的結束處將變量的值賦給信號量存儲起來。
case在不同分支對同一量賦值
這個問題不知道算不算是問題,但是我在做板載測試的時候確實遇到了,一個case語句,在不同的分支中,如果都有對同一個量的賦值運算,這個量的最后值可能不合乎邏輯。不論是信號量還是變量。
解決方法:
我對EDA的內部邏輯并不是很懂,可能是因為并行性?如果對于軟件設計來說,case語句中不同分支是相互獨立的,絕不會相互影響的,但是在VHDL中有些小問題。我的解決方法就是將case換成if,最后就可以通過了。
總結
對于本學科,數字系統設計的一些感想,因為我之前已經入門了嵌入式開發,深知硬件設計的難度,尤其是課本前幾章的復雜的理論體系中,我學的有些迷茫。不過當拿到硬件之后,我就開始慢慢的了解EDA,用板子實現一個個常用功能非常有成就感,但是學習過程中,我覺得最大的難點在于,現在網絡上對于EDA的資料太少,當quartus報錯時,很多報錯都查不到相關原因,只能自己一步一步修改代碼,一句一句代碼的改來確定報錯原因。其次的難點在于硬件設計和軟件設計還是有很大的區別,雖然VHDL像高級語言一樣,有很多的方便的用法,但是它始終還是硬件。硬件的學習真是一個比較漫長且難熬的過程,不過幸好在不斷的改錯下完成了大作業。=w=
源代碼
硬件資源:FLEX10k系列的EPF10K20TC144-4
軟件資源:QuartusII 9
參考資料:EDA-I便攜式數字系統實驗與設計平臺說明書.pdf和EDA技術與VHDL設計(第二版)徐志軍、王金明、嚴廷輝等編著。
所需資源:
本次實驗設計輸入端口共需要5個:2個時鐘信號、2個輸入按鍵、1個輸入開關;輸出端口為2類,1個是數碼管的選擇位矢量,1個是數碼管的顯示數據。所以共需要資源為:8個數碼管的片選sel(0…7)(pin_119—pin_135),數碼管顯示digital(7…0)(pin_136—pin_144),兩個時鐘信號(clk_digital:pin_55、clk_run:pin_122),2個輸入按鍵(key_begin:pin_43、key_stop:pin_33),1個輸入開關(key_wait:pin_44)
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;ENTITY taxi is --配置實體port(clk_digital : in std_logic;clk_run : in std_logic;key_begin: in std_logic;key_stop: in std_logic;key_wait:in std_logic;sel : out std_logic_vector(7 downto 0);digital : out std_logic_vector(7 downto 0));
end ;architecture cost of taxi is --配置結構體TYPE digital_ARRAY is array (7 downto 0) of std_logic_vector(7 downto 0); --digital個LED燈
signal digitals : digital_ARRAY;
signal digital_index : integer range 0 to 7;TYPE digital_codes is array (0 to 9) of std_logic_vector(7 downto 0); --數碼管譯碼數組
signal digital_code : digital_codes;
signal digital_code_point : digital_codes;signal flag : std_logic := '0';
signal state : integer range 0 to 1 := 0; --0:stop; 1:run;signal km_0 : std_logic_vector(3 downto 0):= "0000";
signal km_1 : std_logic_vector(3 downto 0):= "0000";signal free_0 : std_logic_vector(4 downto 0) :="00000";
signal free_1 : std_logic_vector(4 downto 0) :="01000";
signal free_2 : std_logic_vector(4 downto 0) :="00000";
signal flag_free : std_logic := '0';signal wait_time_min : std_logic_vector(3 downto 0) := "0000";
signal wait_time_sec_0 : std_logic_vector(3 downto 0):= "0000";
signal wait_time_sec_1 : std_logic_vector(3 downto 0):= "0000";
signal counter_6 : integer range 0 to 6 :=0;begin--數碼管譯碼數組--digitals(index) <= digital_code(x_index)digital_code(0) <= "11111100";digital_code(1) <= "01100000";digital_code(2) <= "11011010";digital_code(3) <= "11110010";digital_code(4) <= "01100110";digital_code(5) <= "10110110";digital_code(6) <= "10111110";digital_code(7) <= "11100000";digital_code(8) <= "11111110";digital_code(9) <= "11110110";digital_code_point(0) <= "11111101";digital_code_point(1) <= "01100001";digital_code_point(2) <= "11011011";digital_code_point(3) <= "11110011";digital_code_point(4) <= "01100111";digital_code_point(5) <= "10110111";digital_code_point(6) <= "10111111";digital_code_point(7) <= "11100001";digital_code_point(8) <= "11111111";digital_code_point(9) <= "11110111"; digital_show:process(clk_digital) --動態掃描8個管 beginif clk_digital'event and clk_digital = '1' thendigital <= digitals(digital_index);sel <= NOT ( conv_std_logic_vector(2**digital_index,8) ); --構造位向量,選中哪一個數碼管if digital_index = 7 thendigital_index <= 0;elsedigital_index <= digital_index + 1 ;end if;end if;end process;--結果的展示效果--先用3管顯示等待時間--再用2管顯示已經行駛的公里數--最后用3管顯示價格show:process(clk_digital)begindigitals(7) <= digital_code_point( conv_integer(wait_time_min) );digitals(6) <= digital_code(conv_integer(wait_time_sec_1));digitals(5) <= digital_code(conv_integer(wait_time_sec_0));digitals(4) <= digital_code_point(conv_integer(km_1));digitals(3) <= digital_code(conv_integer(km_0));digitals(2) <= digital_code( conv_integer(free_2) );digitals(1) <= digital_code_point( conv_integer(free_1) );digitals(0) <= digital_code(conv_integer(free_0));end process;process(key_begin,key_stop) --狀態轉化的條件beginif key_stop ='0' then state <= 0;elseif key_begin = '0' thenstate <= 1;end if; end if;end process;process(clk_run,state)--用變量解決信號賦值的延遲問題variable km_0_tmp : std_logic_vector(3 downto 0) := km_0;variable km_1_tmp : std_logic_vector(3 downto 0) := km_1;variable free_0_tmp : std_logic_vector(4 downto 0) := free_0;variable free_1_tmp : std_logic_vector(4 downto 0) := free_1;variable free_2_tmp : std_logic_vector(4 downto 0) := free_2;variable flag_free_tmp : std_logic := flag_free;variable wait_time_min_tmp : std_logic_vector(3 downto 0) := wait_time_min;variable wait_time_sec_0_tmp : std_logic_vector(3 downto 0) := wait_time_sec_0;variable wait_time_sec_1_tmp : std_logic_vector(3 downto 0) := wait_time_sec_1;variable counter_6_tmp : integer range 0 to 6 := counter_6;begincase state iswhen 0 =>--stopflag <= '0';when 1 =>--run if flag = '0' then --每一次出發前清空上一次的結果km_0_tmp := "0000"; km_1_tmp := "0000";free_0_tmp := "00000"; free_1_tmp := "01000"; free_2_tmp := "00000";flag_free_tmp := '0';wait_time_min_tmp := "0000";wait_time_sec_0_tmp := "0000"; wait_time_sec_1_tmp := "0000";flag <= '1';elseif clk_run'event and clk_run = '1' then --統一時鐘以免出現數據被錯誤修改if key_wait = '1' then --在等待狀態下wait_time_sec_0_tmp := wait_time_sec_0_tmp + "0001";--以下代碼用于處理進位if wait_time_sec_0_tmp >= "1010" thenwait_time_sec_1_tmp := wait_time_sec_1_tmp + "0001";wait_time_sec_0_tmp := wait_time_sec_0_tmp - "1010";end if;if wait_time_sec_1_tmp >= "0110" thenwait_time_min_tmp := wait_time_min_tmp + "0001";wait_time_sec_1_tmp := wait_time_sec_1_tmp - "0110";end if;if wait_time_min_tmp >= "1010" thenwait_time_min_tmp := "0000";end if;counter_6_tmp := counter_6_tmp + 1; --計數器,每等待6s費用增加0.1if counter_6_tmp >= 6 thencounter_6_tmp := 0;free_0_tmp := free_0_tmp + "00001";if free_0_tmp >= "01010" thenfree_1_tmp := free_1_tmp + "00001";free_0_tmp := free_0_tmp - "01010"; end if;if free_1_tmp >= "01010" thenfree_2_tmp := free_2_tmp + "00001";free_1_tmp := free_1_tmp - "01010"; end if;if free_2_tmp >= "01010" thenfree_2_tmp := "00000"; end if;end if;else --在運行狀態下km_0_tmp := km_0_tmp + "0001" ;--以下代碼用于處理滿10進位if km_0_tmp >= "1010" thenkm_1_tmp := km_1_tmp + "0001";km_0_tmp := "0000";end if;if km_1_tmp >= "0011" and km_0_tmp > "0000" thenflag_free_tmp := '1';end if;--如果超過9.9公里,清空公里顯示但是計費仍存在if km_1_tmp >= "1010" then km_1_tmp := km_1_tmp - "1010";end if;--超過3公里開始計費if flag_free_tmp = '1' then free_0_tmp := free_0_tmp + "00010";if free_0_tmp >= "01010" thenfree_1_tmp := free_1_tmp + "00001";free_0_tmp := free_0_tmp - "01010"; end if;if free_1_tmp >= "01010" thenfree_2_tmp := free_2_tmp + "00001";free_1_tmp := free_1_tmp - "01010"; end if;if free_2_tmp >= "01010" thenfree_2_tmp := "00000"; end if;end if;end if;--運行/等待狀態 end if;--時鐘上升沿end if; --清空上一次的結果when others =>flag <= '0';end case;--將變量的值賦給信號km_0 <= km_0_tmp;km_1 <= km_1_tmp;flag_free <= flag_free_tmp;wait_time_min <= wait_time_min_tmp;wait_time_sec_0 <= wait_time_sec_0_tmp;wait_time_sec_1 <= wait_time_sec_1_tmp;counter_6 <= counter_6_tmp;free_0 <= free_0_tmp;free_1 <= free_1_tmp;free_2 <= free_2_tmp;end process;end cost;
總結
以上是生活随笔為你收集整理的数字系统设计学习之出租车计价器设计的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。