《DNESP32P4开发指南_V1.0》第十七章 LEDC实验

0 阅读20分钟

第十七章 LEDC实验

LEDC(LED PWM控制器)是一种专门用于生成PWM信号的外设,设计初衷是用于LED的亮度控制。LEDC控制器具备丰富的功能,包括自动调整PWM占空比的渐变(即“渐变功能”),从而无需处理器干预即可实现平滑的亮度变化。此外,LEDC不仅适用于LED控制,还可以用于其他需要PWM信号的应用场景。本章将详细介绍LEDC的架构、功能特性及其配置方法,帮助读者掌握如何在实际项目中使用LEDC生成PWM信号。
17.1 LEDC简介
17.2 硬件设计
17.3 程序设计
17.4 下载验证

17.1 LEDC简介

ESP32的LEDC(LED PWM控制器)外设是一种专为控制LED亮度和其他PWM信号应用而设计的硬件模块。该控制器以其高效、灵活的特性,广泛应用于各种电子设备中,尤其是在需要调节灯光亮度、驱动马达或控制音量等场景中。
LEDC具有以下几个主要特性:
1)多通道支持:LEDC支持多达八个独立的PWM通道,每个通道可以独立配置,实现对多个输出设备的控制。
2)高分辨率占空比:LEDC可提供最高20位的占空比分辨率,能够精确调节输出信号的高低电平,从而实现更细腻的亮度控制。
3)渐变功能:通过自动占空比渐变功能,LEDC能够在不干预处理器的情况下,实现信号输出的平滑过渡,提升用户体验。
4)低功耗模式:在低功耗下,LEDC依然可以稳定输出PWM信号,有效降低整体能耗。
5)丰富的配置选项:LEDC支持多个时钟源和小数分频配置,允许用户根据具体需求灵活调整输出频率。
LEDC的灵活性和高效性使其成为ESP32系列芯片中不可或缺的组件,尤其在需要动态调整输出信号的场景中,LEDC的优势尤为明显。无论是用于LED驱动、音量控制,还是其他PWM信号生成任务,LEDC都能提供强大的支持。下面,我们来看一下LEDC PWM架构图。

image001.png

图17.1.1 LEDC PWM架构图

上图中,LED PWM(脉宽调制)模块的架构由四个定时器(Timer0到Timer3)和多个PWM生成器(PWM0到PWM7)组成。这种设计允许每个PWM生成器独立配置和使用其对应的定时器,从而实现灵活的PWM信号控制。该架构还包含事件和任务信号输入,支持对PWM信号进行更复杂的控制。例如,事件输入可以用来触发特定的PWM操作,而任务信号则用于控制PWM的开启、停止或其他功能。这种灵活性使得PWM模块适用于多种应用场景。
接下来,我们将深入了解LEDC PWM(LED脉宽调制)模块的工作原理和配置方法。

17.1.1 LEDC结构与配置概述

从图17.1.1可以看出,ESP32-P4芯片的LEDC外设包含四个独立的定时器,作为PWM信号的计数基准。同时,LEDC外设包含八个PWM控制器,每个控制器内置比较器,可以将计数器的值与设定的占空比进行比较,从而输出所需的PWM波形。
接下来,笔者将深入解析LEDC外设的详细结构,帮助读者更全面地理解其工作原理。下图为LEDC外设的工作流程结构。

image003.png

图17.1.1.1 定时器和PWM发生器框图

从上图中可以看到,LEDC模块主要由三个部分组成:时钟源选择、定时器计数和PWM输出生成。接下来,我们将详细讲解这三个部分的配置与操作流程。

1,时钟源选择
LEDC控制器中的四个计时器可以选择以下三种时钟源之一作为时钟源,它们分别为XTAL_40M_CLK、FOSC_20M_CLK和FOSC_20M_CLK时钟源,从上图所示,我们可通过HP_SYS_CLKRST_PERI_CLK_CTRL22_REG寄存器中的HP_SYS_CLKRST_LEDC_CLK_SRC_SEL字段来控制,该字段描述如下所示。

image005.png

图17.1.1.2 时钟源选择

