一、设计任务
设计一个能进行分钟计时和秒钟计时的时钟系统,并将计时结果用数码管显示。
二、时钟分频模块
模块设计思路:
- 使用计数的方法,由50MHz系统时钟产生1Hz的分频时钟clk_div。
- 使用延时打拍的方法,采集分频时钟的上升沿,得到1s的控制信号,该信号用来控制秒计时周期。
V e r i l o g 代码 : Verilog代码: Verilog代码:
//时钟分频模块
module clk_div(
input clk ,
input rstn ,
output clk_up
);
parameter N=26'd2500_0000 ;//1s/20ns/2
reg [25:0] cnt;
reg clk_r;
reg clk_div;
assign clk_up=~clk_r&&clk_div;
//计数分频
always@(posedge clk or negedge rstn)begin
if(!rstn)begin
cnt<='b0;
clk_div<='b0;
end
else if(cnt<N)begin
cnt<=cnt+1'b1;
end
else begin
cnt<='b0;
clk_div=~clk_div;
end
end
//捕获上升沿生成控制信号
always@(posedge clk or negedge rstn)begin
if(!rstn)
clk_r<='b0;
else
clk_r<=clk_div;
end
endmodule
三、秒钟计时模块
模块设计思路:
- 秒计时的个位,在1Hz控制信号下自增,计到9后向十位进1。
- 整体计到59后,产生进位co,来控制分计时计数。
V e r i l o g 代码 : Verilog代码: Verilog代码:
//秒钟计时模块
module cnt60_s(
input clk ,
input cin ,
input rstn ,
output reg co ,//进位
output reg [3:0] s_single ,
output reg [3:0] s_ten
);
//秒计数
always@(posedge clk or negedge rstn)begin
if(!rstn)begin
co<=1'b0;
s_single<='b0;
s_ten<='b0;
end
else if(cin)begin
if(s_single==4'd9)begin
s_single<='b0;
if(s_ten==4'd5)begin
s_ten<='b0;
co<=1'b1;
end
else begin
s_ten<=s_ten+1'b1;
co<=1'b0;
end
end
else begin
s_single<=s_single+1'b1;
co<=1'b0;
end
end
else begin
s_single<=s_single;
s_ten<=s_ten;
end
end
endmodule
四、分钟计时模块
模块设计思路:
- 计数原理同秒计时。
- 延时打两拍采集秒计时进位信号的上升沿,来对分计时进行控制。采上升沿是因为秒计时的进位信号持续时间会很长,如果采用电平控制,那么在co持续的这段时间内,分计数器会一直计数,这与设计要求不符。
V e r i l o g 代码 : Verilog代码: Verilog代码:
//分钟计时模块
module cnt60_m(
input clk ,
input rstn ,
input cin ,
output reg [3:0] m_single ,
output reg [3:0] m_ten
);
//检测上升沿
reg cin_r;
reg cin_r_r;
wire flag;
assign flag=~cin_r_r&cin_r;
always@(posedge clk or negedge rstn)begin
if(!rstn)begin
cin_r<='b0;
cin_r_r<='b0;
end
else begin
cin_r<=cin;
cin_r_r<=cin_r;
end
end
//分计数
always@(posedge clk or negedge rstn)begin
if(!rstn)begin
m_single<='b0;
m_ten<='b0;
end
else if(flag)begin
if(m_single==4'd9)begin
m_single<='b0;
if(m_ten==4'd5)begin
m_single<='b0;
m_ten<='b0;
end
else begin
m_ten<=m_ten+1'b1;
end
end
else begin
m_single<=m_single+1'b1;
end
end
else begin
m_single<=m_single;
m_ten<=m_ten;
end
end
endmodule
五、数码管显示模块
- 数码管是一种常用的显示器件,指定相应的段选和位选即可控制数码管的显示。
- 位选指定哪一个数码管点亮,而段选则指定数码管的哪一段点亮。
- 数码管分为共阳极和共阴极两种,共阳极数码管是指将数码管各段发光二极管的阳极连接到一起,赋低电平即可点亮;共阴极数码管是将数码管各段发光二极管的阴极连接到一起,点亮则需要赋高电平,两种数码管如下图所示。
- 本次实验使用的是共阳极数码管,需要赋各段低电平,同时段选信号也是低电平有效。
模块设计思路:
- 以一定的扫描周期赋值位选和相应的段选,从程序的角度来讲是将各个数码管逐个点亮。但在扫描速度很快时,人眼难以分辨,看起来就像同时点亮一样。经了解,代码中设置的扫描周期是较为合适的值,读者自行可尝试改变扫描周期,观察实验现象。
- 使用一个函数来将要显示的数据译码成对应的段选。
V e r i l o g 代码 : Verilog代码: Verilog代码:
//数码管显示模块
module seg(
input clk ,
input rstn ,
input [3:0] s_single ,
input [3:0] s_ten ,
input [3:0] m_single ,
input [3:0] m_ten ,
output reg [3:0] csn ,
output reg [6:0] seg_data
);
parameter SCAN_FREQ=200;
parameter CLK_FREQ=50000000;
parameter SCAN_COUNT=CLK_FREQ/(SCAN_FREQ*6)-1; //计算扫描计数器最大值
reg [20:0] scan_cnt;
reg [2:0] csn_cnt;
//段选扫描计数
always@(posedge clk or negedge rstn)begin
if(!rstn)begin
scan_cnt<='b0;
csn_cnt<='b0;
end
else if(scan_cnt==SCAN_COUNT)begin
scan_cnt<='b0;
if(csn_cnt==3'd3)begin
csn_cnt<=3'd0;
end
else begin
csn_cnt<=csn_cnt+1'b1;
end
end
else begin
scan_cnt<=scan_cnt+1'b1;
end
end
//以扫描周期赋值段选和位选
always@(posedge clk or negedge rstn)begin
if(!rstn)begin
csn<=4'b1111;
seg_data<=translate(0);
end
else begin
case(csn_cnt)
3'd0:begin
csn<=4'b1110;
seg_data<=translate(s_single);
end
3'd1:begin
csn<=4'b1101;
seg_data<=translate(s_ten);
end
3'd2:begin
csn<=4'b1011;
seg_data<=translate(m_single);
end
3'd3:begin
csn<=4'b0111;
seg_data<=translate(m_ten);
end
endcase
end
end
//译码函数
function [6:0] translate;
input [3:0] data;
begin
case(data)
4'd0:translate=7'b000_0001;//高位到低位分别是数码管的a~g
4'd1:translate=7'b100_1111;
4'd2:translate=7'b001_0010;
4'd3:translate=7'b000_0110;
4'd4:translate=7'b100_1100;
4'd5:translate=7'b010_0100;
4'd6:translate=7'b010_0000;
4'd7:translate=7'b000_1111;
4'd8:translate=7'b000_0000;
4'd9:translate=7'b000_0100;
endcase
end
endfunction
endmodule
六、顶层模块
V e r i l o g 代码 : Verilog代码: Verilog代码:
//顶层模块
module sys_top(
input clk,
input rstn,
output [3:0] csn,
output [6:0] seg_data
);
wire clk_up;
wire co_s;
wire [3:0] s_single;
wire [3:0] s_ten ;
wire [3:0] m_single;
wire [3:0] m_ten ;
clk_div clk_div_u(
.clk (clk) ,
.rstn (rstn) ,
.clk_up (clk_up)
);
cnt60_s cnt60_s_u(
. clk (clk) ,
. cin (clk_up) ,
. rstn (rstn) ,
. co (co_s) ,//进位
. s_single (s_single) ,
. s_ten (s_ten)
);
cnt60_m cnt60_m_u(
. clk (clk) ,
. rstn (rstn) ,
. cin (co_s) ,
. m_single (m_single) ,
. m_ten (m_ten)
);
seg seg_u(
. clk (clk) ,
. rstn (rstn) ,
. s_single (s_single) ,
. s_ten (s_ten) ,
. m_single (m_single) ,
. m_ten (m_ten) ,
. csn (csn) ,
. seg_data (seg_data)
);
endmodule