基于FPGA的数码管动态扫描程序与按键消抖程序

基于FPGA的数码管动态扫描程序与按键消抖程序

数码管动态扫描程序

/*
Author   : yffdd
time     : 2022/01/30
function : 数码管动态扫描
测试芯片  : AX301---6位共阳8段数码管
显示字符  :
          seg_date = 5'd0 : 字符'0'
          seg_date = 5'd1 : 字符'1'
          seg_date = 5'd2 : 字符'2'
          seg_date = 5'd3 : 字符'3'
          seg_date = 5'd4 : 字符'4'
          seg_date = 5'd5 : 字符'5'
          seg_date = 5'd6 : 字符'6'
          seg_date = 5'd7 : 字符'7'
          seg_date = 5'd8 : 字符'8'
          seg_date = 5'd9 : 字符'9'
          seg_date = 5'd10: 字符'A'
          seg_date = 5'd11: 字符'B'
          seg_date = 5'd12: 字符'C'
          seg_date = 5'd13: 字符'D'
          seg_date = 5'd14: 字符'E'
          seg_date = 5'd15: 字符'F'
          seg_date = 5'd16: 字符'-' 短横线
          seg_date = 5'd17: 字符'_' 下划线
          seg_date = 5'd31: 不显示任何字符
          PS:可自行更改段选表显示其它需要的字符
模块例化:
    seg_scan seg_scan_inst (
        .clk            (clk            ),
        .rst_n          (rst_n          ),
        .seg_sel        (seg_sel        ),
        .seg_bin        (seg_bin        ),
        .seg_date_0     (seg_date_0     ),
        .seg_date_1     (seg_date_1     ),
        .seg_date_2     (seg_date_2     ),
        .seg_date_3     (seg_date_3     ),
        .seg_date_4     (seg_date_4     ),
        .seg_date_5     (seg_date_5     )
    );
*/