完成上述设置后,LEDC_CLK信号将传递至时钟分频器(18位分频器),为计数器提供所需的时钟频率。分频器的分频系数可通过LEDC_TIMERn_CONF_REG(n: 03)寄存器中的LEDC_CLK_DIV_TIMERx(x: 03)字段进行配置。该字段由18位组成,其中高10位表示整数分频系数(22:13位),低8位表示小数分频系数(12:5位)。以下是该字段的详细描述。

image007.png

图17.1.1.3 分频系数配置

分频值LEDC_CLK_DIV的计算方式如下:

image009.png

上述公式中,A代表配置的整数分频系数,B代表小数分频系数。配置完成后,结合LEDC_CLK时钟和LEDC_CLK_DIV分频系数,可以得到以下公式:

image011.png

上述 ref_pulsex是提供给计数器的时钟频率。如果选择 PLL_F80M_CLK作为计数器的时钟源,并将分频值设置为80,那么根据公式计算,ref_pulsex将为1MHz。这意味着计数器每计数一次的周期为1µs。

2,定时器计数
四个定时器包含一个20位可调位宽的基准计数器,使用ref_pulsex作为参考时钟(见图17.1.1.1)。我们可通过操作LEDC_TIMERn_CONF_REG(n: 0-3)寄存器中的LEDC_TIMERx_DUTY_RES 字段配置了实际使用的计数器位宽。因此,PWM信号的最大分辨率为20位。计数器从0开始递增,最大计数到 2^LEDC_TIMERx_DUTY_RES-1,然后溢出并重新开始计数。计数器的值可以通过软件读取、重置和暂停。下面我们来看一下这LEDC_TIMERx_DUTY_RES字段描述。

image013.png

图17.1.1.4 配置定时器的位宽

要控制计数器的暂停和重置,可以通过配置LEDC_TIMERn_CONF_REG寄存器(n: 0-3)中的LEDC_TIMERx_PAUSE和LEDC_TIMERx_RST字段来实现。具体而言,LEDC_TIMERx_PAUSE字段可用于暂停计数器的运行,将其设置为1时,计数器将停止计数;而LEDC_TIMERx_RST字段则用于重置计数器,将其设置为1时,计数器会被重置。这些字段描述如下所示。

image015.png

图17.1.1.5 暂停和重置计数器

最后,可以通过配置LEDC_CHn_CONF0_REG寄存器(n: 0~7)中的LEDC_TIMER_SEL_CHn字段来选择用于PWM生成器的计数器。具体而言,该字段允许用户指定所需的计数器,以便为PWM信号提供计数值。通过灵活配置该字段,可以实现不同计数器与PWM生成器之间的关联,从而满足各种应用需求,该字段描述如下所示。

image017.png

图17.1.1.6 配置PWM生成器的计数来源

配置完计数值来源后,我们就可以利用PWM生成器来生成PWM信号。接下来,笔者将带领大家深入了解PWM生成器的原理和配置方法。

3,PWM输出生成
为了生成PWM信号,需要一个PWM生成器(PWMn)和一个定时器(Timerx)。每个PWM生成器可以通过设置LEDC_TIMER_SEL_CHn来单独配置,以使用四个定时器中的一个来生成PWM输出。PWM信号生成如下图所示。

image019.png

图17.1.1.7 PWM生成框图

我们知道,每个PWM生成器都包含一个比较器和两个多路复用器。PWM生成器将定时器的20位计数器值(Timerx_cnt)与两个触发值Hpointn和Lpointn进行比较。当定时器的计数器值等于Hpointn或Lpointn时,PWM信号分别为高或低,具体如下:
1)如果 Timerx_cntHpointn,则sig_outn为1。
2)如果 Timerx_cnt
Lpointn,则sig_outn为0。
上图展示了Hpointn和Lpointn如何用于生成固定占空比的PWM输出信号,其中Hpointn数值由LEDC_CHn_HPOINT_REG (n:0~7)寄存器中的LEDC_HPOINT_CHn字段配置,该字段描述如下。

image021.png

图17.1.1.8 配置Hpointn数值

而Lpointn的数值由LEDC_CHn_DUTY_REG(n: 0~7)寄存器中的LEDC_DUTY_CHn[24:4]字段和LEDC_HPOINT_CHn字段的和计算得出。LEDC_DUTY_CHn[24:4]字段描述如下。

image023.png

图17.1.1.9 配置通道信号占空比

