10. PWM输出

news/2025/3/18 22:22:57/文章来源:https://www.cnblogs.com/FlurryHeart/p/18779953

一、PWM简介

  PWM(Pulse Width Modulation),简称脉宽调制,是一种将模拟信号变为脉冲信号的计数。PWM 可以控制 LED 亮度、直流电机的转速等。

  PWM 的主要参数如下:

  • PWM 频率。PWM 频率是 PWM 信号在 1s 内从高电平到低电平再回到高电平的次数,也就是说 1s 内有多少个 PWM 周期,单位为 Hz。
  • PWM 周期。PWM 周期是 PWM 频率的倒数,即 \(T = \frac{1}{f}\),T 是 PWM 周期,f 是 PWM 频率。
  • PWM 占空比。PWM 占空比是指在一个 PWM 周期内,高电平的时间与整个周期时间的比例,取值范围为 0%~100%。

PWM占空比

二、LED PWM控制器介绍

  ESP32 S3 的 LED PWM 控制器,简写为 LEDC,用于生成控制 LED 的脉冲宽度调制信号。LED PWM 控制器具有八个独立的 PWM 生成器(即八个通道)。每个 PWM 生成器会从四个通用定时器中选择一个,以该定时器的计数值作为基准生成 PWM 信号。

  LED PWM 定时器如下图所示。

LED_PWM的定时器

  为了实现 PWM 输出,先需要设置指定通道的 PWM 参数:频率、分辨率、占空比,然后将该通道映射到指定引脚,该引脚输出对应通道的 PWM 信号,通道和引脚的关系所下图所示。

LED_PWM输出示意图

LED PWM 控制器可在没有 CPU 干预的情况下自动改变占空比,实现亮度以及颜色渐变。

三、LED PWM常用的函数

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

#include "driver/ledc.h"

3.1、配置LEDC使用的定时器

  我们可以使用 ledc_timer_config() 函数 配置 LEDC使用的定时器,其函数原型如下:

/*** @brief 配置LEDC使用的定时器* * @param timer_conf 配置 LEDC 定时器的结构体指针* @return esp_err_t ESP_OK表示配置成功,其它配置失败*/
esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);

  形参 timer_conf 指向配置 LEDC 定时器的结构体指针,它的定义如下:

typedef struct 
{ledc_mode_t speed_mode;                 // 速度模式ledc_timer_bit_t duty_resolution;       // PWM占空比分辨率ledc_timer_t  timer_num;                // 通道的定时器源uint32_t freq_hz;                       // PWM信号频率,单位为Hzledc_clk_cfg_t clk_cfg;                 // LED PWM的时钟来源bool deconfigure;  
} ledc_timer_config_t;

  成员 speed_mode 用来 设置速度模式,它的可选值如下:

typedef enum
{
#if SOC_LEDC_SUPPORT_HS_MODELEDC_HIGH_SPEED_MODE = 0, /*!< LEDC high speed speed_mode */
#endifLEDC_LOW_SPEED_MODE,      /*!< LEDC low speed speed_mode */LEDC_SPEED_MODE_MAX,      /*!< LEDC speed limit */
} ledc_mode_t;

ESP32 S3 仅支持低速模式。

  成员 duty_resolution 用来 设置 PWM 占空比分辨率

