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

it2026-08-04  7

问题描述:在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短管的显示数组,来方便我们的赋值工作。 参考如下:

最新回复(0)