前言
本次将学习ESP32C3的LED PWM,由于芯片硬件的限制ESP32C3仅支持LED PWM,不支持MCPWM和PCNT功能,但基本的PWM调制功能还是齐全的,除了控制官方开发的三色灯,还可以实现呼吸灯、舵机的控制,这次就是要实现基于ESP32C3的LED PWM呼吸灯的功能开发。
一、PWM
1.PWM介绍
PWM是英文“Pulse Width Modulation”的缩写,中文叫“脉冲宽度调制”,简称“脉宽调制”。
脉冲宽度调制主要是调整一个方波脉冲周期内高电平的占空比时间,例如:5KHz的脉冲,周期是200us,当高电平/低电平各占100us时,表示当前脉冲的占空比为50%,如下图:
脉冲宽度调制(PWM)是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断 (OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。
2.PWM的应用
PWM主要由MCU控制芯片产生并用于控制外接的设备,也存在其他设备产生PWM输入给MCU进行分析。被广泛使用的STM32系列单片机可以通过定时器产生PWM调制方波。
PWM应用:电机调速、呼吸灯、舵机控制等。
二、ESP32C3的LED PWM
1.概述
ESP32C3受硬件的限制只有LED PWM ,LED PWM 控制器用于生成控制 LED 的脉冲宽度调制信号 (PWM),具有占空比自动渐变等专门功能。该外设也可成生成 PWM 信号用作其他用途。
2.特性
• 有六个独立的 PWM 生成器(即六个通道)
• 四个独立定时器,可实现小数分频
• 占空比自动渐变(即 PWM 信号占空比可逐渐增加或减小,无须处理器干预),渐变完成时产生中断
• PWM 输出信号相位可调节
• 低功耗模式 (Light-sleep mode) 下可输出 PWM 信号
• PWM 最大精度为 14 位
架构如图:
四个定时器可独立配置(可配置时钟分频器和计数器最大值),每个定时器内部有一个时基计数器(即基于基准时钟周期计数的计数器)。每个 PWM 生成器在四个定时器中择一,以该定时器的计数值为基准生成 PWM 信号。
在官方的工程库文件中有定义定时器的选择:
typedef enum {
LEDC_TIMER_0 = 0, /*!< LEDC timer 0 */
LEDC_TIMER_1, /*!< LEDC timer 1 */
LEDC_TIMER_2, /*!< LEDC timer 2 */
LEDC_TIMER_3, /*!< LEDC timer 3 */
LEDC_TIMER_MAX,
} ledc_timer_t;
3.相关配置部分
LED PWM 控制器的每个定时器内部都有一个时基计数器。下图(定时器和 PWM 生成器功能模块图)中时基计数器使用的时钟信号称为 ref_pulsex。所有定时器使用同一个时钟源信号 LEDC_CLKx,该时钟源信号经分频器分频后产生 ref_pulsex 供计数器使用。
时钟源信号:
有三个时钟源信号可以选择: APB_CLK、 RC_FAST_CLK 和 XTAL_CLK。为 LEDC_CLKx 选择时钟源信号的配置如下:
• APB_CLK:将 LEDC_APB_CLK_SEL[1:0] 置 1
• RC_FAST_CLK:将 LEDC_APB_CLK_SEL[1:0] 置 2
• XTAL_CLK:将 LEDC_APB_CLK_SEL[1:0] 置 3
之后, LEDC_CLKx 信号会进入时钟分频器。
分频器:
分频器(Divider):这是一个18位的分频器,用于对所选择的时钟进行分频,分频器的分频系数是可设置的分为:整数部分和小数部分。
分频系数=
A为整数部分:LEDC_TIMERx_CONF_REG寄存器LEDC_CLK_DIV_TIMERx 字段的高 10 位,B为小数部分:LEDC_TIMERx_CONF_REG寄存器LEDC_CLK_DIV_TIMERx 字段的低 8 位。
下图展示分频系数非整数时,LEDC_CLKx 时钟周期和ref_pulsex 时钟脉冲的关系。
计数器:
LED PWM 控制器中定时器的计数器是14位的,所以PWM信号的最大精确度为 14 位。计数器最大可计数至 2**LEDC_T IMERx_DUT Y _RES − 1,然后溢出重新从 0 开始计数。软件可以读取、复位、暂停计数器。
计数器可在每次溢出时触发 (LEDC_TIMERx_OVF_INT) 中断,这个中断为硬件自动产生,不需要配置。计数器也可配置为在溢出 LEDC_OV F_NUM_CHn + 1) 次时触发 LEDC_OVF_CNT_CHn_INT 中断。
PWM 生成器输出信号 sig_outn 的频率取决于定时器的时钟源 LEDC_CLKx、时钟分频系数LEDC_CLK_DIV_TIMERx 以及计数器的计数范围 LEDC_TIMERx_DUTY_RES:
计算公式:
在运行时改变计数器的最大值,需先置位 LEDC_TIMERx_DUTY_RES 字段,然后置位 LEDC_TIMERx_PARA_UP字段。新的配置在计数器下一次溢出时生效。如果重新配置 LEDC_OVF_CNT_EN_CHn 字段,需置位 LEDC_PARA_UP_CHn应用新配置。总之,更改配置时需置位 LEDC_TIMERx_PARA_UP 或 LEDC_PARA_UP_CHn 应用新配置。LEDC_TIMERx_PARA_UP 和 LEDC_PARA_UP_CHn字段由硬件自动清除。
PWM生成器:
通过配置PWM生成器可调节输出脉冲的占空比。
每个 PWM 生成器主要包括一个高低电平比较器和两个选择器。 PWM 生成器将定时器的 14位计数值 (Timerx_cnt) 与高低电平比较器的值 Hpointn 和 Lpointn 比较。如果定时器的计数值等于 Hpointn 或Lpointn, PWM 信号可以输出高低电平:
• 如果 Timerx_cnt == Hpointn,则 sig_outn 为 1。
• 如果 Timerx_cnt == Lpointn,则 sig_outn 为 0。
下图展示了如何使用 Hpointn 和 Lpointn 生成占空比固定的 PWM 信号
每当所选定时器的计数器溢出时, PWM 生成器 (PWMn) 的 Hpointn 值更新为 LEDC_HPOINT_CHn。 Lpointn 的值同样在计数器每次溢出时更新,为 LEDC_DUTY_CHn[18:4] 和 LEDC_HPOINT_CHn 的和。通过配置 LEDC_DUTY_CHn[18:4]和 LEDC_HPOINT_CHn 两个字段,可设置 PWM 输出的相对相位和占空比。
在运行时,如果重新配置 LEDC_TIMER_SEL_CHn、LEDC_HPOINT_CHn、LEDC_DUTY_CHn[18:4] 和 LEDC_SIG_OUT_EN_CHn字段,需置位 LEDC_PARA_UP_CHn 应用新配置。新配置在计数器下次溢出时生效。 LEDC_TIMERx_PARA_UP字段由硬件自动清除。如下代码:
// Set duty to 50%
ESP_ERROR_CHECK(ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, LEDC_DUTY));
// Update duty to apply the new value
ESP_ERROR_CHECK(ledc_update_duty(LEDC_MODE, LEDC_CHANNEL));
中断
• LEDC_OVF_CNT_CHn_INT:定时器计数器溢出 (LEDC_OVF_NUM_CHn + 1) 次且寄存器 LEDC_OVF_CNT_EN_CHn
置 1 时触发中断。
• LEDC_DUTY_CHNG_END_CHn_INT: PWM 生成器渐变完成后触发中断。
• LEDC_TIMERx_OVF_INT:定时器达到最大计数值时触发中断。
三、ESP32C3呼吸灯
采用PWM调节来实现LED灯电压的调节,从而实现呼吸灯的效果。
相关宏定义的定义
#define LEDC_TIMER LEDC_TIMER_0
#define LEDC_MODE LEDC_LOW_SPEED_MODE
#define LEDC_OUTPUT_IO (4) // Define the output GPIO
#define LEDC_CHANNEL LEDC_CHANNEL_0
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits
#define LEDC_DUTY (205) // Set duty to 2.5%. ((2 ** 13) - 1) * 2.5% = 204.775
#define LEDC_FREQUENCY (1000) // Frequency in Hertz. Set frequency at 50 Hz
#define SERVO_MAX_DEGREE (100) // Maximum angle in degree upto which servo can rotate
初始化定时器和PWM输出
static void ledc_pwm_init(void)
{
// Prepare and then apply the LEDC PWM timer configuration
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_MODE,
.timer_num = LEDC_TIMER,
.duty_resolution = LEDC_DUTY_RES,
.freq_hz = LEDC_FREQUENCY, // Set output frequency at 5 kHz
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
// Prepare and then apply the LEDC PWM channel configuration
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_MODE,
.channel = LEDC_CHANNEL,
.timer_sel = LEDC_TIMER,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = LEDC_OUTPUT_IO,
.duty = 0, // Set duty to 0%
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}
主函数
void app_main(void)
{
// Set the LEDC peripheral configuration
ledc_pwm_init();
while (1) {
for (uint32_t angle = 0; angle < (SERVO_MAX_DEGREE + 1); angle = angle + 5) {
uint32_t led_duty = (8191 * angle) / 100;
printf("Angle of rotation: %d,led_duty: %d\r\n", angle, led_duty);
ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_duty);
ledc_update_duty(LEDC_MODE, LEDC_CHANNEL);
vTaskDelay(pdMS_TO_TICKS(200));
}
for (uint32_t angle = 100; angle > 0; angle = angle - 5) {
uint32_t led_duty = (8191 * angle) / 100;
printf("Angle of rotation: %d,led_duty: %d\r\n", angle, led_duty);
ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, led_duty);
ledc_update_duty(LEDC_MODE, LEDC_CHANNEL);
vTaskDelay(pdMS_TO_TICKS(200));
}
}
}
调试现象
总结
本次主要是开发ESP32C3 LED PWM功能,实现呼吸灯。掌握PWM是机器控制的第一步,电机的调速离不开PWM。