1. 头文件包含

#include "driver/ledc.h"

2. LEDC的配置

配置LEDC生成PWM波,主要分为2个步骤完成:

  1. 配置LEDC的定时器。定时器决定了PWM的一些基础特性,例如频率。
  2. 配置LEDC的通道,并将通道绑定到定时器。通道可以配置和控制PWM占空比。

2.1 配置定时器

使用如下代码配置定时器。LEDC外设内一共有4个定时器,8个通道。通道可以绑定在任意一个定时器上。定时器决定了PWM的频率,因此绑定在一个定时器上的所有通道都具有相同的PWM频率。

esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);

参数 timer_conf 的定义为:

typedef struct {
    ledc_mode_t speed_mode;                /*!< LEDC speed speed_mode, high-speed mode (only exists on esp32) or low-speed mode */
    ledc_timer_bit_t duty_resolution;      /*!< LEDC channel duty resolution */
    ledc_timer_t  timer_num;               /*!< The timer source of channel (0 - LEDC_TIMER_MAX-1) */
    uint32_t freq_hz;                      /*!< LEDC timer frequency (Hz) */
    ledc_clk_cfg_t clk_cfg;                /*!< Configure LEDC source clock from ledc_clk_cfg_t.
                                                Note that LEDC_USE_RC_FAST_CLK and LEDC_USE_XTAL_CLK are
                                                non-timer-specific clock sources. You can not have one LEDC timer uses
                                                RC_FAST_CLK as the clock source and have another LEDC timer uses XTAL_CLK
                                                as its clock source. All chips except esp32 and esp32s2 do not have
                                                timer-specific clock sources, which means clock source for all timers
                                                must be the same one. */
    bool deconfigure;                      /*!< Set this field to de-configure a LEDC timer which has been configured before
                                                Note that it will not check whether the timer wants to be de-configured
                                                is binded to any channel. Also, the timer has to be paused first before
                                                it can be de-configured.
                                                When this field is set, duty_resolution, freq_hz, clk_cfg fields are ignored. */
} ledc_timer_config_t;

2.1.1 speed_mode/设置速度模式

定义如下。ESP32-S3仅支持 LEDC_LOW_SPEED_MODE

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;

2.1.2 duty_resolution/设置占空比分辨率

所谓分辨率,就是PWM波的一个完整周期中,定时器计数多少次。在给定的输入时钟情况下。对于不同的 freq_hz(PWM频率),有不同的分辨率范围可选。可以认为,最大的duty_resolution(2的duty_resolution次方) 乘以 freq_hz,接近于输入时钟频率(但不相等)。下表罗列了不同时钟源情况下,不同的PWM频率可选的最大分辨率和最小分辨率。分辨率越大,输出PWM的占空比调节越精细。
在这里插入图片描述

2.1.3 timer_num/选择定时器

以下选项选择LEDC中的定时器:

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;

2.1.4 freq_hz/设定PWM频率

设定PWM波的频率。

2.1.5 clk_cfg/选择LEDC的外设时钟源

可用的选项有:

typedef enum {
    LEDC_AUTO_CLK = 0,                              /*!< LEDC source clock will be automatically selected based on the giving resolution and duty parameter when init the timer*/
    LEDC_USE_APB_CLK = SOC_MOD_CLK_APB,             /*!< Select APB as the source clock */
    LEDC_USE_RC_FAST_CLK = SOC_MOD_CLK_RC_FAST,     /*!< Select RC_FAST as the source clock */
    LEDC_USE_XTAL_CLK = SOC_MOD_CLK_XTAL,           /*!< Select XTAL as the source clock */

    LEDC_USE_RTC8M_CLK __attribute__((deprecated("please use 'LEDC_USE_RC_FAST_CLK' instead"))) = LEDC_USE_RC_FAST_CLK,   /*!< Alias of 'LEDC_USE_RC_FAST_CLK' */
} soc_periph_ledc_clk_src_legacy_t;

2.1.6 deconfigure/撤销定时器配置

如果选项 deconfigure,设置为了1,则函数仅取消对定时器的配置,不会使用上面的参数对定时器配置。

2.2 配置通道

定时器配置完成,则可以使用如下函数配置PWM通道。

esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf);

参数 ledc_conf 的定义如下:

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 (only exists on esp32) or low-speed mode */
    ledc_channel_t channel;         /*!< LEDC channel (0 - LEDC_CHANNEL_MAX-1) */
    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 - LEDC_TIMER_MAX-1) */
    uint32_t duty;                  /*!< LEDC channel duty, the range of duty setting is [0, (2**duty_resolution)] */
    int hpoint;                     /*!< LEDC channel hpoint value, the range is [0, (2**duty_resolution)-1] */
    struct {
        unsigned int output_invert: 1;/*!< Enable (1) or disable (0) gpio output invert */
    } flags;                        /*!< LEDC flags */

} ledc_channel_config_t;

2.2.1 gpio_num/设置PWM输出的GPIO

2.2.2 speed_mode/速度模式

对于ESP32-S3而言仅选择 LEDC_LOW_SPEED_MODE

2.2.3 channel/选择通道

参数 channel 选择一个通道进行配置。可用选项有:

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;

2.2.4 intr_type/是否使能中断

LEDC外设支持硬件自动调节PWM占空比(用来调节LED灯的渐亮渐暗、呼吸效果之类的)。如果使用这项功能,可在此处选择是否使用中断。如果是软件控制占空比,则不必使能中断。

