本文部分代码参考的b站教学,记录下来仅方便期末考试的时候复习。
要想实现数字时钟,我们首先要明确几个问题:1.怎么形成以秒为单位的“输入”(这里并非输入,其实是一秒钟产生一次响应);2.处理时分秒的进位问题;3.将其显示在数码管上。理清这三个需求其实verilog的编写也就很明确了,即三个问题各对应三个模块。
首先解决第一个问题。为了模拟现实中的1s我们可以采用对周期信号进行计数的方式来实现,很容易就想到clk信号,而时钟信号是周期激励,显然我们不可能手动对它完成输入,那这时候我们就需要接到FPGA开发板上自带的时钟信号激励源上。以我手上的BASYS 3为例,自带100MHz的clk激励,也就是1ns产生一次clk信号,那我们就可以通过对clk计数,当counter达到100_000_000次时,时长就达到了1s。输出就用0和1区分
代码如下:
module clk_1s(
input clk,
input rstn,
output flag_1s
);
parameter num_1s = 100_000_000;
reg [26:0]cnt_1s;
always@(posedge clk or negedge rstn)
begin
if(!rstn) begin
cnt_1s <= 0;
end
else
begin
if(cnt_1s == num_1s) cnt_1s <= 0;
else cnt_1s <= cnt_1s+1;
end
end
assign flag_1s = (cnt_1s == num_1s) ? 1 : 0;
endmodule
第二个问题,处理时分秒进位问题其实和C语言是一样的用if else语句一一处理不同的情况就行了
输出是6个三位位宽的二进制数分别对应时分秒的十位和个位
代码如下:
module clock(
input clk,
input rstn,
input flag_1s,
output reg[3:0] data0,
output reg[3:0] data1,
output reg[3:0] data2,
output reg[3:0] data3,
output reg[3:0] data4,
output reg[3:0] data5
);
reg [5:0] second;
reg [5:0] minute;
reg [4:0] hour;
always@(posedge clk or negedge rstn)
begin
if(!rstn) begin
second <= 0;
end else begin
if(second==59&&flag_1s)begin
second <= 0;
end else if(flag_1s)begin
second = second + 1;
end else begin
second <= second;
end
end
end
always@(posedge clk or negedge rstn)
begin
if(!rstn) minute <= 0;
else
begin
if(minute==59&&second==59&&flag_1s) minute <= 0;
else if(second==59&&flag_1s) minute <= minute + 1;
else minute <= minute;
end
end
always@(posedge clk or negedge rstn)
begin
if(!rstn) hour <= 0;
else
begin
if(hour==23&&minute==59&&second==59&&flag_1s) hour<=0;
else if(minute==59&&second==59&&flag_1s) hour <= hour + 1;
else hour <= hour;
end
end
always@(posedge clk or negedge rstn)
begin
if(!rstn) begin
data0 <= 0;
data1 <= 0;
data2 <= 0;
data3 <= 0;
data4 <= 0;
data5 <= 0;
end else begin
data0 <= hour/10;
data1 <= hour%10;
data2 <= minute/10;
data3 <= minute%10;
data4 <= second/10;
data5 <= second%10;
end
end
endmodule
第三个问题:数码管的输出。要想解决这个问题首先要明确数码管点亮的逻辑是什么。
需要控制数码管的时间在人眼可滞留范围的同时保证数据不会更新,否则就会出现显示误差
直接贴图了
简而言之,就是一次性只能点亮一个数码管,而且是低电平点亮。想呈现出四个数码管同时亮的情况就得对clk信号进行分频,太快会让数码管扫描过快,出现显示上的出错,比如混亮,过亮,来不及变化等情况;太慢又会出现灯光过暗或者只有一个灯光在亮的情况。我把频率分到了1kHz
分频的代码如下:
reg[15:0]cnl;
reg clk1k;
always@(posedge clk or negedge rstn)
begin
if(!rstn)
begin
cnl<=0;
clk1k<=0;
end
else if(cnl>=49999)
begin
clk1k<=!clk1k;
cnl<=0;
end
else cnl<=cnl+1;
end
接下来就是具体数码管的显示处理,因为我们前面提到了有六个数字需要显示,再结合同时只能点亮一个数码管(只有四个数码管,所以其中有两个数据显示在led上),就想到需要六个状态去一一对应,改变状态就改变了数码管的选择。同时一个中间变量去承接每个状态需要输出的数字,再将数字用译码器变化成对应的编码去数码管上显示就好了,注意区别这里的数码管选择和数码管显示。
代码如下:
module digital(
input clk,
input rstn,
input [3:0]data0,
input [3:0]data1,
input [3:0]data2,
input [3:0]data3,
input [3:0]data4,
input [3:0]data5,
output reg[7:0]seg,
output reg[3:0]sel,
output reg[2:0]hours1,
output reg[2:0]hours2
);
reg[15:0]cnl;
reg clk1k;
always@(posedge clk or negedge rstn)//分频
begin
if(!rstn)
begin
cnl<=0;
clk1k<=0;
end
else if(cnl>=24999)
begin
clk1k<=!clk1k;
cnl<=0;
end
else cnl<=cnl+1;
end
reg [3:0]tub;//中间变量
reg [2:0]state;//状态变量
always@(posedge clk1k or negedge rstn)
begin
if(!rstn)
begin
tub<=0;
state<=0;
sel<=0;
hours1<=0;
hours2<=0;
end
else
begin
case(state)//每种状态的数码管选择,0111即第四个数码管,hours1和2显示在led上,并且在状态转换的同时,数据也更新一次
0:begin hours1<=data0;sel<=4'b1111;state<=1;end
1:begin hours2<=data1;sel<=4'b1111;state<=2;end
2:begin tub<=data2;sel<=4'b0111;state<=3;end
3:begin tub<=data3;sel<=4'b1011;state<=4;end
4:begin tub<=data4;sel<=4'b1101;state<=5;end
5:begin tub<=data5;sel<=4'b1110;state<=0;end
default:state<=0;
endcase
end
end
always@(*)//数码管数字显示
if(!rstn)
seg<=8'b1100_0000;
else
case(tub)
0:seg<=8'b1100_0000;
1:seg<=8'b1111_1001;
2:seg<=8'b1010_0100;
3:seg<=8'b1011_0000;
4:seg<=8'b1001_1001;
5:seg<=8'b1001_0010;
6:seg<=8'b1000_0010;
7:seg<=8'b1111_1000;
8:seg<=8'b1000_0000;
9:seg<=8'b1001_0000;
default:seg<=8'b1100_0000;
endcase
endmodule
最后的流程就是用一个顶层模块把三个模块穿起来就行了:
module top(
input clk,
input rstn,
output [7:0]seg,//输出用于数码管数字的显示
output [3:0]sel,//输出控制六个数码管的显示
output [2:0]hours1,//输出时的
output [2:0]hours2
);
wire flag_1s;
wire [3:0]data0;
wire [3:0]data1;
wire [3:0]data2;
wire [3:0]data3;
wire [3:0]data4;
wire [3:0]data5;
clk_1s clk_1s_u(
.clk(clk),
.rstn(rstn),
.flag_1s(flag_1s)
);
clock clock_u(
.clk(clk),
.rstn(rstn),
.flag_1s(flag_1s),
.data0(data0),
.data1(data1),
.data2(data2),
.data3(data3),
.data4(data4),
.data5(data5)
);
digital digital_u(
.clk(clk),
.rstn(rstn),
.data0(data0),
.data1(data1),
.data2(data2),
.data3(data3),
.data4(data4),
.data5(data5),
.seg(seg),
.sel(sel),
.hours1(hours1),
.hours2(hours2)
);
endmodule
下面贴上具体的电路图
约束文件的编写因为有数码管所以需要去查表进行约束,采用图像化约束会简单一些
这个是约束后的代码
set_property IOSTANDARD LVCMOS33 [get_ports {seg[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {sel[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports rstn]
set_property PACKAGE_PIN W5 [get_ports clk]
set_property PACKAGE_PIN R2 [get_ports rstn]
set_property PACKAGE_PIN U2 [get_ports {sel[0]}]
set_property PACKAGE_PIN U4 [get_ports {sel[1]}]
set_property PACKAGE_PIN V4 [get_ports {sel[2]}]
set_property PACKAGE_PIN W4 [get_ports {sel[3]}]
set_property PACKAGE_PIN V7 [get_ports {seg[7]}]
set_property PACKAGE_PIN W7 [get_ports {seg[0]}]
set_property PACKAGE_PIN W6 [get_ports {seg[1]}]
set_property PACKAGE_PIN U8 [get_ports {seg[2]}]
set_property PACKAGE_PIN V8 [get_ports {seg[3]}]
set_property PACKAGE_PIN U5 [get_ports {seg[4]}]
set_property PACKAGE_PIN V5 [get_ports {seg[5]}]
set_property PACKAGE_PIN U7 [get_ports {seg[6]}]
set_property PACKAGE_PIN P3 [get_ports {hours1[2]}]
set_property PACKAGE_PIN U3 [get_ports {hours1[1]}]
set_property PACKAGE_PIN W3 [get_ports {hours1[0]}]
set_property PACKAGE_PIN V3 [get_ports {hours2[2]}]
set_property PACKAGE_PIN V13 [get_ports {hours2[1]}]
set_property PACKAGE_PIN V14 [get_ports {hours2[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {hours1[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {hours1[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {hours1[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {hours2[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {hours2[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {hours2[2]}]