当前位置: 首页 > news >正文

第十八章 ESP32S3 HW_PWM 实验

        本章使用 ESP32-S3 LED控制器(LEDC)。上一章节我们介绍了通过软件改变 PWM占空比。这一章学习硬件改变 PWM 占空比的运用。
本章分为如下几个小节:
18.1 PWM 简介
18.2 硬件设计
18.3 程序设计
18.4 下载验证

18.1 PWM 简介

        关于 PWM 的一些知识,在第十七章已经介绍过,在此便不做赘述。使用硬件的方式改变 PWM 占空比与使用软件的方式改变 PWM 占空比的不同之处在于, LED PWM 控制器硬件可逐渐改变占空比的数值, 要使用此功能,需用函数 ledc_fade_func_install() 使能渐变, 之后用下列可用渐变函数之一配置:
(1) ledc_set_fade_with_time()
(2) ledc_set_fade_with_step()
(3) ledc_set_fade()
最后需要调用 ledc_fade_start() 开启渐变。

18.2 硬件设计

18.2.1 例程功能
通过硬件改变 PWM 的形式使得 LED 由亮到暗,再由暗到亮,依次循环。
18.2.2 硬件资源
1. LED
LED - IO1
2. 定时器1
通道 1 - IO1

18.2.3 原理图
本章实验使用的定时器1 为 ESP32-S3 的片上资源,因此没有对应的连接原理图。

18.3 程序设计

18.3.1 程序流程图
本实验的程序流程图:

图 18.3.1.1 HW_PWM 实验程序流程图

18.3.2 HW_PWM 函数解析
ESP-IDF 提供了一套 API 来配置 PWM。要使用此功能,需要导入必要的头文件:

#include "driver/ledc.h"

        接下来,介绍一些常用的 HW_PWM 函数,这些函数的描述及其作用如下:
(1)使能渐变
LED PWM控制器硬件可逐渐改变占空比的数值。开启此功能,需要用函数ledc_fade_func_install()使能渐变,该函数原型如下所示:

esp_err_t ledc_fade_func_install(int intr_alloc_flags);

        该函数的形参描述,如下表所示:

形参描述推荐值
intr_alloc_flags用于分配中断的标志0(默认) 或 ESP_INTR_FLAG_IRAM

表 18.3.2.1 函数 ledc_fade_func_install()形参描述

        返回值: ESP_OK 表示配置成功,其他表示配置失败。

调用此函数后会启用以下关键组件:

1)渐变后台任务​​ (FreeRTOS任务)

  • 优先级:LEDC_FADE_SERVICE_TASK_PRI
  • 栈大小:LEDC_FADE_SERVICE_TASK_STACK

2)中断服务程序​​ (ISR)
3)​​内存资源分配​​

  • 队列:用于传递渐变指令
  • 缓冲区:存储渐变状态

(2)设置 LEDC 渐变功能
经过上一步渐变功能的配置后,需要设置占空比以及渐变时长,该函数原型如下所示:

esp_err_t ledc_set_fade_with_time(ledc_mode_t speed_mode,ledc_channel_t channel,uint32_t target_duty,int max_fade_time_ms);

        该函数的形参描述,如下表所示:

形参描述
speed_mode速度模式选择:
LEDC_HIGH_SPEED_MODE
LEDC_LOW_SPEED_MODE
channelLEDC 通道:
(0 - LEDC_CHANNEL_MAX-1),从 ledc_channel_t 中选择
target_duty目标占空比,0 - (2ⁿ-1),n为分辨率位数
max_fade_time_ms最大渐变时间(ms),≥ LEDC_FADE_TIME_MIN_MS

表 18.3.2.2 函数 ledc_set_fade_with_time()形参描述

        返回值:ESP_OK 表示配置成功,其他表示配置失败。

(3)开启渐变
设置占空比以及渐变时长后,便可开启渐变功能,该函数原型如下所示:

esp_err_t ledc_fade_start(ledc_mode_t speed_mode,ledc_channel_t channel,ledc_fade_mode_t fade_mode);

        该函数的形参描述,如下表所示:

形参描述
speed_mode速度模式选择:
LEDC_HIGH_SPEED_MODE
LEDC_LOW_SPEED_MODE
channelLEDC 通道:
(0 - LEDC_CHANNEL_MAX-1),从 ledc_channel_t 中选择
fade_mode渐变模式, ledc_fade_mode_t 为索引,有几个模式可选:
LEDC_FADE_NO_WAIT
LEDC_FADE_WAIT_DONE
LEDC_FADE_MAX

表 18.3.2.3 函数 ledc_fade_start()形参描述

        返回值: ESP_OK 表示配置成功,其他表示配置失败。