typedef enum 
{LEDC_TIMER_1_BIT = 1,   /*!< LEDC PWM duty resolution of  1 bits */LEDC_TIMER_2_BIT,       /*!< LEDC PWM duty resolution of  2 bits */LEDC_TIMER_3_BIT,       /*!< LEDC PWM duty resolution of  3 bits */LEDC_TIMER_4_BIT,       /*!< LEDC PWM duty resolution of  4 bits */LEDC_TIMER_5_BIT,       /*!< LEDC PWM duty resolution of  5 bits */LEDC_TIMER_6_BIT,       /*!< LEDC PWM duty resolution of  6 bits */LEDC_TIMER_7_BIT,       /*!< LEDC PWM duty resolution of  7 bits */LEDC_TIMER_8_BIT,       /*!< LEDC PWM duty resolution of  8 bits */LEDC_TIMER_9_BIT,       /*!< LEDC PWM duty resolution of  9 bits */LEDC_TIMER_10_BIT,      /*!< LEDC PWM duty resolution of 10 bits */LEDC_TIMER_11_BIT,      /*!< LEDC PWM duty resolution of 11 bits */LEDC_TIMER_12_BIT,      /*!< LEDC PWM duty resolution of 12 bits */LEDC_TIMER_13_BIT,      /*!< LEDC PWM duty resolution of 13 bits */LEDC_TIMER_14_BIT,      /*!< LEDC PWM duty resolution of 14 bits */
#if SOC_LEDC_TIMER_BIT_WIDTH > 14LEDC_TIMER_15_BIT,      /*!< LEDC PWM duty resolution of 15 bits */LEDC_TIMER_16_BIT,      /*!< LEDC PWM duty resolution of 16 bits */LEDC_TIMER_17_BIT,      /*!< LEDC PWM duty resolution of 17 bits */LEDC_TIMER_18_BIT,      /*!< LEDC PWM duty resolution of 18 bits */LEDC_TIMER_19_BIT,      /*!< LEDC PWM duty resolution of 19 bits */LEDC_TIMER_20_BIT,      /*!< LEDC PWM duty resolution of 20 bits */
#endifLEDC_TIMER_BIT_MAX,
} ledc_timer_bit_t;

  成员 timer_num 用来 设置通道的定时器源,它的可选值如下:

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;

  成员 clk_cfg 用来 设置 LED PWM 的时钟来源,它的可选值如下:

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_REF_TICK = SOC_MOD_CLK_REF_TICK,       /*!< Select REF_TICK 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;
typedef soc_periph_ledc_clk_src_legacy_t ledc_clk_cfg_t;

LED PWM 控制器主要用于驱动 LED。该控制器 PWM 占空比设置的分辨率范围较广。比如,PWM 频率为 5 kHz 时,占空比分辨率最大可为 13 位。这意味着占空比可为 0 至 100% 之间的任意值,分辨率为 ~0.012%(\(2^{13} = 8192\) LED 亮度的离散电平)。然而,这些参数取决于为 LED PWM 控制器定时器计时的时钟信号,LED PWM 控制器为通道提供时钟。

PWM 频率越高,占空比分辨率越低,反之亦然。

3.2、通道配置函数

  我们可以使用 ledc_channel_config() 函数 配置 LEDC 的通道,其函数原型如下:

/*** @brief 配置LEDC通道* * @param ledc_conf 指向配置LEDC通道的结构体指针* @return esp_err_t ESP_OK表示配置成功,其它配置失败*/
esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf);

  成员 ledc_conf指向配置LEDC通道的结构体指针,它的定义如下:

typedef struct 
{int gpio_num;                       // 配置输出引脚ledc_mode_t speed_mode;             // 速度模式ledc_channel_t channel;             // LEDC的输出通道ledc_intr_type_t intr_type;         // 配置中断ledc_timer_t timer_sel;             // 选择通道的定时器源uint32_t duty;                      // LEDC 通道的占空比设置int hpoint;                         // 表示占空比对应的时钟计数值struct {unsigned int output_invert: 1;  // 启用(1)或禁用(0)gpio输出反相} flags;
} ledc_channel_config_t;

  成员 channel 用来 设置 LEDC 的输出通道,它的可选值如下:

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 > 6LEDC_CHANNEL_6,     /*!< LEDC channel 6 */LEDC_CHANNEL_7,     /*!< LEDC channel 7 */
#endifLEDC_CHANNEL_MAX,
} ledc_channel_t;

  成员 intr_type 用来 配置中断,它的可选值如下:

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

  成员 timer_sel 用来 选择通道的定时器源,它的可选值如下:

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;

  成员 duty 用来 设置 LEDC 通道的占空比设置,占空比设定范围为 0 ~ \(2^{duty\_resolution}\)

3.3、改变PWM占空比

  我们可以调用函数 ledc_set_duty()设置新的占空比。之后,调用函数 ledc_update_duty() 使 新配置的占空比生效。要 查看当前设置的占空比,可使用 ledc_get_duty() 函数进行查看,该函数原型如下所示:

