SOC-ESP32S3部分:15-PWM脉冲宽度调制
飞书文档https://x509p6c8to.feishu.cn/wiki/BuoBwzcpoikwZdkOGiEcmsoonod
脉冲宽度调制(PWM)是一种在数字系统中模拟模拟信号的方法。通过改变脉冲的宽度,我们可以控制电源的输出,从而控制例如LED的亮度或电机的速度。
ESP32-S3 提供了LED PWM 控制器,简写为LEDC,用于生成控制LED 的脉冲宽度调制信号,这里仅仅是命名的区别,其实控制LEDC就是通用的PWM控制器。 LED PWM 控制器具有八个独立的 PWM 生成器(即八个通道)。每个 PWM 生成器会从四个通用定时器中选择一个,以该定时器的计数值作为基准生成 PWM 信号。
ESP32-S3的SW_PWM功能允许我们通过软件来控制PWM,而不需要额外的硬件。这意味着我们可以使用任何GPIO引脚作为PWM输出,极大地提高了系统的灵活性。
LED PWM文档参考:
https://docs.espressif.com/projects/esp-idf/zh_CN/v5.4/esp32s3/api-reference/peripherals/ledc.html
要使用ESP32-S3的SW_PWM,我们需要进行以下步骤:
- 配置LEDC 使用的定时器为某个定时器
- 配置LEDC 使用的通道为某个通道
- 配置LEDC 定时器占空比值
1.1、配置LEDC定时器
ledc_timer_config_t 结构体用于配置 LEDC 定时器
esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);
功能: ledc_timer_config 函数用于配置 LED 控制器(LEDC)的定时器。通过传递一个 ledc_timer_config_t 结构体,可以设置定时器的速度模式、占空比分辨率、定时器编号、频率和时钟源。
参数:
timer_conf: 指向 ledc_timer_config_t 结构体的指针,包含定时器的配置参数。
返回值:
ESP_OK: 定时器配置成功。
ESP_ERR_INVALID_ARG: 参数无效。
ESP_ERR_INVALID_STATE: 定时器未创建或已启用。ledc_timer_config_t 结构体用于定义和配置 LED 控制器(LEDC)的定时器。通过这个结构体,你可以指定定时器的速度模式、占空比分辨率、定时器编号、频率和时钟源。
typedef struct {ledc_mode_t speed_mode; ledc_timer_bit_t duty_resolution; //!< LEDC duty resolutionledc_timer_t timer_num; //!< LEDC timer numberuint32_t freq_hz; //!< LEDC timer frequency (Hz)ledc_clk_cfg_t clk_cfg; //!< LEDC timer clock source
} ledc_timer_config_t;ledc_mode_t speed_mode:
描述: 指定 LEDC 的速度模式。可以是以下值之一:
LEDC_LOW_SPEED_MODE: 低速模式(默认),适用于大多数应用。
LEDC_HIGH_SPEED_MODE: 高速模式,适用于需要更高频率的应用。ledc_timer_bit_t duty_resolution:
描述: 设置占空比的分辨率,即占空比的位数。分辨率越高,占空比的精度越高。
可选值包括 LEDC_TIMER_1_BIT 到 LEDC_TIMER_16_BIT,通常使用较高的分辨率如 LEDC_TIMER_13_BIT。ledc_timer_t timer_num:
描述: 指定使用的定时器编号。LEDC 支持多个定时器,每个定时器可以独立配置。
LEDC_TIMER_0: 定时器 0。
LEDC_TIMER_1: 定时器 1。
LEDC_TIMER_2: 定时器 2。
LEDC_TIMER_3: 定时器 3。uint32_t freq_hz:
描述: 设置输出 PWM 信号的频率,单位为赫兹 (Hz)。频率决定了 PWM 波形每秒的周期数。ledc_clk_cfg_t clk_cfg:
描述: 指定定时器的时钟源配置。可以选择自动选择时钟源或手动指定。
LEDC_AUTO_CLK: 自动选择时钟源。
LEDC_USE_APB_CLK: 使用 APB 时钟源。
LEDC_USE_RTC8M_CLK: 使用 RTC 8 MHz 时钟源。
LEDC_USE_XTAL_CLK: 使用晶振时钟源。
LEDC_USE_PLL_F80M_CLK: 使用 PLL 80 MHz 时钟源。
LEDC_USE_PLL_D2_CLK: 使用 PLL 分频后的时钟源。
应用参考:
ledc_timer_config_t ledc_timer = {.speed_mode = LEDC_LOW_SPEED_MODE, // 设置为低速模式.duty_resolution = LEDC_TIMER_13_BIT, // 设置占空比分辨率为 13 位.timer_num = LEDC_TIMER_0, // 使用定时器 0.freq_hz = 4000, // 设置输出频率为 4 kHz.clk_cfg = LEDC_AUTO_CLK // 自动选择时钟源};//ledc_timer_config 函数用于配置 LEDC(LED 控制器)定时器的参数。ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
2.1、配置LEDC通道参数
ledc_channel_config_t用于配置 LEDC PWM 通道,并将其应用到指定的 GPIO 引脚。
esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf);
功能: ledc_channel_config 函数用于配置 LED 控制器(LEDC)的通道。通过传递一个 ledc_channel_config_t 结构体,可以设置通道的速度模式、通道编号、关联的定时器、中断类型、GPIO 引脚、初始占空比和高电平起点。
参数:
ledc_conf: 指向 ledc_channel_config_t 结构体的指针,包含通道的配置参数。
返回值:
ESP_OK: 通道配置成功。
ESP_ERR_INVALID_ARG: 参数无效。
ESP_ERR_INVALID_STATE: 通道未创建或已启用。ledc_channel_config_t 结构体用于定义和配置 LED 控制器(LEDC)的通道。通过这个结构体,你可以指定通道的速度模式、通道编号、关联的定时器、中断类型、GPIO 引脚、初始占空比和高电平起点。
typedef struct {ledc_mode_t speed_mode; ledc_channel_t channel; //!< LEDC channel numberledc_timer_t timer_sel; //!< LEDC timer number to be usedledc_intr_type_t intr_type; //!< LEDC interrupt typegpio_num_t gpio_num; //!< GPIO number to be useduint32_t duty; //!< Initial duty cycleuint32_t hpoint; //!< Initial high point of duty cycle
} ledc_channel_config_t;
功能:
ledc_mode_t speed_mode:
描述: 指定 LEDC 的速度模式。可以是以下值之一:
LEDC_LOW_SPEED_MODE: 低速模式。
LEDC_HIGH_SPEED_MODE: 高速模式。ledc_channel_t channel:
描述: 指定使用的 LEDC 通道编号。可以是以下值之一:
LEDC_CHANNEL_0 到 LEDC_CHANNEL_15可选。ledc_timer_t timer_sel:
描述: 指定关联的定时器编号。定时器决定了该通道的频率和占空比分辨率。可以是以下值之一:
LEDC_TIMER_0: 定时器 0。
LEDC_TIMER_1: 定时器 1。
LEDC_TIMER_2: 定时器 2。
LEDC_TIMER_3: 定时器 3。ledc_intr_type_t intr_type:
描述: 指定是否启用中断以及中断触发条件。
#define GPIO_INTR_DISABLE 0 /*!< 禁用 GPIO 中断 */
#define GPIO_INTR_POSEDGE 1 /*!< GPIO 中断类型 : 上升沿触发 */
#define GPIO_INTR_NEGEDGE 2 /*!< GPIO 中断类型 : 下降沿触发 */
#define GPIO_INTR_ANYEDGE 3 /*!< GPIO 中断类型 : 上升沿和下降沿触发 */
#define GPIO_INTR_LOW_LEVEL 4 /*!< GPIO 中断类型 : 低电平触发 */
#define GPIO_INTR_HIGH_LEVEL 5 /*!< GPIO 中断类型 : 高电平触发 */gpio_num_t gpio_num:
描述: 指定使用的 GPIO 引脚编号。PWM 信号将通过这个引脚输出uint32_t duty:
描述: 设置初始占空比。占空比决定了高电平时间与周期时间的比例。uint32_t hpoint:
描述: 高电平起点,用于某些特殊模式(如相位控制)。对于普通 PWM 应用,通常设置为 0。
应用参考:
// 准备并应用 LEDC PWM 通道配置
ledc_channel_config_t ledc_channel = {.speed_mode = LEDC_LOW_SPEED_MODE, // 设置为低速模式.channel = LEDC_CHANNEL_0, // 使用通道 0.timer_sel = LEDC_TIMER_0, // 关联定时器 0.intr_type = LEDC_INTR_DISABLE, // 禁用中断.gpio_num = 5, // 定义输出 GPIO 引脚为 GPIO 5.duty = 0, // 设置初始占空比为 0%.hpoint = 0 // 设置高电平起点为 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
3.1、更新PWM占空比
ledc_set_duty 和 ledc_update_duty 是用于设置和更新 LEDC 通道占空比的两个关键函数。
esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty);
参数
speed_mode::LEDC 的速度模式,可以是低速模式或高速模式。
channel:指定使用的 LEDC 通道编号。
duty:要设置的占空比值。这个值是一个无符号整数,范围取决于定时器的占空比分辨率。
示例中设置为 4096,表示 50% 的占空比(因为占空比分辨率为 13 位,即 (2^{13} = 8192),所以 50% 占空比对应 4096)。esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
参数
speed_mode:LEDC 的速度模式,可以是低速模式或高速模式。
channel:指定使用的 LEDC 通道编号。
使用参考
// 设置占空比为 50%//(4096) // 设置占空比为 50%。 (2 ** 13) * 50% = 4096ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 4096));// 更新占空比以应用新值ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
最终参考代码
这里我们使用PWM控制GPIO9上的LED亮度值为一半,我们可以通过修改占空比改变它的亮灭程度
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include "esp_err.h"static void ledc_init(void)
{// 准备并应用 LEDC PWM 定时器配置ledc_timer_config_t ledc_timer = {.speed_mode = LEDC_LOW_SPEED_MODE, // 设置为低速模式.duty_resolution = LEDC_TIMER_13_BIT, // 设置占空比分辨率为 13 位.timer_num = LEDC_TIMER_0, // 使用定时器 0.freq_hz = 4000, // 设置输出频率为 4 kHz.clk_cfg = LEDC_AUTO_CLK // 自动选择时钟源};ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));// 准备并应用 LEDC PWM 通道配置ledc_channel_config_t ledc_channel = {.speed_mode = LEDC_LOW_SPEED_MODE, // 设置为低速模式.channel = LEDC_CHANNEL_0, // 使用通道 0.timer_sel = LEDC_TIMER_0, // 关联定时器 0.intr_type = LEDC_INTR_DISABLE, // 禁用中断.gpio_num = 9, // 定义输出 GPIO 引脚为 GPIO 9,对应LED引脚.duty = 0, // 设置初始占空比为 0%.hpoint = 0 // 设置高电平起点为 0};ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}void update_duty(uint32_t duty) {ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, duty));ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
}void app_main(void)
{// 设置 LEDC 外设配置ledc_init();// 设置占空比为 50%ESP_ERROR_CHECK(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 8000)); //设置占空比为50%。(2^13)*50%=4096// 更新占空比以应用新值ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));int duty = 0;while (1){duty = duty + 100;if(duty >= 8192)duty = 0;//硬件电路上,LED1灯是低电平点亮的,所以我们可以看到占空比越低灯越亮update_duty(duty);vTaskDelay(100 / portTICK_PERIOD_MS);}}