FPGA通过数码管实现电子时钟


前言

环境:
1、Quartus18.1
2、vscode
3、板子型号:EP4CE6F17C8N


一、原理

视觉暂留原理:人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,视觉的这一现象则被称为“视觉暂留”。
这里我们的数码管其实是在一直切换的,但是由于切换得太快,产生了视觉暂留,从而影响我们的眼睛以为数码管没有变换。

1、共阴极数码管or共阳极数码管

共阴极:公共端为阴极,加阳极数码管点亮。即当真值为1时,数码管点亮;真值为0时,数码管不亮。
共阳极:公共端为阳极,加阴极数码管点亮。即当真值为0时,数码管点亮;真值为1时,数码管不亮。
所以在使用到数码管时,我们需要去判断板子的数码管是共阴极还是共阳极。

在这里插入图片描述

2、共阴极与共阳极的真值表

共阴极真值表:
在这里插入图片描述
共阳极真值表:
在这里插入图片描述

二、系统设计

1、总体框图:

在这里插入图片描述

2、模块调用

在这里插入图片描述

3、模块原理图

在这里插入图片描述

三、源码

1、计数模块

module counter(
    input     wire         clk      ,
    input     wire         rst_n    ,

    output    wire   [16:0] dout
);
localparam [25:0] CNTMAX_1s   = 26'd50_000_000;
localparam [16:0] CNTMAX_days = 17'd86400;
reg [25:0] cnt_1s;
reg [16:0] cnt_days;
wire   [4:0] hour;
wire   [5:0] min ;
wire   [5:0] sec;
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt_1s<=26'd0;
    else if(cnt_1s==CNTMAX_1s-1)
        cnt_1s<=26'd0;
    else
        cnt_1s<=cnt_1s+1;
end

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt_days<=17'd0;
    else if(cnt_days==CNTMAX_days-1&&cnt_1s==CNTMAX_1s-1)
        cnt_days<=17'd0;
    else if(cnt_1s==CNTMAX_1s-1)
        cnt_days<=cnt_days+1;
    else
        cnt_days<=cnt_days;
end
assign hour=cnt_days/3600;
assign min =(cnt_days-(hour*3600))/60;
assign sec =cnt_days-(hour*3600+min*60);
assign dout= {
    
    hour,min,sec};
endmodule

2、数码管驱动模块

module seg_driver(  //数码管驱动
    input               clk     ,
    input               rst_n   ,
    input       [16:0]  din     ,

    output reg  [5:0]   sel     , //片选
    output reg  [7:0]   dig       //段选     
);

    // parameter TIME_SCAN = 50_000 ; // 1MS 让片选一直扫描的移动
parameter TIME_SCAN = 50_000;
    parameter   ZER = 7'b100_0000,  // 0亮 1灭
                ONE = 7'b111_1001,
                TWO = 7'b010_0100,
                THR = 7'b011_0000,
                FOR = 7'b001_1001,
                FIV = 7'b001_0010,
                SIX = 7'b000_0010,
                SEV = 7'b111_1000,
                EIG = 7'b000_0000,
                NIN = 7'b001_0000;

    reg    [15:0]   cnt_scan    ; //扫描计数器
    wire            add_cnt_scan;
    wire            end_cnt_scan;

    reg    [3:0]    data        ; //寄存器 缓存数据
    reg             dot         ; //小数点

    wire   [3:0]    sec_l       ;  //秒低位
    wire   [3:0]    sec_h       ;  //秒 高位
    wire   [3:0]    min_l       ;  // 分地位
    wire   [3:0]    min_h       ;  //分高位
    wire   [3:0]    hou_l       ; 
    wire   [3:0]    hou_h       ;

    assign  sec_l = din[5:0]   % 10 ;  // 59 % 10 = 9 
    assign  sec_h = din[5:0]   / 10 ;  // 59 / 10 = 5 
    assign  min_l = din[11:6]  % 10 ;
    assign  min_h = din[11:6]  / 10 ;
    assign  hou_l = din[16:12] % 10 ;
    assign  hou_h = din[16:12] / 10 ;

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            cnt_scan <= 16'b0;
        end
        else if(add_cnt_scan)begin
            if(end_cnt_scan)begin
                cnt_scan <= 16'b0;
            end
            else begin
                cnt_scan <= cnt_scan + 1'b1;
            end
        end
        else begin
            cnt_scan <= cnt_scan ;
        end
    end

    assign add_cnt_scan = 1'b1;
    assign end_cnt_scan = add_cnt_scan && cnt_scan == TIME_SCAN - 1;

    always @(posedge clk or negedge rst_n)begin   // 片选
        if(!rst_n)begin
            sel <= 6'b011_111;
        end
        else if(end_cnt_scan)begin
            sel <= {
    
    sel[0],sel[5:1]};//循环向右移动  
        end
        else begin
            sel <= sel;
        end
    end 

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            data <= 3'b0;
            dot  <= 1'b1;
        end
        else begin
            case(sel)
                6'b011_111 : begin data <= sec_l; dot <= 1'b1; end
                6'b101_111 : begin data <= sec_h; dot <= 1'b1; end
                6'b110_111 : begin data <= min_l; dot <= 1'b0; end
                6'b111_011 : begin data <= min_h; dot <= 1'b1; end
                6'b111_101 : begin data <= hou_l; dot <= 1'b0; end
                6'b111_110 : begin data <= hou_h; dot <= 1'b1; end
                default : begin data <= 3'b0; dot <= 1'b1;end
            endcase
        end
    end
    
    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            dig <= 8'b0;
        end
        else begin
            case(data)
                0 : dig <= {
    
    dot,ZER};
                1 : dig <= {
    
    dot,ONE};
                2 : dig <= {
    
    dot,TWO};
                3 : dig <= {
    
    dot,THR};
                4 : dig <= {
    
    dot,FOR};
                5 : dig <= {
    
    dot,FIV};
                6 : dig <= {
    
    dot,SIX};
                7 : dig <= {
    
    dot,SEV};
                8 : dig <= {
    
    dot,EIG};
                9 : dig <= {
    
    dot,NIN};
                default : dig <= 8'b0;
            endcase
        end
    end
endmodule

3、顶层模块

module Clock_top(
    input       clk   ,
    input       rst_n ,

    output  [5:0]  sel ,
    output  [7:0]  dig 
);
wire [16:0] dout;
counter counter_inst(
    .clk      (clk),
    .rst_n    (rst_n),
    
    .dout(dout)
);
seg_driver seg_driver_inst(  //数码管驱动
    .clk     (clk),
    .rst_n   (rst_n),
    .din     (dout),

    .sel     (sel), //片选
    .dig     (dig)  //段选     
);
endmodule

四、运行效果

数码管实现电子时钟


五、总结

此次的工程并不难,只是需要我们对基础有一定的理解能力即可。首先是必须的计时器的实现,还有就是对数码管的片选、段选的操作。

六、参考资料

1、共阴极和共阳数码管详解
2、数码管电子时钟

猜你喜欢

转载自blog.csdn.net/qq_52215423/article/details/130402854