module seg_scan(
    input                    clk            ,
    input                    rst_n          ,
    output  reg    [ 5:0]    seg_sel        ,//数码管位选号
    output  reg    [ 7:0]    seg_bin        ,//数码管段选值
    input          [ 4:0]    seg_date_0     ,//0号数码管显示值
    input          [ 4:0]    seg_date_1     ,//1号数码管显示值
    input          [ 4:0]    seg_date_2     ,//2号数码管显示值
    input          [ 4:0]    seg_date_3     ,//3号数码管显示值
    input          [ 4:0]    seg_date_4     ,//4号数码管显示值
    input          [ 4:0]    seg_date_5      //5号数码管显示值
    );

    reg            [ 7:0]    seg_bin_0      ;//0号数码管段选值
    reg            [ 7:0]    seg_bin_1      ;//1号数码管段选值
    reg            [ 7:0]    seg_bin_2      ;//2号数码管段选值
    reg            [ 7:0]    seg_bin_3      ;//2号数码管段选值
    reg            [ 7:0]    seg_bin_4      ;//4号数码管段选值
    reg            [ 7:0]    seg_bin_5      ;//5号数码管段选值

    
    parameter      SCAN_CLK   = 1000                  ;//扫描频率,每秒扫描1000次
    parameter      SYS_CLK    = 50000000              ;//系统时钟频率
    parameter      SCAN_COUNT = SYS_CLK / SCAN_CLK - 1;

    reg            [31:0]    scan_time      ;//扫描时间
    reg            [ 3:0]    scan_sel       ;//数码管位选控制信号
    
    //动态扫描
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            scan_time <= 32'd0;
            scan_sel  <= 4'd0;
        end
        else if(scan_time >= SCAN_COUNT)begin
            scan_time <= 32'd0;
            if(scan_sel == 4'd5)
                scan_sel  <= 4'd0;
            else
                scan_sel  <= scan_sel + 4'd1;
        end
        else begin
                scan_time <= scan_time + 32'd1;
        end
    end

    //位选段选值控制
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            seg_sel <= 6'b111111;
            seg_bin <= 8'hff;
        end
        else begin
            case(scan_sel)//第0个数码管
                4'd0:begin
                    seg_sel <= 6'b11_1110;
                    seg_bin <= seg_bin_0;
                end
                4'd1:begin//第1个数码管
                    seg_sel <= 6'b11_1101;
                    seg_bin <= seg_bin_1;
                end
                4'd2:begin//第2个数码管
                    seg_sel <= 6'b11_1011;
                    seg_bin <= seg_bin_2;
                end
                4'd3:begin//第3个数码管
                    seg_sel <= 6'b11_0111;
                    seg_bin <= seg_bin_3;
                end
                4'd4:begin//第4个数码管
                    seg_sel <= 6'b10_1111;
                    seg_bin <= seg_bin_4;
                end
                4'd5:begin//第5个数码管
                    seg_sel <= 6'b01_1111;
                    seg_bin <= seg_bin_5;
                end
                default:begin
                    seg_sel <= 6'b11_1111;
                    seg_bin <= 8'hff;
                end
            endcase
        end
    end

    //数码管第0位的段选设置
    always@(*)begin
        case(seg_date_0)
            5'd0   :seg_bin_0 <= 8'b1100_0000;
            5'd1   :seg_bin_0 <= 8'b1111_1001;
            5'd2   :seg_bin_0 <= 8'b1010_0100;
            5'd3   :seg_bin_0 <= 8'b1011_0000;
            5'd4   :seg_bin_0 <= 8'b1001_1001;
            5'd5   :seg_bin_0 <= 8'b1001_0010;
            5'd6   :seg_bin_0 <= 8'b1000_0010;
            5'd7   :seg_bin_0 <= 8'b1111_1000;
            5'd8   :seg_bin_0 <= 8'b1000_0000;
            5'd9   :seg_bin_0 <= 8'b1001_0000;
            5'd10  :seg_bin_0 <= 8'b1000_1000;
            5'd11  :seg_bin_0 <= 8'b1000_0011;
            5'd12  :seg_bin_0 <= 8'b1100_0110;
            5'd13  :seg_bin_0 <= 8'b1010_0001;
            5'd14  :seg_bin_0 <= 8'b1000_0110;
            5'd15  :seg_bin_0 <= 8'b1000_1110;
            5'd16  :seg_bin_0 <= 8'b1011_1111;
            5'd17  :seg_bin_0 <= 8'b1111_0111;
            5'd31  :seg_bin_0 <= 8'b1111_1111;
            default:seg_bin_0 <= 8'b1111_1111;
        endcase
    end

    //数码管第1位的段选设置
    always@(*)begin
        case(seg_date_1)
            5'd0   :seg_bin_1 <= 8'b1100_0000;
            5'd1   :seg_bin_1 <= 8'b1111_1001;
            5'd2   :seg_bin_1 <= 8'b1010_0100;
            5'd3   :seg_bin_1 <= 8'b1011_0000;
            5'd4   :seg_bin_1 <= 8'b1001_1001;
            5'd5   :seg_bin_1 <= 8'b1001_0010;
            5'd6   :seg_bin_1 <= 8'b1000_0010;
            5'd7   :seg_bin_1 <= 8'b1111_1000;
            5'd8   :seg_bin_1 <= 8'b1000_0000;
            5'd9   :seg_bin_1 <= 8'b1001_0000;
            5'd10  :seg_bin_1 <= 8'b1000_1000;
            5'd11  :seg_bin_1 <= 8'b1000_0011;
            5'd12  :seg_bin_1 <= 8'b1100_0110;
            5'd13  :seg_bin_1 <= 8'b1010_0001;
            5'd14  :seg_bin_1 <= 8'b1000_0110;
            5'd15  :seg_bin_1 <= 8'b1000_1110;
            5'd16  :seg_bin_1 <= 8'b1011_1111;
            5'd17  :seg_bin_1 <= 8'b1111_0111;
            5'd31  :seg_bin_0 <= 8'b1111_1111;
            default:seg_bin_1 <= 8'b1111_1111;
        endcase
    end

    //数码管第2位的段选设置
    always@(*)begin
        case(seg_date_2)
            5'd0   :seg_bin_2 <= 8'b1100_0000;
            5'd1   :seg_bin_2 <= 8'b1111_1001;
            5'd2   :seg_bin_2 <= 8'b1010_0100;
            5'd3   :seg_bin_2 <= 8'b1011_0000;
            5'd4   :seg_bin_2 <= 8'b1001_1001;
            5'd5   :seg_bin_2 <= 8'b1001_0010;
            5'd6   :seg_bin_2 <= 8'b1000_0010;
            5'd7   :seg_bin_2 <= 8'b1111_1000;
            5'd8   :seg_bin_2 <= 8'b1000_0000;
            5'd9   :seg_bin_2 <= 8'b1001_0000;
            5'd10  :seg_bin_2 <= 8'b1000_1000;
            5'd11  :seg_bin_2 <= 8'b1000_0011;
            5'd12  :seg_bin_2 <= 8'b1100_0110;
            5'd13  :seg_bin_2 <= 8'b1010_0001;
            5'd14  :seg_bin_2 <= 8'b1000_0110;
            5'd15  :seg_bin_2 <= 8'b1000_1110;
            5'd16  :seg_bin_2 <= 8'b1011_1111;
            5'd17  :seg_bin_2 <= 8'b1111_0111;
            5'd31  :seg_bin_0 <= 8'b1111_1111;
            default:seg_bin_2 <= 8'b1111_1111;
        endcase
    end

    //数码管3位的段选设置
    always@(*)begin
        case(seg_date_3)
            5'd0   :seg_bin_3 <= 8'b1100_0000;
            5'd1   :seg_bin_3 <= 8'b1111_1001;
            5'd2   :seg_bin_3 <= 8'b1010_0100;
            5'd3   :seg_bin_3 <= 8'b1011_0000;
            5'd4   :seg_bin_3 <= 8'b1001_1001;
            5'd5   :seg_bin_3 <= 8'b1001_0010;
            5'd6   :seg_bin_3 <= 8'b1000_0010;
            5'd7   :seg_bin_3 <= 8'b1111_1000;
            5'd8   :seg_bin_3 <= 8'b1000_0000;
            5'd9   :seg_bin_3 <= 8'b1001_0000;
            5'd10  :seg_bin_3 <= 8'b1000_1000;
            5'd11  :seg_bin_3 <= 8'b1000_0011;
            5'd12  :seg_bin_3 <= 8'b1100_0110;
            5'd13  :seg_bin_3 <= 8'b1010_0001;
            5'd14  :seg_bin_3 <= 8'b1000_0110;
            5'd15  :seg_bin_3 <= 8'b1000_1110;
            5'd16  :seg_bin_3 <= 8'b1011_1111;
            5'd17  :seg_bin_3 <= 8'b1111_0111;
            5'd31  :seg_bin_0 <= 8'b1111_1111;
            default:seg_bin_3 <= 8'b1111_1111;
        endcase
    end

    //数码管第4位的段选设置
    always@(*)begin
        case(seg_date_4)
            5'd0   :seg_bin_4 <= 8'b1100_0000;
            5'd1   :seg_bin_4 <= 8'b1111_1001;
            5'd2   :seg_bin_4 <= 8'b1010_0100;
            5'd3   :seg_bin_4 <= 8'b1011_0000;
            5'd4   :seg_bin_4 <= 8'b1001_1001;
            5'd5   :seg_bin_4 <= 8'b1001_0010;
            5'd6   :seg_bin_4 <= 8'b1000_0010;
            5'd7   :seg_bin_4 <= 8'b1111_1000;
            5'd8   :seg_bin_4 <= 8'b1000_0000;
            5'd9   :seg_bin_4 <= 8'b1001_0000;
            5'd10  :seg_bin_4 <= 8'b1000_1000;
            5'd11  :seg_bin_4 <= 8'b1000_0011;
            5'd12  :seg_bin_4 <= 8'b1100_0110;
            5'd13  :seg_bin_4 <= 8'b1010_0001;
            5'd14  :seg_bin_4 <= 8'b1000_0110;
            5'd15  :seg_bin_4 <= 8'b1000_1110;
            5'd16  :seg_bin_4 <= 8'b1011_1111;
            5'd17  :seg_bin_4 <= 8'b1111_0111;
            5'd31  :seg_bin_0 <= 8'b1111_1111;
            default:seg_bin_4 <= 8'b1111_1111;
        endcase
    end

    //数码管第5位的段选设置
    always@(*)begin
        case(seg_date_5)
            5'd0   :seg_bin_5 <= 8'b1100_0000;
            5'd1   :seg_bin_5 <= 8'b1111_1001;
            5'd2   :seg_bin_5 <= 8'b1010_0100;
            5'd3   :seg_bin_5 <= 8'b1011_0000;
            5'd4   :seg_bin_5 <= 8'b1001_1001;
            5'd5   :seg_bin_5 <= 8'b1001_0010;
            5'd6   :seg_bin_5 <= 8'b1000_0010;
            5'd7   :seg_bin_5 <= 8'b1111_1000;
            5'd8   :seg_bin_5 <= 8'b1000_0000;
            5'd9   :seg_bin_5 <= 8'b1001_0000;
            5'd10  :seg_bin_5 <= 8'b1000_1000;
            5'd11  :seg_bin_5 <= 8'b1000_0011;
            5'd12  :seg_bin_5 <= 8'b1100_0110;
            5'd13  :seg_bin_5 <= 8'b1010_0001;
            5'd14  :seg_bin_5 <= 8'b1000_0110;
            5'd15  :seg_bin_5 <= 8'b1000_1110;
            5'd16  :seg_bin_5 <= 8'b1011_1111;
            5'd17  :seg_bin_5 <= 8'b1111_0111;
            5'd31  :seg_bin_0 <= 8'b1111_1111;
            default:seg_bin_5 <= 8'b1111_1111;
        endcase
    end

endmodule

按键消抖程序

/*
Author   : yffdd
time     : 2022/01/30
function : 按键消抖检测模块
使用方法:   模块例化之后,检测key_fs标志即可检测消抖后的按键输入,key_fs高电平有效

模块例化:
    key_filter key_filter_inst(
        .clk            (clk            ),
        .rst_n          (rst_n          ),
        .key_in         (key_in         ),
        .key_fs         (key_fs         )
    );
*/

module key_filter(
    input                    clk            ,
    input                    rst_n          ,
    input                    key_in         ,//按键输入
    output  reg              key_fs          //按键返回值,按下1次有一个时钟周期的高电平脉冲
);

    reg                      key_flag       ;//按键按下标志
    reg                      key_state      ;//滤波后按键理想状态
    
    reg            [ 3:0]    state          ;//按键状态变量
    reg            [19:0]    cnt_20ms       ;//20ms计数
    reg                      en_cnt         ;//20ms定时使能计数寄存器
    reg            key_tmpa, key_tmpb       ;//按键在两个相邻时钟上升沿的状态
    wire           pedge,    nedge          ;//按键上升沿与下降沿
    reg                      cnt_full       ;//计数满标志信号
    reg            key_in_sa,key_in_sb      ;//按键在两个相邻时钟上升沿的状态

    localparam
        IDEL    = 4'b0001,//空闲状态
        FILTER0 = 4'b0010,//按下抖动状态
        DOWN    = 4'b0100,//按下稳定状态
        FILTER1 = 4'b1000;//释放抖动状态    
    
    
    //对外部输入的异步信号进行同步处理
    always@(posedge clk or negedge rst_n)
        if(rst_n == 1'b0)begin
            key_in_sa <= 1'b0;
            key_in_sb <= 1'b0;
        end
        else begin
            key_in_sa <= key_in;
            key_in_sb <= key_in_sa;
        end
    
    //使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            key_tmpa <= 1'b0;
            key_tmpb <= 1'b0;
        end
        else begin
            key_tmpa <= key_in_sb;
            key_tmpb <= key_tmpa;    
        end
    end

    //产生跳变沿信号
    assign nedge = key_tmpa & (!key_tmpb);//按键下降沿
    assign pedge = !key_tmpa & key_tmpb  ;//按键上升沿
    
    //按键状态判断
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            en_cnt    <= 1'b0;
            state     <= IDEL;
            key_flag  <= 1'b0;
            key_state <= 1'b1;
        end
        else begin
            case(state)
                IDEL   :begin
                        key_flag   <= 1'b0;
                        if(nedge)begin
                            state  <= FILTER0;
                            en_cnt <= 1'b1;
                        end
                        else
                            state  <= IDEL;
                end
                        
                FILTER0:begin
                        if(cnt_full)begin
                            key_flag  <= 1'b1;
                            key_state <= 1'b0;
                            en_cnt    <= 1'b0;
                            state     <= DOWN;
                        end
                        else if(pedge)begin
                            state     <= IDEL;
                            en_cnt    <= 1'b0;
                        end
                        else
                            state     <= FILTER0;
                end
                        
                DOWN  :begin
                        key_flag   <= 1'b0;
                        if(pedge)begin
                            state  <= FILTER1;
                            en_cnt <= 1'b1;
                        end
                        else
                            state  <= DOWN;
                end
                
                FILTER1:begin
                        if(cnt_full)begin
                            key_flag  <= 1'b1;
                            key_state <= 1'b1;
                            en_cnt    <= 1'b0;
                            state     <= IDEL;
                        end
                        else if(nedge)begin
                            en_cnt    <= 1'b0;
                            state     <= DOWN;
                        end
                        else
                            state     <= FILTER1;
                end

                default:begin 
                        state     <= IDEL; 
                        en_cnt    <= 1'b0;        
                        key_flag  <= 1'b0;
                        key_state <= 1'b1;
                end
            endcase    
        end
    end
    
    //按键标志处理
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            key_fs <= 0;
        end
        else begin
            key_fs <= key_flag && (!key_state);
        end
    end
    
    //20ms计数
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)
            cnt_20ms <= 20'd0;
        else if(en_cnt)
            cnt_20ms <= cnt_20ms + 1'b1;
        else
            cnt_20ms <= 20'd0;
    end

    //20ms计数满
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)
            cnt_full <= 1'b0;
        else if(cnt_20ms == 999_999)
            cnt_full <= 1'b1;
        else
            cnt_full <= 1'b0;
    end