最后,我们通过LEDC_CHn_CONF0_REG(n: 0~7)寄存器中的LEDC_SIG_OUT_EN_CHn和LEDC_IDLE_LV_CHn字段来配置PWM在空闲状态下的电平以及开启PWM通道输出。这两个字段的描述如下。

image025.png

图17.1.1.10 配置空闲电平和PWM信号输出

关于PWM生成器占空比的渐变技术,可以参考《ESP32-P4技术参考手册》以获取更详细的信息和实现方法。

17.1.2 动态配置LEDC计时器的溢出值与PWM频率

每当计数器溢出时,它会触发LEDC_TIMERx_OVF_INT中断,该中断由硬件自动生成,无需配置。此外,还可以配置LEDC_OVF_CNT_CHn_INT中断,在计数器溢出LEDC_OVF_NUM_CHn + 1次后触发。要设置LEDC_OVF_CNT_CHn_INT中断,请按照以下步骤操作:
1)选择定时器:配置LEDC_TIMER_SEL_CHn以选择PWM生成器的定时器。
2)启用溢出计数器:通过设置LEDC_OVF_CNT_EN_CHn启用溢出计数器。
3)配置溢出计数:将LEDC_OVF_NUM_CHn设置为触发中断的计数器溢出次数减1。
4)启用溢出中断:通过设置LEDC_OVF_CNT_CHn_INT_ENA启用溢出中断。
5)设置占空比分辨率:配置LEDC_TIMERx_DUTY_RES以指定所选Timerx的计数器位宽,然后等待LEDC_OVF_CNT_CHn_INT中断。
在运行时更改溢出值时,首先需要设置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将由硬件自动清除。
根据图17.1.1.1,PWM生成器输出信号(sig_outn)的频率依赖于定时器时钟源 LEDC_CLK 的频率、时钟分频器 LEDC_CLK_DIV 以及占空比分辨率(计数器宽度)LEDC_TIMERx_DUTY_RES,其计算公式为:

image027.png

根据上述公式,可以计算所需的占空比分辨率:

image029.png

下表列出了常用频率及其对应的分辨率。通过这种方式,开发者能够灵活地调整PWM信号的频率和分辨率,以满足不同应用的需求。

QQ截图20260415101715.png

表17.1.2.1 常用的频率和分辨率

值得注意的是,LED PWM寄存器通过APB_CLK时钟信号进行时钟驱动。要使用LED PWM外设,必须启用传输到LED PWM的APB_CLK信号。可以通过设置HP_SYS_CLKRST_LEDC_APB_CLK_EN字段在HP_SYS_CLKRST_SOC_CLK_CTRL3_REG寄存器中启用LED PWM 的 APB_CLK信号。此外,可以通过设置HP_SYS_CLKRST_LEDC_CLK_EN字段(见下图所示)在HP_SYS_CLKRST_PERI_CLK_CTRL22_REG寄存器中启用传输到LED PWM 的LEDC_CLK信号。LED PWM外设也可以通过设置HP_SYS_CLKRST_RST_EN_LEDC字段在HP_SYS_CLKRST_HP_RST_EN1_REG寄存器中实现软件复位。

image031.png

图17.1.2.1 开启LEDC时钟

17.2 硬件设计

17.2.1 程序功能

使用LEDC输出特定频率的PWM信号,并调节PWM占空比来控制LED亮度,从而实现呼吸灯效果。

17.2.2 硬件资源

1)LEDC
LEDC_CHANNEL_0 - IO51

17.2.3 原理图

本章实验使用的LEDC为ESP32-P4的片上资源,因此并没有相应的连接原理图。

17.3 程序设计

17.3.1 LEDC的IDF驱动

LEDC外设驱动位于ESP-IDF的components\esp_driver_ledc目录。该目录中的include文件夹存放LEDC相关的头文件,声明了LEDC函数和结构体等;而src文件夹则存放实际的LEDC操作函数。要使用LEDC功能,必须先导入以下头文件。

#include "driver/ledc.h"

接下来,作者将介绍一些常用的LEDC函数,这些函数的描述及其作用如下:
1,LEDC定时器配置ledc_timer_config
该函数用于LEDC定时器配置,其函数原型如下:

esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);

函数形参:

