数码管动态显示

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

原文地址:https://www.cnblogs.com/xianyufpga/p/11006321.html