zoukankan      html  css  js  c++  java
  • 基于FPGA的数字秒表(数码管显示模块和按键消抖)实现

    本文主要是学习按键消抖和数码管动态显示,秒表显示什么的,个人认为,拿FPGA做秒表真是嫌钱多。

    感谢

    感谢学校和至芯科技,笔者专业最近去北京至芯科技培训交流了一周。老师的经验还是可以的,优化了自己的代码也学习了新的知识。北京是个好地方,故宫没有想象中的那么大,但人真是多到密集恐惧症。至芯科技的最小开发板设计的一般般。。。

    言归正传,本次主要实现数字数码管的主要功能,按键触发:秒表开始,暂停,记录,回显。一共四个按键,第一个按键控制全局复位,第二个按键控制秒表的开始与暂停,第三个按键控制秒表在运行中按下记录,第四个按键控制秒表在暂停的时候,记录的数据可以按一下则顺序回显一个数据,一共三个数据。

    秒表可以从00:00:00 -- 59:59:99 , 6位数码管。

    设计功能分解

    至芯的板子数码管那位选采用了三八译码器,由于一般不用数码管,二进制转bcd的方法就不在这里阐述(不用也不会,至芯科技直接用了除号等不符合设计规范的编码实现)。直接采用到9则进1的方式,由于采用单时钟域,时序对齐还是需要特别注意的。

    按键消抖模块设计

    系统时钟50MHZ,采用时钟使能的方式10ms采样一个值,用一个8bit的移位寄存器实现消抖并只检测下降沿(按键按下为低电平),当按键按下产生1个系统时钟周期的高电平。

    代码仅供学习使用:

    //************************************************
    //  Filename      : debounce.v                             
    //  Author        : kingstacker                  
    //  Company       : School                       
    //  Email         : kingstacker_work@163.com     
    //  Device        : Altera cyclone4 ep4ce6f17c8  
    //  Description   : when the key is pressed ,porduce 1 period clk high;                            
    //************************************************
    module  debounce (  //use shift reg logic;
    /*i*/   input    wire    clk          ,
            input    wire    rst_n        ,
            input    wire    key_i        ,
    /*o*/   output   wire    key_o     
    );
    parameter CLK_MAX = 19'd49_9999; //0.01s;
    reg [18:0] cnt;
    reg clk_en;
    reg key_o_r;
    always @(posedge clk or negedge rst_n) begin //control cnt value;
        if (~rst_n) begin
            cnt <= 0;
        end //if
        else begin
            cnt <= (cnt == CLK_MAX) ? 19'd0 : cnt + 1'b1;    
        end //else
    end //always
    always @(posedge clk or negedge rst_n) begin //produce clk_en;
        if (~rst_n) begin
            clk_en <= 1'b0;
        end //if
        else begin
            clk_en <= (cnt == CLK_MAX) ? 1'b1 : 1'b0;    
        end //else
    end //always
    reg [7:0] shift_val;
    always @(posedge clk or negedge rst_n) begin //shift logic;
        if (~rst_n) begin
            shift_val <= 8'hff;
        end //if
        else begin
            shift_val <= (clk_en) ? {shift_val[6:0],key_i} : shift_val;    
        end //else
    end //always
    always @(posedge clk or negedge rst_n) begin
        if (~rst_n) begin
            key_o_r <= 1'b0;
        end //if
        else begin
            if (shift_val == 8'b1000_0000 && clk_en) begin  //check key pressed negedge;
                key_o_r <= 1'b1;
            end    
            else begin
                key_o_r <= 1'b0;
            end
        end //else
    end //always
    assign key_o = key_o_r;
    
    endmodule

    6位数码管动态显示模块设计

     数码管由于dp位需要控制,本模块中dp位逻辑单独了出来,动态显示1ms刷新数码管,6个管需要6ms,注意间隔不要太大,否则会闪烁,太紧也没有必要,保证数据对齐,采用组合逻辑实现:

    //************************************************
    //  Filename      : led_display.v                             
    //  Author        : kingstacker                  
    //  Company       : School                       
    //  Email         : kingstacker_work@163.com     
    //  Device        : Altera cyclone4 ep4ce6f17c8  
    //  Description   :on board ok; one clk domain;input din is bcd ;                           
    //************************************************
    module  led_display (
    /*i*/   input    wire              clk              ,
            input    wire              rst_n            ,
            input    wire    [23:0]    din              ,  //data from sco model;
    /*o*/   output   wire    [2:0]     sel              ,  //38 decoder;
            output   wire    [7:0]     seg                 //segement;      
    );
    parameter MS_MAX = 17'd4_9999; //1ms dynamic refresh;//4_9999;
    reg          flag_1ms;
    reg  [16:0]  cnt_1ms;
    reg  [2:0]   sel_r;
    reg  [6:0]   seg_r;
    reg  [3:0]   seg_sel;
    reg  [2:0]   cnt_sel;
    wire         dp;  
    always @(posedge clk or negedge rst_n) begin   //control cnt_1ms value;
        if (~rst_n) begin
            cnt_1ms <= 0;
        end //if
        else begin
            cnt_1ms <= (cnt_1ms == MS_MAX) ? 16'd0 : cnt_1ms + 1'b1;    
        end //else
    end //always
    always @(posedge clk or negedge rst_n) begin //produce the flag_1ms high level;
        if (~rst_n) begin
            flag_1ms <= 1'b0;
        end //if
        else begin
            flag_1ms <= (cnt_1ms == MS_MAX) ? 1'b1 : 1'b0;    
        end //else
    end //always
    always @(posedge clk or negedge rst_n) begin //cnt sel value control;
        if (~rst_n) begin
            cnt_sel <= 0;
        end //if
        else begin
            cnt_sel <= (cnt_sel == 3'd5 && flag_1ms) ? 3'd0 : (flag_1ms) ? cnt_sel + 1'b1 : cnt_sel;   
        end //else
    end //always
    always @(*) begin          //sel logic exchange;
            case (cnt_sel)
                3'd0: begin sel_r = 3'b101;seg_sel = din[3:0];   end
                3'd1: begin sel_r = 3'b100;seg_sel = din[7:4];   end
                3'd2: begin sel_r = 3'b011;seg_sel = din[11:8];  end
                3'd3: begin sel_r = 3'b010;seg_sel = din[15:12]; end
                3'd4: begin sel_r = 3'b001;seg_sel = din[19:16]; end
                3'd5: begin sel_r = 3'b000;seg_sel = din[23:20]; end
                default: begin sel_r = 3'b111;seg_sel = 4'd10;   end 
            endcase //case   
    end //always
    always @(*) begin         //display coder;
            case (seg_sel)
                4'd0: begin seg_r = 7'b100_0000;    end //0; 
                4'd1: begin seg_r = 7'b111_1001;    end //1; 
                4'd2: begin seg_r = 7'b010_0100;    end //2; 
                4'd3: begin seg_r = 7'b011_0000;    end //3; 
                4'd4: begin seg_r = 7'b001_1001;    end //4; 
                4'd5: begin seg_r = 7'b001_0010;    end //5; 
                4'd6: begin seg_r = 7'b000_0010;    end //6; 
                4'd7: begin seg_r = 7'b111_1000;    end //7; 
                4'd8: begin seg_r = 7'b000_0000;    end //8; 
                4'd9: begin seg_r = 7'b001_0000;    end //9; 
                default: begin seg_r = 7'b100_0000; end //x;
                endcase //case    
    end //always
    
    assign dp = !(cnt_sel == 3'd2 || cnt_sel == 3'd4);  //dp produce logic;
    assign seg = {dp,seg_r};
    assign sel = sel_r;
    
    endmodule

    sco模块就没啥意思了,本文主要讲述按键消抖的实现方式以及数码管动态显示的问题。

    如果需要整体源码,请上github查看:git@github.com:kingstacker/second_counter.git

    再次感谢至芯科技公司以及李老师。

    以上。

  • 相关阅读:
    Devexpress [汇总链接]
    [转]修改LayoutControlitem容器内的控件长宽
    How to hide border of XtraTabControl
    CEF / Chromium 重新编译2018 官网地址 一路是坑 千万别跟着官方step by step走一定多思考多查资料 因为改动地方太多了编译都每个版本都不一样
    c#:配置引用程序集的路径(分离exe和dll)和 如何处理[dllImport]中的程序集的加载 [笔记]
    [redis] -- 缓存雪崩和缓存穿透、缓存击穿问题解决方案篇
    [redis] -- 为什么那么快
    [redis] -- 集群篇
    [spring cloud] -- 服务注册与服务发现篇
    [spring cloud] -- 核心篇
  • 原文地址:https://www.cnblogs.com/kingstacker/p/8001064.html
Copyright © 2011-2022 走看看