QQ截图20260415101732.png

表17.3.1.1 ledc_timer_config函数形参描述

返回值:
ESP_OK表示成功。
ESP_ERR_INVALID_ARG表示参数错误。
ESP_FAIL表示无法找到合适的预分频器数字,基于给定的频率和当前的占空比分辨率。
ESP_ERR_INVALID_STATE表示定时器无法被去配置,因为定时器尚未配置或未暂停。
timer_conf为指向LEDC配置结构体的指针。接下来,笔者将详细介绍ledc_timer_config_t结构体中的各个成员变量,如下代码所示:

typedef struct {

    ledc_mode_t speed_mode;	/* LEDC速度模式,高速模式(仅在esp32上存在)或低速模式 */
    ledc_timer_bit_t duty_resolution;	/* LEDC通道的占空比分辨率 */
    ledc_timer_t  timer_num;        	/* 通道的定时器源(0 - LEDC_TIMER_MAX-1) */
    uint32_t freq_hz;               	/* LEDC定时器频率(Hz) */
    ledc_clk_cfg_t clk_cfg;         	/* 从ledc_clk_cfg_t配置LEDC源时钟 */
    bool deconfigure;              	/* 是否去配置之前已配置的LEDC定时器 */
} ledc_timer_config_t;

上述结构体用于配置LEDC的PWM参数,以下对各个成员做简单介绍。
1)speed_mode:
设置LEDC速度模式。此字段可配置为LEDC_LOW_SPEED_MODE低速模式、LEDC_HIGH_SPEED_MODE高速模式。ESP32-P4只能配置为低速模式。
2)duty_resolution:
设置占空比分辨率。
3)timer_num:
选择计数器。选择范围为:0 ~ 3。
4)freq_hz:
设置定时器分辨率。
5)clk_cfg
设置定时器时钟源。此字段可配置为LEDC_AUTO_CLK、LEDC_USE_XTAL_CLK、LEDC_USE_PLL_DIV_CLK和LEDC_USE_RC_FAST_CLK。本章节实验配置为LEDC_AUTO_CLK。
6)deconfigure
是否去配置之前已配置的LEDC定时器。

2,LEDC通道配置ledc_channel_config
该函数用于LEDC通道配置,其函数原型如下:

esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf);

函数形参:

QQ截图20260415101748.png

表17.3.1.2 ledc_channel_config函数形参描述

返回值:
ESP_OK表示成功
ESP_ERR_INVALID_ARG表示参数错误。
ledc_conf为指向LEDC PWM通道配置结构体的指针。接下来,笔者将详细 介绍ledc_channel_config_t结构体中的各个成员变量,如下代码所示:

typedef struct {
    int gpio_num;  	/* LEDC输出GPIO编号,如果要使用GPIO16,则gpio_num = 16 */
    ledc_mode_t speed_mode;	/* LEDC速度模式,高速模式(仅在ESP32上存在)或低速模式 */
    ledc_channel_t channel;	/* LEDC通道(0 - LEDC_CHANNEL_MAX-1) */
    ledc_intr_type_t intr_type;	/* 配置中断,淡入中断使能或淡入中断禁用 */
ledc_timer_t timer_sel;   	/* 选择通道的定时器源(0 - LEDC_TIMER_MAX-1) */
/* LEDC通道的占空比,占空比设置的范围为[0, (2**duty_resolution)] */
uint32_t duty; 
    int hpoint;		/* LEDC通道的hpoint值,范围为[0, (2**duty_resolution)-1] */
    struct {
        unsigned int output_invert: 1; /* 启用(1)或禁用(0)GPIO输出反转 */
    } flags;                        	/* LEDC标志 */

} ledc_channel_config_t;

ledc_channel_config_t结构体用于传递LEDC通道的配置参数,以便在调用ledc_channel_config时进行初始化和设置。通过这个结构体,开发者可以灵活配置LEDC通道的工作模式,以满足不同的定时需求。以下是各个参数的说明及可选项。

QQ截图20260415101759.png

表17.3.1.3 ledc_channel_config_t结构体的各个参数描述及可选项

3,设置LEDC占空比ledc_set_duty
该函数用于设置LEDC占空比,其函数原型如下:

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

函数形参:

QQ截图20260415101809.png

