图17.3.1.1 SW_PWM实验程序流程图
17.3.2 SW_PWM函数解析
ESP-IDF提供了一套API来配置PWM。要使用此功能,需要导入必要的头文件:
#include "driver/ledc.h"
接下来,作者将介绍一些常用的SW_PWM函数,这些函数的描述及其作用如下:
1,配置LEDC使用的定时器为定时器1
需要注意的一点是,在首次配置LEDC时,建议先配置定时器(调用函数ledc_timer_config()),再配置通道(调用函数ledc_channel_config())。这样可以确保IO引脚上的PWM信号自输出开始那一刻起,其频率就是正确的。
方才提到,要设置定时器,可调用函数ledc_timer_config(),需要将一些参数的数据结构传递给该函数,该函数原型如下所示:
esp_err_t ledc_timer_config(const ledc_timer_config_t *timer_conf);
该函数的形参描述,如下表所示:
表17.3.2.1 函数ledc_timer_config ()形参描述
返回值:ESP_OK表示配置成功,其他配置失败。
该函数使用ledc_timer_config_t类型的结构体变量传入,该结构体的定义如下所示:
| 成员变量 | |
| | |
LEDC_HIGH_SPEED_MODE: 高速模式 |
|
timer_num : 通道的定时器源,定时器索引 ledc_timer_t | LEDC_TIMER_0 |
LEDC_TIMER_1 |
LEDC_TIMER_2 |
LEDC_TIMER_3 |
LEDC_TIMER_MAX |
freq_hz : PWM 信号频率,表示LEDC模块的定时器时钟频率设置,单位为Hz | |
duty_resolution : PWM 占空比分辨率。占空比分辨率通常用ledc_timer_bit_t设置,范围是 10 至 15 位。如需较低的占空比分辨率(上至 10,下至 1),可直接输入相应数值。相关参数请参考 ledc_timer_bit_t。 | |
| LEDC_AUTO_CLK: 启动定时器时,将根据给定的分辨率和占空率参数自动选择ledc源时钟; |
LEDC_USE_APB_CLK: 选择APB作为源时钟; |
LEDC_USE_RC_FAST_CLK: 选择“RC_FAST”作为源时钟; |
LEDC_USE_XTAL_CLK: 选择XTAL作为源时钟; |
LEDC_USE_RTC8M_CLK: ”LEDC_USE_RC_FAST_CLK” 的别名 |
表17.3.2.2 ledc_timer_config_t 结构体参数值描述
完成上述结构体参数配置之后,可以将结构传递给 ledc_timer_config () 函数,用以实例化SW_PWM并返回SW_PWM句柄。另外值得一提的是,时钟源同样可以限制PWM频率。选择的时钟源频率越高,可以配置的PWM频率上限就越高。
2,通道配置函数
该函数原型如下所示:
esp_err_t ledc_channel_config(const ledc_channel_config_t *ledc_conf);
该函数的形参描述,如下表所示:
表17.3.2.3 函数ledc_channel_config ()形参描述
返回值:ESP_OK表示配置成功,其他配置失败。
该函数使用ledc_channel_config_t类型的结构体变量传入,该结构体的定义如下所示:
| 成员变量 | |
| | |
| |
LEDC_HIGH_SPEED_MODE: 高速模式 |
channel : LEDC的输出通道(PWM的输出通道)。 | |
| 使能中断:LEDC_INTR_FADE_END 失能中断:LEDC_INTR_DISABLE |
timer_sel: 选择通道的定时器源。 定时器索引 ledc_timer_t | |
|
|
|
|
| 占空比设定范围为 0~2duty_resolution |
hpoint : led通道 hpoint 值。表示占空比对应的时钟计数值 | |
output_invert: 启用(1)或禁用(0)gpio输出反相 | |
表17.3.2.4 ledc_channel_config_t结构体参数值描述
完成上述结构体参数配置之后,可以将结构传递给 ledc_channel_config() 函数,用以实例化PWM通道。
3,改变PWM占空比
调用函数ledc_set_duty()可以设置新的占空比。之后,调用函数ledc_update_duty()使新配置生效。要查看当前设置的占空比,可使用get 函数ledc_get_duty(),该函数原型如下所示:
esp_err_t ledc_set_duty(ledc_mode_t speed_mode,
ledc_channel_t channel,
uint32_t duty);
该函数的形参描述,如下表所示:
| |
| 速度模式选择: LEDC_HIGH_SPEED_MODE LEDC_LOW_SPEED_MODE |
| LEDC通道: (0 - LEDC_CHANNEL_MAX-1),从 ledc_channel_t 中选择 |
| 设置led的负载,负载设置范围为: 0~[(2 duty_resolution ) – |
表17.3.2.5 函数ledc_set_dut ()形参描述
返回值:ESP_OK表示配置成功,其他配置失败。
4,改变PWM占空比
在上一步调用ledc_set_duty()设置新的占空比后,调用函数ledc_update_duty()使新配置生效,该函数原型如下所示:
esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel);
该函数的形参描述,如下表所示:
| |
| 速度模式选择: LEDC_HIGH_SPEED_MODE LEDC_LOW_SPEED_MODE |
| LEDC通道: (0 - LEDC_CHANNEL_MAX-1),从 ledc_channel_t 中选择 |
表17.3.2.6 函数ledc_update_duty()形参描述
返回值:ESP_OK表示配置成功,其他配置失败。
17.3.3 SW_PWM驱动解析
在IDF版的08-1_sw_pwm例程中,作者在08-1_sw_pwm \components\BSP路径下新增了一个PWM文件夹,用于存放pwm.c和pwm.h这两个文件。其中,pwm.h文件负责声明SW_PWM相关的函数和变量,而pwm.c文件则实现了SW_PWM的驱动代码。下面,我们将详细解析这两个文件的实现内容。
1,pwm.h文件
/* 引脚以及重要参数定义 */
#define LEDC_PWM_TIMER LEDC_TIMER_1 /* 使用定时器1 */
#define LEDC_PWM_CH0_GPIO GPIO_NUM_1 /* LED控制器通道对应GPIO */
#define LEDC_PWM_CH0_CHANNEL LEDC_CHANNEL_1 /* LED控制器通道号 */
2,pwm.c文件
/**
* @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_LOW_SPEED_MODE; /* 定时器模式 */
ledc_timer.timer_num = LEDC_PWM_TIMER; /* 定时器序号 */
ledc_timer.clk_cfg = LEDC_AUTO_CLK; /* LEDC时钟源 */
ledc_timer_config(&ledc_timer); /* 配置定时器 */
/* 配置LEDC通道 */
ledc_channel.gpio_num = LEDC_PWM_CH0_GPIO; /* LED控制器通道对应引脚 */
ledc_channel.speed_mode = LEDC_LOW_SPEED_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_config(&ledc_channel); /* 配置LEDC通道 */
}
/**
* @param duty:PWM占空比
* @retval 无
*/
void pwm_set_duty(uint16_t duty)
{
ledc_set_duty(LEDC_LOW_SPEED_MODE,
LEDC_PWM_CH0_CHANNEL,
duty); /* 设置占空比 */
ledc_update_duty(LEDC_LOW_SPEED_MODE,
LEDC_PWM_CH0_CHANNEL); /* 更新占空比 */
}
LEDC定时器设置好后,请配置所需的通道(ledc_channel_t)。配置通道需调用函数ledc_channel_config()。通道的配置与定时器设置类似,需向通道配置函数传递相应的结构体ledc_channel_config_t。此时,通道会按照ledc_channel_config_t的配置开始运作,并在选定的GPIO上生成由定时器指定的频率和占空比的PWM信号。
调用函数ledc_set_duty()可以设置新的占空比。之后,调用函数ledc_update_duty()使新配置生效。为了方便使用,笔者将这两个函数进行了“封装”,通过传参的形式来配置PWM占空比。
关于配置过程中所涉及到的结构体成员变量的含义以及用法,请读者们回顾本章节前面的内容。
17.3.4 CMakeLists.txt文件
打开本实验BSP下的CMakeLists.txt文件,其内容如下所示:
set(src_dirs
PWM)
set(include_dirs
PWM)
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)
上述的红色SW_PWM驱动需要由开发者自行添加,以确保SW_PWM驱动能够顺利集成到构建系统中。这一步骤是必不可少的,它确保了SW_PWM驱动的正确性和可用性,为后续的开发工作提供了坚实的基础。
17.3.5 实验应用代码
打开main/main.c文件,该文件定义了工程入口函数,名为app_main。该函数代码如下。
int main(void)
{
esp_err_t ret;
uint8_t dir = 1;
uint16_t ledrpwmval = 0;
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(10, 1000); /* 初始化PWM */
while (1)
{
vTaskDelay(10);
if (dir == 1)
{
/* dir==1,ledrpwmval递增 */
ledrpwmval += 5;
}
else
{
/* dir==0,ledrpwmval递减 */
ledrpwmval -= 5;
}
if (ledrpwmval > 1005)
{
/* ledrpwmval到达1005后,方向改为递减 */
dir = 0;
}
if (ledrpwmval < 5)
{
/* ledrpwmval递减到5后,方向改为递增 */
dir = 1;
}
/* 设置占空比 */
pwm_set_duty(ledpwmval);
}
}
从上面的代码中可以看到,在初始化LEDC定时器,并输出PWM后,就不断地改变定时器0的值,以达到改变PWM占功比的目的。又因为PWM由IO1引脚输出,IO1引脚连接至LED,所以LED的亮度也会随之发生变化,从而实现呼吸灯的效果。
17.4 下载验证
在完成编译和烧录后,可以看到板子上的LED先由暗再逐渐变亮,以此循环,实现了呼吸灯的效果。