/*** @brief 设置LEDC的占空比* * @param speed_mode 速度模式* @param channel LEDC通道* @param duty 占空比* @return esp_err_t ESP_OK表示配置成功,其它配置失败*/
esp_err_t ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty);
/*** @brief 更新LEDC的占空比* * @param speed_mode 速度模式* @param channel LEDC通道* @return esp_err_t ESP_OK表示更新成功,其它更新失败*/
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
/*** @brief 获取LEDC的占空比* * @param speed_mode 速度模式* @param channel LEDC通道* @return uint32_t 占空比*/
uint32_t ledc_get_duty(ledc_mode_t speed_mode, ledc_channel_t channel);

3.4、使能渐变

  LED PWM 控制器硬件可逐渐改变占空比的数值。开启此功能,需要用函数 ledc_fade_func_install()使能渐变,该函数原型如下所示:

/*** @brief 使能渐变* * @param intr_alloc_flags 用于分配中断的标志* @return esp_err_t ESP_OK表示配置成功,其它表示配置失败*/
esp_err_t ledc_fade_func_install(int intr_alloc_flags);

3.5、设置LEDC渐变功能

  经过上一步渐变功能的配置后,我们还需要使用 函数 设置占空比以及渐变时长,该函数原型如下所示:

/*** @brief 设置LEDC渐变功能* * @param speed_mode 速度模式选择* @param channel LEDC通道* @param target_duty 目标占空比* @param max_fade_time_ms 最大渐变时间* @return esp_err_t ESP_OK表示配置成功,其它表示配置失败*/
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);

3.6、开启渐变

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

/*** @brief 开启渐变* * @param speed_mode 速度模式选择* @param channel LEDC通道* @param fade_mode 渐变模式* @return esp_err_t ESP_OK表示配置成功,其它表示配置失败*/
esp_err_t ledc_fade_start(ledc_mode_t speed_mode, ledc_channel_t channel, ledc_fade_mode_t fade_mode);

  形参 fade_mode 表示 渐变模式,它的可选值如下:

typedef enum 
{LEDC_FADE_NO_WAIT = 0,  /*!< LEDC fade function will return immediately */LEDC_FADE_WAIT_DONE,    /*!< LEDC fade function will block until fading to the target duty */LEDC_FADE_MAX,
} ledc_fade_mode_t;

四、实验例程

  这里,我们在【components】文件夹下的【peripheral】文件夹下的【inc】文件夹(用来存放头文件)新建一个 bsp_ledc.h 文件,在【components】文件夹下的【peripheral】文件夹下的【src】文件夹(用来存放源文件)新建一个 bsp_ledc.c 文件。

