EDA设计(verilog)—— 七段管+字符串位移

问题描述:在8 个7 段管上显示HELLO_ _ _(可以显示下划线或不亮也可),每隔1 秒钟,字符序列左移或右移一个七段管的位置。系统外部时钟50 MHz。左/右移位可以通过一个波动开关sw0 来控制。

1、题目解析:

分析1:如何实现这个功能?
     我们需要8个7为的寄存器(或者一个7*8 = 56 位的寄存器)来保留"hello_ _ _"这个字符串,每秒对这8个寄存器里的值做出改变,让这八个七段管的里的值发生阻塞赋值,达到移动目的(如果是7*8的寄存器做一个循环左移/右移)。

分析2:如果实现每一秒进行一次变化?
     根据系统外部时钟为50 MHz,我们可以写一个计数器,时钟每变化一个周期就记录一次,一直数到50 M时输出一个电平。或者每数到25M改变一个让一个输出寄存器里的值发生反转,那么对于这个寄存器,他的周期就是1Hz了。

分析3"hello_ _ _"字符串怎么输入?
     最简单的我们可以定义一个56位的输入,把值一股脑的全部塞进去。当然现实生活中我们往往不会这么高 ,而使用一个7位的寄存器存放要输入的变量,加上一个3(8个7段管 2*3 =8)位的寄存器来指定输入要放入的具体位置,有点类似计算机中的数据总线和地址总线。这样我们要赋值8次达到将字符串写入寄存器。

2、综合代码:

module show_hello(
	// 基本控制
	input clk50, // clk50 为输入50Mhz系统外部时钟
	input reset, // reset 为复位信号
	input sw0,   // 控制方向变量, 为正值的时候向右变化,为负值的时候向左变化
	
	// 输入控制
	input ifinput,  // 是否正在输入, 定义为低位时表示正在输入
	input[2:0] inp, // input_position 输入的管子位置 2^3 = 8 从0-7 一共表示8个管子 
	input[6:0] inv, // input_value 输入的字符型
	
	// 输出控制
	output reg[55:0] out, // 1-8 一共8个七段管的输出位置(56位)
	output reg err      // 错误标志位
);

wire clk1;      // 接收内部时钟产生的1Hz信号

divclk1hz clock1(
	.reset(ifinput),  // 如果用户进行了输入,那么我们就重新开始计数
	.clk50(clk50),    // 传入的基本信号,用于做分频
	.clk1(clk1)       // 产生的1Hz信号
);



always @(posedge clk1 or negedge ifinput) begin
	if (!ifinput) begin  // 如果是在输入状态下就进行寄存的复制
		case(inp)    // 根据用户要求改变的位置进行赋值。
			 0: out[6:0]   <= inv;
			 1: out[13:7]  <= inv;
			 2: out[20:14] <= inv;
			 3: out[27:21] <= inv;
			 4: out[34:28] <= inv;
			 5: out[41:35] <= inv;
			 6: out[48:42] <= inv;
			 7: out[55:49] <= inv;
		 endcase
	end else begin
		err=0;
		if(sw0) begin  // 如果没有输入,并且sw0 是高电平
			out <= {out[6:0], out[55:7]};// 就进行向右移动
		end else if(!sw0) begin     // 如果没有输入,并且sw0 是低电平
			out <= {out[48:0],out[55:49]};// 就进行向左移动
		end else begin
			err=1;
		end 
	end
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
// 注意上面的时间,要 设置最小的时间单位应该为 1/f * 0.5 = 1 / 50M * 0.5 = 10^-8s = 10ns
module show_hello_vlg_tst();

reg clk50;
reg ifinput;
reg [2:0] inp;
reg [6:0] inv;
reg reset;
reg sw0;                                              
wire [55:0]  out;
wire err;

show_hello i1 (  
	.clk50(clk50),
	.ifinput(ifinput),
	.inp(inp),
	.inv(inv),
	.out(out),
	.reset(reset),
	.sw0(sw0),
	.err(err)
);


initial begin
	// 初始化方向 和 时钟
	sw0 = 1; clk50 = 0;
	// 给每一个七段管子赋值,这里为了让大家看的清楚,将8个管子的值赋值为了"88 88888","hello__"需要跟改为
	/**
		1001000
		0110000
		1110001
		1110001
		1100010
		1110111
		1110111
		1110111
	*/
	#00; ifinput = 1; inp = 0; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 1; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 2; inv=7'b1111111;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 3; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 4; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 5; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 6; inv=7'b0000000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 7; inv=7'b0000000;
	#10 ifinput = 0; #1 ifinput = 1;  // 赋值过程结束
	
	// 经过900 个单位时间的等待,观察输出的右移过程
	#899 sw0 = 0;
	
	// 再经过600 个单位时间的等待,暂停运行
	#600 $stop;
	
end

always begin
#1 clk50 =~ clk50;
end
endmodule

4、效果展示

在这里插入图片描述

我们可以清楚的看见,开始时sw0处于高电位, 输出的高电平从高位移动向低位(七段管上文字向右移动),当 sw0 的值从高电平转换为低电平时,输出的高电平从低位移动向高位(七段管上的文字向左移动)。

4.5 测试代码2

如果你觉得没有输出字符,不怎么方便观察,可以使用以下测试代码。主要思想是使用函数来完成将七段管的数字转换为字符的工作,再由监视器监听七段管寄存器的输出,每当其发生移动就把输出的值显示在Transcript里。

`timescale 10 ns/ 1 ns
module show_hello_vlg_tst();

reg clk50;
reg ifinput;
reg [2:0] inp;
reg [6:0] inv;
reg reset;
reg sw0;                                              
wire [55:0]  out;
wire err;

show_hello i1 (  
	.clk50(clk50),
	.ifinput(ifinput),
	.inp(inp),
	.inv(inv),
	.out(out),
	.reset(reset),
	.sw0(sw0),
	.err(err)
);


function[7:0] showChar;
	input[6:0] _in;
	case(_in)
		7'b1001000:showChar ="H";
		7'b0110000:showChar ="E";
		7'b1100010:showChar ="O";
		7'b1110001:showChar ="L";
		default:   showChar ="_";
	endcase
endfunction



initial begin
	// 初始化方向 和 时钟
	sw0 = 1; clk50 = 0;
	// 给每一个七段管子赋值

	#00; ifinput = 1; inp = 7; inv=7'b1001000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 6; inv=7'b0110000;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 5; inv=7'b1110001;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 4; inv=7'b1110001;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 3; inv=7'b1100010;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 2; inv=7'b1110111;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 1; inv=7'b1110111;
	#10 ifinput = 0;
	#10; ifinput = 1; inp = 0; inv=7'b1110111;
	#10 ifinput = 0; #1 ifinput = 1;  // 赋值过程结束
	
	// 经过1800 个单位时间的等待,观察输出的右移过程
	#1799 sw0 = 0; #1;
	
	// 再经过1600 个单位时间的等待,暂停运行
	#1600 $stop;
	
end

initial $monitor(out,,,"%c %c %c %c %c %c %c %c", 
	showChar(out[55:49]),
	showChar(out[48:42]),
	showChar(out[41:35]),
	showChar(out[34:28]),
	showChar(out[27:21]),
	showChar(out[20:14]),
	showChar(out[13: 7]),
	showChar(out[6:  0])	
);  

always begin
#1 clk50 =~ clk50;
end

endmodule


在这里插入图片描述

5、还可以改进的地方

现在对于我们来说,要向寄存器中写入“hello_ _ _" 需要用户知道七段管的表式方法才行,但我们常用的是 ASCLL 码或者是数值,所以我们可以在写一个转化的模块,将用户传来的数值或者ascll码转化为7短管的显示数组,来方便我们的赋值工作。
参考如下:

在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值