呼吸灯breath_led
实验目的
实现LED灯从完全熄灭到完成点亮的循环,每次熄灭到完全点亮时间为1s.
PWM
PWM是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。
呼吸灯原理
采用PWM的方式,在固定的频率下,采用占空比的方式来实现led零度的变化。占空比为0,Led灯不亮,占空比为100%,则led灯最亮。所以将占空比从0到100%,再从100%到0循环变化,就可以实现led灯呼吸效果。
从图中可以看出,例如如果将一段时间中分段设置为10个周期T1、T2…将T1设置为全高电平,然后T2从一开始设置一个时钟的低电平9个高电平,T3保持2个低电平8个高电平…依次到T11将保持全低电平,T12则保持一个时钟的高电平9个低电平,依次到T21。如果设定T1到T11为点亮过程,T11到T21为熄灭过程,这样就实现了从熄灭到完全点亮再逐渐熄灭的过程,同理如果将T设置的更多,熄灭和点亮过程将更加顺滑和细腻。
如果将该原理扩展,例如应用到电机上,则可以实现逐渐运行到逐渐停止的工程效果。
实验框图与波形
代码原理
接下来重点介绍如何用PWM的方式实现占空比的控制
实验中采用的系统时钟为50Mhz,即每20ns为一个时钟周期。设置3个计数器:
- cnt_1s:设置最大值为999,即计数1000次为1s。**计数条件为cnt_1ms达到最大值且cnt_1us技术到最大值。**同时由于PWM原理,可以将计数器设置更大实现效果更细腻,当然占用资源相对更多。
- cnt_1ms:设置最大值为999,即计数1000次为1ms。计数条件是cnt_1us计数达到最大值。
- cnt_1us:设置最大值为49,因为一个时钟周期为20ns,即计数50次为1us。计数条件就是系统时钟。
接下来是如何根据三个计数器对输出led_out赋值。
if((cnt_1ms<=cnt_1s))
led_out <= 1'd0 ;
针对占空比的增加,可以理解为实现1s之中的1000_000份(1000x1000),每隔1000个,其中的1000份依次一份一份的增加,最后从0份增加到1000份,实现从熄灭到完全点亮
根据这个代码可以很明显看出这是一个将电平逐渐拉低的过程,也就是说逐渐点亮的过程(led低电平点亮)。
**那如何实现逐渐熄灭的过程?**将上述过程取反即可,根基实验目的需要2s才实现一个完整的熄灭到点亮到熄灭的完整过程。设置变量cnt_en,当cnt_1s计数到999翻转,即每1s翻转一次。再将以上赋值语句优化
if((cnt_1ms<=cnt_1s)&&(cnt_en==1'b0)||((cnt_1ms>cnt_1s)&&(cnt_en==1'b1)))
led_out<=1'b0;
else
led_out<=1'b1;
实验代码
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 [5:0] cnt_1us;
reg [9:0] cnt_1ms;
reg [9:0] cnt_1s;
reg cnt_en;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt_1us<=6'd0;
else if(cnt_1us==CNT_1US_MAX)
cnt_1us<=6'd0;
else cnt_1us<=cnt_1us+6'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt_1ms<=10'd0;
else if((cnt_1us==CNT_1US_MAX)&&(cnt_1ms==CNT_1MS_MAX))
cnt_1ms<=10'd0;
else if(cnt_1us==CNT_1US_MAX)
cnt_1ms<=cnt_1ms+10'd1;
else cnt_1ms<=cnt_1ms;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt_1s<=10'd0;
else if((cnt_1ms==CNT_1MS_MAX)&&(cnt_1s== CNT_1S_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+10'd1;
else
cnt_1s<=cnt_1s;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt_en<=1'b0;
else if((cnt_1ms==CNT_1MS_MAX)&&(cnt_1s== CNT_1S_MAX)&&(cnt_1us==CNT_1US_MAX))
cnt_en<=~cnt_en;
else cnt_en<=cnt_en;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
led_out<=1'b1;
else if((cnt_1ms<=cnt_1s)&&(cnt_en==1'b0)||((cnt_1ms>cnt_1s)&&(cnt_en==1'b1)))
led_out<=1'b0;
else
led_out<=1'b1;
endmodule
一定注意三个计数器的归零和计数条件,当归零时不仅是本计数器计数到最大值,还一定要当低级计数器计数到最大值才进行归零,计数时需要低级计数器全部达到最大值才进行+1
仿真代码
`timescale 1ns/1ns
module tb_breath_led();
reg sys_clk;
reg sys_rst_n;
wire led_out;
initial
begin
sys_clk=1'b0;
sys_rst_n<=1'b0;
#20
sys_rst_n<=1'b1;
end
always #10 sys_clk=~sys_clk;
breath_led
#(
. CNT_1US_MAX(6'd4),
. CNT_1MS_MAX(10'd9),
. CNT_1S_MAX (10'd9)
)
breath_led_inst
(
. sys_clk(sys_clk),
. sys_rst_n(sys_rst_n),
. led_out(led_out)
);
endmodule