FPGA学习第五天

《数码管动态显示》

  • 理论学习

利用人眼的视觉暂留(说白了就是趁我们人眼没反应过来,我就灭了,就亮下一个了,以此往复循环,我们人眼就以为他们是一直亮的,可恶好卑鄙)和余晖效应(数码管会短暂的停留一下下)来完成动态显示

  • 实战演练

目的:使用六位八段数码管实现数码管的动态显示十进制0~999999,达到最大值之后回到0(第一个0.1s(100ms)显示0,第二个0.1s显示1....)

  1. 顶层模块:串行数据ds;使能信号oe;移位寄存器时钟shcp;存储寄存器时钟stcp;

  • 代码部分:

module top_seg_595
(
input   wire    sys_clk     ,
input   wire    sys_rst_n   ,

output  wire    ds          ,
output  wire    oe          ,
output  wire    shcp        ,
output  wire    stcp         
);
wire    [19:0]  data        ;
wire    [5:0]   point       ;
wire            sign        ;
wire            seg_en      ;

data_gen    
#(
      .  CNT_MAX    (23'd4999_999),
      .  data_MAX   (20'd999_999)
)
data_gen_inst
(
   .sys_clk         (sys_clk),
   .sys_rst_n       (sys_rst_n),
   .data            (data),
   .point           (point),
   .sign            (sign),
   .seg_en          (seg_en)
);

seg_595_dynamic seg_595_dynamic_inst
(
    .sys_clk        (sys_clk)         ,
    .sys_rst_n      (sys_rst_n)       ,
    .data           (data)            ,
    .point          (point)           ,
    .sign           (sign)            ,
    .seg_en         (seg_en)          ,
    .ds             (ds)              ,
    .oe             (oe)              ,
    .shcp           (shcp)            ,
    .stcp           (stcp)  
);

  
endmodule

  • tb部分:

 module tb_top_seg_595();

reg         sys_clk         ;
reg         sys_rst_n       ;
wire        ds              ;
wire        oe              ;
wire        shcp            ;
wire        stcp            ;

initial
    begin   
        sys_clk = 1'b1;
        sys_rst_n   <=  1'b0;
        #30
        sys_rst_n   <=  1'b1;
    end

always  #10 sys_clk  <= ~sys_clk;

defparam    top_seg_595_inst.data_gen_inst.CNT_MAX = 23'd49;
defparam    top_seg_595_inst.seg_595_dynamic_inst.seg_dynamic_inst.CNT_MAX = 16'd19;

top_seg_595     top_seg_595_inst
(
       .sys_clk        (sys_clk),
       .sys_rst_n      (sys_rst_n),
       .ds             (ds),
       .oe             (oe),
       .shcp           (shcp),
       .stcp           (stcp)
);



endmodule
  • 1.1数据产生模块:

该开发板使用的时六位八段数码管都有小数点位,位宽为6位宽,每一位表示每个数码位的小数点位,但我们没有用到,所以让他始终保持0,负号位也没有用到因此也为0,使能信号得保持高电平。(这里的cnt_100ms相当于是分频,也就是要想0.1s闪一次,就要过500000个系统时钟周期,计数器才会闪一次,因此才会有cnt_100ms的存在)

  • 波形图:

  • 代码编写:

module    data
#(
    parameter    CNT_MAX = 23'd4999_999,
    parameter    data_MAX = 20'd999_999
)
(    input    wire    sys_clk    ,
     input    wire    sys_rst_n  ,
     output   reg    [19:0]    data,
     output   wire    [5:0]     point,
     output   wire    sign,
     output   reg    seg_en
);
reg    [22:0]    cnt_100ms;
reg              cnt_flag;

