一丶数码管介绍
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个计数器,相对复杂一点,但是计数值直接就是时、分、秒的个位和十位值,不需要除法器进行取余、取整操作,使用的触发器资源略多,但节省组合逻辑资源。
因为本文是作为作者stark-lin: 数码管电子时钟的拓展,原文使用了方法1,我们这里选择方法3
最后实现上面图片的效果,由于开发板上的数码管没有冒号(:),所以我们用小数点代替
三丶系统框图
计时器模块:我们把计时器模块细分了6个计时器,分别计时整个时间的小时的十位和个位,分钟的十位和个位,秒的十位和个位,最后将6个计时器拼接成一个dout_time输出给数码管驱动。与stark-lin
的方法1不同,我们不需要对计数器做运算,只需要在数码管驱动中取出对应数码管的数字
(对应dout_time[x:y]的第x到y位,比如我要取出小时的十位,由于小时的十位最大能计时到2,换算成2进制数就是2位的,因为一天24小时嘛,所以应该对应dout_time[19:18])
数码管驱动:对dout_time数值进行译码,产生驱动数码管动态显示数字的位选信号和段选信号。
四丶模块调用
五丶模块原理图
六丶工程源码
1.计数器模块
counter.v:
module counter (
input wire clk ,
input wire rst_n ,
output reg [19:0] dout_time //输出时间 HH:MM:SS
);
//计数器
reg [25:0] cnt ;
wire add_cnt;
wire end_cnt;
//S计时器
//个位 (0~9)
reg [3:0] cnt_s_bit;
wire add_cnt_s_bit;
wire end_cnt_s_bit;
//十位 (0~5)
reg [2:0] cnt_s_ten;
wire add_cnt_s_ten;
wire end_cnt_s_ten;
//M计时器
//个位 (0~9)
reg [3:0] cnt_m_bit;
wire add_cnt_m_bit;
wire end_cnt_m_bit;
//十位 (0~5)
reg [2:0] cnt_m_ten;
wire add_cnt_m_ten;
wire end_cnt_m_ten;
//H计时器
//个位 (0~9)
reg [3:0] cnt_h_bit;
wire add_cnt_h_bit;
wire end_cnt_h_bit;
//十位 (0~2)
reg [1:0] cnt_h_ten;
wire add_cnt_h_ten;
wire end_cnt_h_ten;
reg [3:0] flag;
parameter MAX_CNT=26'd50_000_000;
//计数器
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt<=0;
end
else if (add_cnt) begin
if (end_cnt) begin
cnt<=0;
end
else
cnt<=cnt+1;
end
end
assign add_cnt=1'b1;
assign end_cnt=add_cnt&&cnt==MAX_CNT-1;
//秒计时器---个位(0~9)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_s_bit<=0;
end
else if (add_cnt_s_bit) begin
if (end_cnt_s_bit) begin
cnt_s_bit<=0;
end
else
cnt_s_bit<=cnt_s_bit+1;
end
end
assign add_cnt_s_bit=end_cnt;
assign end_cnt_s_bit=add_cnt_s_bit&&cnt_s_bit==9;
//秒计时器---十位(0~5)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_s_ten<=0;
end
else if (add_cnt_s_ten) begin
if (end_cnt_s_ten) begin
cnt_s_ten<=0;
end
else
cnt_s_ten<=cnt_s_ten+1;
end
end
assign add_cnt_s_ten=end_cnt_s_bit;
assign end_cnt_s_ten=add_cnt_s_ten&&cnt_s_ten==5;
//分计时器---个位(0~9)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_m_bit<=9;
end
else if (add_cnt_m_bit) begin
if (end_cnt_m_bit) begin
cnt_m_bit<=0;
end
else
cnt_m_bit<=cnt_m_bit+1;
end
end
assign add_cnt_m_bit=end_cnt_s_ten;
assign end_cnt_m_bit=add_cnt_m_bit&&cnt_m_bit==9;
//分计时器---十位(0~5)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_m_ten<=5;
end
else if (add_cnt_m_ten) begin
if (end_cnt_m_ten) begin
cnt_m_ten<=0;
end
else
cnt_m_ten<=cnt_m_ten+1;
end
end
assign add_cnt_m_ten=end_cnt_m_bit;
assign end_cnt_m_ten=add_cnt_m_ten&&cnt_m_ten==5;
//时计时器---个位(0~9)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_h_bit<=3;
end
else if (add_cnt_h_bit) begin
if (end_cnt_h_bit) begin
cnt_h_bit<=0;
end
else
cnt_h_bit<=cnt_h_bit+1;
end
end
assign add_cnt_h_bit=end_cnt_m_ten;
assign end_cnt_h_bit=add_cnt_h_bit&&cnt_h_bit==flag;
//时计时器---十位(0~2)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_h_ten<=2;
end
else if (add_cnt_h_ten) begin
if (end_cnt_h_ten) begin
cnt_h_ten<=0;
end
else
cnt_h_ten<=cnt_h_ten+1;
end
end
assign add_cnt_h_ten=end_cnt_h_bit;
assign end_cnt_h_ten=add_cnt_h_ten&&cnt_h_ten==2;
//判断小时计时器十位是否记到 2
always @(*) begin
if (cnt_h_ten==2) begin
flag=4'd3;
end
else
flag=4'd9;
end
//dout_time输出
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dout_time<=20'b0;
end
else
dout_time<={cnt_h_ten,cnt_h_bit,cnt_m_ten,cnt_m_bit,cnt_s_ten,cnt_s_bit}; //拼接成 HH:MM:SS
end
endmodule //counter
2.数码管驱动模块
seg_driver:
module seg_driver (
input wire clk,
input wire rst_n,
input wire [19:0] dout_time,
output reg [5:0] sel,
output reg [7:0] seg
);
reg [3:0] seg_flag;
reg dot; //小数点 用来显示 HH.MM.SS 这样的格式
//10ms计时器---用来切换数码管位选,以达到轮流显示时间的各位(肉眼可以看到动态的时间计数)
reg [15:0] cnt;
wire add_cnt;
wire end_cnt;
parameter MAX_CNT =50_000 ,
ZERO =7'b100_0000,
ONE =7'b111_1001,
TWO =7'b010_0100,
THREE =7'b011_0000,
FOUR =7'b001_1001,
FIVE =7'b001_0010,
SIX =7'b000_0010,
SEVEN =7'b111_1000,
EIGHT =7'b000_0000,
NINE =7'b001_0000;
//计时器
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt<=0;
end
else if(add_cnt) begin
if (end_cnt) begin
cnt<=0;
end
else
cnt<=cnt+1;
end
end
assign add_cnt=1'b1;
assign end_cnt=add_cnt&&cnt==MAX_CNT-1;
//切换数码管位选
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sel<=6'b111_110;
end
else if(cnt==MAX_CNT-1) begin
sel<={sel[4:0],sel[5]};
end
end
//切换数码管段选
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
seg_flag<=0;
end
else begin
case (sel)
6'b111_110: begin seg_flag<=dout_time[19:18]; dot<=1'b1;end //小时 十位
6'b111_101: begin seg_flag<=dout_time[17:14]; dot<=1'b0;end //小时 个位
6'b111_011: begin seg_flag<=dout_time[13:11]; dot<=1'b1;end //分钟 十位
6'b110_111: begin seg_flag<=dout_time[10:7]; dot<=1'b0;end //分钟 个位
6'b101_111: begin seg_flag<=dout_time[6:4]; dot<=1'b1;end //秒 十位
6'b011_111: begin seg_flag<=dout_time[3:0]; dot<=1'b1;end //秒 个位
default :seg_flag<=0;
endcase
end
end
//段选译码
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
seg<=8'b1111_1111;
end
else begin
case (seg_flag)
0: seg<={dot,ZERO} ;
1: seg<={dot,ONE} ;
2: seg<={dot,TWO} ;
3: seg<={dot,THREE} ;
4: seg<={dot,FOUR} ;
5: seg<={dot,FIVE} ;
6: seg<={dot,SIX} ;
7: seg<={dot,SEVEN} ;
8: seg<={dot,EIGHT} ;
9: seg<={dot,NINE} ;
default: seg<=8'b1111_1111;
endcase
end
end
endmodule //seg_driver
3.顶层模块
top.v:
module top (
input wire clk , //系统时钟
input wire rst_n , //复位信号
output wire [5:0] sel , //数码管位选
output wire [7:0] seg //数码管段选
);
wire [19:0] dout_time;
//例化计时模块
counter u_counter(
.clk (clk) ,
.rst_n (rst_n) ,
.dout_time (dout_time) //输出时间 HH:MM:SS
);
//例化数码管驱动
seg_driver u_seg_driver(
.clk (clk) ,
.rst_n (rst_n) ,
.sel (sel) ,
.seg (seg) ,
.dout_time (dout_time)
);
endmodule //top
七丶仿真测试
1.TestBench
`timescale 1ns/1ns
module tb_top();
reg clk;
reg rst_n;
wire [5:0] sel;
wire [7:0] seg;
top u_top(
.clk (clk) ,
.rst_n (rst_n) ,
.sel (sel) ,
.seg (seg)
);
defparam u_top.u_counter.MAX_CNT=2;
defparam u_top.u_seg_driver.MAX_CNT=2;
always #10 clk=~clk;
initial begin
clk=1;
rst_n=1;
#100;
rst_n=0;
#100;
rst_n=1;
#200000;
$stop;
end
endmodule
2.仿真结果
八丶管脚信息
九丶上板验证
数码管电子时钟文章来源:https://www.toymoban.com/news/detail-473806.html
十丶源码
https://github.com/xuranww/digital_clock.git文章来源地址https://www.toymoban.com/news/detail-473806.html
到了这里,关于【FPGA】数码管电子时钟的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!