表17.3.1.4 ledc_set_duty函数形参描述

返回值:
ESP_OK表示成功;
ESP_ERR_INVALID_ARG表示参数错误。

4,更新LEDC通道参数ledc_update_duty
该函数用于更新LEDC通道参数,其函数原型如下:

esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);

函数形参:

QQ截图20260415101818.png

表17.3.1.5 ledc_update_duty函数形参描述

返回值:
ESP_OK表示成功;
ESP_ERR_INVALID_ARG表示参数错误。
至于其他LEDC配置函数,请读者查阅ESP-IDF的LEDC驱动文件。

17.3.2 程序流程图

image034.png

图17.3.2.1 LEDC实验程序流程图

17.3.3 程序解析

在07_ledc例程中,作者在07_ledc\components\BSP路径下新建LEDC文件夹,并且需要更改CMakeLists.txt内容,以便在其他文件上调用。
1,LEDC驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。LEDC驱动源码包括两个文件:ledc.c和ledc.h。
ledc.h主要用于声明ledc_init等函数,以便在其他文件中调用,具体内容不再赘述。
下面我们再解析ledc.c的程序,看一下初始化函数ledc_init,代码如下:

/**
 * @brief      	初始化LEDC
 * @param     	ledc_config: ledc配置结构体
 * @retval     	无
 */
void ledc_init(ledc_config_t *ledc_config)
{
ledc_config->duty = ledc_duty_pow(ledc_config->duty, 2,
                                  ledc_config->duty_resolution);

    ledc_timer_config_t ledc_timer = {
        .speed_mode       = LEDC_LOW_SPEED_MODE,            /* 低速模式*/
        .duty_resolution  = ledc_config->duty_resolution,   /* 占空比分辨率 */
        .timer_num        = ledc_config->timer_num,         /* 定时器选择 */
        .freq_hz          = ledc_config->freq_hz,           /* 设置频率 */
        .clk_cfg          = ledc_config->clk_cfg            /* 设置时钟源 */
    };
    /* 配置ledc定时器 */
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

    /* 配置pwm通道 */
    ledc_channel_config_t ledc_channel = {
        .speed_mode     = LEDC_LOW_SPEED_MODE,      /* 低速模式 */
        .channel        = ledc_config->channel,     /* PWM输出通道 */
        .timer_sel      = ledc_config->timer_num,   /* 那个定时器提供计数值 */
        .intr_type      = LEDC_INTR_DISABLE,        /* 关闭LEDC中断 */
        .gpio_num       = ledc_config->gpio_num,    /* 输出GPIO管脚 */
        .duty           = ledc_config->duty,        /* 占空比 */
        .hpoint         = 0                         /* 设置hpoint数值 */
    };
    /* Lpoint = duty + hpoint */

    /* 配置pwm通道 */
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}

该函数用于初始化LEDC模块。它接收一个指向LEDC配置结构体的指针,计算占空比并配置定时器和PWM通道。通过调用相应的配置函数,它设置定时器的工作模式、频率和时钟源,确保LEDC在预期的模式下工作。最终,它注册PWM通道,准备好为后续的PWM信号输出提供支持。
接下来,我们来看一下ledc_duty_pow函数,它根据占空比分辨率来计算占空比,即高电平时间。如下代码所示。

/**
 * @brief      	计算一个周期的占空比计数值
 * @param     	duty:   占空比
 * @param    	m^n:    输入参数
 * @retval   	返回一个周期的占空比计数值
 */
uint32_t ledc_duty_pow(uint32_t duty, uint8_t m, uint8_t n)
{
    uint32_t result = 1;

    while (n--)
    {
        result *= m;
    }
    return (result * duty) / 100;
}

该函数用于计算一个周期的占空比计数值。通过输入的占空比和基数、指数参数,该函数计算出一个相应的计数值。它在实际占空比设置中起到辅助作用,将占空比从百分比转换为对应的计数值,以便于后续的LEDC配置和使用。
计算占空比的数值后,使用ledc_pwm_set_duty函数设置和更新占空比,如下代码所示。

/**
 * @brief     	设置占空比
 * @param     	ledc_config: ledc配置结构体
 *           	duty: 占空比
 * @retval    	无
 */
