文章目录
- 前言
- 一、PWM脉宽调制技术介绍
- 二、pwm的使用
- 2.1 LEDC定时器结构体
- 结构体介绍
- 配置定时器
- 2.2 配置LEDC通道
- 结构体介绍
- 初始化pwm
- 2.3 设置占空比
- 设置占空比
- 更新占空比
- 三、示例代码
- 总结
前言
PWM(Pulse Width Modulation,脉宽调制)是一种常用的调节模拟信号的技术,也在嵌入式系统中扮演着重要的角色。ESP32是一款强大的微控制器,具备多个硬件模块支持PWM功能。本文将介绍ESP32 IDF中的PWM脉宽调制技术,包括其原理、应用场景和使用方法。
一、PWM脉宽调制技术介绍
当我们想要控制设备的亮度或速度时,PWM脉宽调制技术就派上用场了。它的原理非常简单,就好像我们在灯泡上使用开关一样。开关可以打开或关闭灯泡,但无法改变亮度。但是,如果我们快速地开关灯泡,让它在打开和关闭之间不断变化,我们就能够通过控制开关的开启时间比例来改变亮度。
PWM脉宽调制的原理就是通过调整脉冲信号的宽度来控制设备的平均电平。脉冲信号是一种快速的开关信号,它会以固定的周期重复。脉冲的宽度(也称为占空比)决定了信号在一个周期内的开启时间比例。通过改变占空比,我们可以调整设备的亮度或速度。
例如,考虑一个LED灯。当脉冲信号处于高电平状态时,LED灯会亮起;当脉冲信号处于低电平状态时,LED灯会熄灭。如果我们增加脉冲信号处于高电平状态的时间,即增加占空比,LED灯就会更亮。反之,如果减少脉冲信号处于高电平状态的时间,即减小占空比,LED灯就会变暗。
通过快速调整脉冲信号的宽度,PWM技术可以精确控制设备的亮度或速度,无论是LED灯、电机还是其他设备。这种技术在嵌入式系统和物联网应用中非常常见,因为它简单而高效,能够满足各种控制需求。
大家可以看下面这篇文章来学习如何理解和计算pwm的占空比:【单片机概念基础】迟迟搞不懂预分频系数、PWM占空比等等概念和计算怎么办?
其中pwm的频率就是pwm的周期
二、pwm的使用
2.1 LEDC定时器结构体
结构体介绍
如果你想使用pwm,需要先初始化一个定时器用于输出ledc_timer_config_t
,他的定义如下:
typedef struct {
ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
union {
ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */
ledc_timer_bit_t bit_num __attribute__((deprecated)); /*!< Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1 */
};
ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */
uint32_t freq_hz; /*!< LEDC timer frequency (Hz) */
ledc_clk_cfg_t clk_cfg; /*!< Configure LEDC source clock.
For low speed channels and high speed channels, you can specify the source clock using LEDC_USE_REF_TICK, LEDC_USE_APB_CLK or LEDC_AUTO_CLK.
For low speed channels, you can also specify the source clock using LEDC_USE_RTC8M_CLK, in this case, all low speed channel's source clock must be RTC8M_CLK*/
} ledc_timer_config_t;
speed_mode: LEDC的速度模式,可以是高速模式或低速模式。
他的取值如下:
typedef enum {
#if SOC_LEDC_SUPPORT_HS_MODE
LEDC_HIGH_SPEED_MODE = 0, /*!< LEDC high speed speed_mode */
#endif
LEDC_LOW_SPEED_MODE, /*!< LEDC low speed speed_mode */
LEDC_SPEED_MODE_MAX, /*!< LEDC speed limit */
} ledc_mode_t;
duty_resolution: LEDC通道的占空比分辨率,用于设置PWM信号的精度。
PWM(脉冲宽度调制)信号的精度指的是在一个周期内对信号的分辨率或精确度。它表示了PWM信号在每个周期内可以表示多少个离散的取值。
信号精度的作用在于确定PWM信号在不同占空比下的分辨能力和精确度。一个具有较高精度的PWM信号可以提供更细粒度的控制,使得在不同占空比下可以实现更精确的电平控制。
以下是PWM信号精度的几个作用:
控制精度:信号精度越高,可以实现更精确的控制。例如,如果有一个8位的PWM信号,它可以提供256个离散的取值,而一个10位的PWM信号可以提供1024个离散的取值。因此,10位的PWM信号具有更高的控制精度。
平滑性:较高的信号精度可以提供更平滑的输出。通过增加PWM信号的精度,可以减小输出之间的步进程度,从而实现更平滑的变化。
输出分辨率:信号精度决定了PWM信号可以表达的不同离散电平的数量。较高的信号精度可以提供更多的输出分辨率,允许更精细的电平调节。
应用范围:一些应用需要较高的PWM信号精度,特别是在需要精确控制电机速度、LED亮度或音频信号等应用中。较高的信号精度可以提供更精确的输出来满足这些应用的要求。
总而言之,PWM信号的精度对于实现精确的电平控制和调节至关重要,它影响着输出的平滑性、控制精度和应用范围。选择合适的信号精度取决于具体应用的需求,以确保PWM信号能够满足所需的控制精度和精确度。
bit_num: 在ESP-IDF 3.0版本中已废弃。这是对’duty_resolution’的别名,为了向后兼容ESP-IDF 2.1而存在。
timer_num: 通道的计时器源,范围是0到3。
freq_hz: LEDC计时器的频率,以赫兹(Hz)为单位。
clk_cfg: 配置LEDC的时钟源。对于低速通道和高速通道,可以使用LEDC_USE_REF_TICK
、LEDC_USE_APB_CLK
或LEDC_AUTO_CLK
指定时钟源。对于低速通道,还可以使用LEDC_USE_RTC8M_CLK
指定时钟源,但此时所有低速通道的时钟源必须是RTC8M_CLK
。
配置定时器
我们可以使用下面这个函数配置定时器:
esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf)
他的参数只需要使用ledc_timer_config_t
的指针而已
2.2 配置LEDC通道
结构体介绍
如果你想使用pwm,你需要使用这个结构体ledc_channel_config_t
,他的定义如下:
typedef struct {
int gpio_num; /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16 */
ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */
ledc_channel_t channel; /*!< LEDC channel (0 - 7) */
ledc_intr_type_t intr_type; /*!< configure interrupt, Fade interrupt enable or Fade interrupt disable */
ledc_timer_t timer_sel; /*!< Select the timer source of channel (0 - 3) */
uint32_t duty; /*!< LEDC channel duty, the range of duty setting is [0, (2**duty_resolution)] */
int hpoint; /*!< LEDC channel hpoint value, the max value is 0xfffff */
} ledc_channel_config_t;
gpio_num: LEDC输出所连接的GPIO引脚号。例如,如果要使用GPIO16,则gpio_num = 16。
speed_mode: LEDC速度模式,可以是高速模式(LEDC_HIGH_SPEED_MODE)或低速模式(LEDC_LOW_SPEED_MODE)。
channel: LEDC通道号,范围为0到7,共8个通道可以用于LED控制。
他的定义如下:
typedef enum {
LEDC_CHANNEL_0 = 0, /*!< LEDC channel 0 */
LEDC_CHANNEL_1, /*!< LEDC channel 1 */
LEDC_CHANNEL_2, /*!< LEDC channel 2 */
LEDC_CHANNEL_3, /*!< LEDC channel 3 */
LEDC_CHANNEL_4, /*!< LEDC channel 4 */
LEDC_CHANNEL_5, /*!< LEDC channel 5 */
#if SOC_LEDC_CHANNEL_NUM > 6
LEDC_CHANNEL_6, /*!< LEDC channel 6 */
LEDC_CHANNEL_7, /*!< LEDC channel 7 */
#endif
LEDC_CHANNEL_MAX,
} ledc_channel_t;
intr_type: 中断类型配置,用于设置是否启用FADE中断。可以选择启用渐变中断(LEDC_INTR_FADE_EN)或禁用中断(LEDC_INTR_FADE_DIS)。
timer_sel: 选择通道的计时器源,范围为0到3,共有4个计时器可供选择。计时器用于确定PWM信号的频率。他需要和我们的ledc_timer_config_t
的timer_num
一致
duty: LEDC通道的占空比值,取值范围为0到(2^duty_resolution - 1)。占空比值表示了PWM信号在一个周期内处于高电平的时间比例,用于控制LED亮度或电机转速等。
hpoint: LEDC通道的高电平时间偏移值,最大值为0xfffff。此值用于设置PWM信号的高电平起始时间,通常用于同步多个LED或控制器。
初始化pwm
我们可以使用下面这个函数来初始化pwm结构体:
esp_err_t ledc_channel_config(const ledc_channel_config_t* ledc_conf)
参数仅仅是pwm的结构体的指针
2.3 设置占空比
设置占空比
我们可以使用下面这个函数设置占空比:
esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty);
参数一和二上面已经介绍过了,这个函数要和上面的设置的一致,参数3为占空比,要注意他的分辨率是多少,进而来设置他的值,比如10分辨率就是0~1024
更新占空比
我们可以使用下面这个函数来更新占空比:
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
参数一和二上面已经介绍过了,这个函数要和上面的设置的一致
三、示例代码
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#define LEDC_HS_TIMER LEDC_TIMER_0
#define LEDC_HS_MODE LEDC_HIGH_SPEED_MODE
#define LEDC_HS_CH0_GPIO (16)
#define LEDC_HS_CH0_CHANNEL LEDC_CHANNEL_0
#define LEDC_TEST_DUTY (4000)
#define LEDC_TEST_FADE_TIME (3000)
void pwm_example()
{
ledc_timer_config_t ledc_timer = {
.duty_resolution = LEDC_TIMER_13_BIT, // 13-bit duty resolution
.freq_hz = 5000, // PWM frequency
.speed_mode = LEDC_HS_MODE, // High-speed mode
.timer_num = LEDC_HS_TIMER // Timer index
};
// Set up PWM timer with the configuration
ledc_timer_config(&ledc_timer);
ledc_channel_config_t ledc_channel = {
.gpio_num = LEDC_HS_CH0_GPIO,
.speed_mode = LEDC_HS_MODE,
.channel = LEDC_HS_CH0_CHANNEL,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = LEDC_HS_TIMER,
.duty = 0,
.hpoint = 0
};
// Set up PWM channel with the configuration
ledc_channel_config(&ledc_channel);
// Start PWM
ledc_fade_func_install(ESP_INTR_FLAG_LEVEL1);
while (1) {
ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, LEDC_TEST_DUTY);
ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);
vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);
ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, 0);
ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);
vTaskDelay(LEDC_TEST_FADE_TIME / portTICK_PERIOD_MS);
}
}
void app_main()
{
// Initialize the PWM
ledc_init();
// Create a task to run the PWM example
xTaskCreate(pwm_example, "pwm_example", configMINIMAL_STACK_SIZE, NULL, 5, NULL);
}
总结
本文介绍了ESP32 IDF中的PWM脉宽调制技术。通过利用ESP32的硬件模块支持,我们可以轻松实现高精度的PWM信号生成。PWM技术在许多应用中都有广泛的应用,例如LED亮度控制、电机速度调节等。ESP32 IDF提供了简单且灵活的API,方便开发者配置和控制PWM信号的频率、占空比等参数。通过合理的配置和使用,可以实现精确的信号调节,满足各种应用需求。
在实际应用中,根据具体需求选择合适的PWM引脚和参数配置,可以实现精确的信号控制和实时响应。同时,需要注意PWM信号的频率和占空比的设置,以满足具体应用的要求,并避免信号抖动和资源占用等问题。
通过掌握ESP32 IDF中的PWM脉宽调制技术,开发者可以灵活运用PWM功能,实现各种应用场景。ESP32强大的性能和丰富的功能使其成为嵌入式开发的理想选择。
希望本文对读者对ESP32 IDF中的PWM脉宽调制技术有初步了解,并能在实际开发中灵活运用。通过深入学习和实践,您可以进一步探索PWM技术的更多应用和优化方法。
如有任何疑问或进一步探讨,欢迎留言交流。谢谢阅读!