typedef enum {
    LEDC_INTR_DISABLE = 0,    /*!< Disable LEDC interrupt */
    LEDC_INTR_FADE_END,       /*!< Enable LEDC interrupt */
    LEDC_INTR_MAX,
} ledc_intr_type_t;

2.2.5 timer_sel/选择通道绑定定时器

定时器一共有4个,这里选择将通道绑定到哪个定时器。

2.2.6 duty/占空比选择

设置占空比。这里是配置时设置的,相当于默认占空比。后续运行时也有其他API可以修改占空比。其可设定的最小值为0,最大值为 2的duty_resolution次方
假设前面设置定时器时 duty_resolution 是10位,则最大值为1024。当 duty 设置为512时,输出信号高电平占空比为50%。设置为256时,输出高电平占空比为25%。

2.2.7 hpoint/设置高电平切换点

通道内部有2个参数,hpointlpoint。前者确定输出PWM信号从低电平到高电平切换的计数器的值。后者则决定信号从高电平到低电平切换的计数器的值。前者在此处设置。一旦 hpointduty 是确定的,则 lpoint 也是确定的。所以 lpoint 不需要用户设置,驱动代码会根据前两个参数去算。
hpoint的值最小为0,最大为2的duty_resolution次方-1
在这里插入图片描述

2.2.8 output_invert/指定输出是否需要反相

PWM输出信号默认是低电平的。设置此位,则反相输出。

3. 软件控制PWM占空比

使用如下函数设置PWM输出的占空比:

esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) ;
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) ;

前一个函数设置占空比门限。调用此函数不会立即生效。调用后一个函数来使得设定生效。

4. 示例

以下示例程序,先配置一个LEDC定时器,然后设置定时器的一个通道。默认情况下,输出占空比50%。在主函数中,将占空比改为25%。
在这里插入图片描述

test_ledc.h文件:

#define TEST_LEDC_TIMER_SELECT        (LEDC_TIMER_0        )
#define TEST_LEDC_TIMER_RESOLUTION    (LEDC_TIMER_10_BIT   )
#define TEST_LEDC_TIMER_FREQ          (5000                )

#define TEST_LEDC_CHANNEL_GPIO        (GPIO_NUM_4          )
#define TEST_LEDC_CHANNEL_SELECT      (LEDC_CHANNEL_0      )
#define TEST_LEDC_CHANNEL_DUTY        (512                 )
#define TEST_LEDC_CHANNEL_HPOINT      (1                   )

void TEST_LEDC_LEDCConfig(void) ;
void TEST_LEDC_SoftUpdateDuty(void) ;

test_ledc.c文件:

#include "driver/ledc.h"
#include "esp_log.h"

#include "test_ledc.h"

void TEST_LEDC_LEDCConfig(void)
{
    esp_err_t                 iErrCode ;
    const ledc_timer_config_t stLEDCConfigInfo =
    {
        .speed_mode      = LEDC_LOW_SPEED_MODE ,
        .duty_resolution = TEST_LEDC_TIMER_RESOLUTION ,
        .timer_num       = TEST_LEDC_TIMER_SELECT ,
        .freq_hz         = TEST_LEDC_TIMER_FREQ ,
        .clk_cfg         = LEDC_USE_XTAL_CLK ,
        .deconfigure     = 0
    } ;
    
    const ledc_channel_config_t stChannelConfigInfo = 
    {
        .gpio_num            = TEST_LEDC_CHANNEL_GPIO ,
        .speed_mode          = LEDC_LOW_SPEED_MODE ,
        .channel             = TEST_LEDC_CHANNEL_SELECT ,
        .intr_type           = LEDC_INTR_DISABLE ,
        .timer_sel           = TEST_LEDC_TIMER_SELECT ,
        .duty                = TEST_LEDC_CHANNEL_DUTY ,
        .hpoint              = TEST_LEDC_CHANNEL_HPOINT ,
        .flags.output_invert = 0
    } ;
    
    iErrCode = ledc_timer_config(&stLEDCConfigInfo) ;
    
    if(ESP_OK != iErrCode)
    {
        ESP_LOGE("test_ledc", "Config LEDC timer error! Return value is %d\n", iErrCode) ;
    }
    
    iErrCode = ledc_channel_config(&stChannelConfigInfo) ;
    
    if(ESP_OK != iErrCode)
    {
        ESP_LOGE("test_ledc", "Config LEDC channel error! Return value is %d\n", iErrCode) ;
    }
    
    return ;
}


void TEST_LEDC_SoftUpdateDuty(void)
{
    esp_err_t iErrCode ;
    
    iErrCode = ledc_set_duty(LEDC_LOW_SPEED_MODE, TEST_LEDC_CHANNEL_SELECT, 256) ;
    
    if(ESP_OK != iErrCode)
    {
        ESP_LOGE("test_ledc", "Set duty error! Return value is %d\n", iErrCode) ;
    }
    
    iErrCode = ledc_update_duty(LEDC_LOW_SPEED_MODE, TEST_LEDC_CHANNEL_SELECT) ;
    
    if(ESP_OK != iErrCode)
    {
        ESP_LOGE("test_ledc", "Update duty error! Return value is %d\n", iErrCode) ;
    }
    
    return ;
}

main.c文件:

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"

#include "test_ledc.h"

void app_main(void)
{
    TEST_LEDC_LEDCConfig() ;
    
    while (true)
    {
        vTaskDelay(1000) ;
        TEST_LEDC_SoftUpdateDuty() ;
    }
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