18.3.3 HW_PWM 驱动解析
在 IDF 版的 08-2_hw_pwm 例程中,在 08-2_hw_pwm \components\BSP 路径下新增了一个 PWM文件夹,用于存放 pwm.c和 pwm.h这两个文件。其中, pwm.h文件负责声明HW_PWM相关的函数和变量,而 pwm.c 文件则实现了 HW_PWM 的驱动代码。下面,我们将详细解析这两个文件的实现内容。

        注意:这个08-2_hw_pwm示例在IDF 5.4.2,程序下载后并没有实现预期的呼吸灯,原因是ledc_timer_config(deconfigure参数) 和 ledc_channel_config(sleep_mode参数)校验失败了,需要显示初始化为正确值。

(1) pwm.h 文件

/* 引脚以及重要参数定义 */
#define LEDC_PWM_TIMER          LEDC_TIMER_0        /* 使用定时器0 */
#define LEDC_PWM_MODE           LEDC_LOW_SPEED_MODE /* 模式设定必须使用LEDC低速模式 */
#define LEDC_PWM_CH0_GPIO       GPIO_NUM_1          /* LED控制器通道对应GPIO */
#define LEDC_PWM_CH0_CHANNEL    LEDC_CHANNEL_0      /* LED控制器通道号 */
#define LEDC_PWM_DUTY           8000                /* 渐变的变大最终目标占空比 */
#define LEDC_PWM_FADE_TIME      3000                /* 完整呼吸周期 3秒 */
#define LEDC_DUTY_RES           LEDC_TIMER_13_BIT   /* 13位分辨率 (8192级亮度)  */
#define LEDC_FREQ               5000                /* PWM频率 5KHz *//* 函数声明 */
void pwm_init(uint8_t resolution, uint16_t freq);   /* 初始化PWM */
void pwm_set_duty(uint32_t duty);                   /* PWM占空比设置 */

(2) pwm.c 文件

        在 PWM 初始化函数中,配置好 LEDC 定时器的频率、占空比、定时器模式以及定时器通道,并在 pwm_set_duty()函数中, 调用函数 ledc_set_fade_with_time ()用以设置占空比和渐变时长。此时,我们需要再次调用该函数,将占空比配置为 0,最后调用函数 ledc_fade_start ()开启渐变。

static const char *TAG = "BreathingLED";/*** @brief       初始化PWM* @param       resolution:PWM占空比分辨率*              freq: PWM信号频率* @retval      无*/
void pwm_init(uint8_t resolution, uint16_t freq)
{ledc_timer_config_t ledc_timer;                 /* LEDC定时器句柄 */ledc_channel_config_t ledc_channel;             /* LEDC通道配置句柄 *//* 配置LEDC定时器 */ledc_timer.duty_resolution = resolution;        /* PWM占空比分辨率 */ledc_timer.freq_hz = freq;                      /* PWM信号频率 */ledc_timer.speed_mode = LEDC_PWM_MODE;          /* 定时器模式 */ledc_timer.timer_num = LEDC_PWM_TIMER;          /* 定时器序号 */ledc_timer.clk_cfg = LEDC_AUTO_CLK;             /* LEDC时钟源 */ledc_timer.deconfigure = false;esp_err_t ret = ledc_timer_config(&ledc_timer);                 /* 配置定时器 */if (ret != ESP_OK) {ESP_LOGE(TAG, "定时器配置失败: %s", esp_err_to_name(ret));return;}/* 配置LEDC通道 */ledc_channel.gpio_num = LEDC_PWM_CH0_GPIO;      /* LED控制器通道对应引脚 */ledc_channel.speed_mode = LEDC_PWM_MODE;  /* LEDC睡眠模式 */ledc_channel.channel = LEDC_PWM_CH0_CHANNEL;    /* LEDC控制器通道号 */ledc_channel.intr_type = LEDC_INTR_DISABLE;     /* LEDC失能中断 */ledc_channel.timer_sel = LEDC_PWM_TIMER;        /* 定时器序号 */ledc_channel.duty = 0;                          /* 占空比值 */ledc_channel.hpoint = 0;ledc_channel.sleep_mode = LEDC_SLEEP_MODE_NO_ALIVE_NO_PD;ret = ledc_channel_config(&ledc_channel);             /* 配置LEDC通道 */if (ret != ESP_OK) {ESP_LOGE(TAG, "通道配置失败: %s", esp_err_to_name(ret));return;}ret = ledc_fade_func_install(0);                      /* 使能渐变(该函数不可或缺) */if (ret != ESP_OK) {ESP_LOGE(TAG, "渐变功能安装失败: %s", esp_err_to_name(ret));return;}
}/*** @brief       PWM占空比设置* @param       duty:PWM占空比* @retval      无*/
void pwm_set_duty(uint32_t duty)
{// 渐亮 (0 -> 最大亮度)esp_err_t ret = ledc_set_fade_with_time(LEDC_PWM_MODE, LEDC_PWM_CH0_CHANNEL, duty, LEDC_PWM_FADE_TIME/2);   /* 设置占空比以及渐变时长 */if (ret != ESP_OK) {ESP_LOGE(TAG, "设置渐亮失败: %s", esp_err_to_name(ret));}ret = ledc_fade_start(LEDC_PWM_MODE, LEDC_PWM_CH0_CHANNEL, LEDC_FADE_NO_WAIT);                  /* 开始渐变 */if (ret != ESP_OK) {ESP_LOGE(TAG, "启动渐亮失败: %s", esp_err_to_name(ret));}vTaskDelay(pdMS_TO_TICKS(LEDC_PWM_FADE_TIME / 2));// 渐暗 (最大亮度 -> 0)ret = ledc_set_fade_with_time(LEDC_PWM_MODE, LEDC_PWM_CH0_CHANNEL, 0, LEDC_PWM_FADE_TIME/2);      /* 设置占空比以及渐变时长 */if (ret != ESP_OK) {ESP_LOGE(TAG, "设置渐暗失败: %s", esp_err_to_name(ret));}ret = ledc_fade_start(LEDC_PWM_MODE, LEDC_PWM_CH0_CHANNEL, LEDC_FADE_NO_WAIT);                  /* 开始渐变 */if (ret != ESP_OK) {ESP_LOGE(TAG, "启动渐暗失败: %s", esp_err_to_name(ret));}vTaskDelay(pdMS_TO_TICKS(LEDC_PWM_FADE_TIME / 2));
}


