ESP32 - Micropython ESP-IDF 双线教程 脉宽调制(PWM)
- PWM 的基本原理
- PWM 的应用
- PWM 的优点
- PWM 的实现方式
- ESP32-micropython 中的 PWM 功能
- 使用 micropython 控制 PWM 的代码示例
- 代码介绍
- ESP32-IDF 中的 PWM 功能
- 1. 初始化配置函数
- 2. 引脚绑定函数
- 3. 占空比设置函数
- 4. 读取函数
- 5. 更改频率函数
- 6. 其他功能函数
- 归纳
- 1. 初始化PWM
- 2. 配置GPIO引脚
- 3. 编写呼吸效果函数
- 4. 编写主循环
- 示例代码
脉宽调制(PWM,Pulse Width Modulation)是一种模拟控制技术,通过数字手段来产生模拟效果。它基于一种思路:通过对一系列脉冲的宽度进行调制,从而等效地获得所需要的波形(含形状和幅值)。在电子电路中,PWM 波形通常用于控制模拟电路,因为它具有比传统模拟方法更高的分辨率和更简单的电路结构。
PWM 的基本原理
PWM 的基本原理是在一个固定的周期(或称为“载波周期”)内,改变脉冲信号的高电平时间(或称为“占空比”)来模拟不同的模拟信号。占空比是指在一个周期内,高电平时间(脉冲宽度)与整个周期时间的比值。例如,如果占空比为 50%,则在一个周期内,高电平时间等于低电平时间。
PWM 的应用
PWM 在许多领域都有广泛的应用,包括但不限于:
-
LED 亮度控制:通过改变 PWM 的占空比,可以控制 LED 的平均电流,从而控制其亮度。这种方法比传统的模拟电压控制更为精确和高效。
-
电机速度控制:PWM 可以用于控制直流电机或步进电机的速度。通过改变 PWM 的占空比,可以控制电机的平均输入电压,从而控制其转速。
-
音频放大:PWM 可以用于音频放大器的功率控制。与传统的线性放大器相比,PWM 放大器具有更高的效率和更低的失真。
-
电源管理:PWM 可以用于电源管理中的电压调节和电流控制。例如,在计算机电源的 DC-DC 转换器中,PWM 用于控制输出电压。
-
通信和信号处理:在某些通信和信号处理系统中,PWM 可以用于编码和解码信息。
PWM 的优点
-
分辨率高:PWM 的分辨率仅受限于载波频率和脉冲宽度的精度。通过提高载波频率和使用高精度的脉冲宽度控制,可以实现非常高的分辨率。
-
效率高:由于 PWM 是一种数字控制方法,因此它可以利用数字电路的高效性。与传统的模拟控制方法相比,PWM 控制通常具有更高的效率。
-
灵活性强:PWM 可以很容易地通过改变占空比来模拟不同的模拟信号。这使得 PWM 在许多应用中都非常灵活和方便。
-
抗干扰能力强:由于 PWM 是一种数字信号,因此它具有较强的抗干扰能力。即使在存在噪声和干扰的情况下,PWM 信号也能保持较好的稳定性和可靠性。
PWM 的实现方式
PWM 的实现方式有很多种,包括软件 PWM 和硬件 PWM。软件 PWM 是通过编程来产生 PWM 信号的方法,它通常使用定时器中断来周期性地改变脉冲的宽度。硬件 PWM 是通过专门的硬件电路来产生 PWM 信号的方法,它通常具有更高的精度和更低的噪声。在 ESP32 这样的微控制器中,通常提供了硬件 PWM 支持,使得用户可以方便地实现 PWM 控制。
ESP32-micropython 中的 PWM 功能
在 ESP32-micropython 中,可以使用 machine
模块中的 PWM
类来创建和操作 PWM 信号。PWM 对象可以配置为不同的频率和占空比,以产生所需的输出信号。
使用 micropython 控制 PWM 的代码示例
以下是一个简单的示例,展示了如何使用 ESP32-micropython 和 GPIO 来控制一个 LED 的亮度,模拟呼吸效果。我们将使用一个按钮(连接到另一个 GPIO)来改变呼吸速度。
import machine
import utime# 配置 PWM 引脚和频率
led_pin = machine.Pin(2, machine.Pin.OUT) # 假设 LED 连接到 GPIO 2
pwm = machine.PWM(led_pin)
pwm.freq(1000) # 设置 PWM 频率为 1kHz# 配置按钮引脚
button_pin = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP) # 假设按钮连接到 GPIO 0,并启用上拉电阻# 呼吸效果函数
def breathe(brightness_max, speed):brightness = 0increment = brightness_max / 10 # 分为 10 步增加/减少亮度while True:for i in range(brightness_max, 0, -increment):pwm.duty_u16(int(i * 65535 / brightness_max)) # 设置 PWM 占空比utime.sleep_ms(speed) # 等待一段时间以控制呼吸速度for i in range(0, brightness_max, increment):pwm.duty_u16(int(i * 65535 / brightness_max))utime.sleep_ms(speed)# 初始呼吸速度
speed = 50 # 毫秒try:while True:if not button_pin.value(): # 检测到按钮按下# 等待按钮释放while not button_pin.value():pass# 改变呼吸速度speed = speed * 2 if speed < 200 else 50 # 如果速度小于 200ms,则加倍;否则重置为 50msprint("Changed breath speed to:", speed, "ms")breathe(255, speed) # 调用呼吸效果函数
except KeyboardInterrupt:pwm.deinit() # 清理 PWM 对象machine.reset() # 重启设备
代码介绍
- 导入必要的模块:我们导入了
machine
模块,用于访问 ESP32 的硬件功能,以及utime
模块,用于精确的时间控制。 - 配置 PWM 和按钮引脚:我们设置了 LED 和按钮连接的 GPIO 引脚,并初始化了 PWM 对象,设置了其频率。
- 定义呼吸效果函数:这个函数通过改变 PWM 的占空比来模拟呼吸效果。它使用两个嵌套的 for 循环来逐渐增加和减少亮度。
- 主循环:在主循环中,我们不断调用呼吸效果函数。当检测到按钮按下时,我们改变呼吸速度。注意,我们使用了简单的去抖动逻辑来确保只检测一次按钮按下。
- 异常处理:我们使用
try-except
块来处理可能的 KeyboardInterrupt 异常(例如,用户按下了复位按钮)。在异常处理程序中,我们清理了 PWM 对象并重启了设备。
ESP32-IDF 中的 PWM 功能
ESP32的PWM库函数主要用于配置和控制PWM(脉宽调制)信号。这些函数通常是在ESP-IDF(Espressif IoT Development Framework)中提供的,以下是对ESP32 PWM库函数的一些讲解:
1. 初始化配置函数
- ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits)
- 功能:设置PWM通道的频率和分辨率。
- 参数:
channel
:PWM通道号,范围从0到15。freq
:PWM频率,最大频率由公式80000000 / 2^bit_num
给出,其中bit_num
是分辨率位数。resolution_bits
:PWM分辨率位数,支持1到16位。分辨率和频率成反比。
2. 引脚绑定函数
- ledcAttachPin(uint8_t pin, uint8_t channel)
- 功能:将GPIO引脚绑定到指定的PWM通道。
- 参数:
pin
:要绑定的GPIO引脚号。channel
:PWM通道号,与ledcSetup
函数中设置的通道对应。
3. 占空比设置函数
- ledcWrite(uint8_t channel, uint32_t duty)
- 功能:设置指定PWM通道的占空比。
- 参数:
channel
:PWM通道号。duty
:占空比值,与PWM分辨率有关。
4. 读取函数
- ledcRead(uint8_t channel)
- 功能:读取指定PWM通道的当前占空比值。
- 参数:
channel
,PWM通道号。
5. 更改频率函数
- ledcChangeFrequency(uint8_t chan, uint32_t freq, uint8_t bit_num)
- 功能:更改PWM通道的频率和分辨率。
- 参数:
chan
:PWM通道号。freq
:新的PWM频率。bit_num
:新的PWM分辨率位数。
6. 其他功能函数
- ledcWriteTone(通道,频率) 和 ledcWriteNote(channel, note, oc)(注意:这些函数可能在某些库版本中不存在或名称略有不同)
- 功能:这些函数允许开发者以特定的频率或音符播放PWM信号,通常用于音频应用。
归纳
- 初始化:使用
ledcSetup
函数设置PWM通道的频率和分辨率。 - 引脚绑定:使用
ledcAttachPin
函数将GPIO引脚绑定到PWM通道。 - 占空比控制:使用
ledcWrite
函数设置PWM通道的占空比。 - 读取:使用
ledcRead
函数读取PWM通道的当前占空比。 - 更改频率:使用
ledcChangeFrequency
函数更改PWM通道的频率和分辨率(如果需要)。 - 其他功能:使用其他函数(如
ledcWriteTone
和ledcWriteNote
)实现特定应用需求。
请注意,以上函数和参数是基于ESP-IDF库的一般描述,实际使用时可能需要根据具体的库版本和开发环境进行调整。建议查阅ESP-IDF的官方文档以获取最准确和最新的信息。
1. 初始化PWM
首先,需要初始化PWM模块,并配置PWM通道的参数,如频率、占空比等。
2. 配置GPIO引脚
需要配置用于PWM输出的GPIO引脚,以及用于按钮输入的GPIO引脚。
3. 编写呼吸效果函数
这个函数将循环改变PWM的占空比,以模拟呼吸效果。
4. 编写主循环
在主循环中,检测按钮的输入,并根据需要改变呼吸速度。
示例代码
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/pwm.h"
#include "driver/gpio.h"#define LED_PWM_CHANNEL 0 // 假设使用PWM通道0
#define LED_GPIO_NUM 2 // 假设LED连接到GPIO 2
#define BUTTON_GPIO_NUM 0 // 假设按钮连接到GPIO 0
#define PWM_HZ 1000// PWM频率设置为1kHz
#define BREATHE_MAX 1023// 占空比最大值(10位PWM)static void breathe_led(uint16_t max_brightness, uint32_t speed_ms);void app_main(void)
{// 初始化PWMpwm_config_t pwm_config = {.freq_hz = PWM_HZ,.duty_mode = PWM_DUTY_MODE_MS,.intr_mode = PWM_INTR_DISABLE,.output_select_low = PWM_OUTPUT_LOW_HIGH,.clk_sel = PWM_SEL_APB_CLK,};pwm_init(LED_PWM_CHANNEL, &pwm_config, 1, NULL);pwm_set_pin(LED_PWM_CHANNEL, LED_GPIO_NUM);// 初始化GPIO(按钮)gpio_pad_select_gpio(BUTTON_GPIO_NUM);gpio_set_direction(BUTTON_GPIO_NUM, GPIO_MODE_INPUT);gpio_set_pull_mode(BUTTON_GPIO_NUM, GPIO_PULLUP_ONLY);// 初始呼吸速度uint32_t speed_ms = 50;// 呼吸效果主循环while (1) {if (gpio_get_level(BUTTON_GPIO_NUM) == 0) { // 检测到按钮按下// 等待按钮释放(简单去抖动)vTaskDelay(pdMS_TO_TICKS(20));if (gpio_get_level(BUTTON_GPIO_NUM) == 0) {// 改变呼吸速度speed_ms = (speed_ms < 200) ? speed_ms * 2 : 50;printf("Changed breath speed to: %d ms\n", speed_ms);// 稍微等待以确保按钮完全释放vTaskDelay(pdMS_TO_TICKS(20));}}breathe_led(BREATHE_MAX, speed_ms);}
}static void breathe_led(uint16_t max_brightness, uint32_t speed_ms)
{uint16_t brightness = 0;uint16_t increment = max_brightness / 10; // 分为10步增加/减少亮度while (1) {for (brightness = 0; brightness <= max_brightness; brightness += increment) {pwm_set_duty(LED_PWM_CHANNEL, 0, brightness);vTaskDelay(pdMS_TO_TICKS(speed_ms));}for (brightness = max_brightness; brightness > 0; brightness -= increment) {pwm_set_duty(LED_PWM_CHANNEL, 0, brightness);vTaskDelay(pdMS_TO_TICKS(speed_ms));}}
}