寫在前面的話
在項目設計中,我們通常需要一些顯示設備來顯示我們需要的信息,可以選擇的顯示設備更是種類繁多,玲琅滿目,數碼管無疑是最常用,最簡單的顯示設備之一。本節,夢翼師兄和大家一起學習數碼管的顯示原理和驅動方式,為我們以后項目的開發做好准備。
項目需求
設計一個數碼管的驅動電路,使數碼管能夠同時顯示出任意的六位數字(夢翼師兄使用的開發板集成的數碼管為六位連體數碼管)。
原理分析
數碼管作為一種外設,我們首先需要了解它的工作原理以及它的對應電路連接關系,七段數碼管結構示意圖如下:
顧名思義,七段數碼管就是使用七段點亮的線段來拼成常見的數字和某些字母,這種顯示方式我們在數字電路中非常容易見到。再加上右下角顯示的小數點,實際上一個顯示單元包括了8根信號線。根據電路設計的不同,這些信號線可能高有效也可能低有效。我們通過FPGA控制這些線段的亮滅,就可以達到相應的顯示效果。
對於多個數碼管的顯示模塊,將每一個都連接到FPGA的管腳會耗用大量FPGA的管腳資源。因此我們同樣引入一種類似矩陣鍵盤的掃描方式。任何時刻我們只使用8根信號點亮一個數碼管,但是8個數碼管是隨着時鍾步調交替點亮的,只要時鍾的速度夠快,我們觀察到數碼管就好像幾個同時點亮一樣。夢翼師兄使用的開發板原理圖如下:
如圖所示,我們的開發板使用的是六位共陽極數碼管,六個PNP型三極管分別作為六組數碼管電源的輸入開關,也就是我們常說的位選信號,PNP三極管為低電平導通,所以我們的位選信號低有效。在這里,為了節約FPGA的IO資源,我們把六個位選信號連接到了三八譯碼器74HC138D,該三八譯碼器的真值表如下:
由此,我們可以得出結論,當{SEL2, SEL1, SEL0}=3’b000時,Y0變為低電平,而由於Y0連接到了第一個數碼管,所以第一個數碼管點亮。當{SEL2, SEL1, SEL0}=3’b001時,對應第二個數碼管點亮,以此類推。SEG_0到SEG_7分別對應二極管a-g以及“小數點”,即我們所說的段選信號。由於是共陽極數碼管,所以二極管只要給低電平就可以點亮,根據點亮的二極管不同,就可以顯示出不同的字符。假如我們要點亮第一個數碼管,並且顯示出字符“A”,那么我們就只需要選中第一個數碼管{SEL2, SEL1, SEL0}=3’b000,而且SEG=8’b1000_1000。
如果要讓數碼管“全部亮起來”,並同時顯示相同字符,那我們只能通過比較快速的切換位選信號來實現這一目的。但是切換頻率如果過高,數碼管顯示也會出現不穩定的狀態,這和器件的工藝有關,我們可以選擇切換的經驗頻率1KHZ。那么這時,我們就需要用到分頻模塊,將50MHZ的晶振時鍾分頻成我們所需要的1KHZ。
單個數碼管顯示
單個數碼管顯示的系統架構
單個數碼管顯示最大的數字是十六進制中的F(15),15對應的二進制數是4’b1111,所以我們的輸入應該是四位。
單個數碼管顯示的模塊模塊功能介紹
模塊名 |
功能描述 |
SEG7 |
輸出控制線 |
單個數碼管顯示模塊的端口描述
端口名 |
端口說明 |
clk |
系統時鍾輸入 |
Rst_n |
系統復位 |
Data[3:0] |
數據輸入 |
sel[2:0] |
片選信號輸出 |
seg[7:0] |
段選信號輸出 |
代碼解釋
SEG7模塊代碼
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056
* The module function:控制單個數碼管顯示任意的數字 *****************************************************/ 00 module SEG7 ( 01 clk, //系統時鍾 02 rst_n,//系統復位 03 data, //輸入數據 04 seg,//數碼管段選 05 sel//數碼管位選 06 ); 07 //系統輸入 08 input clk;//系統時鍾 09 input rst_n;//系統復位 10 input [3:0] data;//輸入數據 11 //系統輸出 12 output reg [7:0] seg;//數碼管段選 13 output reg [2:0] sel;//數碼管位選 14 15 always @ (posedge clk or negedge rst_n) 16 begin 17 if (!rst_n)//復位的時候選擇第一個數碼管 18 begin 19 sel <= 0; 20 end 21 else 22 begin//選擇第一個數碼管 23 sel <= 0; 24 end 25 end 26 27 always @ (*)//用組合邏輯進行輸出段選信號 28 begin 29 if (!rst_n)//復位的時候數碼管熄滅 30 begin 31 seg = 8'b1111_1111; 32 end 33 else 34 begin 35 case(data) 36 0 : seg = 8'b1100_0000;//顯示“0” 37 1 : seg = 8'b1111_1001;//顯示“1” 38 2 : seg = 8'b1010_0100;//顯示“2” 39 3 : seg = 8'b1011_0000;//顯示“3” 40 4 : seg = 8'b1001_1001;//顯示“4” 41 5 : seg = 8'b1001_0010;//顯示“5” 42 6 : seg = 8'b1000_0010;//顯示“6” 43 7 : seg = 8'b1111_1000;//顯示“7” 44 8 : seg = 8'b1000_0000;//顯示“8” 45 9 : seg = 8'b1001_0000;//顯示“9” 46 10 : seg = 8'b1000_1000;//顯示“A” 47 11 : seg = 8'b1000_0011;//顯示“B” 48 12 : seg = 8'b1100_0110;//顯示“C” 49 13 : seg = 8'b1010_0001;//顯示“D” 50 14 : seg = 8'b1000_0110;//顯示“E” 51 15 : seg = 8'b1000_1110;//顯示“F” 52 default : seg = 8'b1111_1111;//全滅 53 endcase 54 end 55 end 56 57 endmodule |
仿真代碼
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056 * The module function:測試SEG7模塊,並顯示“A” *****************************************************/ 00 `timescale 1ns/1ps //定義時間單位和精度 01 02 module SEG7_tb; 03 //系統輸入 04 reg clk;//系統時鍾 05 reg rst_n;//系統復位 06 reg [3:0] data;//輸入數據 07 //系統輸出 08 wire [7:0] seg;//數碼管段選 09 wire [2:0] sel;//數碼管位選 10 11 initial begin 12 clk = 1; 13 rst_n = 0; 14 data = 10;//data = 4'hA; 這兩種方式都可以 15 # 200.1 //復位200ns 16 rst_n = 1; 17 end 18 19 always # 10 clk = ~clk;//50M的時鍾 20 21 SEG7 SEG7 ( 22 .clk(clk), //系統時鍾 23 .rst_n(rst_n),//系統復位 24 .data(data), //輸入數據 25 .seg(seg),//數碼管段選 26 .sel(sel)//數碼管位選 27 ); 28 29 endmodule |
在本模塊中,夢翼師兄只是測試了顯示”A”,有興趣的話,可以把0~9、A~F,全部測試一下。
單個數碼管顯示的仿真分析
在復位期間,seg信號全部為“1”,數碼管熄滅。當復位信號拉高以后,seg信號變成了“10001000”,正好是“A”的段選,而sel(位選)一直就是0(選擇第一個數碼管),證明我們的設計是正確的。
六個數碼管顯示
六個數碼顯示的系統架構
應用六個數碼管去顯示任意數字,每個數碼管顯示的數字需要用4位二進制數去表示,那么六個數碼管一共需要24位二進制數
數碼管各模塊功能介紹
模塊名 |
功能描述 |
SEG7 |
輸出數碼管控制位選和段選信號 |
freq |
時鍾分頻模塊,輸出1KHz時鍾 |
top |
頂層模塊,負責模塊級聯 |
端口和內部連線描述
頂層端口
端口名 |
端口說明 |
clk |
系統時鍾輸入 |
Rst_n |
系統復位 |
Data[23:0] |
數據輸入 |
sel[2:0] |
片選信號輸出 |
seg[7:0] |
段選信號輸出 |
模塊內部連線
連線名 |
連線說明 |
Clk_1K |
數碼管的切換時鍾 |
代碼解釋
freq模塊代碼
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056 * The module function: 產生慢時鍾 *****************************************************/ 00 module freq ( 01 clk, //系統時鍾 02 rst_n, //系統復位 03 clk_1K//切換時鍾 04 ); 05 //系統輸入 06 input clk;//系統時鍾 07 input rst_n;//系統復位 08 //系統輸出 09 output reg clk_1K;//切換時鍾 10 //定義中間寄存器 11 reg [19:0] count;//定義一個計數的寄存器 12 13 always @ (posedge clk or negedge rst_n) 14 begin 15 if (!rst_n) 16 begin 17 clk_1K <= 1; 18 count <= 0; 19 end 20 else 21 begin 22 if (count < 24999)// 50000分頻,得出1K的時鍾 23 count <= count + 1; 24 else 25 begin 26 count <= 0; 27 clk_1K <= ~clk_1K; 28 end 29 end 30 end 31 32 endmodule |
SEG7模塊代碼
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056 * The module function: 產生段選信號和位選信號 *****************************************************/ 000 module SEG7 ( 001 clk, //模塊時鍾 002 rst_n,//系統復位 003 data, //輸入數據 004 seg,//數碼管段選 005 sel//數碼管位選 006 ); 007 //系統輸入 008 input clk;//模塊時鍾 009 input rst_n;//系統復位 010 input [23:0] data;//輸入數據 011 //系統輸出 012 output reg [7:0] seg;//數碼管段選 013 output reg [2:0] sel;//數碼管位選 014 //定義中間寄存器 015 reg [3:0] data_temp;//數碼管顯示的數值 016 reg [2:0] state;//狀態寄存器 017 018 always @ (posedge clk or negedge rst_n) 019 begin 020 if (!rst_n)//復位的時候選擇第一個數碼管 021 begin 022 sel <= 0; 023 data_temp <= 0; 024 state <= 0; 025 end 026 else 027 begin 028 case (state) 029 0 : begin//將最高位的數顯示在第一個數碼管上 030 sel <= 0; 031 data_temp <= data[23:20]; 032 state <= 1; 033 end 034 035 1 : begin//將第2位的數顯示在第二個數碼管上 036 sel <= 1; 037 data_temp <= data[19:16]; 038 state <= 2; 039 end 040 041 2 : begin//將第3位的數顯示在第三個數碼管上 042 sel <= 2; 043 data_temp <= data[15:12]; 044 state <= 3; 045 end 046 047 3 : begin//將第4位的數顯示在第四個數碼管上 048 sel <= 3; 049 data_temp <= data[11:8]; 050 state <= 4; 051 end 052 053 4 : begin//將最5位的數顯示在第五個數碼管上 054 sel <= 4; 055 data_temp <= data[7:4]; 056 state <= 5; 057 end 058 059 5 : begin//將最低位的數顯示在第六個數碼管上 060 sel <= 5; 061 data_temp <= data[3:0]; 062 state <= 0; 063 end 064 065 default : state <= 0; 066 endcase 067 end 068 end 069 070 always @ (*)//根據data_temp的中的值,用組合邏輯進行輸出段選信號 071 begin 072 if (!rst_n)//復位的時候數碼管熄滅 073 begin 074 seg = 8'b1111_1111; 075 end 076 else 077 begin 078 case(data_temp) 079 0 : seg = 8'b1100_0000;//顯示“0” 080 1 : seg = 8'b1111_1001;//顯示“1” 081 2 : seg = 8'b1010_0100;//顯示“2” 082 3 : seg = 8'b1011_0000;//顯示“3” 083 4 : seg = 8'b1001_1001;//顯示“4” 084 5 : seg = 8'b1001_0010;//顯示“5” 085 6 : seg = 8'b1000_0010;//顯示“6” 086 7 : seg = 8'b1111_1000;//顯示“7” 087 8 : seg = 8'b1000_0000;//顯示“8” 088 9 : seg = 8'b1001_0000;//顯示“9” 089 10 : seg = 8'b1000_1000;//顯示“A” 090 11 : seg = 8'b1000_0011;//顯示“B” 091 12 : seg = 8'b1100_0110;//顯示“C” 092 13 : seg = 8'b1010_0001;//顯示“D” 093 14 : seg = 8'b1000_0110;//顯示“E” 094 15 : seg = 8'b1000_1110;//顯示“F” 095 default : seg = 8'b1000_1110;//顯示“F” 096 endcase 097 end 098 end 099 100 endmodule |
top模塊代碼
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056 * The module function: 頂層模塊 *****************************************************/ 00 module top ( 01 clk, //系統時鍾 02 rst_n, //系統復位 03 data, //輸入數據 04 seg, //數碼管段選 05 sel//數碼管位選 06 ); 07 //系統輸入 08 input clk;//系統時鍾 09 input rst_n;//系統復位 10 input [23:0] data;//輸入數據 11 //系統輸出 12 output [7:0] seg;//數碼管段選 13 output [2:0] sel;//數碼管位選 14 //定義中間連線 15 wire clk_1K;//定義切換時鍾 16 //調用pll(鎖相環) 17 freq freq( 18 .clk( clk ),//外部時鍾 19 .rst_n(rst_n),//系統復位 20 .clk_1K( clk_1K ) //切換時鍾 21 ); 22 //實例化SEG7 23 SEG7 SEG7 ( 24 .clk(clk_1K), //切換時鍾 25 .rst_n(rst_n),//系統復位 26 .data(data), //輸入數據 27 .seg(seg),//數碼管段選 28 .sel(sel)//數碼管位選 29 ); 30 31 endmodule |
編寫完可綜合代碼之后查看RTL視圖如下:
由RTL視圖可知代碼綜合以后得到的電路和我們設計的系統框圖一致,說明頂層連接關系正確,接下來編寫測試代碼如下:
/**************************************************** * Engineer : 夢翼師兄 * QQ : 761664056 * The module function: 測試數碼管模塊 *****************************************************/ 00 `timescale 1ns/1ps //定義時間單位和精度 01 02 module top_tb; 03 //系統輸入 04 reg clk;//系統時鍾 05 reg rst_n;//系統復位 06 reg [23:0] data;//輸入數據 07 //系統輸出 08 wire [7:0] seg;//數碼管段選 09 wire [2:0] sel;//數碼管位選 10 11 initial begin 12 clk = 1; 13 rst_n = 0; 14 data = 24'h123456; 15 # 200.1 //復位200.1ns 16 rst_n = 1; 17 end 18 19 always # 10 clk = ~clk;//50M的時鍾 20 21 top top( 22 .clk(clk), //系統時鍾 23 .rst_n(rst_n),//系統復位 24 .data(data), //輸入數據 25 .seg(seg),//數碼管段選 26 .sel(sel)//數碼管位選 27 ); 28 29 endmodule |
仿真分析
在對應的數碼管上,給出對應數值的段選信號,經過比對,我們的設計都是正確的。