FPGA动态数码管应用——60S计数

目录

1.实验描述

2.系统模块框图

3.软件程序

3.1数据生成模块

 3.2按键消抖模块

3.3二进制转BCD码模块

3.4数码驱动模块

3.5测设程序

1.实验描述

      实现60s计数器的数码显示,当计数为60后回到1又循环,过程中可以通过按键控制暂停与继续。

 key:按键控制计数开始或暂停 

 rst:清零

2.系统模块框图

系统模块主要包含四个模块:

1.数据生成模块:每过1s在原数据基础上加1,并且在等到按键按下的高电平控制数据的计数与暂停。

2.按键消抖模块:检测按键是否按下,按下则产生一个脉冲的高电平。

3.二进制转BCD码模块:将数据生成模块中的数据进行转换为BCD码,提供给数码驱动模块。

4.数码驱动模块:得到BCD码后,每过1ms驱动一个数码管位并显示内容。

3.软件程序

3.1数据生成模块

`timescale 1ns / 1ns   

module top_seg
(
    input            sys_clk,          
    input            sys_rst_n,
    input            key      ,  	
    
    output    [5:0]  sel,               //数码管位选信号
    output    [7:0]  seg                //数码管段选信号
);                         
           
wire          [7:0]  data;                    //数码管显示的数值
 
data_gen    data_gen_inst(              //数据产生模块,每过0.1s数据自加一     
                                         //@2seg_data_inst ?= u_seg_data 
    .sys_clk        (sys_clk)       ,   
    .sys_rst_n      (sys_rst_n)     , 
    .key            (key)           ,
	
    .data           (data)                  
    
);

seg_dynamic   seg_dynamic_inst(         //数码管动态显示模块
    .sys_clk       (sys_clk)       ,   
    .sys_rst_n     (sys_rst_n)     ,                           
    .data          (data)          , 
	
    .sel           (sel)           ,   
    .seg           (seg)           
);
endmodule

 3.2按键消抖模块

`timescale  1ns/1ns

module  key_filter
#(
    parameter CNT_MAX = 20'd999_999 //计数器计数最大值
)
(
    input   wire    sys_clk     ,   //系统时钟50Mhz
    input   wire    sys_rst_n   ,   //全局复位
    input   wire    key_in      ,   //按键输入信号

    output  reg     key_flag        //key_flag为1时表示消抖后检测到按键被按下
                                    //key_flag为0时表示没有检测到按键被按下
);
//reg   define
reg     [19:0]  cnt_20ms    ;   //计数器

