FPGA巡游之旅——在数码管上进行数字时钟显示

1.更改数据生成模块生成一个时钟(时分秒)显示在数码管上

需要更改动态数码管的data_gen模块,按照时钟数据格式和逻辑生成时分秒数据,并不断刷新数据,第四个和第二个小数点显示,即point[2]和point[4]取低有效;

此外还要将seg_deynamic模块中的 else if((h_hun) || (point[5]))改为else if((h_hun) || (point[5])||(point[4])),使最开始最高位没有数字的部分显示为0,而不是不显示,这样更美观一点。

data_gen.v
`timescale  1ns/1ns
module  data_gen
#(
    parameter   CNT_MAX_1S = 26'd49_999_999 //1s计数值
)
(
    input                   sys_clk     ,   //系统时钟,频率50MHz
    input                   sys_rst_n   ,   //复位信号,低电平有效

    output   reg    [19:0]  data        ,   //数码管要显示的值 时钟最大为23:59:59 最小为00:00:00
    output          [5:0]   point       ,   //小数点显示,高电平有效
    output   reg            seg_en      ,   //数码管使能信号,高电平有效
    output                  sign            //符号位,高电平显示负号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//reg   define
reg     [25:0]  cnt_1s         ;   //1s计数器
reg             cnt_flag_1s    ;   //1s标志信号

reg     [5:0 ]  hour           ;
reg     [6:0 ]  min            ;
reg     [6:0 ]  sec            ;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//显示小数点以及负数
assign  point   =   6'b010_100;
assign  sign    =   1'b0;

//cnt_1s:用50MHz时钟从0到49_999_999计数即为1s
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1s   <=  26'd0;
    else    if(cnt_1s == CNT_MAX_1S)
        cnt_1s   <=  26'd0;
    else 
        cnt_1s   <= cnt_1s + 1'b1;


//cnt_flag_1s:每1s产生一个标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_flag_1s    <=  1'b0;
    else    if(cnt_1s == CNT_MAX_1S - 1'b1)
        cnt_flag_1s    <=  1'b1;
    else
        cnt_flag_1s    <=  1'b0;

always@(posedge sys_clk or negedge sys_rst_n)//         
    if(sys_rst_n == 1'b0)
        hour <= 5'd0;
    else    if((hour == 5'd23 && min == 6'd59 && sec == 6'd59 && cnt_flag_1s == 1'b1))
        hour <= 5'd0;
    else    if(min==6'd59 && sec==6'd59 && cnt_flag_1s)
        hour <= hour + 5'd1;
    else
        hour <= hour;
//min
always@(posedge sys_clk or negedge sys_rst_n)//         
    if(sys_rst_n == 1'b0)
        min <= 6'd0;
    else    if((min == 6'd59 && sec == 6'd59 && cnt_flag_1s == 1'b1))
        min <= 6'd0;
    else    if(sec==6'd59 && cnt_flag_1s)//
        min <= min + 6'd1;
    else
        min <= min;
//sec
always@(posedge sys_clk or negedge sys_rst_n)//         
    if(sys_rst_n == 1'b0)
        sec <= 6'd0;
    else    if((sec == 6'd59 && cnt_flag_1s == 1'b1))
        sec <= 6'd0;
    else    if(cnt_flag_1s)
        sec <= sec + 6'd1;
    else
        sec <= sec;
//数码管使能信号给高即可
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        seg_en  <=  1'b0;
    else
        seg_en  <=  1'b1;
//data
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data  <=  20'd0;
    else
        data  <=  10000 * hour + 100 * min + sec;
endmodule
data_gen_tb.v
 `include "../rtl/data_gen.v"
