FPGA学习日志——呼吸灯与PWM breath_led&PWM

发布于:2022-11-09 ⋅ 阅读:(9) ⋅ 点赞:(0) ⋅ 评论:(0)

呼吸灯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个计数器:

  1. cnt_1s:设置最大值为999,即计数1000次为1s。**计数条件为cnt_1ms达到最大值且cnt_1us技术到最大值。**同时由于PWM原理,可以将计数器设置更大实现效果更细腻,当然占用资源相对更多。
  2. cnt_1ms:设置最大值为999,即计数1000次为1ms。计数条件是cnt_1us计数达到最大值。
  3. 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


网站公告

欢迎关注微信公众号

今日签到

点亮你在社区的每一天
签到