//cnt_20ms:如果时钟的上升沿检测到外部按键输入的值为低电平时,计数器开始计数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_20ms <= 20'b0;
    else    if(cnt_20ms == CNT_MAX && key_in == 1'b0)
        cnt_20ms <= cnt_20ms;
	else    if(key_in == 1'b1)      //按键没有按下,不计时
        cnt_20ms <= 20'b0;
    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'b1)
        key_flag <= 1'b1;
    else
        key_flag <= 1'b0;

endmodule

3.3二进制转BCD码模块

`timescale  1ns/1ns

module  bcd_8421
(
    input   wire            sys_clk     ,   
    input   wire            sys_rst_n   ,   
    input   wire    [7: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码
);                                       
reg     [4:0]   cnt_shift   ;            //移位判断计数器  移动次数由十转为二进制的位数决定
reg     [43:0]  data_shift  ;            //移位判断数据 寄存器  存放BCD码与二进制码
reg             shift_flag  ;            //移位判断标志信号   用于控制移位判断的先后顺序

//cnt_shift:从0到21循环计数(当计为20时进行判断移位,21时则是取BCD码数据)
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)   //计数器为0时赋初值
        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

3.4数码驱动模块

`timescale  1ns/1ns

module  seg_dynamic
#(
    parameter   CNT_MAX =   16'd49_999  //数码管刷新 时间计数最大值 1ms
)
(
    input   wire            sys_rst_n   , 
    input   wire            sys_clk     , 
    input   wire    [7:0]   data        , //数码管要显示的值

    output  reg     [5:0]   sel         , //数码管位选信号
    output  reg     [7:0]   seg           //数码管段选信号
);
//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   ;   //当前数码管显示的数据

//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;                                                  // unit  为BCD码,可以总效果4'bXXXX
    else                                                                       // ten   为BCD码,可以总效果4'bXXXX
        flag_1ms    <=  1'b0;                                                  // hun   为BCD码,可以总效果4'bXXXX
		                                                                       // tho   为BCD码,可以总效果4'bXXXX
//data_reg:控制数码管显示数据                                                 // t_tho 为BCD码,可以总效果4'bXXXX
 always@(posedge sys_clk or  negedge sys_rst_n)                                // h_hun 为BCD码,可以总效果4'bXXXX
    if(sys_rst_n == 1'b0)                                                      
        data_reg    <=  24'b0;                                                 
//若显示的十进制数的 十万 位为非零数据则六个数码管全显示                       
    else    if(h_hun)    //h_hun非空即数据有六位                               
        data_reg    <=  {h_hun,t_tho,tho,hun,ten,unit};
//若显示的十进制数的 万 位为非零数据则值显示在5个数码管上
    else    if(t_tho)
        data_reg <= {4'd11,t_tho,tho,hun,ten,unit}; //4'd11(第六位)我们定义为不显示
//若显示的十进制数的 千 位为非零数据则值显示4个数码管
    else    if(tho)
        data_reg <= {4'd11,4'd11,tho,hun,ten,unit};
//若显示的十进制数的 百 位为非零数据则值显示3个数码管
    else    if(hun)
        data_reg <= {4'd11,4'd11,4'd11,hun,ten,unit};
//若显示的十进制数的 十 位为非零数据则值显示2个数码管
    else    if(ten)
        data_reg <= {4'd11,4'd11,4'd11,4'd11,ten,unit};
//若上面都不满足都只显示 个 位数码管
    else
        data_reg <= {4'd11,4'd11,4'd11,4'd11,4'd11,unit};
		
//cnt_sel:标记0-5ms所对应显示的BCD码
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(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;

//控制数码管段选信号,显示数字
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  <=  8'b1100_0000;    //显示数字0
            4'd1  : seg  <=  8'b1111_1001;    //显示数字1
            4'd2  : seg  <=  8'b1010_0100;    //显示数字2
            4'd3  : seg  <=  8'b1011_0000;    //显示数字3
            4'd4  : seg  <=  8'b1001_1001;    //显示数字4
            4'd5  : seg  <=  8'b1001_0010;    //显示数字5
            4'd6  : seg  <=  8'b1000_0010;    //显示数字6
            4'd7  : seg  <=  8'b1111_1000;    //显示数字7
            4'd8  : seg  <=  8'b1000_0000;    //显示数字8
            4'd9  : seg  <=  8'b1001_0000;    //显示数字9
            default:seg  <=  8'b1111_1111;
        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;

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

3.5测试程序

`timescale  1ns/1ns 
module  tb_top_seg();

     reg           sys_clk   ;  
     reg           sys_rst_n ;  
     reg           key       ;  
                                 
     wire    [5:0]       sel       ;        
     wire    [7:0]       seg       ;
					
initial
    begin                                      
         sys_clk = 1'b0;
		 sys_rst_n <= 1'b0;
		 key  <= 1'b1;
		 #20;
		 sys_rst_n <= 1'b1;
		 #60;
		 key  <= 1'b0;          //按键第一次拉低
		 #500;
		 key  <= 1'b1;
		 #80000
		 key  <= 1'b0;          //按键第二次拉低
		 #500;
		 key  <= 1'b1;
    end	
	
always #10 sys_clk = ~sys_clk;

defparam  top_seg_inst.data_gen_inst.KEY_CNT =  20'd11;
defparam  top_seg_inst.data_gen_inst.CNT_MAX_1S    = 26'd49;
defparam  top_seg_inst.seg_dynamic_inst.CNT_MAX   = 16'd20;



top_seg     top_seg_inst
(
     .sys_clk    (sys_clk   ) ,
     .sys_rst_n  (sys_rst_n ) ,
     .key        (key       ) ,
                            
     .sel        (sel      ) , 
	 .seg        (seg       ) 
);
endmodule

烧入板子后可以成功实现相关功能,欢迎评论区讨论!

猜你喜欢

转载自blog.csdn.net/m0_72885897/article/details/128004173