一、PWM
要实现PWM调呼吸灯,就要知道PWM怎么构成。
PWM是由计数器和比较器构成。通过改变计数器的大小,来实现呼吸灯的闪烁速度(详细的PWM说明可以去找其他博主的博客)。因此,示例代码如下。
parameter cnt_1us_max = 6'd49 ;
parameter cnt_1ms_max = 10'd999 ;
parameter cnt_1s_max = 10'd999 ;
reg [9:0] cnt_1s ;
reg [9:0] cnt_1ms ;
reg [5:0] cnt_1us ;
reg cnt_en ;//使能信号 到1s的时候使能
// 1us 计数器
always@(posedge sys_clk)
if(rst_n == 1'b0)
cnt_1us <= 6'd0;
else if(cnt_1us == cnt_1us_max)
cnt_1us <= 6'd0;
else
cnt_1us <= cnt_1us + 1;
// 1ms 计数器
always@(posedge sys_clk)
if(rst_n == 1'b0)
cnt_1ms <= 10'd0;
else if((cnt_1ms == cnt_1ms_max) && (cnt_1us == cnt_1us_max))
cnt_1ms <= 10'd0;
else if (cnt_1us == cnt_1us_max)
cnt_1ms <= cnt_1ms + 1;
else
cnt_1ms <= cnt_1ms ;
// 1s 计数器
always@(posedge sys_clk)
if(rst_n == 1'b0)
cnt_1s <= 10'd0;
else if((cnt_1s == cnt_1s_max) && (cnt_1ms == cnt_1ms_max) && (cnt_1us == cnt_1us_max))
cnt_1s <= 10'd0;
else if ((cnt_1ms == cnt_1ms_max) && (cnt_1us == cnt_1us_max))
cnt_1s <= cnt_1s + 1;
else
cnt_1s <= cnt_1s ;
//使能信号
always@(posedge sys_clk)
if (!rst_n)
cnt_en <= 0 ;
else if ((cnt_1s == cnt_1s_max) && (cnt_1ms == cnt_1ms_max) && (cnt_1us == cnt_1us_max))
cnt_en <= ~cnt_en ;
else
cnt_en <= cnt_en ;
//PWM
always@(posedge sys_clk)
if (!rst_n)
led_out <= 0;
else if ((cnt_1s >= cnt_1ms) && (cnt_en == 0))
led_out <= ~led_out;
else if ((cnt_1s < cnt_1ms) && (cnt_en == 1))
led_out <= ~led_out;
else
led_out <= led_out;
我这里定义了三个计数器,可以让现象更加的细腻。
在第一个周期时,只有1/10的周期,让LED保持点亮,每个周期增加1/10的LED保持带点亮的时间,在第10个周期时被完全点亮。同理,然后再依次减少1/10的周期时间,直至熄灭。然后重复。就实现了PWM调呼吸灯。
二、画visio图
三、写代码
module breath_led(
input wire sys_clk ,
input wire rst_n ,
output reg led_out
);
parameter cnt_1us_max = 6'd49 ; //最大值
parameter cnt_1ms_max = 10'd999 ; //最大值
parameter cnt_1s_max = 10'd999 ; //最大值
reg [9:0] cnt_1s ;
reg [9:0] cnt_1ms ;
reg [5:0] cnt_1us ;
reg cnt_en ;//使能信号 到1s的时候使能
// 1us 计数器
always@(posedge sys_clk)
if(rst_n == 1'b0)
cnt_1us <= 6'd0;
else if(cnt_1us == cnt_1us_max)
cnt_1us <= 6'd0;
else
cnt_1us <= cnt_1us + 1;
// 1ms 计数器
always@(posedge sys_clk)
if(rst_n == 1'b0)
cnt_1ms <= 10'd0;
else if((cnt_1ms == cnt_1ms_max) && (cnt_1us == cnt_1us_max))
cnt_1ms <= 10'd0;
else if (cnt_1us == cnt_1us_max)
cnt_1ms <= cnt_1ms + 1;
else
cnt_1ms <= cnt_1ms ;
// 1s 计数器
always@(posedge sys_clk)
if(rst_n == 1'b0)
cnt_1s <= 10'd0;
else if((cnt_1s == cnt_1s_max) && (cnt_1ms == cnt_1ms_max) && (cnt_1us == cnt_1us_max))
cnt_1s <= 10'd0;
else if ((cnt_1ms == cnt_1ms_max) && (cnt_1us == cnt_1us_max))
cnt_1s <= cnt_1s + 1;
else
cnt_1s <= cnt_1s ;
//使能信号
always@(posedge sys_clk)
if (!rst_n)
cnt_en <= 0 ;
else if ((cnt_1s == cnt_1s_max) && (cnt_1ms == cnt_1ms_max) && (cnt_1us == cnt_1us_max))
cnt_en <= ~cnt_en ;
else
cnt_en <= cnt_en ;
//PWM
always@(posedge sys_clk)
if (!rst_n)
led_out <= 0;
else if ((cnt_1s >= cnt_1ms) && (cnt_en == 0))
led_out <= ~led_out;
else if ((cnt_1s < cnt_1ms) && (cnt_en == 1))
led_out <= ~led_out;
else
led_out <= led_out;
endmodule
根据visio图可以定义输入和输出变量,呼吸灯是由从灭到亮,再从亮到灭。因此呢,我们还需要一个变量cnt_en来使能,就是为0的时候,是从灭到亮这个过程,为1的时候是从亮到灭这个过程。
我这里是用1us计数器,来给1ms计数器,1s计数器来计数,可以很好的减少FPGA的缓存。
当1us计数到最大值的时候,让1ms加一,等到1us计数到最大值和1ms计数到最大值,就让1s计数器加一,实现了一个周期的PWM。
再经过比较器,可以控制输出高电平或者低电平的时间,就实现了PWM,就可以是实现LED呼吸灯。