9. PWM
9.1 PWM介绍
PWM(Pulse Width Modulation 脉宽调制)是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。它是一种对模拟信号电平进行数字编码的方法。是指在一定时间内波形的高电平(即 1 状态)所占用的时间比例。通过高分辨率计数器的使用,方波占空比被调制用来对一个模拟信号的电平进行编码。PWM 信号任然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有,要么完全无。比如我们的电压输出是 5V的,那么经过改变 PWM 的占空比,可以达到在一定时间内输出 3.3V 或者 1.3V 的效果。
举个例子
想象你有一个LED和一个开关,你以肉眼看不清的速度,快速反复地开关一次,这样LED灯就会亮一半时间,暗一半时间。如果你快速地进行这个操作,对于观察者来说,LED就像是以半亮度持续亮着。这就是PWM的基本原理。
如果你把大部分时间保持开关为关闭状态,那么LED会显得更暗;相反,如果你把开关大部分时间保持为打开状态,LED会显得更亮。这就是PWM调节占空比来控制亮度的过程。
9.2 PWM的基本参数
PWM 是脉冲宽度调制,具有两个非常重要的参数:频率和占空比。
- 频率:PWM 信号的周期长度,通常使用赫兹 (Hz) 表示,表示每秒钟有多少个脉冲。PWM 的频率是整个周期的倒数。指 1 秒钟内信号从高电平到低电平再回到高电平的次数(一个周期)
- 占空比:占空比是指一个周期内高电平所占的比例。占空比可以表示为百分比,比如50%的占空比意味着高电平持续一半的周期,然后低电平持续另外一半。
- 分辨率:ESP32 支持的 PWM 信号分辨率是指设备可输出的不同占空比级别的数量。例如,8 位分辨率就表示设备可以输出 2^8 个不同占空比级别,即 0%、1/256、2/256 … 直到 100%。
9.3 ESP32S3上的PWM
在ESP32-S3中有两个硬件外设可以输出PWM信号,分别是LED PWM 控制器 (LEDC) 和 电机控制脉宽调制器 (MCPWM)。它们各有其特点和用途:
- LED PWM 控制器 (LEDC):这个模块的主要设计目标是产生高精度的 PWM 波形,用以控制 LED 灯的亮度或者产生声音。LEDC 的分辨率可以达到 16 位,能够产生准确且平滑的变化,适用于控制 LED 灯的亮度和产生声音。并且,LEDC 支持多达 8 个通道的 PWM 输出,且支持任意的GPIO引脚。用户可以配置每个通道的频率和占空比。
- 电机控制脉宽调制器 (MCPWM):这个模块主要用于马达控制,包括伺服马达、步进马达和普通电机。MCPWM 支持更加复杂的控制模式,如电机的向前/向后驱动、断电刹车等,并且支持闭环控制模式,能满足更复杂的电机控制需求。MCPWM 支持高达
- 个通道的独立 PWM 输出,并且支持死区控制和外部信号捕获。
总的来说,它们两者在处理 PWM 方面有所不同,并被应用于不同的场景。LEDC 更加适合控制灯光、声音等线性设备,而 MCPWM 包含更高级的功能,适合电机控制。本章我们以LED PWM 控制器作为案例输出PWM,后面简称LEDC。
9.4 PWM 的控制方法
在arduino中有多种控制 PWM 信号的方式,以下是其中的两种:
9.4.1 通过Arduino内部函数调用控制
在 ESP32S3 开发板上,可以使用 Arduino IDE 编写程序并通过修改调用 analogWrite()函数的方式,来控制 PWM 信号输出的频率、占空比和分辨率等。 使用Arduino 自带的 analogWrite(pin, value)
函数方式: 其中的两个参数:
pin
:要写入的 GPIO 引脚。允许的数据类型:int,支持任意的GPIO引脚value
:占空比:介于 0(始终关闭)和 255(始终开启)之间。允许的数据类型:int. 按照 PWM介绍 章节的例子写个代码案例,实现灯的渐亮渐灭:
// 使用Arduino 自带的 analogWrite(pin, value) 函数方式:
// 宏定义 GPIO 输出引脚
#define LED_PIN 48
void setup()
{
// 配置 GPIO 输出引脚
pinMode(LED_PIN, OUTPUT);
}
void loop()
{
// 实现渐亮效果
for(int i=0;i<256;i++)
{
// 设置亮度模拟值,占空比不断加大
analogWrite(LED_PIN, i);
// 延时 10ms
delay(10);
}
// 实现渐灭效果
for(int i=255;i>=0;i--)
{
// 设置亮度模拟值,占空比不断减少
analogWrite(LED_PIN, i);
// 延时 10ms
delay(10);
}
}
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
📌如何使用PWM控制LED的亮度?
LED的亮度是由流过它的电流或电压的大小决定的。PWM通过快速地切换LED的电源(开/关),以一种非常快速的方式,以至于人眼无法感知到闪烁,控制LED的平均亮度。调节占空比,可以使灯在一个周期内保持更长时间的ON状态(亮度更亮)或OFF状态(亮度更暗)。这是一种非常有效的调制亮度的方法,因为它仅仅改变灯亮和灭的时间比例,而不是变化供电电压或电流。这意味着LED在亮的时候总是以最佳亮度和效率运行。
9.4.2 通过PWM外设库调用控制
ESP32S3 也支持通过 PWM API 调用 LEDC 外设的函数来设置 PWM 通道的输出参数(比如频率和占空比),从而控制设备的速度和亮度等。除此外,还可以使用相关参数设置、附加参数等函数,来进一步控制 PWM 信号。
配置LEDC输出PWM通常是走以下步骤:
- 使用
ledcAttach()
函数建立 LEDC 通道; - 通过
ledcWrite()
输出PWM信号;
重要函数说明
ledcAttach()
函数,它的作用是初始化通道的PWM信号输出参数。
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
其中,参数pin
表示引脚号,即LED控制器要输出PWM到哪一个引脚。参数freq
表示PWM输出频率,单位是赫兹(Hz)。参数bit_num
表示PWM输出的精度,范围是1到14。
ledcAttachPin()
函数,它是用于将指定的引脚与特定的LEDC通道关联起来,以便进行PWM输出。ledcWrite()
函数,它是ESP32S3的Arduino库中的一个函数,用于设置LED控制器通道的PWM输出值。
bool ledcWrite(uint8_t pin, uint32_t duty);
其中,参数pin
表示LED控制器要输出PWM到哪一个引脚。参数duty
表示要设置的PWM占空比,范围是 0 到 (2^bit_num- 1),其中 bit_num是通过 ledcAttach()
函数设置的输出的精度。通过调用ledcWrite()
函数,可以设置指定通道上的PWM输出值。PWM占空比范围从0表示完全关闭到(2^bit_num- 1)表示完全开启。
9.5 硬件连接与准备
本案例使用板载的LED进行呼吸灯测试。一般人眼睛对于 80HZ 以上刷新频率则完全没有闪烁感,由于频率很高时看不到闪烁,占空比越大 LED 越亮,占空比越小 LED 越暗。所以在频率一定时,可以用不同占空比改变 LED 灯的亮度,使其达到一个呼吸灯的效果(逐渐亮在逐渐灭,如此反复)。
板载的LED接到的引脚是GPIO48,所以我们在初始化时,需要将LEDC功能绑定到GPIO48。
9.6 PWM呼吸灯验证
#define FREQ 2000 // 频率
#define RESOLUTION 8 // 分辨率
#define LED 48 // LED 引脚
void setup()
{
ledcAttach(LED, FREQ, RESOLUTION); // 设置通道
}
void loop()
{
// 逐渐变亮
for (int i=0;i<pow(2, RESOLUTION); i++)
{
ledcWrite(LED, i); // 输出PWM
delay(5);
}
// 逐渐变暗
for (int i=pow(2, RESOLUTION)-1;i>=0;i--)
{
ledcWrite(LED, i); // 输出PWM
delay(5);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
pow(2, RESOLUTION)
是一个数学表达式,它调用了C语言中的指数运算函数 pow()
,将2的 RESOLUTION
次方作为结果返回。在这个例子中,RESOLUTION
的值为8,所以 pow(2, RESOLUTION)
的结果就是2的8次方,即256。