18.3.4 CMakeLists.txt 文件
打开本实验 BSP 下的 CMakeLists.txt 文件,其内容如下所示:

set(src_dirsPWM)set(include_dirsPWM)set(requiresdriver)idf_component_register(SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)

18.3.5 实验应用代码
打开 main/main.c 文件,该文件定义了工程入口函数,名为 app_main。该函数代码如下。

/*** @brief       程序入口* @param       无* @retval      无*/
void app_main(void)
{esp_err_t ret;// 计算最大占空比值 (2^13 - 1 = 8191)const uint32_t max_duty = (1 << LEDC_DUTY_RES) - 1;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());ret = nvs_flash_init();}pwm_init(LEDC_DUTY_RES, LEDC_FREQ);   /* 初始化PWM */while(1) {pwm_set_duty(max_duty);    /* 设置占空比 */}
}
18.4 下载验证

        在完成编译和烧录后,可以看到板子上的 LED 先由暗再逐渐变亮,以此循环,实现了呼吸
灯的效果。

http://www.xdnf.cn/news/1412893.html

相关文章:

  • 基于springboot的摄影器材租赁回收系统
  • Docker 容器(二)
  • 大模型面试题剖析:PPO 与 GRPO 强化学习算法核心差异解析
  • 大模型应用总结
  • shell编程之shell脚本基础(未完待续)
  • 飞牛Docker部署免费frp内网穿透
  • 2025.8.18-2025.8.24第35周:备稿演讲有进步
  • 从零构建中间件:Tower 核心设计的来龙去脉
  • AI 编程新玩法:用 yunqi-saas-kit 框架制作小游戏,看广告变现轻松赚钱​
  • 【Linux】linux进程 vs 线程
  • VisionProC#联合编程火花塞距离检测与VisionPro操作
  • Augment 宣布 Auggie CLI正式向所有用户开放
  • 旦旦的农场打卡计划
  • 刷题日记0831
  • Animal fats vs. seed oils
  • 云渲染如何重新定义视觉艺术的边界
  • DOS 程序
  • DBeaver:一款免费开源的数据库管理工具
  • BLE广播与扫描
  • 前端学习——CSS
  • 随时随地开发:通过 FRP 搭建从 Ubuntu 到 Windows 的远程 Android 调试环境
  • Hutool DsFactory多数据源切换
  • 企业级架构师综合能力项目案例一(各种组件集群搭建+SpringBoot整合)
  • 决策思维研究体系主要构成
  • Python入门教程之类型判别
  • STM32F103C8T6的智能医疗药品存储柜系统设计与华为云实现
  • 解决git push时的错误提示:“error: src refspec master does not match any”
  • 漏洞基础与文件包含漏洞原理级分析
  • 【重学MySQL】九十四、MySQL请求到响应过程中字符集的变化
  • 盛最多水的容器:双指针法的巧妙运用(leetcode 11)