endmodule

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、设计要求基于小脚丫FPGA开发板和四位数码管实现数字时钟的设计,要求: 1)采用FPGA+按键+四位数码管实现数字时钟功能; 2)时间显示格式:XX:XX:XX (时:分:秒),采用24小时制; 3)四位数码管显示时分秒,可以通过按键控制选择显示时分界面还是分秒界面。 4)通过按键设定初始时间。 5)设置在整点12点时,通过蜂鸣器响示意整点报时。蜂鸣器响维持大概5S; 2、硬件连接FPGA的系统时钟来自于小脚丫FPGA开发板配置的24MHz时钟晶振,连接FPGA的C1引脚。 本设计用到五个个按键K1~K5,硬件设计如图1所示,五个按键分别连接到FPGA的B8、C8、A10、A11和A12引脚。 图1. 按键硬件设计 本设计用到一个蜂鸣器来示意整点报时,硬件设计如图1所示,蜂鸣器连接到FPGA的B2引脚。 图2. 蜂鸣器硬件设计 本设计用到四位数码管来显示时间,四位数码管用两个74HC595驱动,硬件设计如图1所示,74HC595的串行时钟SCK、并行时钟RCK和串行数据DIN分别连接到FPGA的N2、M1和K1引脚上。 图3. 数码管驱动74HC595硬件设计 3、工作原理1)使用计数器做分频处理,得到周期为1秒的脉冲信号; 2)使用三个8bit的BCD码表示时钟、分钟、秒钟的值,其中高4bit表示值的十位,低4bit表示值的个位; 3)正常运行时,每来一个1S脉冲信号个位加1,个位满10清零同时十位加1,当秒钟满60清零同时分钟个位加1,依次进行...直到23:59:59的下一刻全部清零; 4)按键K5,模式调节,设计共分4中模式(分秒显示、分秒调节、时分显示、时分调节),按动K5依次切换模式; 5)按键K2,时间调节,当数字时钟在时针调节、分针调节或秒针调节模式时,按动K2调节对应时间位; 6)在调时分和调分秒两个状态,可以通过K4和K2键分别左移右移要调整的位,要调整的位会通过对应位的闪烁来示意。通过K4和 K2左右移动选择好要调整的位以后,就可以通过K1和K3来增大或调小对应的位; 4、代码设计为了实现所需要的功能,我们将整个设计划分不同的模块,如图4所示。 图4. 数字时钟程序设计框架 4.1五位按键模块 图5. 五位按键模块 Ø输入:五位的按键电平信息输入 Ø输出:五位后的脉冲输出 Ø功能:将按键按下一次的电平信号,经过后变成一个维持一个时钟周期的脉冲信号; Ø原理: 图6. 按键动特性 FPGA过20ms检测按键是否按下,存储检测到的值,并且按位取反与前一个20ms检测的值相与,得到一个值,如果为1,则判断按键按下,否则则无按下。 图7. FPGA按键的理解示意图 4.2电子表显示控制模块。 图8. 电子表显示控制模块 Ø输入:五位的按键脉冲 Ø输出:十六位的BCD码输出,每四个代表一个十进制数; Ø原理:四位的位闪烁控制信号。某一位为一代表这位对应的数码管的一位进行闪烁显示。(在调整状态下,会让当前调整的哪一位进行闪烁。正常显示状态下seg_flash_data全为零); Ø功能:主要就是一个状态机,通过检查输入的按键信息,进行显示状态切换,时间调整。四个状态分别为:显示分秒,调分秒,时分显示,调时分;当K5按键按下(key_pulse[4])时依次跳转,如图9所示。 图 9. 数字时钟状态控制设计 4.3数码管译码模块 图10. 数码管译码模块 Ø输入:四位的BCD码数据 Ø输出:八位的七段数码数据 Ø功能:一个case语句,将输入的四位BCD码转化为七段数码数据; Ø原理:数码管分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳极(COM)需接+5V才能使其工作。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码,共阴极(COM)需接GND才能使其工作。小脚丫拓展板上的数码管如下图所示: 图11. 数码管内部电路 共阴极数码管: 位选为低电平(即0)选中数码管;各段选为高电平(即接+5V时)选中各数码段;由0到f的编码为: 4.4四位数码管显示控制模块。 图12. 数码管显示控制模块 Ø输入:四个八位的七段数码管数据和位闪烁控制信号seg_flash_data. Ø输出:需要串行输出给74HC595的十六位数据; Ø功能:模块就是循环的将四位七段数码数据,组合一个十六位的输出数据; 4.5 74HC595驱动功能模块 图13. 74HC595驱动功能模块 Ø输入:十六位的位选段选数据。 1)duan_wei_data[13:0]分别对应: 2)[ X,X,H+,H-,DIG4,DIG3,DIG2,DIG1,DP,G,F,E,D,C,B,A] ; Ø输出:SPI接口输出,串行输出十六位位选段选信号; Ø

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值