`timescale 1ns/1ns
module data_gen_tb;
reg                 sys_clk         ;
reg                 sys_rst_n       ;
wire     [19:0]     data            ;
wire     [5:0]      point           ;
wire                seg_e           ;
wire                sign            ;

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

always #10 sys_clk = ~sys_clk;

initial begin
    $dumpfile("data_gen.vcd");
    $dumpvars();
end

initial begin
    #4000000 $finish;
end
data_gen 
#(
    .CNT_MAX_1S(26'd49) //1s计数值
)
data_gen_inst
(
    .sys_clk            (sys_clk        ),   //系统时钟,频率50MHz
    .sys_rst_n          (sys_rst_n      ),   //复位信号,低电平有效

    .data               (data           ),   //数码管要显示的值 时钟最大为23:59:59 最小为00:00:00
    .point              (point          ),   //小数点显示,高电平有效
    .seg_en             (seg_en         ),   //数码管使能信号,高电平有效
    .sign               (sign           )       //符号位,高电平显示负号
);
endmodule

Vscode WaveTrace插件观测波形正常,上板调试功能正常。

note:在做这种数字系统设计时 写tb进行波形仿真调试时很有必要的,对着波形一步一步的debug,可以很快帮我们理清思路,更改设计逻辑代码。

2.在1的基础上加入按键信号,可通过按键去设置时钟值

思路:通过按键设置时钟的值也就是通过外部按键提供输入给data_gen模块,然后在data_gen模块内部通过按键输入产生相应的时分秒位的数据,因为是机械按键,所以需要用到按键消抖模块。这里我们使用四个按键,key1-key4;

key1用于秒计数器的增加,按下后从当前值开始累加,随后每按一下+1s,在0-59s之间循环;

key2用于分计时器的增加,按下后从当前值开始累加,随后每按一下+1,在0-59之间循环;

key3用于小时计时器的增加,按下后从当前值开始累加,随后每按一下+1,在0-23h之间循环;

key4为设置键,未按下时时钟正常运行,key1-key3无效;按下后时钟暂停,key1-key3有效,可对时钟值进行设定,当设定完成时再次按下key4键,时钟按照设定值启动,继续正常运行。

key_setting.v
 module key_setting
#(
    parameter CNT_MAX = 20'd999_999 // 按键消抖计数器
)
(
    input            sys_clk        , // 系统时钟
    input            sys_rst_n      , // 复位信号
    input            key_add_sec    , 
    input            key_add_min    , 
    input            key_add_hour   , 
    input            key_setting    , 
    
    output           add_sec        ,
    output           add_min        ,
    output           add_hour       ,
    output           setting          

);

/*按键消抖 定义中间参数*/
reg [19:0] cnt_20ms; // 按键消抖计数器
reg key_flag; //1:表示消抖后检测到按键被按下;0:表示没有检测到按键被按下
reg setting_en;
/************************************************************************按键消抖代码****************************************************************************/
//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else if(key_add_sec == 1'b1 && key_add_min == 1'b1 && key_add_hour == 1'b1 && key_setting == 1'b1)//一旦有按键出现高电平,说明就是抖动,要么就是没有按过
        cnt_20ms <= 20'b0;
    else if(cnt_20ms == CNT_MAX && (key_add_sec == 1'b0 || key_add_min == 1'b0 || key_add_hour ==1'b0 || key_setting == 1'b0))//一旦计数满20ms 且有按键仍按下,停止计数,并保持
        cnt_20ms <= cnt_20ms;
    else
        cnt_20ms <= cnt_20ms + 1'b1;
//key_flag:当计数满20ms后产生按键有效标志位
//且key_flag在999_999时拉高,维持一个时钟的高电平
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        key_flag <= 1'b0;
    else if(cnt_20ms == CNT_MAX-1)//999_998时拉高,若在999_999时拉高,后面是长长的高电平
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        setting_en <= 1'b0;
    else if(!key_setting  && key_flag)
        setting_en <= ~setting_en;
      
assign add_sec  = !key_add_sec  && key_flag;
assign add_min  = !key_add_min  && key_flag;
assign add_hour = !key_add_hour && key_flag;
assign setting  = setting_en;
             
endmodule
data_gen.v
`timescale  1ns/1ns
module  data_gen
#(
    parameter   CNT_MAX_1S = 26'd49_999_999 //1s计数值
)
(
    input                   sys_clk     ,   //系统时钟,频率50MHz
    input                   sys_rst_n   ,   //复位信号,低电平有效
    input                   add_sec     ,
    input                   add_min     ,
    input                   add_hour    ,
    input                   setting     ,

    output   reg    [19:0]  data        ,   //数码管要显示的值 时钟最大为23:59:59 最小为00:00:00
    output          [5:0]   point       ,   //小数点显示,高电平有效
    output   reg            seg_en      ,   //数码管使能信号,高电平有效
    output                  sign            //符号位,高电平显示负号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//reg   define
reg     [25:0]  cnt_1s         ;   //1s计数器
reg             cnt_flag_1s    ;   //1s标志信号

reg     [5:0 ]  hour           ;
reg     [6:0 ]  min            ;
reg     [6:0 ]  sec            ;
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//不显示小数点以及负数
assign  point   =   6'b010_100;
assign  sign    =   1'b0;

//cnt_1s:用50MHz时钟从0到49_999_999计数即为1s
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1s   <=  26'd0;
    else    if(cnt_1s == CNT_MAX_1S)
        cnt_1s   <=  26'd0;
    else    if(setting)
        cnt_1s   <=  cnt_1s;
    else    if(~setting)
        cnt_1s   <= cnt_1s + 1'b1;


//cnt_flag_1s:每1s产生一个标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_flag_1s    <=  1'b0;
    else    if(cnt_1s == CNT_MAX_1S - 1'b1)
        cnt_flag_1s    <=  1'b1;
    else
        cnt_flag_1s    <=  1'b0;

always@(posedge sys_clk or negedge sys_rst_n)//         
    if(sys_rst_n == 1'b0)
        hour <= 5'd0;
    else    if((hour == 5'd23 && min == 6'd59 && sec == 6'd59 && cnt_flag_1s == 1'b1))
        hour <= 5'd0;
    else    if(hour == 5'd23 && setting && add_hour)
        hour <= 5'd0;
    else    if(setting && add_hour)
        hour <= hour + 5'd1;
    else    if(min==6'd59 && sec==6'd59 && cnt_flag_1s)
        hour <= hour + 5'd1;
    else
        hour <= hour;
//min
always@(posedge sys_clk or negedge sys_rst_n)//         
    if(sys_rst_n == 1'b0)
        min <= 6'd0;
    else    if((min == 6'd59 && sec == 6'd59 && cnt_flag_1s == 1'b1))
        min <= 6'd0;
    else    if(min == 6'd59 && setting && add_min)
        min <= 6'd0;
    else    if(setting && add_min)
        min <= min + 6'd1;
    else    if(sec==6'd59 && cnt_flag_1s)//
        min <= min + 6'd1;
    else
        min <= min;
//sec
always@(posedge sys_clk or negedge sys_rst_n)//         
    if(sys_rst_n == 1'b0)
        sec <= 6'd0;
    else    if((sec == 6'd59 && cnt_flag_1s == 1'b1))
        sec <= 6'd0;
    else    if(sec == 6'd59 && setting && add_sec)
        sec <= 6'd0;
    else    if(setting && add_sec)
        sec <= sec + 6'd1;
    else    if(cnt_flag_1s)
        sec <= sec + 6'd1;
    else
        sec <= sec;
//数码管使能信号给高即可
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        seg_en  <=  1'b0;
    else
        seg_en  <=  1'b1;
//data
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data  <=  20'd0;
    else
        data  <=  10000 * hour + 100 * min + sec;
endmodule
bcd_8421.v
 `timescale  1ns/1ns
module  bcd_8421
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低电平有效
    input   wire    [19:0]  data        ,   //输入需要转换的数据

    output  reg     [3:0]   unit        ,   //个位BCD码
    output  reg     [3:0]   ten         ,   //十位BCD码
    output  reg     [3:0]   hun         ,   //百位BCD码
    output  reg     [3:0]   tho         ,   //千位BCD码
    output  reg     [3:0]   t_tho       ,   //万位BCD码
    output  reg     [3:0]   h_hun           //十万位BCD码
);

//********************************************************************//
//******************** Parameter And Internal Signal *****************//
//********************************************************************//

//reg   define
reg     [4:0]   cnt_shift   ;   //移位判断计数器
reg     [43:0]  data_shift  ;   //移位判断数据寄存器
reg             shift_flag  ;   //移位判断标志信号

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//cnt_shift:从0到21循环计数
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(shift_flag == 1'b1)
        cnt_shift   <=  cnt_shift + 1'b1;
    else
        cnt_shift   <=  cnt_shift;
       
//data_shift:计数器为0时赋初值,计数器为1~20时进行移位判断操作
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;

//shift_flag:移位判断标志信号,用于控制移位判断的先后顺序
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        shift_flag  <=  1'b0;
    else
        shift_flag  <=  ~shift_flag;

//当计数器等于20时,移位判断操作完成,对各个位数的BCD码进行赋值
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];
        end

endmodule
hc595_ctrl.v
 `timescale  1ns/1ns
module  hc595_ctrl
(
    input   wire            sys_clk     ,   //系统时钟,频率50MHz
    input   wire            sys_rst_n   ,   //复位信号,低有效
    input   wire    [5:0]   sel         ,   //数码管位选信号
    input   wire    [7:0]   seg         ,   //数码管段选信号
    
    output  reg             stcp        ,   //数据存储器时钟
    output  reg             shcp        ,   //移位寄存器时钟
    output  reg             ds          ,   //串行数据输入
    output  wire            oe              //使能信号,低有效
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//reg   define
reg     [1:0]   cnt_4   ;   //分频计数器
reg     [3:0]   cnt_bit ;   //传输位数计数器

//wire  define
wire    [13:0]  data    ;   //数码管信号寄存

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//将数码管信号寄存
assign  data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel};

//将复位取反后赋值给其即可
assign oe = ~sys_rst_n;

//分频计数器:0~3循环计数
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_4 <=  2'd0;
    else    if(cnt_4 == 2'd3)
        cnt_4 <=  2'd0;
    else
        cnt_4 <=  cnt_4 +   1'b1;

//cnt_bit:每输入一位数据加一
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit   <=  4'd0;
    else    if(cnt_4 == 2'd3 && cnt_bit == 4'd13)
        cnt_bit   <=  4'd0;
    else    if(cnt_4  ==  2'd3)
        cnt_bit   <=  cnt_bit   +   1'b1;
    else
        cnt_bit   <=  cnt_bit;

//stcp:14个信号传输完成之后产生一个上升沿
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        stcp    <=  1'b0;
    else    if(cnt_bit == 4'd13 && cnt_4 == 2'd3)
        stcp    <=  1'b1;
    else
        stcp    <=  1'b0;

//shcp:产生四分频移位时钟
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        shcp    <=  1'b0;
    else    if(cnt_4 >= 4'd2)
        shcp    <=  1'b1;
    else
        shcp    <=  1'b0;

//ds:将寄存器里存储的数码管信号输入即
always@(posedge sys_clk or  negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        ds  <=  1'b0;
    else    if(cnt_4 == 2'd0)
        ds  <=  data[cnt_bit];
    else
        ds  <=  ds;

endmodule
seg_595_dynamic.v
`timescale  1ns/1ns
module  seg_595_dynamic
(
    input   wire            sys_clk     , //系统时钟,频率50MHz
    input   wire            sys_rst_n   , //复位信号,低有效
    input   wire    [19:0]  data        , //数码管要显示的值
    input   wire    [5:0]   point       , //小数点显示,高电平有效
    input   wire            seg_en      , //数码管使能信号,高电平有效
    input   wire            sign        , //符号位,高电平显示负号

    output  wire            stcp        , //数据存储器时钟
    output  wire            shcp        , //移位寄存器时钟
    output  wire            ds          , //串行数据输入
    output  wire            oe            //使能信号

);

//********************************************************************//
//******************** Parameter And Internal Signal *****************//
//********************************************************************//
//wire  define
wire    [5:0]   sel;    //数码管位选信号
wire    [7:0]   seg;    //数码管段选信号

//************************************************************************//
//***************************** Instantiation ****************************//
//************************************************************************//
//---------- seg_dynamic_inst ----------
seg_dynamic seg_dynamic_inst
(
    .sys_clk     (sys_clk  ),   //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n),   //复位信号,低有效
    .data        (data     ),   //数码管要显示的值
    .point       (point    ),   //小数点显示,高电平有效
    .seg_en      (seg_en   ),   //数码管使能信号,高电平有效
    .sign        (sign     ),   //符号位,高电平显示负号

    .sel         (sel      ),   //数码管位选信号
    .seg         (seg      )    //数码管段选信号

);

//---------- hc595_ctrl_inst ----------
hc595_ctrl  hc595_ctrl_inst
(
    .sys_clk     (sys_clk  ),   //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n),   //复位信号,低有效
    .sel         (sel      ),   //数码管位选信号
    .seg         (seg      ),   //数码管段选信号

    .stcp        (stcp     ),   //输出数据存储寄时钟
    .shcp        (shcp     ),   //移位寄存器的时钟输入
    .ds          (ds       ),   //串行数据输入
    .oe          (oe       )

);

endmodule
seg_dynamic.v
 `timescale  1ns/1ns
module  seg_dynamic
(
    input   wire            sys_clk     , //系统时钟,频率50MHz
    input   wire            sys_rst_n   , //复位信号,低有效
    input   wire    [19:0]  data        , //数码管要显示的值
    input   wire    [5:0]   point       , //小数点显示,高电平有效
    input   wire            seg_en      , //数码管使能信号,高电平有效
    input   wire            sign        , //符号位,高电平显示负号

    output  reg     [5:0]   sel         , //数码管位选信号
    output  reg     [7:0]   seg           //数码管段选信号
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//parameter define
parameter   CNT_MAX =   16'd49_999;  //数码管刷新时间计数最大值

//wire  define
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   define
reg     [23:0]  data_reg    ;   //待显示数据寄存器
reg     [15:0]  cnt_1ms     ;   //1ms计数器
reg             flag_1ms    ;   //1ms标志信号
reg     [2:0]   cnt_sel     ;   //数码管位选计数器
reg     [5:0]   sel_reg     ;   //位选信号
reg     [3:0]   data_disp   ;   //当前数码管显示的数据
reg             dot_disp    ;   //当前数码管显示的小数点

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//data_reg:控制数码管显示数据
 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])||(point[4]))//else    if((h_hun) || (point[5]))
        data_reg    <=  {h_hun,t_tho,tho,hun,ten,unit};
//若显示的十进制数的万位为非零数据或需显示小数点,则值显示在5个数码管上
//打比方我们输入的十进制数据为20’d12345,我们就让数码管显示12345而不是012345
    else    if(((t_tho) || (point[4])) && (sign == 1'b1))//显示负号
        data_reg <= {4'd10,t_tho,tho,hun,ten,unit};//4'd10我们定义为显示负号
    else    if(((t_tho) || (point[4])) && (sign == 1'b0))
        data_reg <= {4'd11,t_tho,tho,hun,ten,unit};//4'd11我们定义为不显示
//若显示的十进制数的千位为非零数据或需显示小数点,则值显示4个数码管
    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};
//若显示的十进制数的百位为非零数据或需显示小数点,则值显示3个数码管
    else    if(((hun) || (point[2])) && (sign == 1'b1))
        data_reg <= {4'd11,4'd11,4'd10,hun,ten,unit};
    else    if(((hun) || (point[2])) && (sign == 1'b0))
        data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit};
//若显示的十进制数的十位为非零数据或需显示小数点,则值显示2个数码管
    else    if(((ten) || (point[1])) && (sign == 1'b1))
        data_reg <= {4'd11,4'd11,4'd11,4'd10,ten,unit};
    else    if(((ten) || (point[1])) && (sign == 1'b0))
        data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit};
//若显示的十进制数的个位且需显示负号
    else    if(((unit) || (point[0])) && (sign == 1'b1))
        data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd10,unit};
//若上面都不满足都只显示一位数码管
    else
        data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit};

//cnt_1ms:1ms循环计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1ms <=  16'd0;
    else    if(cnt_1ms == CNT_MAX)
        cnt_1ms <=  16'd0;
    else
        cnt_1ms <=  cnt_1ms + 1'b1;

//flag_1ms:1ms标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        flag_1ms    <=  1'b0;
    else    if(cnt_1ms == CNT_MAX - 1'b1)
        flag_1ms    <=  1'b1;
    else
        flag_1ms    <=  1'b0;

//cnt_sel:从0到5循环数,用于选择当前显示的数码管
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'b0;
    else    if((seg_en == 1'b1) && (flag_1ms == 1'b1))
        case(cnt_sel)
        3'd0:   data_disp    <=  data_reg[3:0]  ;  //给第1个数码管赋个位值
        3'd1:   data_disp    <=  data_reg[7:4]  ;  //给第2个数码管赋十位值
        3'd2:   data_disp    <=  data_reg[11:8] ;  //给第3个数码管赋百位值
        3'd3:   data_disp    <=  data_reg[15:12];  //给第4个数码管赋千位值
        3'd4:   data_disp    <=  data_reg[19:16];  //给第5个数码管赋万位值
        3'd5:   data_disp    <=  data_reg[23:20];  //给第6个数码管赋十万位值
        default:data_disp    <=  4'b0        ;
        endcase
    else
        data_disp   <=  data_disp;

//dot_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'b1111_1111;
    else    
        case(data_disp)
            4'd0  : seg  <=  {dot_disp,7'b100_0000};    //显示数字0
            4'd1  : seg  <=  {dot_disp,7'b111_1001};    //显示数字1
            4'd2  : seg  <=  {dot_disp,7'b010_0100};    //显示数字2
            4'd3  : seg  <=  {dot_disp,7'b011_0000};    //显示数字3
            4'd4  : seg  <=  {dot_disp,7'b001_1001};    //显示数字4
            4'd5  : seg  <=  {dot_disp,7'b001_0010};    //显示数字5
            4'd6  : seg  <=  {dot_disp,7'b000_0010};    //显示数字6
            4'd7  : seg  <=  {dot_disp,7'b111_1000};    //显示数字7
            4'd8  : seg  <=  {dot_disp,7'b000_0000};    //显示数字8
            4'd9  : seg  <=  {dot_disp,7'b001_0000};    //显示数字9
            4'd10 : seg  <=  8'b1011_1111          ;    //显示负号
            4'd11 : seg  <=  8'b1111_1111          ;    //不显示任何字符
            default:seg  <=  8'b1100_0000;
        endcase

//sel:数码管位选信号赋值
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sel <=  6'b000_000;
    else
        sel <=  sel_reg;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//---------- bsd_8421_inst ----------
bcd_8421    bcd_8421_inst
(
    .sys_clk     (sys_clk  ),   //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n),   //复位信号,低电平有效
    .data        (data     ),   //输入需要转换的数据

    .unit        (unit     ),   //个位BCD码
    .ten         (ten      ),   //十位BCD码
    .hun         (hun      ),   //百位BCD码
    .tho         (tho      ),   //千位BCD码
    .t_tho       (t_tho    ),   //万位BCD码
    .h_hun       (h_hun    )    //十万位BCD码
);

endmodule
top_seg_595.v
 `timescale  1ns/1ns
module  top_seg_595
(
    input                   sys_clk     ,   //系统时钟,频率50MHz
    input                   sys_rst_n   ,   //复位信号,低电平有效
    input                   key_add_sec ,
    input                   key_add_min ,
    input                   key_add_hour,
    input                   key_setting ,

    output                  stcp        ,   //输出数据存储寄时钟
    output                  shcp        ,   //移位寄存器的时钟输入
    output                  ds          ,   //串行数据输入
    output                  oe              //输出使能信号
);

//********************************************************************//
//******************** Parameter And Internal Signal *****************//
//********************************************************************//
//wire  define
wire    [19:0]  data    ;   //数码管要显示的值
wire    [5:0]   point   ;   //小数点显示,高电平有效top_seg_595
wire            seg_en  ;   //数码管使能信号,高电平有效
wire            sign    ;   //符号位,高电平显示负号

//********************************************************************//
//**************************** Main Code *****************************//
//********************************************************************//
//-------------key_setting_inst-----------
key_setting    key_setting_inst
(
    .sys_clk     (sys_clk     ), // 系统时钟
    .sys_rst_n   (sys_rst_n   ), // 复位信号
    .key_add_sec (key_add_sec ), 
    .key_add_min (key_add_min ), 
    .key_add_hour(key_add_hour), 
    .key_setting (key_setting ), 
    .add_sec     (add_sec     ),
    .add_min     (add_min     ),
    .add_hour    (add_hour    ),
    .setting     (setting     )     

);
//-------------data_gen_inst--------------
data_gen    data_gen_inst
(
    .sys_clk     (sys_clk     ),   //系统时钟,频率50MHz
    .sys_rst_n   (sys_rst_n   ),   //复位信号,低电平有效
    .add_sec     (add_sec     ),
    .add_min     (add_min     ),
    .add_hour    (add_hour    ),
    .setting     (setting     ), 
    
    .data        (data        ),   //数码管要显示的值
    .point       (point       ),   //小数点显示,高电平有效
    .seg_en      (seg_en      ),   //数码管使能信号,高电平有效
    .sign        (sign        )    //符号位,高电平显示负号
);

//-------------seg7_dynamic_inst--------------
seg_595_dynamic    seg_595_dynamic_inst
(
    .sys_clk    (sys_clk   ),   //系统时钟,频率50MHz
    .sys_rst_n  (sys_rst_n ),   //复位信号,低有效
    .data       (data      ),   //数码管要显示的值
    .point      (point     ),   //小数点显示,高电平有效
    .seg_en     (seg_en    ),   //数码管使能信号,高电平有效
    .sign       (sign      ),   //符号位,高电平显示负号

    .stcp       (stcp      ),   //输出数据存储寄时钟
    .shcp       (shcp      ),   //移位寄存器的时钟输入
    .ds         (ds        ),   //串行数据输入
    .oe         (oe        )    //输出使能信号
);
endmodule

上板验证后功能正确

posted @ 2023-05-10 18:02  million_yh  阅读(363)  评论(1)    收藏  举报