数码管动态显示
数码管显示分为静态显示和动态显示。静态显示没什么卵用,和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教程