void ledc_pwm_set_duty(ledc_config_t *ledc_config, uint16_t duty)
{
ledc_config->duty = ledc_duty_pow(duty, 2, ledc_config->duty_resolution); 
/* 设置占空比 */
ledc_set_duty(LEDC_LOW_SPEED_MODE, ledc_config->channel, ledc_config->duty); 
/* 更新占空比 */    
    ledc_update_duty(LEDC_LOW_SPEED_MODE, ledc_config->channel);                    
}

该函数用于设置LEDC通道的占空比。接收一个指向LEDC配置结构体的指针和新的占空比值,通过计算新的占空比后,调用ledc_set_duty设置该通道的占空比,并随后调用ledc_update_duty更新设置。该函数确保在运行时能够动态调整PWM信号的占空比,以实现不同的输出效果。

2,CMakeLists.txt文件
本例程的功能实现主要依靠LEDC驱动。要在main函数中,成功调用LEDC文件中的内容,就得需要修改BSP文件夹下的CMakeLists.txt文件(重点看红色内容),修改如下:

set(src_dirs
            LEDC)

set(include_dirs
            LEDC)

set(requires
            driver)

idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs}
                       REQUIRES ${requires})

component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)

3,main.c驱动代码
在main.c里面编写如下代码。

void app_main(void)
{
    esp_err_t ret;
    uint8_t dir = 1;
    uint16_t ledpwmval = 0;

    ledc_config_t *ledc_config = malloc(sizeof(ledc_config_t));

    ret = nvs_flash_init();     /* 初始化NVS */
    if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ESP_ERROR_CHECK(nvs_flash_init());
    }

/* 启动定时器时,根据给出的分辨率和占空率参数自动选择ledc源时钟 */
ledc_config->clk_cfg         = LEDC_USE_PLL_DIV_CLK;    
/* 选择哪个定时器计数(LEDC_TIMER_0~LEDC_TIMER_3) */
ledc_config->timer_num       = LEDC_TIMER_0;            
/* 1KHz(系统自动计算分配系数,并提供freq_hz频率给到定时器) */
ledc_config->freq_hz         = 1000;                    
/* 设置定时器最大计数值(请看ESP32-P4技术参考手册表32.4.1) */
ledc_config->duty_resolution = LEDC_TIMER_16_BIT;       
/* 设置输出通道(LEDC_CHANNEL_0 ~ LEDC_CHANNEL_7) */
ledc_config->channel         = LEDC_CHANNEL_0;          
/* 一个周期内占高电平时间(占空比) */
ledc_config->duty            = 0;                       
/* PWM信号输出那个管脚 */
ledc_config->gpio_num        = LEDC_PWM_CH0_GPIO;       
    ledc_init(ledc_config);

    while(1)
    {
        vTaskDelay(pdMS_TO_TICKS(100));

        if (dir == 1)
        {
            ledpwmval += 5; 	/* dir==1 ledpwmval递增 */
        }
        else
        {
            ledpwmval -= 5; 	/* dir==0 ledpwmval递减 */
        }

        if (ledpwmval > 95)
        {
            dir = 0;        	/* ledpwmval到达100后,方向为递减 */
        }
        if (ledpwmval < 5)
        {
            dir = 1;        	/* ledpwmval递减到5后,方向改为递增 */
        }

        ledc_pwm_set_duty(ledc_config, ledpwmval);		/* 设置占空比 */
    }
}

app_main函数是ESP32应用程序的主入口,用于初始化系统并控制LED灯的亮度。首先,函数通过调用NVS(非易失性存储器)初始化,处理任何可能的错误。接着,它动态分配内存以存储LEDC配置结构体,并设置定时器、频率、占空比分辨率和输出通道等参数。通过调用 ledc_init 函数,完成LEDC的初始化。
在主循环中,函数实现了LED亮度的渐变效果,每100毫秒更新一次占空比。根据 ledpwmval 的值,LED的亮度逐渐递增至最高点后,再逐渐递减。当亮度达到临界值时,调整方向以实现循环渐变。最终,通过ledc_pwm_set_duty函数更新当前占空比,确保LED灯的亮度变化平滑且连续。

17.4 下载验证

程序下载完成后,系统通过调节PWM占空比来控制LED亮度,从而实现呼吸灯效果。