數碼管顯示分為靜態顯示和動態顯示。靜態顯示沒什么卵用,和led燈沒差別。而動態顯示用處很大,基本上數碼管的使用都是動態顯示。其原理很簡單:視覺暫留效應。數碼管從右到左,一個接一個的亮起熄滅,讓其總的速度加快,人眼看上去就像是一直亮着一樣。掃描時間間隔建議為20ms以內,人眼才不會感到閃爍。一般來說一位數碼管掃描1ms就能得到不錯的效果了。
一、方法1
第一種數碼管動態顯示:采用分頻時鍾掃描方法
1 //====================================================================== 2 // --- 名稱 : seg_display 3 // --- 作者 : xianyu_FPGA 4 // --- 日期 : 2018-11-10 5 // --- 描述 : 數碼管動態顯示模塊,改自小梅哥FPGA 6 //====================================================================== 7 8 module seg_display 9 //---------------------<端口聲明>--------------------------------------- 10 ( 11 input clk , //時鍾,50Mhz 12 input rst_n , //復位,低電平有效 13 input en , //數碼管顯示使能 14 input [31:0] data , //輸入數據 15 output [ 7:0] seg_sel , //數碼管位選 16 output reg [ 7:0] seg_data //數碼管段選,即內容顯示 17 ); 18 //---------------------<信號定義>--------------------------------------- 19 reg [14:0] cnt ; 20 wire add_cnt ; 21 wire end_cnt ; 22 reg clk_1k ; 23 reg [ 7:0] seg_sel_r ; 24 reg [ 3:0] data_tmp ; 25 26 //---------------------------------------------------------------------- 27 //-- 1k分頻,掃描一個數目管時間為1ms 28 //---------------------------------------------------------------------- 29 30 //計數 31 always @(posedge clk or negedge rst_n)begin 32 if(!rst_n) 33 cnt <= 0; 34 else if(add_cnt)begin 35 if(end_cnt) 36 cnt <= 0; 37 else 38 cnt <= cnt + 1; 39 end 40 else 41 cnt <= cnt; 42 end 43 44 assign add_cnt = en; //en為1才允許計數 45 assign end_cnt = add_cnt && cnt==25000-1; 46 47 //分頻 48 always @(posedge clk or negedge rst_n)begin 49 if(!rst_n) 50 clk_1k <= 0; 51 else if(end_cnt) 52 clk_1k <= ~clk_1k; 53 else 54 clk_1k <= clk_1k; 55 end 56 57 //---------------------------------------------------------------------- 58 //-- 數碼管掃描,8位循環掃描,頻率為1k 59 //---------------------------------------------------------------------- 60 always @(posedge clk_1k or negedge rst_n)begin 61 if(!rst_n) 62 seg_sel_r <= 8'b0000_0001; 63 else if(seg_sel==8'b1000_0000) 64 seg_sel_r <= 8'b0000_0001; 65 else 66 seg_sel_r <= seg_sel << 1; 67 end 68 69 //---------------------------------------------------------------------- 70 //-- 位選,不同計數對應不同位選編碼,也對應分割的不同數據 71 //---------------------------------------------------------------------- 72 always @(*)begin 73 case(seg_sel_r) 74 8'b0000_0001: data_tmp = data[ 3: 0] ; // 位1 75 8'b0000_0010: data_tmp = data[ 7: 4] ; // 位2 76 8'b0000_0100: data_tmp = data[11: 8] ; // 位3 77 8'b0000_1000: data_tmp = data[15:12] ; // 位4 78 8'b0001_0000: data_tmp = data[19:16] ; // 位5 79 8'b0010_0000: data_tmp = data[23:20] ; // 位6 80 8'b0100_0000: data_tmp = data[27:24] ; // 位7 81 8'b1000_0000: data_tmp = data[31:28] ; // 位8 82 default: data_tmp = 4'b0000 ; 83 endcase 84 end 85 86 assign seg_sel = en ? seg_sel_r : 8'b0000_0000; // 位選使能 87 88 //---------------------------------------------------------------------- 89 //-- 段選,將不同分割數據進行段選編碼 90 //---------------------------------------------------------------------- 91 always @(*)begin 92 case(data_tmp) 93 4'h0: seg_data = 8'b1100_0000; 94 4'h1: seg_data = 8'b1111_1001; 95 4'h2: seg_data = 8'b1010_0100; 96 4'h3: seg_data = 8'b1011_0000; 97 4'h4: seg_data = 8'b1001_1001; 98 4'h5: seg_data = 8'b1001_0010; 99 4'h6: seg_data = 8'b1000_0010; 100 4'h7: seg_data = 8'b1111_1000; 101 4'h8: seg_data = 8'b1000_0000; 102 4'h9: seg_data = 8'b1001_0000; 103 4'ha: seg_data = 8'b1000_1000; 104 4'hb: seg_data = 8'b1000_0011; 105 4'hc: seg_data = 8'b1100_0110; 106 4'hd: seg_data = 8'b1010_0001; 107 4'he: seg_data = 8'b1000_0110; 108 4'hf: seg_data = 8'b1011_1111; 109 default:seg_data = 8'b1111_1111; 110 endcase 111 end 112 113 114 endmodule
二、方法2
第二種數碼管動態顯示方法:采用直接計數掃描方法
1 //====================================================================== 2 // --- 名稱 : seg_display 3 // --- 作者 : xianyu_FPGA 4 // --- 日期 : 2018-11-10 5 // --- 描述 : 數碼管動態顯示模塊 6 //====================================================================== 7 8 module seg_display 9 //---------------------<參數定義>--------------------------------------- 10 #( 11 parameter SEG_SEL = 8 , //數碼管位數:8 12 parameter SEG_DATA = 8 , //數碼管段數:8 13 parameter TIME_1MS = 50000 , //1ms時間 14 parameter TIME_1MS_W = 16 //1ms時間位寬 15 ) 16 //---------------------<端口聲明>--------------------------------------- 17 ( 18 input clk , //時鍾,50Mhz 19 input rst_n , //復位,低電平有效 20 input en , //數碼管顯示使能 21 input [SEG_SEL*4-1:0] data , //輸入數據 22 output [SEG_SEL -1:0] seg_sel , //數碼管位選 23 output reg [SEG_DATA -1:0] seg_data //數碼管段選,即內容顯示 24 ); 25 //---------------------<信號定義>--------------------------------------- 26 reg [TIME_1MS_W-1:0] cnt0 ; 27 wire add_cnt0 ; 28 wire end_cnt0 ; 29 reg [2:0] cnt1 ; 30 wire add_cnt1 ; 31 wire end_cnt1 ; 32 reg [3:0] data_tmp ; 33 reg [SEG_SEL-1:0] seg_sel_r ; 34 35 //---------------------------------------------------------------------- 36 //-- 1個數碼管亮1ms 37 //---------------------------------------------------------------------- 38 always @(posedge clk or negedge rst_n)begin 39 if(!rst_n) 40 cnt0 <= 0; 41 else if(add_cnt0)begin 42 if(end_cnt0) 43 cnt0 <= 0; 44 else 45 cnt0 <= cnt0 + 1; 46 end 47 else 48 cnt0 <= cnt0; 49 end 50 51 assign add_cnt0 = en; // 使能有效才計數 52 assign end_cnt0 = add_cnt0 && cnt0==TIME_1MS-1; 53 54 //---------------------------------------------------------------------- 55 //-- 對各位數碼管不斷掃描 56 //---------------------------------------------------------------------- 57 always @(posedge clk or negedge rst_n)begin 58 if(!rst_n) 59 cnt1 <= 0; 60 else if(add_cnt1)begin 61 if(end_cnt1) 62 cnt1 <= 0; 63 else 64 cnt1 <= cnt1 + 1; 65 end 66 else 67 cnt1 <= cnt1; 68 end 69 70 assign add_cnt1 = end_cnt0; 71 assign end_cnt1 = add_cnt1 && cnt1==SEG_SEL-1; 72 73 //---------------------------------------------------------------------- 74 //-- 位選,不同計數對應不同位選編碼,也對應分割的不同數據 75 //---------------------------------------------------------------------- 76 always @(*)begin 77 case(cnt1) 78 3'd0 : begin seg_sel_r = 8'b0000_0001; data_tmp = data[ 3: 0]; end // 位1 79 3'd1 : begin seg_sel_r = 8'b0000_0010; data_tmp = data[ 7: 4]; end // 位2 80 3'd2 : begin seg_sel_r = 8'b0000_0100; data_tmp = data[11: 8]; end // 位3 81 3'd3 : begin seg_sel_r = 8'b0000_1000; data_tmp = data[15:12]; end // 位4 82 3'd4 : begin seg_sel_r = 8'b0001_0000; data_tmp = data[19:16]; end // 位5 83 3'd5 : begin seg_sel_r = 8'b0010_0000; data_tmp = data[23:20]; end // 位6 84 3'd6 : begin seg_sel_r = 8'b0100_0000; data_tmp = data[27:24]; end // 位7 85 3'd7 : begin seg_sel_r = 8'b1000_0000; data_tmp = data[31:28]; end // 位8 86 default : begin seg_sel_r = 8'b0000_0000; data_tmp = 4'b0000; end 87 endcase 88 end 89 90 assign seg_sel = en ? seg_sel_r : 8'b0000_0000; // 位選使能 91 92 //---------------------------------------------------------------------- 93 //-- 段選,將不同分割數據進行段選編碼 94 //---------------------------------------------------------------------- 95 always @(*)begin 96 case(data_tmp) 97 4'h0: seg_data = 8'b1100_0000; 98 4'h1: seg_data = 8'b1111_1001; 99 4'h2: seg_data = 8'b1010_0100; 100 4'h3: seg_data = 8'b1011_0000; 101 4'h4: seg_data = 8'b1001_1001; 102 4'h5: seg_data = 8'b1001_0010; 103 4'h6: seg_data = 8'b1000_0010; 104 4'h7: seg_data = 8'b1111_1000; 105 4'h8: seg_data = 8'b1000_0000; 106 4'h9: seg_data = 8'b1001_0000; 107 4'ha: seg_data = 8'b1000_1000; 108 4'hb: seg_data = 8'b1000_0011; 109 4'hc: seg_data = 8'b1100_0110; 110 4'hd: seg_data = 8'b1010_0001; 111 4'he: seg_data = 8'b1000_0110; 112 4'hf: seg_data = 8'b1000_1110; 113 default:seg_data = 8'b1111_1111; 114 endcase 115 end 116 117 118 endmodule 119 120 /* 121 //---------------------------------------------------------------------- 122 //-- 位選這樣寫也行,代碼變得很秀 123 //---------------------------------------------------------------------- 124 always @(posedge clk or negedge rst_n)begin 125 if(!rst_n) 126 seg_sel <= {SEG_SEL{1'b0}; 127 else if(en) 128 seg_sel <= ~(1'b1<<cnt1); 129 else 130 seg_sel <= {SEG_SEL{1'b0}; 131 end 132 133 always @(*)begin 134 data_tmp = data[(cnt1+1)*4-1 -:4]; 135 end 136 137 */
以上兩種方法實測均有效,數碼管足夠亮且不閃爍不重影,以后使用時可以直接拿來當數碼管的顯示模塊。使用時要注意你的數碼管器件的位數是幾位?段數是7段還是8段?低電平有效還是高電平有效?接口是直接連接還是通過HC595芯片連接?這幾個問題搞清楚了,數碼管顯示也就不是問題了。
三、HC595
附上小梅哥FPGA的HC595模塊的代碼,可以直接搭配數碼管動態顯示模塊使用,在頂層例化連接一下就行了。
1 //====================================================================== 2 // --- 名稱 : HC595 3 // --- 作者 : xianyu_FPGA 4 // --- 日期 : 2018-11-10 5 // --- 描述 : HC595驅動 6 //====================================================================== 7 8 module HC595 9 //---------------------<參數定義>--------------------------------------- 10 #( 11 parameter SEG_SEL = 8 , //8位 12 parameter SEG_DATA = 8 //8段 13 ) 14 //---------------------<端口聲明>--------------------------------------- 15 ( 16 input clk , //時鍾,50Mhz 17 input rst_n , //復位,低電平有效 18 input en , //數碼管使能信號 19 input [SEG_SEL -1:0] seg_sel , //數碼管位選 20 input [SEG_DATA-1:0] seg_data , //數碼管段選 21 output reg ST_CP , //存儲寄存器時鍾輸出 22 output reg SH_CP , //移位寄存器時鍾輸出 23 output reg DS //串行數據輸入 24 ); 25 //---------------------<信號定義>--------------------------------------- 26 reg [ 1:0] cnt0 ; 27 wire add_cnt0 ; 28 wire end_cnt0 ; 29 reg [ 5:0] cnt1 ; 30 wire add_cnt1 ; 31 wire end_cnt1 ; 32 wire sclk ; //分頻時鍾信號 33 wire [ 5:0] sclk_cnt ; //序列機計數器 34 35 //---------------------------------------------------------------------- 36 //-- 4分頻計數器 37 //---------------------------------------------------------------------- 38 always @(posedge clk or negedge rst_n)begin 39 if(!rst_n) 40 cnt0 <= 0; 41 else if(add_cnt0)begin 42 if(end_cnt0) 43 cnt0 <= 0; 44 else 45 cnt0 <= cnt0 + 1; 46 end 47 else 48 cnt0 <= cnt0; 49 end 50 51 assign add_cnt0 = en; 52 assign end_cnt0 = add_cnt0 && cnt0==4-1; 53 54 assign sclk = end_cnt0; 55 56 //---------------------------------------------------------------------- 57 //-- 序列機計數器 58 //---------------------------------------------------------------------- 59 always @(posedge clk or negedge rst_n)begin 60 if(!rst_n) 61 cnt1 <= 0; 62 else if(add_cnt1)begin 63 if(end_cnt1) 64 cnt1 <= 0; 65 else 66 cnt1 <= cnt1 + 1; 67 end 68 else 69 cnt1 <= cnt1; 70 end 71 72 assign add_cnt1 = sclk; 73 assign end_cnt1 = add_cnt1 && cnt1==35-1; 74 75 assign sclk_cnt = cnt1; 76 77 //------------------------------------------------------------------------------- 78 //-- SHCP:移位寄存器的時鍾輸入,上升沿時移位寄存器中的數據依次移動一位 79 //-- STCP:存儲寄存器的時鍾輸入,上升沿時移位寄存器中的數據進入存儲寄存器 80 //-- 通常STCP置為低電平,移位結束后再在ST_CP端產生一個正脈沖更新顯示數據 81 //------------------------------------------------------------------------------- 82 always @(posedge clk or negedge rst_n)begin 83 if(!rst_n)begin 84 ST_CP <= 0; 85 SH_CP <= 0; 86 DS <= 0; 87 end 88 else begin 89 case(sclk_cnt) 90 0: begin SH_CP <= 0; end 91 1: begin ST_CP <= 0; SH_CP <= 1; end 92 2: begin SH_CP <= 0; DS <= seg_data[7]; end 93 3: begin SH_CP <= 1; end 94 4: begin SH_CP <= 0; DS <= seg_data[6]; end 95 5: begin SH_CP <= 1; end 96 6: begin SH_CP <= 0; DS <= seg_data[5]; end 97 7: begin SH_CP <= 1; end 98 8: begin SH_CP <= 0; DS <= seg_data[4]; end 99 9: begin SH_CP <= 1; end 100 10: begin SH_CP <= 0; DS <= seg_data[3]; end 101 11: begin SH_CP <= 1; end 102 12: begin SH_CP <= 0; DS <= seg_data[2]; end 103 13: begin SH_CP <= 1; end 104 14: begin SH_CP <= 0; DS <= seg_data[1]; end 105 15: begin SH_CP <= 1; end 106 16: begin SH_CP <= 0; DS <= seg_data[0]; end 107 17: begin SH_CP <= 1; end 108 18: begin SH_CP <= 0; DS <= seg_sel[7]; end 109 19: begin SH_CP <= 1; end 110 20: begin SH_CP <= 0; DS <= seg_sel[6]; end 111 21: begin SH_CP <= 1; end 112 22: begin SH_CP <= 0; DS <= seg_sel[5]; end 113 23: begin SH_CP <= 1; end 114 24: begin SH_CP <= 0; DS <= seg_sel[4]; end 115 25: begin SH_CP <= 1; end 116 26: begin SH_CP <= 0; DS <= seg_sel[3]; end 117 27: begin SH_CP <= 1; end 118 28: begin SH_CP <= 0; DS <= seg_sel[2]; end 119 29: begin SH_CP <= 1; end 120 30: begin SH_CP <= 0; DS <= seg_sel[1]; end 121 31: begin SH_CP <= 1; end 122 32: begin SH_CP <= 0; DS <= seg_sel[0]; end 123 33: begin SH_CP <= 1; end 124 34: begin ST_CP <= 1; end 125 default: 126 begin ST_CP <= 0; SH_CP <= 0; DS <= 0 ; end 127 endcase 128 end 129 end 130 131 endmodule
ok,數碼管的學習就到這里,以后遇到問題再補充進來。
參考資料:
[1]小梅哥FPGA教程
[2]鋯石科技FPGA教程