FPGA 28 ,基于 Vivado Verilog 的呼吸灯效果设计与实现( 使用 Vivado Verilog 实现呼吸灯效果 )

目录

前言
[一. 设计流程](#一. 设计流程)
[1.1 需求分析](#1.1 需求分析)
[1.2 方案设计](#1.2 方案设计)
[1.3 PWM解析](#1.3 PWM解析)
[二. 实现流程](#二. 实现流程)
[2.1 确定时间单位和精度](#2.1 确定时间单位和精度)
[2.2 定义参数和寄存器](#2.2 定义参数和寄存器)
[2.3 实现计数器逻辑](#2.3 实现计数器逻辑)
[2.4 控制 LED 状态](#2.4 控制 LED 状态)
[三. 整体流程](#三. 整体流程)
[3.1 全部代码](#3.1 全部代码)
[3.2 代码逻辑](#3.2 代码逻辑)
[1. 参数定义](#1. 参数定义)
[2. 分级计数](#2. 分级计数)
[3. 状态切换](#3. 状态切换)
[4. LED 输出控制](#4. LED 输出控制)
[四. 注意事项](#四. 注意事项)
[五. 本文总结](#五. 本文总结)
[六. 更多操作](#六. 更多操作)

前言

在数字电路设计领域,呼吸灯是一个经典且有趣的项目,它模拟人类呼吸的节奏,使 LED 灯呈现出从暗到亮再从亮到暗的渐变效果,常被用于电子产品的状态指示、氛围营造等场景。这里将详细介绍如何使用 Verilog 硬件描述语言实现一个呼吸灯效果,并对实现过程中的关键知识点、设计流程、代码逻辑以及注意事项进行深入探讨。

实现的呼吸灯效果,渐明渐暗、渐明渐暗,循环往复:

一. 设计流程

1.1 需求分析

呼吸灯的核心需求是让 LED 灯呈现出类似人类呼吸的渐变效果,即亮度从暗到亮再从亮到暗循环变化。为了实现这一效果,我们需要通过控制 LED 灯的驱动信号来改变其亮度,而在数字电路中,通常使用脉冲宽度调制(PWM)技术来模拟模拟电压,从而控制 LED 灯的亮度。

1.2 方案设计

为了实现 PWM 控制,我们将采用分级计数的方式来实现不同时间尺度的延时。具体来说,我们会设计三个计数器:微秒级计数器、毫秒级计数器和秒级计数器。通过这三个计数器的协同工作,我们可以精确控制 PWM 信号的占空比,从而实现 LED 灯亮度的渐变。

1.3 PWM解析

脉冲宽度调制(PWM),是英文"Pulse Width Modulation"的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信、功率控制等领域中。


二. 实现流程

2.1 确定时间单位和精度

在进行 Verilog 代码编写之前,首先要明确时间单位和精度。这一步对于后续处理不同时间尺度的延时至关重要。在本例中,我们使用timescale编译指令来声明时间单位和精度。

cpp 复制代码
// 时间尺度声明,仿真时间单位为1ns,仿真精度为1ps
`timescale 1ns / 1ps

这里选择 1ns 作为时间单位,1ps 作为时间精度,意味着在仿真过程中,时间的最小步进是 1ps,能够满足处理微秒(us)、毫秒(ms)等较大时间尺度的需求。这样的设置可以让我们更精确地模拟时间流逝,从而实现对呼吸灯渐变效果的精细控制。

2.2 定义参数和寄存器

确定好时间单位和精度后,接下来要定义一些关键的参数和寄存器。这些参数和寄存器将用于控制计数器的行为,进而影响 LED 灯的状态变化。

cpp 复制代码
// 定义呼吸灯模块,模块开始
module breath_led(
    input clk,      // 时钟信号,用于同步电路的操作
    input rst_n,    // 复位信号,低电平有效,用于初始化电路状态
    output reg led  // 输出信号,控制LED灯的亮灭
    );
// 定义参数,用于延时计数
parameter us_delay = 50;  // 定义微秒级延时的计数值
parameter ms_delay = 1000; // 定义毫秒级延时的计数值
parameter s_delay = 1000;  // 定义秒级延时的计数值
// 定义计数器寄存器
reg [5:0] cnt_lus;  // 微秒级计数器,6位宽,最大计数值为63
reg [9:0] cnt_lms;  // 毫秒级计数器,10位宽,最大计数值为1023
reg [9:0] cnt_ls;   // 秒级计数器,10位宽,最大计数值为1023
reg led_en;         // LED使能信号,用于控制LED的亮灭模式
  • 参数部分

    • us_delay:用于设置微秒级延时的计数值。当微秒级计数器cnt_lus达到us_delay - 1时,会触发一些操作,如毫秒级计数器的递增。
    • ms_delay:定义毫秒级延时的计数值。毫秒级计数器cnt_lms达到ms_delay - 1时,会影响秒级计数器的状态。
    • s_delay:表示秒级延时的计数值。秒级计数器cnt_ls达到s_delay - 1时,会引发 LED 使能信号led_en的状态切换。
  • 寄存器部分

    • cnt_lus:微秒级计数器,6 位宽,其最大计数值为 63。在时钟信号的驱动下,它会不断递增,用于精确计时微秒级的时间间隔。
    • cnt_lms:毫秒级计数器,10 位宽,最大计数值为 1023。它依赖于微秒级计数器的状态进行递增,用于计时毫秒级的时间。
    • cnt_ls:秒级计数器,同样是 10 位宽,最大计数值为 1023。它在微秒级和毫秒级计数器满足特定条件时才会递增,用于计时秒级的时间。
    • led_en:LED 使能信号,用于控制 LED 灯的亮灭模式。通过改变led_en的值,可以实现 LED 灯亮度的渐变效果。

2.3 实现计数器逻辑

为了模拟时间的流逝,我们需要实现三个层次的计数器:微秒计数器、毫秒计数器和秒计数器。每个计数器根据不同的条件进行递增或重置,从而实现精确的时间控制。

cpp 复制代码
// 微秒级计数器逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        cnt_lus <= 0;  // 微秒级计数器清零
    else if(cnt_lus == us_delay - 1)  // 如果微秒级计数器达到设定的延时值减1
        cnt_lus <= 0;  // 微秒级计数器清零
    else
        cnt_lus <= cnt_lus + 1;  // 微秒级计数器加1
end

// 毫秒级计数器逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        cnt_lms <= 0;  // 毫秒级计数器清零
    else if(cnt_lus == us_delay - 1)begin  // 当微秒级计数器达到设定的延时值减1
        if(cnt_lms == ms_delay - 1)  // 如果毫秒级计数器达到设定的延时值减1
            cnt_lms <= 0;  // 毫秒级计数器清零
        else
            cnt_lms <= cnt_lms + 1;  // 毫秒级计数器加1
    end
    // 其他情况保持不变,由于时序逻辑,不需要写
end

// 秒级计数器逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        cnt_ls <= 0;  // 秒级计数器清零
    else if(cnt_lus == us_delay - 1)begin  // 当微秒级计数器达到设定的延时值减1
        if(cnt_lms == ms_delay - 1)begin  // 当毫秒级计数器达到设定的延时值减1
            if(cnt_ls == s_delay - 1)  // 如果秒级计数器达到设定的延时值减1
                cnt_ls <= 0;  // 秒级计数器清零
            else
                cnt_ls <= cnt_ls + 1;  // 秒级计数器加1
        end
    end
end
  • 微秒级计数器 cnt_lus :在每个时钟信号的上升沿,首先检查复位信号rst_n是否有效。如果有效,将微秒级计数器清零,以确保电路在复位时能够恢复到初始状态。如果复位信号无效,接着检查计数器是否达到us_delay - 1。若达到该值,说明已经完成了一次微秒级的延时,将计数器清零,重新开始计时;若未达到,则将计数器加 1,继续计时。
  • 毫秒级计数器 cnt_lms :同样在时钟信号的上升沿进行操作。当复位信号有效时,将毫秒级计数器清零。当微秒级计数器达到us_delay - 1时,说明已经经过了一个微秒级的时间间隔,此时检查毫秒级计数器是否达到ms_delay - 1。若达到,则将毫秒级计数器清零,开始新的毫秒计时;若未达到,则将毫秒级计数器加 1。
  • 秒级计数器 cnt_ls :在时钟上升沿,当复位信号有效时,秒级计数器清零。只有当微秒级计数器达到us_delay - 1且毫秒级计数器达到ms_delay - 1时,才会进一步检查秒级计数器是否达到s_delay - 1。若达到,则将秒级计数器清零,重新开始秒计时;若未达到,则将秒级计数器加 1。

通过这三个计数器的协同工作,我们可以实现从微秒到毫秒再到秒的精确计时,为后续控制 LED 灯的状态变化提供时间基础。

2.4 控制 LED 状态

在完成计数器逻辑的实现后,接下来要根据计数器的状态来控制 LED 灯的状态。这里我们通过一个 LED 使能信号led_en来切换 LED 灯的亮灭模式,从而实现呼吸灯的渐变效果。

cpp 复制代码
// LED使能信号逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        led_en <= 0;  // LED使能信号清零
    else if(cnt_lus == us_delay - 1 && cnt_lms == ms_delay - 1 && cnt_ls == s_delay - 1)
        // 当微秒级、毫秒级和秒级计数器都达到设定的延时值减1时
        led_en <= ~led_en;  // 取反LED使能信号
end
// LED输出逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        led <= 0;  // LED输出信号清零
    else if(led_en == 0)  // 如果LED使能信号为0
        // 如果秒级计数器大于毫秒级计数器,LED输出高电平,否则输出低电平
        led <= (cnt_ls > cnt_lms)? 1 : 0;  
    else if(led_en == 1)  // 如果LED使能信号为1
        // 如果秒级计数器大于毫秒级计数器,LED输出低电平,否则输出高电平
        led <= (cnt_ls > cnt_lms)? 0 : 1; 
end
//模块结束
endmodule
  • LED 使能信号逻辑 :在时钟信号的上升沿,首先检查复位信号rst_n。若复位信号有效,将 LED 使能信号led_en清零。当微秒级计数器cnt_lus、毫秒级计数器cnt_lms和秒级计数器cnt_ls都达到各自设定的延时值减 1 时,说明已经经过了一个完整的计时周期,此时将 LED 使能信号取反,从而切换 LED 灯的亮灭模式。
  • LED 输出逻辑 :同样在时钟上升沿进行操作。当复位信号有效时,将 LED 输出信号led清零。当 LED 使能信号led_en为 0 时,比较秒级计数器cnt_ls和毫秒级计数器cnt_lms的大小。如果cnt_ls > cnt_lms,则 LED 输出高电平,使 LED 灯点亮;否则输出低电平,使 LED 灯熄灭。当 LED 使能信号led_en为 1 时,逻辑相反,即cnt_ls > cnt_lms时 LED 输出低电平,否则输出高电平。

随着时间的推移,计数器的值不断变化,LED 使能信号也会周期性地切换,从而使 LED 灯的亮度呈现出从暗到亮再从亮到暗的渐变效果,模拟出呼吸的节奏。


三. 整体流程

3.1 全部代码

cpp 复制代码
// 时间尺度声明,仿真时间单位为1ns,仿真精度为1ps
`timescale 1ns / 1ps

// 定义呼吸灯模块
module breath_led(
    input clk,      // 时钟信号,用于同步电路的操作
    input rst_n,    // 复位信号,低电平有效,用于初始化电路状态
    output reg led  // 输出信号,控制LED灯的亮灭
    );

// 定义参数,用于延时计数
parameter us_delay = 50;  // 定义微秒级延时的计数值
parameter ms_delay = 1000; // 定义毫秒级延时的计数值
parameter s_delay = 1000;  // 定义秒级延时的计数值

// 定义计数器寄存器
reg [5:0] cnt_lus;  // 微秒级计数器,6位宽,最大计数值为63
reg [9:0] cnt_lms;  // 毫秒级计数器,10位宽,最大计数值为1023
reg [9:0] cnt_ls;   // 秒级计数器,10位宽,最大计数值为1023
reg led_en;         // LED使能信号,用于控制LED的亮灭模式

// 微秒级计数器逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        cnt_lus <= 0;  // 微秒级计数器清零
    else if(cnt_lus == us_delay - 1)  // 如果微秒级计数器达到设定的延时值减1
        cnt_lus <= 0;  // 微秒级计数器清零
    else
        cnt_lus <= cnt_lus + 1;  // 微秒级计数器加1
end

// 毫秒级计数器逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        cnt_lms <= 0;  // 毫秒级计数器清零
    else if(cnt_lus == us_delay - 1)begin  // 当微秒级计数器达到设定的延时值减1
        if(cnt_lms == ms_delay - 1)  // 如果毫秒级计数器达到设定的延时值减1
            cnt_lms <= 0;  // 毫秒级计数器清零
        else
            cnt_lms <= cnt_lms + 1;  // 毫秒级计数器加1
    end
    // 其他情况保持不变,由于时序逻辑,不需要写
end

// 秒级计数器逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        cnt_ls <= 0;  // 秒级计数器清零
    else if(cnt_lus == us_delay - 1)begin  // 当微秒级计数器达到设定的延时值减1
        if(cnt_lms == ms_delay - 1)begin  // 当毫秒级计数器达到设定的延时值减1
            if(cnt_ls == s_delay - 1)  // 如果秒级计数器达到设定的延时值减1
                cnt_ls <= 0;  // 秒级计数器清零
            else
                cnt_ls <= cnt_ls + 1;  // 秒级计数器加1
        end
    end
end

// LED使能信号逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        led_en <= 0;  // LED使能信号清零
    else if(cnt_lus == us_delay - 1 && cnt_lms == ms_delay - 1 && cnt_ls == s_delay - 1)
        // 当微秒级、毫秒级和秒级计数器都达到设定的延时值减1时
        led_en <= ~led_en;  // 取反LED使能信号
end

// LED输出逻辑
always@(posedge clk)begin
    if(!rst_n)  // 如果复位信号有效(低电平)
        led <= 0;  // LED输出信号清零
    else if(led_en == 0)  // 如果LED使能信号为0
        led <= (cnt_ls > cnt_lms)? 1 : 0;  // 如果秒级计数器大于毫秒级计数器,LED输出高电平,否则输出低电平
    else if(led_en == 1)  // 如果LED使能信号为1
        led <= (cnt_ls > cnt_lms)? 0 : 1;  // 如果秒级计数器大于毫秒级计数器,LED输出低电平,否则输出高电平
end

endmodule

3.2 代码逻辑

1. 参数定义
  • us_delay:微秒级延时计数值,用于控制微秒级计数器重置条件。
  • ms_delay:毫秒级延时计数值,用于控制毫秒级计数器重置条件。
  • s_delay:秒级延时计数值,用于控制秒级计数器重置条件。
2. 分级计数
  • 微秒级(cnt_lus :时钟上升沿,复位信号 rst_n 有效则清零;否则计到 us_delay - 1 清零,未到则加 1。
  • 毫秒级(cnt_lms :时钟上升沿,复位有效则清零;cnt_lusus_delay - 1 时,计到 ms_delay - 1 清零,未到则加 1。
  • 秒级(cnt_ls :时钟上升沿,复位有效则清零;cnt_lusus_delay - 1cnt_lmsms_delay - 1 时,计到 s_delay - 1 清零,未到则加 1。
3. 状态切换

led_en 控制 LED 亮灭模式。三个计数器均达延时值,led_en 取反,依其值比较 cnt_lscnt_lms 控制 led 输出。

4. LED 输出控制
  • led_en 为 0,cnt_ls > cnt_lmsled 高电平,反之低电平。
  • led_en 为 1,逻辑相反。 随时间推进,cnt_lscnt_lms 大小关系变化,使 LED 呈呼吸渐变效果。

四. 注意事项

  1. 参数设置:代码中的参数 us_delayms_delays_delay 决定了呼吸灯渐变的速度。若参数设置不合理,可能导致呼吸灯渐变速度过快或过慢,甚至因计数值过大造成资源占用过多或仿真时间过长。例如,若 us_delayms_delays_delay 设置过小,呼吸灯渐变过程会极快,人眼难以察觉;反之,设置过大则渐变过程缓慢,影响用户体验。
  2. 时钟频率:代码依赖输入的时钟信号 clk 进行同步操作。时钟频率不合适会影响呼吸灯效果。若时钟频率过高,计数器计数速度快,呼吸灯渐变过程难以察觉;若时钟频率过低,呼吸灯渐变过程慢,甚至可能出现闪烁不连贯的情况。因此,实际设计中需根据具体需求和硬件条件选择合适的时钟频率。
  3. 硬件实现:若将代码烧录到实际硬件中,需考虑硬件特性和限制。如硬件驱动能力、LED 灯特性等都可能影响呼吸灯实际效果。若硬件驱动能力不足,可能无法正常驱动 LED 灯;若 LED 灯响应速度慢,也可能无法实现理想的渐变效果。进行硬件实现时,需对硬件充分测试和调试,确保呼吸灯正常工作。

五. 本文总结

通过本文的分享记录,我们详细了解了如何使用 Verilog 硬件描述语言实现一个呼吸灯,涵盖了相关时间单位知识、设计流程、代码逻辑以及注意事项。呼吸灯设计不仅是一个有趣的项目,还涉及数字电路设计中的许多重要概念,如分级计数、状态切换和 PWM 控制等。希望这里能帮助你更好地掌握数字电路设计相关知识和技能。


六. 更多操作

完整FPGA系列,请看

FPGA系列,文章目录https://blog.csdn.net/weixin_65793170/article/details/144185217?spm=1001.2014.3001.5501https://blog.csdn.net/weixin_65793170/article/details/144185217?spm=1001.2014.3001.5501

相关推荐
9527华安3 小时前
FPGA视频缩放转GTY光口传输,基于Aurora 8b/10b编解码架构,提供3套工程源码和技术支持
fpga开发·音视频·aurora·8b/10b·图像缩放·高速接口·gty
zidan14124 小时前
LVDS接口总结--(5)IDELAY3仿真
fpga开发
技术小白爱FPGA21 小时前
使用verilog 实现cordic 算法 ---- 向量模式
算法·fpga开发
G2突破手2591 天前
MIPI 详解:C-PHY
fpga开发·mipi
csdn_gddf1023843981 天前
XY2-100的Verilog实现
fpga开发
沐欣工作室_lvyiyi1 天前
基于fpga的数字频率计(论文+源码)
stm32·单片机·深度学习·物联网·fpga开发·毕业设计
9527华安2 天前
FPGA实现UltraScale GTH光口视频转USB3.0传输,基于FT601+Aurora 8b/10b编解码架构,提供2套工程源码和技术支持
fpga开发·音视频·aurora·8b/10b·usb3.0·ft601·ultrascale gth
fei_sun2 天前
【FPGA】模型机下载FPGA设计
fpga开发
晓晓暮雨潇潇2 天前
FPGA开发技能(10)热电偶测温ADS1118方案
fpga开发·verilog·热电偶·ads1118·温度测试方案