题目描述:计时器:在 6 个七段管上分别显示 小时(0-23 或 11)、分(0-59)、秒(0-59),各占 2 个管。外部时钟 50Mhz。可以用按键来产生一个复位信号 key,当按键按下立刻(异步)将时间复位成 0 小时、0 分、0 秒重新开始计时。
1、题目解析:
分析1:如何实现这个功能?
我们需要6个7为的寄存器(或者一个7*6 = 42 位的寄存器)来保留每一个时刻的时间,“秒”和“分”的4个计数器的显示分别对应着一个60进制的计数模块,而“时”则对应着一个24位的计数器。
分析2:如果实现每一秒进行一次变化?
根据系统外部时钟为50 MHz,我们可以写一个计数器,时钟每变化一个周期就记录一次,一直数到50 M时输出一个电平。或者每数到 25M 改变一个让一个输出寄存器里的值发生反转,那么对于这个寄存器,他的周期就是 1Hz 了。
分析3:如何实现 类似00:00:59
到 00:01:00
的变化?
要实现这个逻辑,需要将“秒”对应的进位信号作为“分”的输入信号,当有进位信号的时候才能将分进行计数。“分” 和 “时” 之间也要有类似的进位关系。
2、综合代码
module timer(
// 控制输入
input reset, // 重置信号, 当其值为低电平时,发生重置
input set, // 置位标致位,当输入高电平时进行输入
input clk50, // 时钟信号
input[1:0] setp, // set_position 选择要输入的位置 00,表示秒位, 01 表示分位, 10表示时位,其余报错
input[5:0] setv, // set_value 输入的数值
// 输出的 6个七段管
output reg[6:0] second1,
output reg[6:0] second2,
output reg[6:0] munite1,
output reg[6:0] munite2,
output reg[6:0] hous1,
output reg[6:0] hous2
);
wire set1, set2, set3; // 对应每一个管子的设置
wire car1, car2, carr3; // 对应每一个管子的输出进位标志
wire clk1;
// 接收每个计数器的计数器值
wire[3:0] num1,num2,num3,num4,num5,num6;
// 接受每一个七段管的输出值
wire[6:0] tub1,tub2,tub3,tub4,tub5,tub6;
divclk1hz clock1(reset,clk50,clk1);
// 定义秒
counter60 c60_1(
.clk(clk1),
.in(1),
.reset(reset),
.set(set1),
.inv(setv),
.carry(car1),
.out1(num1),
.out2(num2)
);
// 定义分
counter60 c60_2(
.clk(clk1),
.in(car1),
.reset(reset),
.set(set2),
.inv(setv),
.carry(car2),
.out1(num3),
.out2(num4)
);
// 定义时
counter24 c24_2(
.clk(clk1),
.in(car2),
.reset(reset),
.set(set3),
.inv(setv),
.carry(car3),
.out1(num5),
.out2(num6)
);
// 将计数器输出的数值信号转化为七段管的亮暗状态
int2tube i1( .in(num1), .out(tub1) );
int2tube i2( .in(num2), .out(tub2) );
int2tube i3( .in(num3), .out(tub3) );
int2tube i4( .in(num4), .out(tub4) );
int2tube i5( .in(num5), .out(tub5) );
int2tube i6( .in(num6), .out(tub6) );
// 以下代码的作业就是链接各个模块
// 这里加入reset 的检测是为了异步处理
always @(posedge clk1 or negedge reset) begin
if(!reset) begin
second1 <= 8'b0000001;
second2 <= 8'b0000001;
munite1 <= 8'b0000001;
munite2 <= 8'b0000001;
hous1 <= 8'b0000001;
hous2 <= 8'b0000001;
end else begin
second1 <= tub1;
second2 <= tub2;
munite1 <= tub3;
munite2 <= tub4;
hous1 <= tub5;
hous2 <= tub6;
end
end
// 设置置位信号
assign set1 = (set==1)?((setp == 0)?1:0):0;
assign set2 = (set==1)?((setp == 1)?1:0):0;
assign set3 = (set==1)?((setp == 2)?1:0):0;
endmodule
/** 60位计数器 */
module counter60(
input clk, // 时钟信号
input in, // 输入信号
input reset, // 重置信号, 当其值为低电平时,发生重置
input set, // 置位标致位,当输入高电平时进行输入
input[5:0] inv,// 置位信号
output reg carry, // 进位信号
output reg[3:0] out1, // 输出十位
output reg[3:0] out2 // 输出个位
);
reg[5:0] inner; // 内置计数器
always @(posedge clk or posedge set or negedge reset) begin
if(!reset) begin // 检测是否要进行重置 低电平有效
inner <= 0;
carry <= 0;
out1 <= 0;
out2 <= 0;
end else if(set) begin // 检测是否要进行置数 高电平有效
inner <= inv;
out1 <= inv / 10;
out2 <= inv % 10;
end else begin
if(in) begin // 没有重置和置数时,in为高电平时进行加数
if(inner < 58) begin // 如果没到58就继续数
inner <= inner + 1;
end else if(inner == 58)begin // 到58结束时输出进位信号,这样在59结束时(新的1秒开始)就可以
carry <= 1; // 并且进行进位
inner <= inner + 1;
end else begin // 如果到了59, 那么下一时刻就要进行从 0 开始数
inner <= 0;
carry <= 0;
end
end
out1 <= inner / 10;
out2 <= inner % 10;
end
end
endmodule
/** 24位计数器 */
module counter24(
input clk, // 时钟信号
input in, // 输入信号
input reset, // 重置信号, 当其值为低电平时,发生重置
input set, // 置位标致位,当输入高电平时进行输入
input[5:0] inv,// 置位信号
output reg carry, // 进位信号
output reg[3:0] out1, // 输出十位
output reg[3:0] out2 // 输出个位
);
reg[4:0] inner; // 内置计数器
always @(posedge clk or posedge set or negedge reset) begin
if(!reset) begin // 检测是否要进行重置 低电平有效
inner <= 0;
carry <= 0;
out1 <= 0;
out2 <= 0;
end else if(set) begin // 检测是否要进行置数 高电平有效
inner <= inv;
out1 <= inv / 10;
out2 <= inner % 10;
end else begin
if(in) begin // 没有重置和置数时,in为高电平时进行加数
if(inner < 23) begin // 如果没到59就继续数
inner <= inner + 1;
carry <= 0;
end else begin // 如果到了59, 那么下一时刻就要进行从 0 开始数
inner <= 0;
carry <= 1; // 并且进行进位
end
end
out1 <= inner / 10;
out2 <= inner % 10;
end
end
endmodule
/** 将数值信号转化为七段管的亮暗状态 */
module int2tube(
input[3:0] in,
output reg[6:0] out
);
always @(in) begin
case (in)
0: out <= 8'b0000001;
1: out <= 8'b0011111;
2: out <= 8'b0010010;
3: out <= 8'b0000110;
4: out <= 8'b1001100;
5: out <= 8'b0100100;
6: out <= 8'b0100000;
7: out <= 8'b0001111;
8: out <= 8'b0000000;
9: out <= 8'b0000100;
endcase
end
endmodule
/** 分频模块,每数到25M改变一个让一个输出寄存器clk1里的值发生反转 */
module divclk1hz(reset,clk50,clk1);
input clk50,reset; //clk50 为输入50Mhz 信号,reset 为复位信号
output reg clk1; // 新产生的1hz 信号
integer i=0; //50Mhz 频率下,周期计数器
always@(posedge clk50) begin
if(!reset) begin
i=0;
clk1 = 0;
end else begin
if(i==30) begin i=0; clk1=~clk1; // 25000000 这里的i应该=25M,但是为了更方便的展示效果,我将i的值改为了30,这样七段管里的数值会改变的更快。
end
else i=i+1;
end
end
endmodule
3、测试代码
`timescale 10 ns/ 1 ns
module timer_vlg_tst();
reg clk50;
reg reset;
reg set;
reg [1:0] setp;
reg [5:0] setv;
wire [6:0] hous1;
wire [6:0] hous2;
wire [6:0] munite1;
wire [6:0] munite2;
wire [6:0] second1;
wire [6:0] second2;
timer i1 (
.clk50(clk50),
.hous1(hous1),
.hous2(hous2),
.munite1(munite1),
.munite2(munite2),
.reset(reset),
.second1(second1),
.second2(second2),
.set(set),
.setp(setp),
.setv(setv)
);
initial begin
// 初始化外部时钟
clk50 = 0;
// 重置一遍timer模块
reset = 1; #5 reset =0; #5 reset =1;
set = 0;
// 准备将秒位设置到55秒
setv = 33; setp = 0;
// 等待2100 个时间单位,观察时间变化
# 2100;
// 进行数值修改
set = 1;
// 修改完毕,结束修改信号
#1 set = 0;
// 运行900 时间单位,观察效果,进行复位
#900 reset = 0;
// 复位信号归位
#5 reset = 1;
// 运行900个单位以后停止运行
#900 $stop;
end
always begin
#1 clk50 =~ clk50;
end
endmodule
4、效果
异步复位信号的效果:可以看到在reset信号进入赋值以后,6个七段管的值都恢复到0000001
对应数值0
。
同步置数的效果:,当set型号进入高点平,改变所设置的时间的值(将“秒”数值置为33),0000110
对应数值为3。