always@(posedge sys_clk or negedge sys_rst_n)
   if (sys_rst_n == 1'b0)
        cnt_100ms    <= 23'd0;
    else    if(cnt_100ms == CNT_MAX)
        cnt_100ms    <= 23'd0;
    else
        cnt_100ms    <= cnt_100ms+1'b1;

always@(posedge sys_clk or negedge sys_rst_n)
   if (sys_rst_n == 1'b0)    
        cnt_flag    <=    1'b0;
else    if(cnt_100ms == CNT_MAX-1'b1)
        cnt_flag    <=    1'b1;
else
        cnt_flag    <=    1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
   if (sys_rst_n == 1'b0)
        data    <=    20'd0;
    else    if(data == data_MAX)
        data    <=    20'd0;
    else    if(cnt_flag == 1'b1)
        data    <=    data    +    1'b1;
    else
        data    <= data;

assign    point    =     6'b000_000;
assign    sign     =     1'b0;    

always@(posedge sys_clk or negedge sys_rst_n)
   if (sys_rst_n == 1'b0)
        seg_en    <=    1'b0;
    else
        seg_en    <=    1'b1;
endmodule
  • tb文件:

module    tb_data_gen();

reg    sys_clk;
reg    sys_rst_n;

wire       data;
wire       point;
wire       sign;
wire       seg_en;
initial
    begin
        sys_clk = 1'b1;
        sys_rst_n    <=    1'b0;
        #30
        sys_rst_n    <=    1'b1;
    end
always #10    sys_clk    =    ~sys_clk;

data_gen
#(
    .CNT_MAX(9),
    .DATA_MAX(9)
)
data_gen_inst
(
    .sys_clk(sys_clk),
    .sys_rst_n(sys_rst_n),
    .data(data),
    .point(point),
    .sign(sign),
    .seg_en(seg_en)
);
endmodule

  • 1.2数码管动态显示的模块:

数据输入端口(data),小数点输入端口(point用于以后温度测量),增加一个负号位(sign用于以后电压测量),使能端口(seg_en更好的控制,使能信号为高电平时可以正常显示,当使能信号为低电平时,不显示)

  • 代码部分:

module seg_595_dynamic(

input   wire            sys_clk       ,
input   wire            sys_rst_n     ,
input   wire    [19:0]  data          ,
input   wire    [5:0]   point         ,
input   wire            sign          ,
input   wire            seg_en        ,

output  wire            ds             ,
output  wire            oe             ,
output  wire            shcp           ,
output  wire            stcp           
    );
wire    [5:0]    seg;
wire    [7:0]    sel;
seg_dynamic  seg_dynamic_inst
(
    .  sys_clk     (sys_clk)        ,
    .  sys_rst_n   (sys_rst_n)      ,
    .  data        (data)           ,
    .  point       (point)          ,
    .  sign        (sign)           ,
    .  seg_en      (seg_en)         ,
    .    sel       (sel)            ,
    .    seg       (seg)
);

hc595_ctrl  hc595_ctrl_1
(
    . sys_clk          (sys_clk),
    . sys_rst_n        (sys_rst_n),
    . sel              (sel),
    . seg              (seg),
    . ds               (ds),
    . shcp             (shcp),
    . stcp             (stcp),
    .  oe              (oe)
    );   
endmodule

将动态显示模块进行进一步的划分:1.动态显示驱动(生成段选信号和位选信号);2.595控制模块(将位选和段选信号转化为输出端口的ds;oe;shcp;stcp传入到我们的芯片

  • 1.2.1动态显示驱动模块:

因为左端产生的data数据是20位十进制数据,这个传入的数据使用二进制表示,并不能直接转化成6位的位选信号和8位的段选信号。要将此数据转化成6位的位选信号和8位的段选信号,所以要将此数据以BCD码表示的十进制数。因此要了解一下BCD码是什么。

  • 1.2.1.1BCD编码模块

BCD码又称二-十进制码,使用4位二进制数来表示1位十进制数中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。

那为什么会选择BCD码呢,这里我们来举例:比如我们现在想用数码管表示一个十进制数234,234用2进制数来表示是11101010,234用8421码来表示则为0010_0011_0100,这样就需要在第一个显示周期内点亮最低位数码位4,在第二个显示周期点亮下一位数码位3,在第三个显示周期内点亮第三位数码位2,这样就实现了234的显示。这种显示方式要求我们从传入的数据中提取出个位、十位、百位。。而传入的二进制表示的十进制数,在其中并不能提取出个位、十位、百位。而BCD表示的十进制数,则可以提取位的信息(0010为百位,0011为十位,0100为个位)。接下来的问题就是如何把二进制码转化成BCD码

我们还是以234为例,参与的十进制数有多少位就需要多少个BCD码,我们将输入的2进制数1110_1010(234),不断地左移一位,将每一次移位后的三位BCD数据与0100(4)比较,如果大于4,则加0011(3),以此类推直至将所有输入的二进制码左移完成,得到转化后的BCD码,具体过程如下图。(此数码管要显示999_999六位,也就需要6*4=24位BCD码)

因为输入的是999_999需要20位宽,因此需要一个移位计数器,计数到21,即0的时候是第一个状态,21的时候是取输出结果(cnt_shift),才能够实现二进制到BCD码的转换(位宽为[4:0]),在移位的过程中产生的中间数据,也需要一个中间变量储存,而且经过20次的移位操作之后呢,要从中间变量中提取我们的BCD编码,因此需要从中间变量寄存我们的中间数据,他的位宽应该是20二进制码+24BCD码=44位,且判断运算在前移位运算在后,所以需要生成一个标志信号来区分这两个操作,这两个操作有一个共同的点:在一个时钟周期内完成的,而且由图标可知判断运算在移位运算之后完成的,所以说声明的移位标志信号也可以作为移位计数计数的一个条件。计数范围(0-21),这时候可能会提出疑问:只需要移位20次就可以了,为什么要加两个计数状态呢,这是因为计数器为0时,对应的是第一个状态(也就是上图中0000_0000_0000_1110_1010)要进行补0操作,计数器为1-20时才是进行判断的时候,21的时候是最终输出结果(999_999),确定之后计数范围之后,要明确计数条件是什么,之前说过可以使用移位标志信号(shift_flag)作为计数器计数条件,移位信号为低电平时进行计数运算,移位信号高电平时进行移位操作,同时高电平也可以作为条件控制移位计数器计数,即高电平+1,低电平保持不变(时序逻辑,所以要滞后一个时钟),移位数据(data_shift)根据cnt_shift进行变化。具体波形如下图

对应输出波形就按照data_shift向后排即可(例如ten为data_shift的第27-24位)

  • 代码部分:

module bcd_8421(
    input   wire    sys_clk,
    input   wire    sys_rst_n,
    input   wire    [19:0] data,
    output  reg    [3:0] unit,
    output  reg    [3:0] ten,
    output  reg    [3:0] hun,
    output  reg    [3:0] tho,
    output  reg    [3:0] t_tho,
    output  reg    [3:0] h_hun
);

reg [4:0]   cnt_shift;
reg [43:0]  data_shift;
reg         shift_flag;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_shift   <= 5'd0;
    else    if((cnt_shift == 5'd21)&&(shift_flag == 1'b1))
         cnt_shift   <= 5'd0;
    else    if(cnt_shift == 1'b1)
         cnt_shift <= cnt_shift + 1'b1;
    else
         cnt_shift <= cnt_shift;
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_shift  <=  44'b0;
    else    if(cnt_shift == 5'd0)
        data_shift  <= {24'b0,data};
    else    if((cnt_shift <=20)&&(shift_flag == 1'b0))
        begin
    data_shift[23:20]    <=  (data_shift[23:20] > 4)    ?   (data_shift[23:20]+2'd3)  :(data_shift[23:20]);
    data_shift[27:24]    <=  (data_shift[27:24] > 4)    ?   (data_shift[27:24]+2'd3)  :(data_shift[27:24]);
    data_shift[31:28]    <=  (data_shift[31:28] > 4 )    ?   (data_shift[31:28]+2'd3)  :(data_shift[31:28]);
    data_shift[35:32]    <=  (data_shift[35:32] > 4)    ?   (data_shift[35:32]+2'd3)  :(data_shift[35:32]);
    data_shift[39:36]    <=  (data_shift[39:36] > 4)    ?   (data_shift[39:36]+2'd3)  :(data_shift[39:36]);
    data_shift[43:40]    <=  (data_shift[43:40] > 4)    ?   (data_shift[43:40]+2'd3)  :(data_shift[43:40]);
        end
    else    if((cnt_shift <=20)&&(shift_flag == 1'b1))
      data_shift <= data_shift << 1;
    else   
        data_shift <= data_shift;
 always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        shift_flag  <=  1'b0;
    else
        shift_flag  <=  ~shift_flag;
        
 always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            unit            <= 4'b0;
            ten             <= 4'b0;
            hun             <= 4'b0;
            tho             <= 4'b0;
            t_tho           <= 4'b0;
            h_hun           <= 4'b0; 
        end
   else   if(cnt_shift  == 5'd21)
        begin
            unit            <=  data_shift[23:20];
            ten             <=  data_shift[27:24];
            hun             <=  data_shift[31:28];
            tho             <=  data_shift[35:32];
            t_tho           <=  data_shift[39:36];
            h_hun           <=  data_shift[43:40];
            h_hun           <=  data_shift[43:40];
        end
endmodule

  • tb代码:

module tb_bcd_8421();
reg     sys_clk;
reg     sys_rst_n;
reg [19:0]  data;

wire    [3:0]   unit;
wire    [3:0]   ten;
wire    [3:0]   hun;
wire    [3:0]   tho;
wire    [3:0]   t_tho;
wire    [3:0]   h_hun;

initial
    begin
        sys_clk = 1'b1;
        sys_rst_n   <=  1'b0;
        data    <=20'd0;
        #30
        sys_rst_n   <= 1'b1;
        data    <=  20'd123_456;
        #3000
        data    <=  20'd654_321;
        #3000
        data    <=  20'd987_654;
        #3000
        data    <=  20'd999_999;
     end
     
always  #10 sys_clk =   ~sys_clk;

bcd_8421    bcd_8421_inst
(
   . sys_clk   (sys_clk)    , 
   . sys_rst_n (sys_rst_n)    ,
   . data      (data)    ,    
   . unit      (unit)    ,    
   . ten       (ten)    ,     
   . hun       (hun)    ,     
   . tho       (tho)    ,     
   . t_tho     (t_tho)    ,   
   . h_hun     (h_hun)
   );
endmodule

  • 动态显示驱动模块的波形图:

时钟信号、复位信号、待显示数据(data给一个固定的值20'd98_76)、小数点位(point)这里000_010代表第二位小数点位、符号位(sign)这里高电平代表负数、使能信号(eg_en)、六路显示信号unit、 ten 、 hun 、tho 、t_tho、h_hun、寄存位data_reg(只包含符号位和数据位不包括小数点位显示的是X-9876),这里也就代表要显示-987.6 。因为每个数码管显示时间为1ms,1ms进行计数,就需要一个计数器,因此需要一个计数器变量(cnt_1ms)、1ms=1*10^6ns,每个时钟周期是20ns,因此要完成1ms的计数,要计数5*10^4个周期,从0开始计数,因此需要计数到49999需要16 位,计数到最大值归0,还需要一个1ms标志信号(flag_1ms),相当于分频器,在这里还需要添加一个计数器变量,因为数码管采用的是动态显示,所有的数码管都显示一次之后,才是一个完整的扫描周期,因此需要一个计数器变量(cnt_sel )从0-5六个管,因此需要三位,当flag_1ms为高电平时,相当于一个数码管显示了一次,这时候cnt_sel+1。

  • 接下来要输出位选信号(sel)的波形,这里以6'b000_000,flag_1ms为高电平,cnt_sel为2的时候代表选中第二位,应该在第一位的基础上左移,变为6'b000_010以此类推,但在cnt_sel归零时,应该对输出信号重新赋值,否则在左移的作用下会变成6'b000_000。

  • 继续是对端选信号(data_disp)进行赋值,先给一个初值0,当位选信号为000_0001时,代表第一位被选中,赋值为6,以此类推直到9876显示完,到第五个数码位时,应该添加为符号-,这里选择用10来表示,最高位不显示时用11来表示,一个周期完成后,回到最低为,显示为6.

  • 继续是小数点位,高电平时不显示,低电平时显示,因此应该在第二位显示的时候小数点位显示,其余都不显示小数点位。

  • 最后是端选信号(seg),首先给一个初值,因为是共阳极数码管,所以初值为F代表不点亮,其他时刻要根据待显示数据进行编码,因为这里直接输出的数据 是不能用于显示的,要进行编码,因为是时序逻辑,因此要滞后一个时钟周期,第一个要显示的是数字6,对应的是82,7对应的是f8,但由于7的时候小数点位要点亮,dp为0,因此16位显示的是0111_1000=78而不是f8,8=80,9=90,其他时候只显示g即可,相当于1000_0000=bf,不点亮就是全1,为ff,这样就完成一个周期的扫描,下一个周期继续为82→78→80....因为端选信号和位选信号相差一个时钟周期,因此要进行打拍,把sel变为sel_reg,使用时序逻辑进行打拍生成我们的位选信号,这样位选和段选就同步了。

  • 代码部分

module seg_dynamic(
    input   wire              sys_clk,
    input   wire              sys_rst_n,
    input   wire    [19:0]    data,
    input   wire    [4:0]     point,    
    input   wire              sign,
    input   wire              seg_en,
    
    output  reg    [5:0]     sel,
    output  reg    [7:0]     seg  
    
    );
    
    
parameter   cnt_max = 16'd49_999;
wire [3:0]   unit;    
wire [3:0]   ten;  
wire [3:0]   hun;  
wire [3:0]   tho;  
wire [3:0]   t_tho;
wire [3:0]   h_hun;
reg  [23:0]  data_reg;
reg  [15:0]  cnt_1ms;
reg         flag_1ms;
reg  [2:0]   cnt_sel;
reg  [5:0]   sel_reg;    
reg  [3:0]   data_disp;
reg         dot_disp;

 always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_reg    <=  24'b0;
    else    if((h_hun)||    (point[5]))//第一种情况:十万位不为0,或者最高位的符号位为高电平
        data_reg    <=  {h_hun,t_tho,tho,hun,ten,unit};//数据寄存不进行小数点的寄存,
        //我们只使用数码位的第五位,最高位没有使用,第五位同时有两种情况:1.使用了符号,2.没有使用符号,
    else    if((t_tho)||  (point[4]) && (sign == 1'b1))
        data_reg    <=  {4'd10,t_tho,tho,hun,ten,unit};//代表情况1 1.负号用10来表示
    else    if((t_tho)||  (point[4]) && (sign == 1'b0))
        data_reg    <=  {4'd11,t_tho,tho,hun,ten,unit};//代表情况2 2.不表示用11来表示
    else    if((tho)  ||  (point[3]) && (sign == 1'b1))    
        data_reg    <=  {4'd11,4'd10,tho,hun,ten,unit};
    else    if((tho)  ||  (point[3]) && (sign == 1'b0))
        data_reg    <=  {4'd11,4'd11,tho,hun,ten,unit};
    else    if((hun)||  (point[2]) && (sign == 1'b1))
        data_reg    <=  {4'd11,4'd11,4'd11,hun,ten,unit};
    else    if((hun)||  (point[2]) && (sign == 1'b0))
        data_reg    <=  {4'd11,4'd11,4'd10,hun,ten,unit};
    else    if((ten)||  (point[1]) && (sign == 1'b1))
        data_reg    <=  {4'd11,4'd11,4'd11,4'd11,ten,unit};
    else    if((ten)||  (point[1]) && (sign == 1'b0))
        data_reg    <=  {4'd11,4'd11,4'd11,4'd10,ten,unit};    
    else    if((unit)||  (point[0]) && (sign == 1'b1))
        data_reg    <=  {4'd11,4'd11,4'd11,4'd11,4'd11,unit};
    else   
        data_reg    <=  {4'd11,4'd11,4'd11,4'd11,4'd10,unit};    
 
 always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1ms <= 16'b0;
    else    if(cnt_1ms == cnt_max)
        cnt_1ms <= 16'b0;
    else
        cnt_1ms <= cnt_1ms + 1'b1;
 
always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        cnt_sel<=3'd0;
    else if((cnt_sel==3'd5) &&(flag_1ms ==1'b1))
        cnt_sel<=3'd0;
    else    if(flag_1ms==1'b1)
        cnt_sel<=cnt_sel +1'b1;
    else
        cnt_sel <=cnt_sel;
 
always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        sel_reg <=  6'b000_000;
    else    if((cnt_sel ==  3'd0)&&(flag_1ms == 1'b1))
        sel_reg <=  6'b000_001;
    else    if(flag_1ms == 1'b1)    
        sel_reg <=  sel_reg << 1 ;
    else
        sel_reg <=  sel_reg;
always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)
        data_disp   <=  4'd0;
    else    if ((seg_en == 1'b1) && (flag_1ms == 1'b1))
        case(cnt_sel)
            3'd0:   data_disp   <=  data_reg[3:0];
            3'd1:   data_disp   <=  data_reg[7:4];
            3'd2:   data_disp   <=  data_reg[11:8];
            3'd3:   data_disp   <=  data_reg[15:12];
            3'd4:   data_disp   <=  data_reg[19:16];
            3'd5:   data_disp   <=  data_reg[23:20];
            default :   data_disp   <=  4'b0;
        endcase
    else
            data_disp   <=  data_disp ;
always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)    
        dot_disp    <=  1'b1;
    else    if(flag_1ms ==  1'b1)
        dot_disp    <=  ~point[cnt_sel];
    else
        dot_disp    <=  dot_disp;


always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0)      
        seg <= 8'b111_111;
    else    
        case(data_disp)
            4'd0:   seg <=  {dot_disp,7'b100_0000};    
            4'd1:   seg <=  {dot_disp,7'b111_1001};
            4'd2:   seg <=  {dot_disp,7'b010_0100};
            4'd3:   seg <=  {dot_disp,7'b011_0000};
            4'd4:   seg <=  {dot_disp,7'b001_1001};
            4'd5:   seg <=  {dot_disp,7'b001_0010};
            4'd6:   seg <=  {dot_disp,7'b000_0010};
            4'd7:   seg <=  {dot_disp,7'b111_1000};
            4'd8:   seg <=  {dot_disp,7'b000_1000};
            4'd9:   seg <=  {dot_disp,7'b000_0000};
            4'd10:  seg <=  8'b1011_1111;
            4'd11:  seg <=  8'b111_111;
            default:    seg<= 8'b1100_0000;
         endcase
         
always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n==1'b0) 
            sel <= 6'b000_000;
    else
            sel <=  sel_reg;             
            
            
bcd_8421    bcd_8421_inst
(
     .sys_clk         (sys_clk),
     .sys_rst_n       (sys_rst_n),
     .data            (data),
     .unit            (unit),
     .ten             (ten),
     .hun             (hun),
     .tho             (tho),
     .t_tho           (t_tho),
     .h_hun           (h_hun)
);



endmodule
  • tb代码:

module tb_seg_dynamic();
reg               sys_clk       ;
reg               sys_rst_n     ;
reg     [19:0]    data          ;
reg     [4:0]     point         ;
reg               sign          ;
reg               seg_en        ;

wire    [5:0]     sel           ;
wire    [7:0]     seg           ;

initial 
        begin   
              sys_clk   =   1'b1;
              sys_rst_n <=  1'b0;
              data      <=  20'd0;
              point     <=  6'b0;
              sign      <=  1'b0;
              seg_en    <=  1'b0;
              #30
              sys_rst_n <=  1'b1;
              data      <=  20'd9876;
              point     <=  6'b000_010;
              sign      <=  1'b1;
              seg_en    <=  1'b1;  
        end
always  #10 sys_clk = ~sys_clk;
defparam    seg_dynamic_inst.cnt_max = 20'd5;
seg_dynamic seg_dynamic_inst
(
    .sys_clk  (sys_clk)             ,
    .sys_rst_n(sys_rst_n)           ,
    .data     (data)                ,
    .point    (point)               ,
    .sign     (sign)                ,
    .seg_en   (seg_en)              ,
    .sel      (sel)                 ,
    .seg      (seg)                 
    );
    
endmodule

1.2.2595控制模块:

这里采用的hc595控制模块选自于上一节中《静态数码管显示》 其主要作用是将段选信号和位选信号传输到我们的595芯片当中。这里不再过多阐述。

  • 波形图

  • 代码部分:

module hc595_ctrl(
    input   wire            sys_clk          ,
    input   wire            sys_rst_n        ,
    input   wire    [5:0]   sel              ,
    input   wire    [7:0]   seg              ,
    
    output  reg             ds                  ,
    output  reg             shcp                ,
    output  reg             stcp                ,
    output  wire             oe                 
    );
wire    [13:0]  data        ;
reg     [1:0]   cnt         ;
reg     [3:0]   cnt_bit     ;

assign  data    =   {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],sel};

always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <=  2'b0;
    else    if(cnt == 2'd3)
        cnt <=  2'd0;
    else
        cnt <=  cnt + 1'b1;
        
always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  4'd0;
    else    if((cnt_bit ==  4'd13)  &&  (cnt == 2'd3))
        cnt_bit <=  4'd0;
    else    if(cnt  ==  2'd3)
        cnt_bit <=  cnt_bit +   1'b1;
    else
        cnt_bit <=  cnt_bit;
        
always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n == 1'b0)
        ds  <=  1'b0;
    else    if(cnt == 2'd0)
        ds  <=  data[cnt_bit];
    else
        ds  <=  ds;

always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n == 1'b0)
        shcp    <=  1'b0;
    else    if(cnt_bit ==   2'd2)
        shcp    <=  1'b1;
    else    if(cnt_bit == 2'd0)
        shcp    <=  1'b0;
    else
        shcp    <=  shcp;

always@ (posedge sys_clk or negedge  sys_rst_n)
    if(sys_rst_n == 1'b0)
        stcp    <=  1'b0;
    else    if((cnt_bit ==4'b0)&&(cnt == 2'd0))
        stcp    <=  1'b1;
    else    if((cnt_bit == 4'b0)&&(cnt==2'd2))
        stcp    <=  1'b0;
    else
        stcp    <=  stcp;
     
assign  oe = 1'b0;

endmodule
  • 最后仿真结果:

符合理想波形图,实验成功。

这样就完成了动态数码管显示。

  • ps:由于手中没有实体板子,所以最后没有办法呈现现实的实现效果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值