|
第十七章 LEDC实验
1)实验平台:正点原子DNESP32P4开发板
2)章节摘自【正点原子】ESP32-P4开发指南— V1.0
3)购买链接:https://detail.tmall.com/item.htm?id=873309579825
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子DNESP32S3开发板技术交流群:132780729
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架构图。
图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外设的工作流程结构。
图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字段来控制,该字段描述如下所示。
图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位)。以下是该字段的详细描述。
图17.1.1.3 分频系数配置
分频值LEDC_CLK_DIV的计算方式如下:
上述公式中,A代表配置的整数分频系数,B代表小数分频系数。配置完成后,结合LEDC_CLK时钟和LEDC_CLK_DIV分频系数,可以得到以下公式:
上述 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字段描述。
图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时,计数器会被重置。这些字段描述如下所示。
图17.1.1.5 暂停和重置计数器
最后,可以通过配置LEDC_CHn_CONF0_REG寄存器(n: 0~7)中的LEDC_TIMER_SEL_CHn字段来选择用于PWM生成器的计数器。具体而言,该字段允许用户指定所需的计数器,以便为PWM信号提供计数值。通过灵活配置该字段,可以实现不同计数器与PWM生成器之间的关联,从而满足各种应用需求,该字段描述如下所示。
图17.1.1.6 配置PWM生成器的计数来源
配置完计数值来源后,我们就可以利用PWM生成器来生成PWM信号。接下来,笔者将带领大家深入了解PWM生成器的原理和配置方法。
3,PWM输出生成
为了生成PWM信号,需要一个PWM生成器(PWMn)和一个定时器(Timerx)。每个PWM生成器可以通过设置LEDC_TIMER_SEL_CHn来单独配置,以使用四个定时器中的一个来生成PWM输出。PWM信号生成如下图所示。
图17.1.1.7 PWM生成框图
我们知道,每个PWM生成器都包含一个比较器和两个多路复用器。PWM生成器将定时器的20位计数器值(Timerx_cnt)与两个触发值Hpointn和Lpointn进行比较。当定时器的计数器值等于Hpointn或Lpointn时,PWM信号分别为高或低,具体如下:
1)如果 Timerx_cnt==Hpointn,则sig_outn为1。
2)如果 Timerx_cnt==Lpointn,则sig_outn为0。
上图展示了Hpointn和Lpointn如何用于生成固定占空比的PWM输出信号,其中Hpointn数值由LEDC_CHn_HPOINT_REG (n:0~7)寄存器中的LEDC_HPOINT_CHn字段配置,该字段描述如下。
图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]字段描述如下。
图17.1.1.9 配置通道信号占空比
最后,我们通过LEDC_CHn_CONF0_REG(n: 0~7)寄存器中的LEDC_SIG_OUT_EN_CHn和LEDC_IDLE_LV_CHn字段来配置PWM在空闲状态下的电平以及开启PWM通道输出。这两个字段的描述如下。
图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,其计算公式为:
根据上述公式,可以计算所需的占空比分辨率:
下表列出了常用频率及其对应的分辨率。通过这种方式,开发者能够灵活地调整PWM信号的频率和分辨率,以满足不同应用的需求。
表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寄存器中实现软件复位。
图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);
复制代码 函数形参:
表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);
复制代码 函数形参:
表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通道的工作模式,以满足不同的定时需求。以下是各个参数的说明及可选项。
表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);
复制代码 函数形参:
表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);
复制代码 函数形参:
表17.3.1.5 ledc_update_duty函数形参描述
返回值:
ESP_OK表示成功;
ESP_ERR_INVALID_ARG表示参数错误。
至于其他LEDC配置函数,请读者查阅ESP-IDF的LEDC驱动文件。
17.3.2 程序流程图
图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,代码如下:
- <font size="3">/**</font>
- <font size="3"> * @brief 初始化LEDC</font>
- <font size="3"> * [url=home.php?mod=space&uid=271674]@param[/url] ledc_config: ledc配置结构体</font>
- <font size="3"> * @retval 无</font>
- <font size="3"> */</font>
- <font size="3">void ledc_init(ledc_config_t *ledc_config)</font>
- <font size="3">{</font>
- <font size="3">ledc_config->duty = ledc_duty_pow(ledc_config->duty, 2,</font>
- <font size="3"> ledc_config->duty_resolution);</font>
- <font size="3"> ledc_timer_config_t ledc_timer = {</font>
- <font size="3"> .speed_mode = LEDC_LOW_SPEED_MODE, /* 低速模式*/</font>
- <font size="3"> .duty_resolution = ledc_config->duty_resolution, /* 占空比分辨率 */</font>
- <font size="3"> .timer_num = ledc_config->timer_num, /* 定时器选择 */</font>
- <font size="3"> .freq_hz = ledc_config->freq_hz, /* 设置频率 */</font>
- <font size="3"> .clk_cfg = ledc_config->clk_cfg /* 设置时钟源 */</font>
- <font size="3"> };</font>
- <font size="3"> /* 配置ledc定时器 */</font>
- <font size="3"> ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));</font>
- <font size="3"> /* 配置pwm通道 */</font>
- <font size="3"> ledc_channel_config_t ledc_channel = {</font>
- <font size="3"> .speed_mode = LEDC_LOW_SPEED_MODE, /* 低速模式 */</font>
- <font size="3"> .channel = ledc_config->channel, /* PWM输出通道 */</font>
- <font size="3"> .timer_sel = ledc_config->timer_num, /* 那个定时器提供计数值 */</font>
- <font size="3"> .intr_type = LEDC_INTR_DISABLE, /* 关闭LEDC中断 */</font>
- <font size="3"> .gpio_num = ledc_config->gpio_num, /* 输出GPIO管脚 */</font>
- <font size="3"> .duty = ledc_config->duty, /* 占空比 */</font>
- <font size="3"> .hpoint = 0 /* 设置hpoint数值 */</font>
- <font size="3"> };</font>
- <font size="3"> /* Lpoint = duty + hpoint */</font>
- <font size="3"> /* 配置pwm通道 */</font>
- <font size="3"> ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));</font>
- <font size="3">}</font>
复制代码 该函数用于初始化LEDC模块。它接收一个指向LEDC配置结构体的指针,计算占空比并配置定时器和PWM通道。通过调用相应的配置函数,它设置定时器的工作模式、频率和时钟源,确保LEDC在预期的模式下工作。最终,它注册PWM通道,准备好为后续的PWM信号输出提供支持。
接下来,我们来看一下ledc_duty_pow函数,它根据占空比分辨率来计算占空比,即高电平时间。如下代码所示。
- <font size="3">/**</font>
- <font size="3"> * @brief 计算一个周期的占空比计数值</font>
- <font size="3"> * @param duty: 占空比</font>
- <font size="3"> * @param m^n: 输入参数</font>
- <font size="3"> * @retval 返回一个周期的占空比计数值</font>
- <font size="3"> */</font>
- <font size="3">uint32_t ledc_duty_pow(uint32_t duty, uint8_t m, uint8_t n)</font>
- <font size="3">{</font>
- <font size="3"> uint32_t result = 1;</font>
- <font size="3"> while (n--)</font>
- <font size="3"> {</font>
- <font size="3"> result *= m;</font>
- <font size="3"> }</font>
- <font size="3"> return (result * duty) / 100;</font>
- <font size="3">}</font>
复制代码 该函数用于计算一个周期的占空比计数值。通过输入的占空比和基数、指数参数,该函数计算出一个相应的计数值。它在实际占空比设置中起到辅助作用,将占空比从百分比转换为对应的计数值,以便于后续的LEDC配置和使用。
计算占空比的数值后,使用ledc_pwm_set_duty函数设置和更新占空比,如下代码所示。
- <font size="3">/**</font>
- <font size="3"> * @brief 设置占空比</font>
- <font size="3"> * @param ledc_config: ledc配置结构体</font>
- <font size="3"> * duty: 占空比</font>
- <font size="3"> * @retval 无</font>
- <font size="3"> */</font>
- <font size="3">void ledc_pwm_set_duty(ledc_config_t *ledc_config, uint16_t duty)</font>
- <font size="3">{</font>
- <font size="3">ledc_config->duty = ledc_duty_pow(duty, 2, ledc_config->duty_resolution); </font>
- <font size="3">/* 设置占空比 */</font>
- <font size="3">ledc_set_duty(LEDC_LOW_SPEED_MODE, ledc_config->channel, ledc_config->duty); </font>
- <font size="3">/* 更新占空比 */ </font>
- <font size="3"> ledc_update_duty(LEDC_LOW_SPEED_MODE, ledc_config->channel); </font>
- <font size="3">}</font>
复制代码 该函数用于设置LEDC通道的占空比。接收一个指向LEDC配置结构体的指针和新的占空比值,通过计算新的占空比后,调用ledc_set_duty设置该通道的占空比,并随后调用ledc_update_duty更新设置。该函数确保在运行时能够动态调整PWM信号的占空比,以实现不同的输出效果。
2,CMakeLists.txt文件
本例程的功能实现主要依靠LEDC驱动。要在main函数中,成功调用LEDC文件中的内容,就得需要修改BSP文件夹下的CMakeLists.txt文件(重点看红色内容),修改如下:
- <font size="3">set(src_dirs</font>
- <font size="3"> LEDC)</font>
- <font size="3">set(include_dirs</font>
- <font size="3"> LEDC)</font>
- <font size="3">set(requires</font>
- <font size="3"> driver)</font>
- <font size="3">idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs}</font>
- <font size="3"> REQUIRES ${requires})</font>
- <font size="3">component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)</font>
复制代码 3,main.c驱动代码
在main.c里面编写如下代码。
- <font size="3">void app_main(void)</font>
- <font size="3">{</font>
- <font size="3"> esp_err_t ret;</font>
- <font size="3"> uint8_t dir = 1;</font>
- <font size="3"> uint16_t ledpwmval = 0;</font>
- <font size="3"> ledc_config_t *ledc_config = malloc(sizeof(ledc_config_t));</font>
- <font size="3"> ret = nvs_flash_init(); /* 初始化NVS */</font>
- <font size="3"> if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)</font>
- <font size="3"> {</font>
- <font size="3"> ESP_ERROR_CHECK(nvs_flash_erase());</font>
- <font size="3"> ESP_ERROR_CHECK(nvs_flash_init());</font>
- <font size="3"> }</font>
- <font size="3">/* 启动定时器时,根据给出的分辨率和占空率参数自动选择ledc源时钟 */</font>
- <font size="3">ledc_config->clk_cfg = LEDC_USE_PLL_DIV_CLK; </font>
- <font size="3">/* 选择哪个定时器计数(LEDC_TIMER_0~LEDC_TIMER_3) */</font>
- <font size="3">ledc_config->timer_num = LEDC_TIMER_0; </font>
- <font size="3">/* 1KHz(系统自动计算分配系数,并提供freq_hz频率给到定时器) */</font>
- <font size="3">ledc_config->freq_hz = 1000; </font>
- <font size="3">/* 设置定时器最大计数值(请看ESP32-P4技术参考手册表32.4.1) */</font>
- <font size="3">ledc_config->duty_resolution = LEDC_TIMER_16_BIT; </font>
- <font size="3">/* 设置输出通道(LEDC_CHANNEL_0 ~ LEDC_CHANNEL_7) */</font>
- <font size="3">ledc_config->channel = LEDC_CHANNEL_0; </font>
- <font size="3">/* 一个周期内占高电平时间(占空比) */</font>
- <font size="3">ledc_config->duty = 0; </font>
- <font size="3">/* PWM信号输出那个管脚 */</font>
- <font size="3">ledc_config->gpio_num = LEDC_PWM_CH0_GPIO; </font>
- <font size="3"> ledc_init(ledc_config);</font>
- <font size="3"> while(1)</font>
- <font size="3"> {</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(100));</font>
- <font size="3"> if (dir == 1)</font>
- <font size="3"> {</font>
- <font size="3"> ledpwmval += 5; /* dir==1 ledpwmval递增 */</font>
- <font size="3"> }</font>
- <font size="3"> else</font>
- <font size="3"> {</font>
- <font size="3"> ledpwmval -= 5; /* dir==0 ledpwmval递减 */</font>
- <font size="3"> }</font>
- <font size="3"> if (ledpwmval > 95)</font>
- <font size="3"> {</font>
- <font size="3"> dir = 0; /* ledpwmval到达100后,方向为递减 */</font>
- <font size="3"> }</font>
- <font size="3"> if (ledpwmval < 5)</font>
- <font size="3"> {</font>
- <font size="3"> dir = 1; /* ledpwmval递减到5后,方向改为递增 */</font>
- <font size="3"> }</font>
- <font size="3"> ledc_pwm_set_duty(ledc_config, ledpwmval); /* 设置占空比 */</font>
- <font size="3"> }</font>
- <font size="3">}</font>
复制代码 app_main函数是ESP32应用程序的主入口,用于初始化系统并控制LED灯的亮度。首先,函数通过调用NVS(非易失性存储器)初始化,处理任何可能的错误。接着,它动态分配内存以存储LEDC配置结构体,并设置定时器、频率、占空比分辨率和输出通道等参数。通过调用 ledc_init 函数,完成LEDC的初始化。
在主循环中,函数实现了LED亮度的渐变效果,每100毫秒更新一次占空比。根据 ledpwmval 的值,LED的亮度逐渐递增至最高点后,再逐渐递减。当亮度达到临界值时,调整方向以实现循环渐变。最终,通过ledc_pwm_set_duty函数更新当前占空比,确保LED灯的亮度变化平滑且连续。
17.4 下载验证
程序下载完成后,系统通过调节PWM占空比来控制LED亮度,从而实现呼吸灯效果。
|