FPGA学习altera 系列 第二十篇 呼吸灯设计

2020-12-29 17:28:11 浏览数 (1)

大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。

今天给大侠带来“FPGA学习系列 altera"系列,持续更新。此学习心得是本人之前所写,所用设计软件为Quartus II 13.1,现Quartus II 新版本已更新到19 ,以下仅供初学者学习参考。后续会更新其他系列,敬请关注。话不多说,上货。

对于每一个的小实验,我们都可以把它看作是一个小项目,逐步的去分析,设计,调试,最后完成功能。下面我们就开始我们的“小项目”。

项目名称:呼吸灯

具体要求:led灯在由亮到暗以及由暗到亮的逐渐变化。

项目分析:

1. 要求分析

灯光在微电脑的控制之下完成由亮到暗的逐渐变化,感觉好像是人在呼吸。根据不同人群以及不用情况下的呼吸频率不同,笔者这里采取一分钟呼吸15次,呼气2秒钟,吸气2秒钟。

2. 实现原理

由于LED的亮度与流过的电流成正比,如果能够去控制流经LED的电流,使电流在2秒钟内从i=0A(0A:0安培)逐渐递增到i=XA(XA:一定的电路强度),就实现了LED的由暗到亮的逐渐变化;使电流在2秒钟内从i=XA逐渐递增到i=0A就实现了LED的由亮到暗的逐渐变化。以此为周期,不断地变化下去,就实现了呼吸灯的功能。

在数字电路中,控制电流的大小几乎是不能实现的。如果将LED在T/2内点亮,将LED在T/2内熄灭,我们会看到什么呢?人的眼睛有视觉暂留(人眼在观察景物时,光信号传入大脑神经,需经过一段短暂的时间,光的作用结束后,视觉形象并不立即消失,这种残留的视觉称“后像”,视觉的这一现象则被称为“视觉暂留”),若T的时间足够短,那么将会看到一直亮着的LED,亮度由点亮的时间所占T的比例决定的。

3. 实现方案

一分钟呼吸15次,呼气2秒钟,吸气2秒钟。我们先做由暗到亮,将2秒钟分成1000个时间段,每个时间段2ms。第一个时间段全部熄灭;第二个时间段2us的时间点亮,其余时间熄灭;第三个时间段4us的时间点亮,其余时间熄灭······

每个时间段为2ms,相邻两个时间段增加2us,可以理解为点亮的比例每个时间段增加千分之一。这样的话,就可以完成由暗到亮。将上述的输出波形取反,就是点亮的比例每个时间段减少千分之一,完成由亮到灭。

架构图如下:

系统设计:

1. 工程的名称:breathe_led

2. 实现方法:用计数器1实现2us的计时,用计数器1驱动计数器2,实现2ms的计数。用计数器1和计数器2驱动计数器3,实现2s的计数。用计数器2和计数器3进行比较,来完成占空比的调节。计数器3每增加1,计数器2都会从0增加到999,如果将计数器3数值大于计数器2数值的时间段,当作点亮的时间,那么就可以实现由暗到亮的过程了。

设计代码如下:

代码语言:javascript复制
/*
模块名称:breathe_led
模块功能:呼吸灯 
作者:郝旭帅
邮箱:746833924@qq.com
*/
module breathe_led (clk, rst_n, led);

  input clk;//50MHz,周期为20ns
  input rst_n;

  output led;

  parameter T2us = 100;
  parameter T2ms = 1000;
  parameter T2s = 1000;

  reg [6:0] cnt1;//2us的计数器

  always @ (posedge clk or negedge rst_n)
    begin
      if (!rst_n)
        begin
          cnt1 <= 7'd0;
        end
      else
        begin
          if (cnt1 < T2us - 1)
            begin
              cnt1 <= cnt1   1'b1;
            end
          else
            begin
              cnt1 <= 7'd0;
            end
        end
    end

  wire flag;//计时到2us时,产生一个同步脉冲

  assign flag = (cnt1 == T2us - 1) ? 1'b1 : 1'b0;

  reg [9:0] cnt2;//2ms计数器

  always @ (posedge clk or negedge rst_n)
    begin
      if (!rst_n)
        begin
          cnt2 <= 10'd0;
        end
      else
        begin
          if ((cnt2 < T2ms - 1)&&(flag))
            begin
              cnt2 <= cnt2   1'b1;
            end
          else
            begin
              if (flag)
                begin
                  cnt2 <= 10'd0;
                end
              else
                begin
                  cnt2 <= cnt2;
                end
            end
        end
    end

  reg [9:0] cnt3;//2s的计数器
  reg change;//控制占空比由大变小或者由小变大

  always @ (posedge clk or negedge rst_n)
    begin
      if (!rst_n)
        begin
          cnt3 <= 10'd0;
          change <= 1'b0;
        end
      else
        begin
        if ((cnt3 < T2s - 1)&&(cnt2 == T2ms - 1) && (flag))
            begin
              cnt3 <= cnt3   1'b1;
            end
          else
            begin
              if ((cnt2 == T2ms - 1) && (flag))
                begin
                  change <= ~change;
                  cnt3 <= 10'd0;
                end
              else
                begin
                  cnt3 <= cnt3;
                end
            end
        end
    end

  reg led_n;

  always @ (posedge clk or negedge rst_n)
    begin
      if (!rst_n)
        begin
          led_n <= 1'b0;
        end
      else
        begin
          if (cnt3 > cnt2)
            begin
              led_n <= 1'b0;
            end
          else
            begin
              led_n <= 1'b1;
            end
        end
    end

  assign led = (change == 1) ? ~led_n : led_n;

endmodule

代码解析:

1. assign led = (change == 1) ? ~led_n : led_n;

代表:当change=1时,led=~led_n,当change=0时,led=led_n。

2. cnt2清零时,必须等待flag。如果没有flag,那么cnt2的最后一个数值将只会存在一个周期。cnt3同理。

3. 当cnt3计时到最后一刻时,将change取反,输出的占空比变化情况发生改变。

激励代码如下:

代码语言:javascript复制
/*
模块名称:breathe_led_tb
模块功能:为breathe_led模块提供激励信号
作者:郝旭帅
邮箱:746833924@qq.com
*/
`timescale 1ns/1ps

module breathe_led_tb;

  reg clk;
  reg rst_n;
  
  wire led;

  parameter T2us = 2;//仿真时,时间缩短
  parameter T2ms = 5;
  parameter T2s = 5;

  initial begin
    clk = 1'b1;
    rst_n = 1'b0;
    # 200.1
    rst_n = 1'b1;
    # 3000
    $stop;
  end
  
  always # 10 clk = ~clk;//50MHz
  
  breathe_led
    #(
      .T2us(T2us),//传递仿真参数
      .T2ms(T2ms),
      .T2s(T2s)
    )
    breathe_led_dut(
      .clk(clk), 
      .rst_n(rst_n),
      .led(led)
    );
    
endmodule

仿真波形如下:

led的占空比从大到小,再从小到大,一直不断地变化下去。

如果设计要求或者本地晶振与笔者的设计不同,请自行更改设计,以保证设计的正确性。如果还是有不明白的读者可以发邮件到我邮箱或者加群询问。

END

0 人点赞