一、電路模組
1、數碼管
開發闆闆載了6個數碼管,全部為共陽型,原理圖如下圖所示,段碼端引腳為DIG[0]~DIG[7]共8位(包含小數點),位選端引腳為SEL[0]~SEL[5]共6位。埠均為低電平有效。
其實物圖如下所示。
數碼管引腳分配見下表。
2、時鐘晶振
開發闆闆載了一個50MHz的有源晶振,為系統提供時鐘。
其實物圖如下所示。
時鐘輸出引腳分配見下表。
3、按鍵
開發闆闆載了4個獨立按鍵,其中有3個使用者按鍵(KEY1~KEY3),1個功能按鍵(RESET)。按鍵按下為低電平(0),釋放為高電平(1),4個按鍵的原理圖如下圖所示。本例中只使用了RESET鍵。
其實物圖如下所示。
按鍵的引腳分配見下表。
二、實驗程式碼
本例實現6個數碼管顯示123456,程式碼使用Verilog編寫,採用例化的形式,共有兩個檔案。
先編寫數碼管實現字形的程式,模組名稱為seg,檔名稱為seg.v,程式碼如下。
module seg( input clk, //板載50HMz系統時鐘 input[3:0] data, //顯示的字形,可顯示0~F十六個字形,所以需要4位 output reg[7:0] seg //字形編碼,包含小數點,共8位 ); always@(posedge clk) //敏感訊號為時鐘上沿 begin case(data) 4'd0:seg <= 8'b1100_0000; //字形0的編碼 4'd1:seg <= 8'b1111_1001; //字形1的編碼 4'd2:seg <= 8'b1010_0100; //字形2的編碼 4'd3:seg <= 8'b1011_0000; //字形3的編碼 4'd4:seg <= 8'b1001_1001; //字形4的編碼 4'd5:seg <= 8'b1001_0010; //字形5的編碼 4'd6:seg <= 8'b1000_0010; //字形6的編碼 4'd7:seg <= 8'b1111_1000; //字形7的編碼 4'd8:seg <= 8'b1000_0000; //字形8的編碼 4'd9:seg <= 8'b1001_0000; //字形9的編碼 4'ha:seg <= 8'b1000_1000; //字形A的編碼 4'hb:seg <= 8'b1000_0011; //字形B的編碼 4'hc:seg <= 8'b1100_0110; //字形C的編碼 4'hd:seg <= 8'b1010_0001; //字形D的編碼 4'he:seg <= 8'b1000_0110; //字形E的編碼 4'hf:seg <= 8'b1000_1110; //字形F的編碼 default:seg <= 7'b111_1111; //預設不顯示 endcase end endmodule
接下來編寫數碼管顯示的程式,模組名稱為seg_show,檔名稱為seg_show.v,程式碼如下。
module seg_show( input clk, //板載50HMz系統時鐘 input rst, //復位按鍵 output reg[7:0] seg7, //段碼埠 output reg[5:0] bit //位選埠 ); reg[19:0] cnt; //定義20位時鐘計數器//下面定義6個存放顯示字形的變數 wire[7:0] seg_data0,seg_data1,seg_data2,seg_data3,seg_data4,seg_data5; //下面定義6個要顯示的數字 wire[3:0] data0 = 4'd1; //data0賦值1 wire[3:0] data1 = 4'd2; //data1賦值2 wire[3:0] data2 = 4'd3; //data2賦值3 wire[3:0] data3 = 4'd4; //data3賦值4 wire[3:0] data4 = 4'd5; //data4賦值5 wire[3:0] data5 = 4'd6; //data5賦值6 //以下例化了6個數碼管seg0~seg5,把各自的顯示數字與字形編碼聯絡起來 seg seg0(.clk(clk), .data(data0), .seg(seg_data0)); seg seg1(.clk(clk), .data(data1), .seg(seg_data1)); seg seg2(.clk(clk), .data(data2), .seg(seg_data2)); seg seg3(.clk(clk), .data(data3), .seg(seg_data3)); seg seg4(.clk(clk), .data(data4), .seg(seg_data4)); seg seg5(.clk(clk), .data(data5), .seg(seg_data5)); //20毫秒迴圈計數 always@(posedge clk or negedge rst) //敏感訊號為時鐘上沿或復位下沿 begin if(!rst) //低電平復位 cnt <= 20'd0; //復位時時鐘計數器清零 else if(cnt == 20'd999_999) //時鐘計數器到達20毫秒時 cnt <= 20'd0; //時鐘計數器清零 else cnt <= cnt + 1; //否則時鐘計數器加1,即來一次時鐘脈衝加一次 end //數碼管掃描顯示 always@(posedge clk or negedge rst) //敏感訊號為時鐘上沿或復位下沿 begin if(!rst) //低電平復位時數碼管全滅 begin bit <= 6'b111111; seg7 <= 8'hff; end else if(cnt == 20'd166_666) //時鐘到3毫秒時,數碼管0顯示1 begin bit <= 6'b111110; seg7 <= seg_data0; end else if(cnt == 20'd333_333) //時鐘到6毫秒時,數碼管1顯示2 begin bit <= 6'b111101; seg7 <= seg_data1; end else if(cnt == 20'd499_999) //時鐘到10毫秒時,數碼管2顯示3 begin bit <= 6'b111011; seg7 <= seg_data2; end else if(cnt == 20'd666_666) //時鐘到13毫秒時,數碼管3顯示4 begin bit <= 6'b110111; seg7 <= seg_data3; end else if(cnt == 20'd833_333) //時鐘到16毫秒時,數碼管4顯示5 begin bit <= 6'b101111; seg7 <= seg_data4; end else if(cnt == 20'd999_999) //時鐘到20毫秒時,數碼管5顯示6 begin bit <= 6'b011111; seg7 <= seg_data5; end end endmodule
三、程式碼說明
1、本例中使用了計數器來分頻時鐘,總計約20ms的迴圈,並在此間分配了6個數碼管進行顯示,每個數碼管大約分得3.3ms左右的顯示時間。
2、時鐘計數器選取了20位,對50MHz時鐘進行分頻,最大能達到20.9ms,剛好大於所需要的20ms,所以取20位進行計數比較合適,太多了浪費硬體資源。
3、時鐘分頻還可以採用PLL的方式,待頻率降下來了之後,再使用少量的計數器來得到所需要的時間,可進一步節約資源,但會消耗一個PLL。
4、程式中使用了數碼管例化的形式,共例化出了6個數碼管。例化的數碼管負責解碼,即把要顯示的數值解碼成顯示的字形編碼,再輸出給數碼管的段碼埠。
5、在定義的變數中,data0~data5分別給了固定值1~6,seg_data0~seg_data5分別接收解碼後的字形編碼,並在下面的過程語句中,分別送給相應數碼管的段埠進行顯示。以0號數碼管為例,它要顯示字元1,先把1賦值給變數data0,然後透過例化seg seg0(.clk(clk), .data(data0), .seg(seg_data0));把data0的值傳給元件內部變數data(即檔案seg.v中的data),而後得到字形編碼,輸出給變數seg_data0,然後當時鍾計數cnt達到20'd166_666時,把seg_data0的資料送給seg7埠,同時開啟0號數碼管的位選通埠(bit <= 6'b111110;),此時該數碼管顯示出字形1。其他數碼管的顯示原理以此類推。
6、由上可以看出,元件例化有點類似於C語句中的函式呼叫及返回,但元件的每一個例化都是要產生出實際的電路的(要消耗邏輯器件),可檢視生成的RTL電路圖。
四、實驗步驟
FPGA開發的詳細步驟請參見“基於EP4CE6F17C8的FPGA開發流程(以半加器為例)”一文,本例只對一同之處進行說明。
本例工程放在D:\EDA_FPGA\Exam_3資料夾下,工程名稱為Exam_3。有兩個模組檔案,一個名稱為seg_show.v,設定為頂層實體,另一個名稱為seg.v,用於提供例化。其餘步驟與“基於EP4CE6F17C8的FPGA開發流程”中的一樣。
接下來看管腳約束,本例中6個數碼管一共有16個引腳,再加上時鐘晶振和復位按鈕,一共18個。具體的埠分配如下圖所示。
對於未用到的引腳設定為三態輸入方式,多用用途引腳全部做為普通I/O埠,電壓設定為3.3-V LVTTL(與”基於EP4CE6F17C8的FPGA開發流程“中的一樣)。需要注意,程式中的每個埠都必須為其分配管腳,如果系統中存在未分配的I/O,軟體可能會進行隨機分配,這將造成不可預料的後果,存在燒壞FPGA晶片的風險。
接下來對工程進行編譯,編譯完成後,可檢視一下邏輯器件的消耗情況,如下圖所示。
另外,還可以點選選單Tools->Netlist Viewers->RTL Viewer,檢視一下生成的RTL電路圖。
最後進行程式下載,並檢視結果。下圖為6個數碼我們管的動態顯示效果。
當按下復位鍵後,所有數碼管均熄滅,如下圖所示。