前言
呼吸灯是一种常用于电子设备上的指示灯,其特点是灯光呈现渐变的亮灭效果,模拟呼吸的节奏。它的主要作用是提供设备状态的视觉提示,如设备开机、连接状态、通知提醒等,帮助用户了解设备的当前状态。除了实用功能,呼吸灯还具有一定的装饰性,能为设备增添现代感和视觉美感。此外,呼吸灯相比于常亮灯光更为节能,有助于延长设备的电池续航时间。在一些情况下,呼吸灯也可用作警示提醒,提示用户设备需要注意的问题。
正文
一、 呼吸灯设计验证
1.项目需求
设计基于FPGA的呼吸灯,实现LED灯光的渐变亮度变化,模拟“呼吸”效果。可以设置不同的呼吸模式,如呼吸频率、亮度范围等。
2.技术介绍
使用三个计数器(cnt_1us
、cnt_1ms
和 cnt_1s
)来生成一个较慢的时钟信号。这些计数器分别用于微秒、毫秒和秒级别的时间计算。
cnt_1us
计数器用于生成1微秒的时间脉冲,
cnt_1ms
计数器用于生成1毫秒的时间脉冲,
cnt_1s
计数器用于生成1秒的时间脉冲。
cnt_1s
计数器在cnt_1ms
达到最大值时更新,达到最大值时重置为0。cnt_1ms
计数器在cnt_1us
达到最大值时更新,达到最大值时重置为0。cnt_1us
计数器在每个时钟周期上升沿更新,达到最大值时重置为0。
呼吸灯控制:在cnt_en
为0时,LED在1毫秒内渐变亮度;在cnt_en
为1时,LED在1毫秒内渐变熄灭。cnt_en
信号用于控制LED的开关周期。每当1秒的计数完成,cnt_en
反转,控制LED灯光的亮灭。
LED控制:根据cnt_en
和计数器的状态来控制LED的亮灭。LED的亮度是基于cnt_1ms
的值来渐变的。
3.顶层架构
4.端口描述
clk | 时钟信号(50Mhz) |
rst_n | 复位信号(低电平有效) |
led | led输出 |
二、代码验证
module huxi_led(
input clk,//时钟50Mhz
input rst_n,//复位信号
output reg led//输出,由于在always模块中赋值,这里需要加reg
);
parameter cnt_1s_max = 10'd999;//1s最大计数值
parameter cnt_1ms_max = 10'd999;//1ms最大计数值
parameter cnt_1us_max = 6'd49;//1us最大计数值
reg [9:0]cnt_1s;//1s计数器
reg [9:0]cnt_1ms;//1ms计数器
reg [5:0]cnt_1us;//1us计数器
reg cnt_en;
always @(posedge clk,negedge rst_n)//1us计数原理
begin
if(rst_n == 0)
cnt_1us <= 6'd0;
else
if(cnt_1us == cnt_1us_max)//到达最大计数值计数器清零
cnt_1us <= 6'd0;
else
cnt_1us <= cnt_1us + 6'd1;
end
always @(posedge clk,negedge rst_n)//1ms计数原理
begin
if(rst_n == 0)
cnt_1ms <= 10'd0;
else //1ms计数器到达最大值时需要等待1us计数器记到最大值时进行清零
if((cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1ms <= 10'd0;//到达最大计数值计数器清零
else //1us计数器到最大值时进行累加
if(cnt_1us == cnt_1us_max)
cnt_1ms <= cnt_1ms + 10'd1;
else
cnt_1ms <= cnt_1ms;
end
always @(posedge clk,negedge rst_n)//1s计数原理
begin
if(rst_n == 0)
cnt_1s <= 10'd0;
else //1s计数器记到最大值且1ms计数器到达最大值,等待1us计数器记到最大值时进行清零
if((cnt_1s == cnt_1s_max)&&(cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1s <= 10'd0;//到达最大计数值计数器清零
else //1us计数器和1ms计数器到最大值时进行累加
if((cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_1s <= cnt_1s + 10'd1;
else
cnt_1s <= cnt_1s;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt_en <= 1'b0;
else //完全点亮或完全熄灭后进行状态反转,模拟出呼吸转换
if((cnt_1s == cnt_1s_max)&&(cnt_1us == cnt_1us_max)&&(cnt_1ms == cnt_1ms_max))
cnt_en <= ~cnt_en;
else
cnt_en <= cnt_en;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
led <= 1'b0;
else //cnt_en 为1时进行呼吸点亮,为0时进行呼吸熄灭
if(((cnt_en == 1'b0)&&(cnt_1ms <= cnt_1s))||((cnt_en == 1'b1)&&(cnt_1ms > cnt_1s)))
led <= 1'b1;
else
led <= 1'b0;
end
endmodule
仿真代码
`timescale 1ns/1ps
module huxi_led_tb;
reg clk;
reg rst_n;
wire led;
//直接对计数器器最大值进行更改,方便观察仿真信号
huxi_led #(.cnt_1s_max(10),.cnt_1ms_max(10),.cnt_1us_max(5))huxi_led_vist(
.clk (clk ),
.rst_n (rst_n),
.led (led )
);
initial clk = 1;
always #10 clk = ~clk;
initial begin
rst_n = 0;
#50
rst_n = 1;
#40000
$stop;
end
endmodule
三、仿真验证
可以看到数据波形显示于我们理论讲解时绘制波形图相同
放大数据进行观察,发现输出跳变始终是在计数器跳变后的下一个时钟周期进行改变,这是由于在时序模块设计中时钟上升沿检测的是时钟上升沿之前的数据,当前时钟的数据会在下个时钟上升沿进行读取判断,输出。