#ifndef __BSP_LEDC_H__
#define __BSP_LEDC_H__#include "driver/ledc.h"void bsp_ledc_init(ledc_timer_t time_num, ledc_channel_t ledc_channel, int gpio_num, ledc_timer_bit_t resolution, uint32_t freq, uint32_t duty);
void bsp_ledc_set_pwm_duty(ledc_channel_t ledc_channel, uint32_t duty);
void bsp_ledc_set_gradient_range(ledc_channel_t ledc_channel, uint32_t start_value, uint32_t end_value, int gradient_time);#endif // !__BSP_LEDC_H__
#include "bsp_ledc.h"/*** @brief LEDC初始化* * @param time_num 定时器编号* @param ledc_channel LEDC通道编号* @param gpio_num GPIO引脚编号* @param resolution LEDC占空比分辨率* @param freq LEDC频率* @param duty LEDC占空比*/
void bsp_ledc_init(ledc_timer_t time_num, ledc_channel_t ledc_channel, int gpio_num, ledc_timer_bit_t resolution, uint32_t freq, uint32_t duty)
{ledc_timer_config_t ledc_timer_config_struct = {0};ledc_channel_config_t ledc_channel_config_struct = {0};// 配置LEDC定时器ledc_timer_config_struct.clk_cfg = LEDC_AUTO_CLK;               // LEDC时钟源选择ledc_timer_config_struct.timer_num = time_num;                  // PWM定时器编号ledc_timer_config_struct.duty_resolution = resolution;          // PWM占空比分辨率ledc_timer_config_struct.freq_hz = freq;                        // PWM频率ledc_timer_config_struct.speed_mode = LEDC_LOW_SPEED_MODE;      // LEDC控制器工作模式ledc_timer_config(&ledc_timer_config_struct);                   // 配置定时器// 配置LEDC通道ledc_channel_config_struct.timer_sel = time_num;                // LEDC控制器通道对应定时器编号ledc_channel_config_struct.gpio_num = gpio_num;                 // LEDC控制器通道对应引脚ledc_channel_config_struct.channel = ledc_channel;              // LEDC控制器通道编号ledc_channel_config_struct.speed_mode = LEDC_LOW_SPEED_MODE;    // LEDC控制器通道工作模式ledc_channel_config_struct.intr_type = LEDC_INTR_DISABLE;       // LEDC失能中断ledc_channel_config_struct.duty = duty;                         // LEDC通道占空比ledc_channel_config(&ledc_channel_config_struct);               // 配置通道
}/*** @brief LEDC设置占空比* * @param ledc_channel LEDC通道编号* @param duty LEDC占空比*/
void bsp_ledc_set_pwm_duty(ledc_channel_t ledc_channel, uint32_t duty)
{ledc_set_duty(LEDC_LOW_SPEED_MODE, ledc_channel, duty);ledc_update_duty(LEDC_LOW_SPEED_MODE, ledc_channel);
}/*** @brief LEDC设置渐变占空比* * @param ledc_channel LEDC通道编号* @param start_value 起始占空比* @param end_value 结束占空比* @param gradient_time 渐变时长*/
void bsp_ledc_set_gradient_range(ledc_channel_t ledc_channel, uint32_t start_value, uint32_t end_value, int gradient_time)
{// 设置占空比以及渐变时长ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, ledc_channel, start_value, gradient_time);// 开始渐变ledc_fade_start(LEDC_LOW_SPEED_MODE, ledc_channel, LEDC_FADE_NO_WAIT);ledc_set_fade_with_time(LEDC_LOW_SPEED_MODE, ledc_channel, end_value, gradient_time);ledc_fade_start(LEDC_LOW_SPEED_MODE, ledc_channel, LEDC_FADE_NO_WAIT);
}

  修改【main】文件夹下的 main.c 文件。

#include "freertos/FreeRTOS.h"#include "bsp_ledc.h"#include "led.h"// app_main()函数是ESP32的入口函数,它是FreRTOS的一个任务,任务优先级是1
// main()函数是C语言入口函数,它会在编译过程中插入到二进制文件中的
void app_main(void)
{led_init(GPIO_NUM_0);bsp_ledc_init(LEDC_TIMER_0, LEDC_CHANNEL_0, GPIO_NUM_0, LEDC_TIMER_10_BIT, 1000, 0);ledc_fade_func_install(0);                                                  // 使能渐变while (1){bsp_ledc_set_gradient_range(LEDC_CHANNEL_0, 0, 1000, 3000);// 将一个任务延迟给定的滴答数,IDF中提供pdMS_TO_TICKS可以将指定的ms转换为对应的tick数vTaskDelay(pdMS_TO_TICKS(10));}
}

在 首次配置 LEDC 时,建议先配置定时器(调用函数 ledc_timer_config()),再配置通道(调用函数 ledc_channel_config())。这样可以确保 IO 引脚上的 PWM 信号自输出开始那一刻起,其频率就是正确的。

PWM 频率越高,占空比分辨率越低,反之亦然。

LED PWM 控制器 API 会在设定的频率和占空比分辨率超过 LED PWM 控制器硬件范围时,ED PWM 驱动器会报错,例如:E (196) ledc: requested frequency and duty resolution cannot be achieved, try reducing freq_hz or duty_resolution. div_param=128

LED PWM 控制器 API 会在设定的频率和占空比分辨率低于 LED PWM 控制器硬件范围时,LED PWM 驱动器也会报错,例如:E (196) ledc: requested frequency and duty resolution cannot be achieved, try increasing freq_hz or duty_resolution. div_param=128000000

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/901145.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

memcached DRDOS攻击实验

