EDA设计(verilog)—— 七段管时钟

题目描述:计时器:在 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:5900: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。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_49736959/article/details/109247091