文章目录
前言
一、回顾数码管
Cyclone IV开发板上的数码管一共有6个,我们每次只能选择其中一个显示,怎么解决电子时钟时、分、秒同时显示呢?要实现电子时钟首先要了解什么是余晖效应。
余晖效应一般指视觉暂留。 视觉暂留现象即视觉暂停现象(Persistence of vision,Visual staying phenomenon,duration of vision)又称“余晖效应”。只要数码管位选信号切换得足够快,数码管由亮到灭这一过程是需要一段时间的,由于时间很短,我们的眼睛是没有办法分清此时此刻数码管的状态,给人的感觉就是数码管是一直亮的。以此来达到欺骗人眼的效果,这样就可以实现同时显示时、分、秒。
二、任务描述
使用数码管设计电子时钟,计数器部分有3种实现方法:
- 采用1个计数器,模为24x60x60;
- 采用3个计数器,模分别为24、60、60;
- 采用6个计数器分别计数时、分、秒的个位、十位;
- 方法1:计数器个数少,设计简单,但是后面数码管译码时,需要对计数值取余、取整,分离出时、分、秒的个位和十位,比较耗费组合逻辑资源;
- 方法2:3个计数器,后面数码管译码时,需要对时、分、秒计数值取余、取整,分离出时、分、秒的个位和十位,比较耗费组合逻辑资源;
- 方法3:6个计数器,相对复杂一点,但是计数值直接就是时、分、秒的个位和十位值,不需要除法器进行取余、取整操作,使用的触发器资源略多,但节省组合逻辑资源。
本实验使用方法1,其他方法同学们可以自行尝试。
我们最后实现的效果如图2所示,由于开发板上的数码管没有冒号(:),所以我们直接忽略冒号。
三、系统框图
- 计数器模块:产生时、分、秒计数值;
- 数码管驱动模块:对时、分、秒计数值进行译码,产生驱动数码管动态显示数字的位选信号和段选信号。
四、模块调用
五、模块原理图
六、工程源码
6.2 时钟计数模块代码
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 MAX_NUM = 26'd49_999_999;//1s
parameter TOTAL_SEC = 17'd86399 ;//24x60x60s
reg [25:0] cnt_sec ;//秒计数器
reg [16:0] cnt_time;//计时器
//计时1s秒钟模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_sec <= 26'd0;
end
else if(cnt_sec == MAX_NUM)begin
cnt_sec <= 26'd0;
end
else begin
cnt_sec <= cnt_sec + 1'd1;
end
end
//时间模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_time <= 17'd0;
end
else if(cnt_time == TOTAL_SEC && cnt_sec == MAX_NUM)begin
cnt_time <= 17'd0;
end
else if(cnt_sec == MAX_NUM)begin
cnt_time <= cnt_time + 1'd1;
end
else begin
cnt_time <= cnt_time;
end
end
//取出时分秒模块
always@(*)begin
hour = cnt_time / 12'd3600 ;//小时
min = (cnt_time % 12'd3600) / 6'd60;//分钟
sec = (cnt_time % 12'd3600) % 6'd60;//秒
end
endmodule
6.2 数码管驱动模块代码
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 reg [5:0] seg_sel,
output reg [7:0] seg_ment
);
parameter CNT_20US = 10'd999;//20微秒
wire [3:0] sec_low ;//秒的低位
wire [2:0] sec_high ;//秒的高位
wire [3:0] min_low ;//分钟的低位
wire [2:0] min_high ;//分钟的高位
wire [1:0] hour_low ;//小时的低位
wire [1:0] hour_high;//小时的高位
reg [9:0] cnt ;//计数器,计20us时间
reg [3:0] number ;//显示时分秒寄存器
//计时模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 10'd0;
end
else if(cnt == CNT_20US)begin
cnt <= 10'd0;
end
else begin
cnt <= cnt + 1'd1;
end
end
//位选信号切换模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seg_sel <= 6'b011111;//初始化第一个数码管亮
end
else if(cnt == CNT_20US)begin
seg_sel <= {seg_sel[0],seg_sel[5:1]};//每隔20us进行位移操作
end
else begin
seg_sel <= seg_sel;//其他时间保持不变
end
end
//位选信号译码模块
always@(*)begin
case(seg_sel)
6'b011111: number = sec_low ;//秒的低位给第一个数码管显示
6'b101111: number = sec_high ;//秒的高位给第二个数码管显示
6'b110111: number = min_low ;//分钟的低位给第三个数码管显示
6'b111011: number = min_high ;//分钟的高位给第四个数码管显示
6'b111101: number = hour_low ;//小时的低位给第五个数码管显示
6'b111110: number = hour_high;//小时的高位给第六个数码管显示
default: number = sec_low ;
endcase
end
//段选信号译码模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seg_ment <= 8'b1100_0000;//初始化显示0
end
else begin
case(number)
4'd0: seg_ment <= 8'b1100_0000;//数码管显示0
4'd1: seg_ment <= 8'b1111_1001;//数码管显示1
4'd2: seg_ment <= 8'b1010_0100;//数码管显示2
4'd3: seg_ment <= 8'b1011_0000;//数码管显示3
4'd4: seg_ment <= 8'b1001_1001;//数码管显示4
4'd5: seg_ment <= 8'b1001_0010;//数码管显示5
4'd6: seg_ment <= 8'b1000_0010;//数码管显示6
4'd7: seg_ment <= 8'b1111_1000;//数码管显示7
4'd8: seg_ment <= 8'b1000_0000;//数码管显示8
4'd9: seg_ment <= 8'b1001_0000;//数码管显示9
default: seg_ment <= 8'b1100_0000;//数码管显示0
endcase
end
end
assign sec_low = sec % 4'd10 ;//秒的低位如59秒--->9
assign sec_high = sec / 4'd10 ;//秒的高位如59秒--->5
assign min_low = min % 4'd10 ;//分钟的低位如59--->9
assign min_high = min / 4'd10 ;//分钟的高位如59--->5
assign hour_low = hour % 4'd10;//小时的低位如23--->3
assign hour_high = hour / 4'd10;//小时的高位如23--->2
endmodule
6.3 顶层模块代码
module digital_clock(
input wire clk ,//时钟
input wire rst_n,//复位信号
output wire [5:0] seg_sel,//数码管位选信号
output wire [7:0] seg_ment//数码管段选信号
);
parameter MAX_NUM = 26'd49_999_999;//1s
parameter TOTAL_SEC = 17'd86399 ;//60x24x24s
parameter CNT_20US = 10'd999 ;//20us
wire [4:0] hour;//小时
wire [5:0] min ;//分钟
wire [5:0] sec ;//秒
//实例化计数器模块
counter#(.MAX_NUM(MAX_NUM),
.TOTAL_SEC(TOTAL_SEC)) u_counter(
.clk (clk) ,
.rst_n (rst_n),
.hour (hour) ,
.min (min) ,
.sec (sec)
);
//实例化数码管驱动模块
seg_driver#(.CNT_20US(CNT_20US)) u_seg_driver(
.clk (clk) ,
.rst_n (rst_n) ,
.hour (hour) ,
.min (min) ,
.sec (sec) ,
.seg_sel (seg_sel),
.seg_ment (seg_ment)
);
endmodule
七、仿真测试
7.1 测试代码
`timescale 1ns/1ns
module digital_clock_tb();
parameter MAX_NUM = 26'd5 ;//100ns
parameter TOTAL_SEC = 17'd100;//100x5x100ns
parameter CNT_20US = 11'd1 ;//20ns,位选信号切换间隔
parameter CYCLE = 20 ;//20ns,模拟时钟
reg clk ;//时钟寄存器
reg rst_n;//复位寄存器
wire [5:0] seg_sel ;//位选信号
wire [7:0] seg_ment;//段选信号
always#(CYCLE/2) clk = ~clk;//模拟时钟信号
//初始化
initial begin
clk = 1'b0 ;//时钟初始化为0
rst_n = 1'b0 ;//复位信号初始化为0
#(CYCLE) ;//延迟一个周期
rst_n = 1'b1 ;//复位信号置1
#(MAX_NUM * TOTAL_SEC * CYCLE);//延迟一段时间
$stop ;//停止
end
//实例化数字时钟顶层模块
digital_clock#(.MAX_NUM(MAX_NUM) ,
.TOTAL_SEC(TOTAL_SEC),
.CNT_20US(CNT_20US))
u_digital_clock(
.clk (clk) ,//时钟
.rst_n (rst_n) ,//复位信号
.seg_sel (seg_sel),//数码管位选信号
.seg_ment(seg_ment)//数码管段选信号
);
endmodule
7.2 仿真结果
八、管脚信息
元件 | 管脚 |
---|---|
SEL0 | A4 |
SEL1 | B4 |
SEL2 | A3 |
SEL3 | B3 |
SEL4 | A2 |
SEL5 | B1 |
DIG0 | B7 |
DIG1 | A8 |
DIG2 | A6 |
DIG3 | B5 |
DIG4 | B6 |
DIG5 | A7 |
DIG6 | B8 |
DIG7 | A5 |
CLOCK(时钟) | E1 |
KEY1 | E15 |
九、运行效果
数码管电子时钟
总结
电子时钟是在数码管动态显示实验基础上开发的项目,只要你熟悉数码管动态显示项目流程,在开发电子时钟的时候也会游刃有余。以上就是数码管电子时钟的全部内容,后期将推出什么是ip课程。敬请期待,谢谢你的观看!