16-流水灯
1.流水灯
利用板载的LED灯进行流水灯的设计,让LED灯依次进行点亮,像流水一样,原理就是依次控制LED灯的IO口的高低电平的变化,让LED灯一次
2.FPGA设计
2.1 模块框图和波形
- 本次的实验是让 led 灯依次闪亮的间隔为0.5s,也就是让 led 灯每次只亮一个,每次亮的时间为 0.5s,这样就速度就比较快了,更像“流水”的效果且肉眼还能够分辨出
- 依次点亮板载的四个LED灯,实现流水灯的效果,两个灯之间的间隔为0.5s,LED灯一次点亮持续时间为0.5s
- 板载LED灯低电平时点亮
- 每个灯持续亮0.5s,需要使用计数器,计数个数为25000_0000,最大计数值为24999_999
- 声明一个cnt变量进行计数
- 声明一个cnt_flag产生脉冲信号,计数到最大值-1的时候产生一个周期的脉冲信号
- led_out信号:当检测到cnt_flag信号为高电平的时候,就点亮一个灯,点亮灯的顺序:4'1110,4'1101,4'1011,4'0111,每经历一个脉冲就左移一位,但是左移是用0进行填充的,也就是说4'1110,左移变为4'1100,这样就是点亮两个灯,如何进行操作?用中间变量进行表示4'0001,4'0010'4'0100,4'1000,输出变量对其进行取反即可
- 采用移位操作会出现问题,左移在右边进行补零,会导致每次多亮一个灯
- 新加一个变量,表示输出信号的相反值
2.2 RTL
module water_led
#(
parameter CNT_MAX = 24_999_999
)
(
input wire sys_clk,
input wire sys_rst_n,
output reg [3:0] led_out
);
reg [24:0] cnt;
reg cnt_flag;
reg [3:0] led_out_reg;
// cnt:计数500ms
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'd0;
else if (cnt == CNT_MAX)
cnt <= 25'd0;
else
cnt <= cnt + 1'b1;
//cnt_flag:计数器计数满 500ms 标志信号
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if(cnt_flag == CNT_MAX-1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
// led_out_reg
always @ (posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out_reg <= 4'b0001;
else if(led_out_reg == 4'b1000 && cnt_flag == 1'b1) // 如果不加这个条件,上板之后经过一级流水,灯全部熄灭
led_out_reg <= 4'b0001;
else
led_out_reg <= led_out_reg << 1;
assign led_out = ~led_out_reg;
endmodule
2.3 Testbench
`timescale 1ns/1ns
module tb_water_led();
reg sys_clk;
reg sys_rst_n;
wire [3:0] led_out;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20;
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
water_led
(
.CNT_MAX (25'd24)
)
water_led_inst
(
.sys_clk (sys_clk)
.sys_rst_n (sys_rst_n)
.led_out (led_out)
)
endmodule
3.移位操作
假设被操作数是a[7:0] 8bit数据
// 逻辑右移两位
b = a >> 2'd2;
b = {2'b00,a[7:2]} ; // 用拼接符也可以表示移位操作
// 逻辑左移两位
b = a << 2'd2;
b = {a[5:0],2'b00};