基于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

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

以上。

posted @ 2017-12-07 21:06  小翁同学  阅读(4504)  评论(0编辑  收藏  举报