FPGA学习——FPGA实现电子时钟

一、数码管

Cyclone IV开发板上的数码管一共有6个,我们每次只能选择其中一个显示,怎么解决电子时钟时、分、秒同时显示呢?要实现电子时钟首先要了解什么是余晖效应。
  余晖效应一般指视觉暂留。 视觉暂留现象即视觉暂停现象(Persistence of vision,Visual staying phenomenon,duration of vision)又称“余晖效应”。只要数码管位选信号切换得足够快,数码管由亮到灭这一过程是需要一段时间的,由于时间很短,我们的眼睛是没有办法分清此时此刻数码管的状态,给人的感觉就是数码管是一直亮的。以此来达到欺骗人眼的效果,这样就可以实现同时显示时、分、秒。

二、CyloneⅣ 数码管原理图

在这里插入图片描述

三、代码实现

本项目是为了实现类似于23:59:59的时钟显示,由于八段式数码管仅包含小数点无冒号,因此本项目为了简化忽略冒号
所用芯片为:EP4CE6F17C8
本项目设计了两个模块,分别为:counter、reg_driver;分别用于设计计数器和数码管驱动,另有dig_clock作为顶层文件

counter模块代码分析:

  • 首先,博主在本模块中设计了一个总计时为24×60×60=86400s的计数器
  • 对于如何从86400s中取出相应的值作为小时,分钟,秒的数据可以参考下列公式:
    hour = cnt_day / 12’d3600 ;
    min = (cnt_day % 12’d3600) / 6’d60 ;
    sec = (cnt_day % 12’d3600) % 6’d60 ;
module counter (
    input   wire            clk     ,
    input   wire            rst_n   ,

    output  reg    [4:0]   hour    ,
    output  reg    [5:0]   min     ,
    output  reg    [5:0]   sec
);
parameter MAX1S    = 26'd50_000_000;
parameter MAXDAY   = 17'd86400;
reg    [25:0]   cnt1s     ;
reg    [16:0]   cnt_day   ;

