呼吸灯

本文最后更新于 2024年8月31日 中午

FPGA呼吸灯

PWM( Pulse Width Modulation),即脉冲宽度调制,它是利用微处理器输出的 PWM 信号对模拟电路进行控制的一种非常有效的技术,广泛应用于测量、通信功率控制等领域。在由计数器产生固定周期的 PWM 信号下,如果其占空比为 0,则 LED 灯不亮;如果其占空比为100%,则 LED 灯最亮。将占空比从 0 到100%,再从 100%到 0 不断变化,就可以实现 LED 灯的“ 呼吸”效果。

PWM 占空比调节示意图如下图所示:

image-20240828210622775.png

2、绘制波形图

根据呼吸灯的实际效果我们可知,呼吸灯是由两个部分组成,一个是由亮到暗的过程,一个是由暗到亮的过程,再由简介可知我们是在固定的频率下通过调整 高电平的占空比来调控 led 灯的亮度。LED 灯的亮度由亮到暗变化,就是 led 灯信号的高电平占空比开始递减,如图 所示; LED 灯的亮度由暗到亮变化,就是 led 灯信号的高电平占空比开始递增,如图所示;因此,我们需要定义一个 led 灯高电平占空比递增/递减信号 inc_dec_flag

image-20240828211330065.png

image-20240828211347397.png

本实验中我们将由暗到亮的渐变过程定义为 2s,接下来我们将这 2s 分成 1000 份,就是由暗到亮的渐变过程中有 1000 个亮度过渡,每一份过渡时间为 2ms;接下开我们再将每一份过渡时间 2ms 均分为 1000份,每份时间为 2us,我们以 2us 为单位递减 2ms 中的高电平的占空比,随着高电平占空比的不断减少,led 灯的亮度不断变暗。

image-20240828213940695.png

通过观察上面的波形图我们发现 cnt_2s 计数值为 0 时, cnt_2ms 有一个计数值 0 小于等于 cnt_2s 计数值 0; cnt_2s 计数值为 1 时, cnt_2ms 有两个计数值 0 与 1 都小于等于 cnt_2s 计数值 1; cnt_2s 计数值为 2时, cnt_2ms 有三个计数值 0、 1 与 2 都小于等于 cnt_2s 计数值 2;随着 cnt_2s 计数值从 0 到 999 增加,直到 cnt_2s 计数值为 999 时, cnt_2ms 的 1000 个计数值都是小于等于 cnt_2s 的计数值 999。由上面的分析总结可知, cnt_2ms 计数值小于等于 cnt_2s 计数值的个数从 1 到 1000 在逐渐增多,并且每次是以 2us 的时间增加的。由此我们可以定义 cnt_2ms 值小于等于 cnt_2s 计数值时,我们给 led 灯高电平,否则给 led 低电平。cnt_2s 计数的 1000 个值等价于 led 从暗到亮的 1000 个过渡期的编号,那么每个过渡期的时长是 2ms。第一个暗到亮的过渡期即 cnt_2s 计数值为 0 时, 我们在第一个 2ms 的时间内给 led 一个 2us 时间的高电平,其余时间全部赋值低电平; cnt_2s 计数值为 1 时,我们在第二个 2ms 的时间内给 led 灯两个 2us 时间的高电平,其余时间全部赋值低电平; cnt_2s 计数值为 2 时,我们在第三个 2ms 的时间内给 led 灯三个 2us 时间的高电平,其余时间全部赋值低电平;直到 cnt_2s 计数值为 999 时,我们在第一千个 2ms 的时间内给 led灯一千个 2us 的高电平,即此时 led 灯 2ms 内全部高电平, led 灯最亮。由上面的分析我们可以绘制呼吸灯由暗变亮过程的波形图如下所示:

image-20240828214234284.png

image-20240828214302805.png

3、编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

module breath_led(
input sys_clk , //系统时钟 50MHz
input sys_rst_n , //系统复位,低电平有效

output reg led //LED灯
);

//parameter define
parameter CNT_2US_MAX = 7'd100;
parameter CNT_2MS_MAX = 10'd1000;
parameter CNT_2S_MAX = 10'd1000;

//reg define
reg [6:0] cnt_2us;
reg [9:0] cnt_2ms;
reg [9:0] cnt_2s ;
reg inc_dec_flag; //亮度递增/递减 0:递增 1:递减

//*****************************************************
//** main code
//*****************************************************

//cnt_2us:计数2us
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2us <= 7'b0;
else if(cnt_2us == (CNT_2US_MAX - 7'b1 ))
cnt_2us <= 7'b0;
else
cnt_2us <= cnt_2us + 7'b1;
end

//cnt_2ms:计数2ms
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2ms <= 10'b0;
else if(cnt_2ms == (CNT_2MS_MAX - 10'b1)
&& cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2ms <= 10'b0;
else if(cnt_2us == CNT_2US_MAX - 7'b1)
cnt_2ms <= cnt_2ms + 10'b1;
else
cnt_2ms <= cnt_2ms;
end

//cnt_2s:计数2s
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_2s <= 10'b0;
else if(cnt_2s == (CNT_2S_MAX - 10'b1)
&& cnt_2ms == (CNT_2MS_MAX - 10'b1)
&& cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2s <= 10'b0;
else if(cnt_2ms == (CNT_2MS_MAX - 10'b1)
&& cnt_2us == (CNT_2US_MAX - 7'b1))
cnt_2s <= cnt_2s + 10'b1;
else
cnt_2s <= cnt_2s;
end

//inc_dec_flag为低电平,led灯由暗变亮,inc_dec_flag为高电平,led灯由亮变暗
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
inc_dec_flag <= 1'b0;
else if(cnt_2s == (CNT_2S_MAX - 10'b1)
&& cnt_2ms ==( CNT_2MS_MAX - 10'b1)
&& cnt_2us == (CNT_2US_MAX - 7'b1))
inc_dec_flag <= ~inc_dec_flag;
else
inc_dec_flag <= inc_dec_flag;
end

//led:输出信号连接到外部的led灯
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led <= 1'b0;
else if((inc_dec_flag == 1'b1 && cnt_2ms >= cnt_2s)
|| (inc_dec_flag == 1'b0 && cnt_2ms <= cnt_2s))
led <= 1'b1;
else
led <= 1'b0;
end

endmodule

仿真文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
`timescale 1ns / 1ns        //仿真单位/仿真精度

module tb_breath_led();

//parameter define
parameter CLK_PERIOD = 20; //时钟周期 20ns
parameter CNT_2US_MAX = 6'd2;
parameter CNT_2MS_MAX = 11'd10;
parameter CNT_2S_MAX = 11'd10;

//reg define
reg sys_clk;
reg sys_rst_n;

//wire define
wire led;

//信号初始化
initial begin
sys_clk <= 1'b0;
sys_rst_n <= 1'b0;
#200
sys_rst_n <= 1'b1;
end

//产生时钟
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;

//例化待测设计
breath_led #(
.CNT_2US_MAX (CNT_2US_MAX),
.CNT_2MS_MAX (CNT_2MS_MAX),
.CNT_2S_MAX (CNT_2S_MAX)
)u_breath_led(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.led (led)
);

endmodule

本文作者: zhangJinLong
本文链接: https://zhang426fly.github.io/2025/07/05/fpga/FPGA呼吸灯/
版权声明: 本博客所有文章除特别声明外,均采用BY-NC-SA许可协议。转载请注明出处!


呼吸灯
https://zhang426fly.github.io/2025/07/05/fpga/FPGA呼吸灯/
作者
zhangJinLong
发布于
2025年7月5日
许可协议