设计规划
呼吸灯的效果是LED灯在一段时间内从完全熄灭的状态逐渐变到最亮,再在同样的时间段内逐渐达到完全熄灭的状态。这里我们需要实现1s内实现从灭到亮,1s内从亮到灭。
LED的明亮有两种方式,第一种是在安全范围内给LED灯不同的供电电压可以控制明灭;第二种是通过控制PWM的 占空比 ,同一时间段内供给LED灯一个脉冲信号的低电平持续时间越长,LED灯越亮。我们取n个相同的时间段,让低电平的持续时间按照相等的时间间隔逐渐增多,led灯就会越来越亮了。
首先我们将1s分为1000个时间段,每段1ms。再将1ms分为1000个时间小段,每段1us。第一个1ms内亮0us(灭),并在第二个1ms内亮1us,...,在第1000个1ms内亮999us。划分的等级越多,看上去效果越好,但是人眼分辨率有限,大于这个分辨率就看起来几乎没有差异。
我们至少需要三个计数器,用来计时1s,1ms,1us。cnt_1us需要从0计数到49,不复位就计数,计满就清零。cnt_1ms完全没必要重新产生,每当cnt_1ms计数到999且cnt_1us计数到49,cnt_1ms就清零。cnt_1s同理在cnt_1ms计数到999且cnt_1us计数到49时就清零。
led_out要在不同时间段低电平持续时长慢慢增加,需要一个小技巧。观察上图,LED为低电平时,cnt_1s是0时,cnt_1ms是1-999;cnt_1s是1时,cnt_1ms是2-999...
LED灯从亮到灭是相反的过程,LED灯为高电平时,cnt_1s>cnt_1ms。
区分从灭到亮和从亮到灭,可以使用一个电平标志信号(使能)cnt_1s_en。cnt_1s_en为低电平时实现呼吸灯从灭到亮的过程,cnt_1s_en为高的时候实现呼吸灯从亮到灭的过程,cnt_1s清零时cnt_1s_en取反。
编写代码
module breath_led
#(
parameter CNT_1US_MAX = 6'd49 ,
parameter CNT_1MS_MAX = 10'd999 ,
parameter CNT_1S_MAX = 10'd999
)
(
input wire sys_clk ,
input wire sys_rst_n ,
output reg led_out
);
//reg define
reg [5:0] cnt_1us ;
reg [9:0] cnt_1ms ;
reg [9:0] cnt_1s ;
reg cnt_1s_en ;
//cnt_1us:1us计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1us <= 6'b0;
else if(cnt_1us == CNT_1US_MAX)
cnt_1us <= 6'b0;
else
cnt_1us <= cnt_1us + 1'b1;
//cnt_1ms:1ms计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1ms <= 10'b0;
else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1ms <= 10'b0;
else if(cnt_1us == CNT_1US_MAX)
cnt_1ms <= cnt_1ms + 1'b1;
//cnt_1s:1s计数器
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1s <= 10'b0;
else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX
&& cnt_1us == CNT_1US_MAX)
cnt_1s <= 10'b0;
else if(cnt_1ms == CNT_1MS_MAX && cnt_1us == CNT_1US_MAX)
cnt_1s <= cnt_1s + 1'b1;
//cnt_1s_en:1s计数器标志信号
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1s_en <= 1'b0;
else if(cnt_1s == CNT_1S_MAX && cnt_1ms == CNT_1MS_MAX
&& cnt_1us == CNT_1US_MAX)
cnt_1s_en <= ~cnt_1s_en;
//led_out:输出信号连接到外部的led灯
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b0;
else if((cnt_1s_en == 1'b1 && cnt_1ms < cnt_1s)||
(cnt_1s_en == 1'b0 && cnt_1ms > cnt_1s))
led_out <= 1'b0;
else
led_out <= 1'b1;
endmodule