1、PWM 基础知识
1.1、什么是呼吸灯
呼吸灯通过PWM(脉冲宽度调制)技术实现,展示出LED灯在固定频率下的循环变化效果:LED的亮度从明到暗再到明,仿佛呼吸般有节奏感。这种效果通过调整PWM信号的占空比来控制,占空比决定了LED亮度的变化速度和幅度,常用于装饰灯、指示灯等场景以增强视觉效果和用户体验。
1.2、什么是 PWM
PWM(Pulse Width Modulation 脉宽调制)是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。PWM 是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波占空比被调制用来对一个具体模拟信号的电平进行编码。PWM 信号任然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有,要么完全无。比如我们的电压输出是 5v 的,那么经过改变 PWM 的占空比,可以达到在一定时间内输出 3.3V 或者 1.3V 的效果。
1.3、PWM 基本参数
PWM 是脉冲宽度调制,具有两个非常重要的参数:频率和占空比。
频率:PWM 的频率是整个周期的倒数。
占空比:占空比是指一个周期内高电平所占的比例。
1.4、控制方法
采样控制理论中有一个重要结论:冲量相等而形状不同的窄脉冲加在具有惯性的环节上时,其效果基本相同。PWM 控制技术就是以该结论为理论基础,对半导体开关器件的导通和关断进行控制,使输出端得到一系列幅值相等而宽度不相等的脉冲,用这些脉冲来代替正弦波或其他所需要的波形。按一定的规则对各脉冲的宽度进行调制,即可改变逆变电路输出电压的大小,也可改变输出频率。
1.5、PWM 优点
PWM 的一个优点是从处理器到被控制系统信号都是数字形式,无需进行数模转换。让信号保持为数字形式可将噪声影响降到最小。噪声只有在强到足以将逻辑 1 改变为逻辑 0 或将逻辑 0 改变为逻辑 1 时,才能对数字信号产生影响。对噪声抵抗能力的增强是 PWM 相对于模拟控制的另一个优点,而且这也是在某些时候将 PWM 用于通信的主要原因。
1.6、PWM 应用
PWM 可应用于电机调速、功率调制、PID 调节、通信等,配置简单、抗干扰能力强。可以通过 PWM 来控制 LED 灯的亮暗变化,可以通过 PWM 信号来控制无源蜂鸣器发出简单的声音以及实现功率继电器的线圈节能等。PWM 用来驱动电机和调节电机转速是非常重要的内容。
2、硬件电路
逻辑派 使用的是 RGB 灯,首先要先学会对一个 LED 进行 PWM 信号控制,,通过举一反三来实现不同颜色的切换通过精确控制红(R)、绿(G)、蓝(B)三种LED的亮度。每种颜色的LED都可以独立调节其PWM(脉冲宽度调制)信号的占空比,这决定了 LED 的亮度。例如,要显示橙色,可以增加红色通道的 PWM 占空比,同时适当降低绿色和蓝色通道的 PWM 占空比。通过调节这三种颜色通道的PWM 信号,可以混合出几乎任何可见的颜色。不同颜色之间的平滑过渡和动态效果可以通过逐渐调整各个通道的PWM信号来实现,类似于呼吸灯效果。
3、实战任务
使用 逻辑派 上的 RGB 绿色灯,实现呼吸灯的效果,即由亮渐暗,然后再由暗渐亮。
4、系统框图
根据实战任务分析我们需要实现的是一个呼吸灯的任务,因此我们将呼吸灯模块命名为 breath 。我们是使用 PWM 信号来控制 RGB 绿色灯的亮度由亮到暗,再由暗到亮。
因此,本实验需要三个输入端口:分别为系统时钟、系统复位,输出端口为一个LED灯,模块框图如下图所示:
5、时序图
根据呼吸灯的实际效果我们可知,呼吸灯由两个部分组成:一个是亮到暗的过渡,另一个是暗到亮的过程。我们是在固定的频率下通过调整占空比来调控 led 灯的亮度。LED 灯的亮度由亮到暗变化,就是 led 灯信号的高电平占空比开始递增,再由暗到亮变化,就是 led 灯信号的高电平占空比开始递减,如下图所示;
6、程序编写
module breath(
input sys_clk, // 系统时钟输入
input sys_rst_n, // 系统复位输入,低电平有效
output reg led
);
parameter COUNT_MAX = 20'd10_000; // 计数值
parameter COUNT_LEVEL_MAX = 20'd20_000; // 亮度变化的计数值
//仿真定义
//parameter COUNT_MAX = 20'd10; // 主计数值
//parameter COUNT_LEVEL_MAX = 20'd20; // 亮度变化的计数值
reg [19:0] count; // 主计数器
reg [19:0] count_level; // 控制亮度变化的计数器
reg [19:0] pwm_count; // 用于生成PWM信号的计数器
reg [19:0] light_level; // 当前亮度值
// 计时200us
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
count <= 20'd0;
else if (count == COUNT_MAX - 20'd1)
count <= 20'd0;
else
count <= count + 20'd1;
end
// 亮度计数值
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
count_level <= 20'd0;
else if (count == 20'd0) begin
if (count_level == COUNT_LEVEL_MAX - 20'd1)
count_level <= 20'd0;
else
count_level <= count_level + 20'd1;
end
end
// PWM信号的计数
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
pwm_count <= 20'd0;
else if (pwm_count == COUNT_MAX - 1)
pwm_count <= 20'd0;
else
pwm_count <= pwm_count + 20'd1;
end
// 计算亮度值,实现呼吸灯效果
always @(*) begin
if (count_level < COUNT_LEVEL_MAX/2)
light_level <= count_level; // 递增亮度
else
light_level <= (COUNT_LEVEL_MAX - count_level); // 递减亮度
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n)
led <= 1'd1;
else
led <= (pwm_count < light_level) ? 1'b1 : 1'd0; // 根据当前亮度值控制LED状态
end
endmodule
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
程序注释:
第 9 -19 行:定义了计数器、亮度、当前值。用于判断当前 LED 该处于那种状态。
第 22 - 41 行:生成一个 200us 和 4s 计时器。
第 44 - 51 行:定义一个 PWM 信号的计数
第 54 - 59 行:根据 count_level 控制亮度变化的计数器,来判断是增加亮度航还是减小亮度。
第 62 - 67 行:通过三目的运算结果来决定 LED 的亮灭状态
7、仿真软件编写
`timescale 1ns / 1ns // 设置仿真时间单位和时间精度为 1 纳秒
module breath_mod(); // 定义测试模块
// reg define
reg sys_clk; // 全局系统时钟信号
reg sys_rst_n; // 全局复位信号(低电平有效)
// wire define
wire led; // LED 输出信号
always #10 sys_clk = ~sys_clk; // 每 10ns 生成一个时钟周期
initial begin
sys_clk = 1'd0; // 初始化时钟信号为低电平
sys_rst_n = 1'd0; // 初始化复位信号为低电平(表示复位状态)
#200; // 等待确保初始状态稳定
sys_rst_n = 1'd1; // 解除复位信号,设置为高电平
end
// 例化
breath u_breath(
.sys_clk(sys_clk), // 将测试模块的 sys_clk 连接到被测模块的时钟输入
.sys_rst_n(sys_rst_n), // 将测试模块的复位信号连接到被测模块的复位输入
.led(led) // 将被测模块的 LED 输出连接到测试模块的 LED 端口
);
endmodule
endmodule
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
程序注释:
第 13 行:sys_clk 每隔 10ns 翻转一次,一个完整的时钟周期包含一个高电平和一个低电平,因此系统时钟为 20ns,对应系统时钟频率为 50MHz。
第 15 - 22 行:我们先将 sys_clk 和 sys_rst_n 初始状态清零,延时200ns后让复位置一整个系统开始工作。
接下来打开 Modelsim 软件对代码进行仿真,需要添加文件如下图所示:
在运行仿真一段时间后,仿真的波形如下图所示:
由上图可知,由于 light_level 先增加到 COUNT_LEVEL_MAX/2 后再减少回到 0,这种变化产生了类似呼吸灯的效果。LED 的亮度从最暗逐渐变亮,然后再逐渐变暗,形成周期性的亮度变化。其中 count_level 是控制亮度变化的核心,当 count_level 达到 COUNT_LEVEL_MAX/2 时,亮度的增加转变为减少。这种设计确保了亮度在两个阶段中都可以平滑地变化,从而实现呼吸灯效果。
接下来我们查看 pwm_count ,它主要是与 light_level 做一个时间长度对比,在这个周期里面某个时间段为高电平,某个时间段为低电平。然后通过控制LED的亮度变化来模拟人类感知的呼吸过程。
8、I/O引脚绑定
在仿真验证完成后,接下来使用 Gowin 对时钟约束和引脚进行分配并上板验证。我们一用到了系统时钟、一个复位按键以及一个 RGB 灯其中的一个管脚分配如下表所示:
信号 | 方向 | 引脚 | 端口作用 | 电平标准 |
---|---|---|---|---|
sys_clk | input | T7 | 时钟 | LVCMOS33 |
sys_rst_n | input | F10 | 复位 | LVCMOS33 |
led | output | C10 | LED | LVCMOS33 |
Gowin 软件中 I/O Constraints 界面如下图所示:
9、程序下载
连接开发板的下载器,下载码流文件到开发板里面,下载完成之后,我们就可以观察到 GRB 灯由亮变暗,然后再由暗变亮。实际位置图如下所示: