Descripción del título: Temporizador: muestra las horas (0-23 u 11), minutos (0-59) y segundos (0-59) en 6 tubos de siete segmentos, cada uno de los cuales ocupa 2 tubos. El reloj externo es de 50Mhz. Puede usar el botón para generar una tecla de señal de reinicio, cuando el botón se presiona inmediatamente (asincrónicamente), el tiempo se reinicia a 0 horas, 0 minutos y 0 segundos para reiniciar el tiempo.
1. Análisis de preguntas:
Análisis 1 : ¿Cómo realizar esta función?
Necesitamos 6 registros de 7 bits (o un registro de 7 * 6 = 42 bits) para retener el tiempo de cada momento La visualización de los 4 contadores de "segundos" y "minutos" corresponde a un contador hexadecimal de 60. Módulo y "hora" corresponde a un contador de 24 bits.
Análisis 2 : ¿Cómo lograr un cambio cada segundo?
De acuerdo con el reloj externo del sistema de 50 MHz, podemos escribir un contador, que registrará cada ciclo del cambio de reloj y generará un nivel al contar hasta 50 M. O cada vez que se cuenta 25M, el valor en un registro de salida se invierte. Entonces, para este registro, su ciclo es de 1Hz.
Análisis : ¿cómo lograr cambios similares 00:00:59
a los 00:01:00
?
Para realizar esta lógica, es necesario utilizar la señal de acarreo correspondiente a "segundo" como señal de entrada de "minuto", y los minutos pueden contarse cuando hay una señal de acarreo. También debería haber una relación de acarreo similar entre "minuto" y "hora".
2. Código completo
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. Código de prueba
`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. Efecto
El efecto de la señal de reinicio asíncrona : se puede ver que después de que la señal de reinicio ingresa a la asignación, los valores de los seis tubos de siete segmentos se restauran a los 0000001
valores correspondientes 0
.
El efecto de la configuración síncrona : cuando el modelo establecido ingresa al nivel de punto alto, cambie el valor del tiempo establecido (establezca el "segundo" valor en 33), y el 0000110
valor correspondiente es 3.