memcached DRDOS攻击实验 一、前提 关于drdos DRDoS(Distributed Reflection Denial of Service) 指的是利用IP Spoofing技术,构造带有受害者IP的数据包,发送给肉鸡,然后肉鸡对受害者IP做出大量回应,造成拒绝服务。 关于memcached命令 第一个是上传有效载荷Memcached set 命…

USB分类

USB特点差分传输 差错管理 恢复机制协议标准左边是原标准,后面是新标准USB1.1:低速、全速 USB2.高速 USB3.0 8b/10b编码 增加一对超高速差分线 USB3.1 采用 128b/132b编码,速度提升一倍 USB3.2 增加一对超高速传输通道,速度再次翻倍,只在C口上运行速度等级1.5Mbps/12M/480M…

0318- Wifi模式选择,信道选择

前言 在设置 H3C 路由器的时候(管理地址居然不是192.168.0.1,而是 http://192.168.124.1/),我发现 有下面的3种设置无线模式 无线频宽 无线信道Wifi 模式选择 推荐 n-only 也就是 Wifi-4 及以上的协议模式 其他的都是古早协议了,如果家里没有那种老设备就完全不需要考虑兼容…

20244214 实验一《Python程序设计》实验报告

20244214 2024-2025-2 《Python程序设计》实验一报告 课程:《Python程序设计》 班级: 2442 姓名: 张家乐 学号:20244214 实验教师:王志强 实验日期:2025年3月18日 必修/选修: 公选课 1.实验内容 1.熟悉Python开发环境; 2.练习Python运行、调试技能; 3.编写程序,练…

日报2025318

今日学习Element-plus组件 首先供上我们的element plus 行为指导库 https://element-plus.org/zh-CN el-button<div><el-button color="rgb(247, 137.4, 137.4)" plain style="color: white" > Komeigi Satori</el-button></div>…

BP算法

反向传播算法,简称BP算法,是多层神经元网络常用的学习算法之一,它建立在梯度下降算法的基础上。BP算法的完整学习过程由正向传播过程和反向传播过程组成。反向传播的过程是利用梯度下降算法,最小化代价函数 的过程。假设使用如图2.1所示的神经网络,该网络有一个输出层、两…

基于GA遗传算法的拱桥静载试验车辆最优布载matlab仿真

1.程序功能描述 基于GA遗传算法的拱桥静载试验车辆最优布载matlab仿真。主要是为了实现桥梁静载试验自动化布载(确定车辆位置使得满足加载效率ηq的要求,0.95≤ηq≤1.05),总体要求是ηq尽量靠近1,所用的加载车辆尽量少,进行布载耗时越少越好。 2.测试软件版本以及运…

攻防世界 guess_num WriteUp

WriteUp 题目信息 来源:攻防世界 名称:guess_num 分类:Pwn 描述:菜鸡在玩一个猜数字的游戏,但他无论如何都银不了,你能帮助他么题目链接: https://adworld.xctf.org.cn/challenges/list解题思路 首先使用DIE对文件进行查壳,发现这是一个无壳的64位ELF文件。于是直接使用…

第一次结对项目

问题 内容这个作业属于哪个课程 班级的链接这个作业要求在哪里 作业要求的链接这个作业的目标 实现一个自动生成小学四则运算题目的命令行程序姓名 朱江学号 3123004770GitHub仓库文件夹链接 第一次编程作业链接姓名 杨思程学号 3123004761GitHub仓库文件夹链接 第一次编程作业…

使用 EchoAPI 实现 API 断言的全面指南

API 断言是 API 测试中的一个关键部分。通过执行 API 断言,您可以验证 API 响应数据的准确性,从而增强 API 的可靠性和稳定性。在本文中,我们将介绍 API 断言的基础知识,并演示如何通过用户友好的 API 测试工具 Apipost 来轻松执行响应断言。 什么是 API 断言?API 断言是指…

变量与执行

目录变量变量命名规则变量的类型变量的赋值python代码的执行 变量 Python 是一种动态类型语言,这意味着你不需要显式地声明变量的类型,Python 会根据你赋给变量的值自动推断其类型。 变量命名规则 1、字母、数字、下划线:变量名可以包含字母(a-z, A-Z)、数字(0-9)和下划…