//1s计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt1s <= 1'b0;        
    end
    else if(cnt1s == MAX1S - 1'b1) begin
        cnt1s <= 1'b0;
    end
    else begin
        cnt1s <= cnt1s + 1'b1;
    end
end

//一天计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_day <= 1'b0;
    end
    else if(cnt_day == MAXDAY - 1'b1 && cnt1s == MAX1S - 1'b1) begin
        cnt_day <= 1'b0;
    end
    else if(cnt1s == MAX1S - 1'b1) begin
        cnt_day <= cnt_day + 1'b1;
    end
    else begin
        cnt_day <= cnt_day;
    end
end

always @(*) begin
    hour = cnt_day / 12'd3600             ;
    min  = (cnt_day % 12'd3600) / 6'd60   ;
    sec  = (cnt_day % 12'd3600) % 6'd60   ;
end
    
endmodule

seg_driver模块代码分析:

  • 本模块需要一个八位的seg寄存器作为八段式数码管的段选信号以及一个六位的sel寄存器作为位选信号
  • 本模块设计了一个20微秒计数器,确保数码管依次显示时能够产生余晖效应,使人在视觉上能够同时感觉到是六个数码管同时显示
  • 此外,设计了一个4位寄存器number用于保存小时位、分钟位、秒位的高位与地位,由于时钟中最大数字为9,因此4位即可满足数值保存条件
  • 本模块中数码管每隔20微秒移动一位,移动后根据位选信号的值选择number寄存器应该保存时分秒的哪一位,随后立即将值赋给段选信号seg(所有always块都是并发运行的)
module seg_driver(
    input   wire            clk     ,
    input   wire            rst_n   ,
    input   wire    [4:0]   hour    ,
    input   wire    [5:0]   min     ,
    input   wire    [5:0]   sec     ,

    output  wire    [7:0]   seg     ,
    output  wire    [5:0]   sel
);

parameter MAX20US = 10'd1000;
parameter ZERO      = 8'b1100_0000,
          ONE       = 8'b1111_1001,
          TWO       = 8'b1010_0100,
          THREE     = 8'b1011_0000,
          FOUR      = 8'b1001_1001,
          FIVE      = 8'b1001_0010,
          SIX       = 8'b1000_0010,
          SEVEN     = 8'b1111_1000,
          EIGHT     = 8'b1000_0000,
          NINE      = 8'b1001_0000;
reg [9:0]   cnt_20us    ;
reg [5:0]   sel_r       ;
reg [3:0]   number      ;
reg [7:0]   seg_r       ;


//20微秒计数器
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        cnt_20us <= 1'b0;
    end
    else if(cnt_20us == MAX20US - 1'b1) begin
        cnt_20us <= 1'b0;
    end
    else begin
        cnt_20us <= cnt_20us + 1'b1;
    end
end

//位选信号约束
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sel_r <= 6'b111_110;
    end
    else if(cnt_20us == MAX20US - 1'b1)begin
        sel_r <= {sel_r[4:0],sel_r[5]};
    end
    else begin
        sel_r <= sel_r;
    end
end

assign sel = sel_r;

//得到各位数字
always @(*)begin
    case(sel_r)
        6'b111_110 :    number = hour /10;
        6'b111_101 :    number = hour %10;
        6'b111_011 :    number = min / 10;
        6'b110_111 :    number = min % 10;
        6'b101_111 :    number = sec / 10;
        6'b011_111 :    number = sec % 10;
        default    :    number = 4'd0;
    endcase
end 


//通过数字number解析出seg_r值
always @(*)begin
    case(number)
        4'd0 :   seg_r <= ZERO ;
        4'd1 :   seg_r <= ONE  ;
        4'd2 :   seg_r <= TWO  ;
        4'd3 :   seg_r <= THREE;
        4'd4 :   seg_r <= FOUR ;
        4'd5 :   seg_r <= FIVE ;
        4'd6 :   seg_r <= SIX  ;
        4'd7 :   seg_r <= SEVEN;
        4'd8 :   seg_r <= EIGHT;
        4'd9 :   seg_r <= NINE ;
        default: seg_r <= ZERO;
    endcase
end
assign seg = seg_r;



endmodule

顶层文件

module clock_top(
    input   wire            clk     ,
    input   wire            rst_n   ,
    output  wire    [7:0]   seg     ,
    output  wire    [5:0]   sel
);

wire    [4:0]   hour    ;
wire    [5:0]   min     ;
wire    [5:0]   sec     ;

counter u_counter (
    .clk     (clk  ),
    .rst_n   (rst_n),
    .hour    (hour),
    .min     (min ),
    .sec     (sec )
);

seg_driver u_seg_driver(
    .clk     ( clk  ),
    .rst_n   ( rst_n),
    .hour    (hour  ),
    .min     (min   ),
    .sec     (sec   ),

    .seg     (seg),
    .sel     (sel)
);

endmodule

TCL引脚绑定代码

# Copyright (C) 2018  Intel Corporation. All rights reserved.
# Your use of Intel Corporation's design tools, logic functions 
# and other software and tools, and its AMPP partner logic 
# functions, and any output files from any of the foregoing 
# (including device programming or simulation files), and any 
# associated documentation or information are expressly subject 
# to the terms and conditions of the Intel Program License 
# Subscription Agreement, the Intel Quartus Prime License Agreement,
# the Intel FPGA IP License Agreement, or other applicable license
# agreement, including, without limitation, that your use is for
# the sole purpose of programming logic devices manufactured by
# Intel and sold by Intel or its authorized distributors.  Please
# refer to the applicable agreement for further details.

# Quartus Prime Version 18.1.0 Build 625 09/12/2018 SJ Standard Edition
# File: D:\intelFPGA\core\seg_led_static\tcl\seg_led_static_top.tcl
# Generated on: Wed Apr 26 09:36:22 2023

package require ::quartus::project

set_location_assignment PIN_A4 -to sel[0]
set_location_assignment PIN_B4 -to sel[1]
set_location_assignment PIN_A3 -to sel[2]
set_location_assignment PIN_B3 -to sel[3]
set_location_assignment PIN_A2 -to sel[4]
set_location_assignment PIN_B1 -to sel[5]
set_location_assignment PIN_B7 -to seg[0]
set_location_assignment PIN_A8 -to seg[1]
set_location_assignment PIN_E1 -to clk
set_location_assignment PIN_E15 -to rst_n
set_location_assignment PIN_A5 -to seg[7]
set_location_assignment PIN_B8 -to seg[6]
set_location_assignment PIN_A7 -to seg[5]
set_location_assignment PIN_B6 -to seg[4]
set_location_assignment PIN_B5 -to seg[3]
set_location_assignment PIN_A6 -to seg[2]

四、实现效果

如果还有不会创建项目,下载驱动,绑定引脚,请参考博主过往博文
在这里插入图片描述

五、参考资料

https://blog.csdn.net/weixin_43828944/article/details/122699533

六、电子时钟新编

博主于正式学习FPGA后所写的新版电子时钟博客

  • 11
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值