大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分。大侠可以关注FPGA技术江湖,在“闯荡江湖”、"行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢。
今天给大侠带来基于FPGA的呼吸灯设计,附源码,获取源码,请在“FPGA技术江湖”公众号内回复“呼吸灯设计源码”,可获取源码文件。话不多说,上货。
设计背景
呼吸灯广泛应用于手机之上,并成为各大品牌新款手机的卖点之一。如果手机里面有未处理的通知,比如说未接来电,未查收的短信等等,呼吸灯就会在控制之下完成由亮到暗的逐渐变化,感觉好像是人在呼吸,起到一个通知提醒的作用。
设计原理
关于呼吸灯设计实现的理论主要是PWM有关知识。PWM(Pluse Width Modulation)脉冲宽度调制,是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。并广泛应用在从测量、通信、功率控制与变换及LED照明等许多领域中。顾名思义,就是占空比可调的信号,那么什么是占空比呢?
占空比(Duty Cycle or Duty Ratio),可以解释为,在一脉冲序列中(方波),正脉冲序列的持续时间与脉冲总周期的比值。也可理解为,电路释放能量的有效时间与总释放时间的比值。
PWM是怎样实现调光呢?想要调节LED的亮度变化,实则是调节控制流经LED的电流。电流增大则LED亮度增强,反之减弱。但由于电流为模拟信号,所以这时就用到了PWM。正如下图所示:
使用一系列等幅不等宽的脉冲来代替一个正弦波,脉冲的宽度根据正弦波a的幅度变化,幅度高,则脉冲宽,反之。
多数负载需要的PWM调制频率都高于10Hz,要想实现呼吸灯的效果,必须提高调制频率,通常调制频率为1Khz~200Khz之间。在LED控制中PWM作用于电源部分,脉宽调制的脉冲频率通常大于100Hz,人眼就不会感到闪烁。这里我们取PWM调制频率为1KHz,PWM周期为1ms。
脉冲频率一定时,输出脉冲的占空比越大,相当于输出的有效电平越大,随着占空比的不同,LED的亮度也将不同。如占空比为0时,则LED不亮,为100%时,则LED最亮,我们让占空比从0~100%变化,再从100%~0不断变化,则就可实现呼吸灯效果。
本设计呼吸灯的一个周期为2s,分为占空比增“吸”和占空比减“呼”两种模式,每个为1s,一个PWM周期为2ms,所以每个模式包含1000个PWM周期,将每个PWM周期分为1000份,即每个时间段2us。
设计框架
设计框架图:
50M时钟
设计代码
设计模块huxi_led_state代码:
代码语言:javascript复制module huxi_led_state(clk,led,rst_n);
input clk;
input rst_n;
output reg led;
parameter T = 100_000;
localparam s0 = 1'b0;
localparam s1 = 1'b1;
reg [25:0] lw;
reg [25:0] hw;
reg [16:0] count;
// 产生2MS的脉冲
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
count <= 1'b0;
end
else
begin
if(count == T - 1)
begin
count <= 1'b0;
end
else
begin
count <= count 1'b1;
end
end
wire flag;
assign flag =(count == T - 1) ? 1'b1:1'b0;
reg state;
// 通过在一个周期中加减高低电平的时间来产生PWM波
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
lw <= T - 100;
hw <= 100;
state <= 1'b0;
end
else
begin
case (state)
s0:begin
if(flag && (lw > 100)) //判断低电平的时间
begin
lw <= lw - 100;
hw <= hw 100;
state <= s0;
end
else if(flag && (lw == 100))
begin
hw <= hw - 100;
lw <= lw 100;
state <= s1;
end
else
begin
hw <= hw;
lw <= lw;
state <= s0;
end
end
s1:begin
if(flag && (hw > 100)) //判断高电平的时间
begin
hw <= hw - 100;
lw <= lw 100;
state <= s1;
end
else if(flag && (hw ==100))
begin
hw <= hw 100;
lw <= lw - 100;
state <= s0;
end
else
begin
hw <= hw;
lw <= lw;
state <= s1;
end
end
default : state <= s0;
endcase
end
reg [25:0] cnt;
reg sum;
always @(posedge clk or negedge rst_n)
if(!rst_n)
begin
sum <= 1'b0;
led <= 1'b1;
cnt <= 1'b0;
end
else
case (sum)
s0:begin
if(cnt < hw -1 )
begin
led <= 1'b0;
cnt <= cnt 1'b1;
end
else
begin
cnt <= 1'b0;
sum <= s1;
end
end
s1:begin
if(cnt < lw -1)
begin
led <= 1'b1;
cnt <= cnt 1'b1;
end
else
begin
cnt <= 1'b0;
sum <= s0;
end
end
default:sum <= s0;
endcase
endmodule
仿真测试
测试模块代码:
代码语言:javascript复制`timescale 1ns/1ps
module huxi_led_state_tb();
reg clk;
reg rst_n;
wire led;
parameter T = 100_000;
initial begin
clk = 1'b1;
rst_n = 1'b0;
#200.1 rst_n = 1'b1;
end
always #10 clk = ~ clk;
huxi_led_state huxi_led_state_date(
.clk(clk),
.led(led),
.rst_n(rst_n)
);
endmodule
仿真图:
仿真中可以看到点亮led等高电平在不停的增高,然后会降低,通过验证我们的设计是正确的。
END
后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。
大侠们,江湖偌大,继续闯荡,愿一切安好,有缘再见!