赞
踩
本文部分代码参考的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]}]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。