一、PWM简介
PWM 是 Pulse Width Modulation 的缩写,中文意思就是脉冲宽度调制,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,其控制简单、灵活和动态响应好等优点而成为电力电子技术最广泛应用的控制方式,其应用领域包括测量,通信,功率控制与变换,电动机控制、伺服控制、调光、开关电源,甚至某些音频放大器,因此学习 PWM 具有十分重要的现实意义。
其实我们也可以这样理解,PWM 是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM 信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用 PWM 进行编码。
PWM 对应模拟信号的等效图,如下所示:
从图中可以看到,上图 a 是一个正弦波即模拟信号,b 是一个数字脉冲波形即数字信号。我们知道在计算机系统中只能识别是 1 和 0,对于单片机,要么输出高电平(3.3V),要么输出低电平(0),假如要输出 1.5V 的电压,那么就必须通过相应的处理,比如本章所要讲解的 PWM 输出,其实从上图也可以看到,只要保证数字信号脉宽足够就可以使用 PWM 进行编码,从而输出 1.5V 的电压。
个人理解的PWM脉宽调制:以LED灯亮度说明PWM脉宽调制。其实对于单片机来说,其输出的状态只有0和1两种,因此对于灯泡来说,也应该只有亮灭两种状态,不应该有亮度区分之说。比如我们有一个占空比为50%的周期2秒钟的PWM脉冲,那么看到的现线就应该是LED灯亮1秒钟,然后又熄灭1秒钟,并周而复始。同理,如果占空比分别为25%及75%,其表现出来的状态也应该是亮灭时间上的不同,不会有亮度的不同,实际其实就是这样的。但是,如果我们调快频率,快到人眼分辨不出的时候,我们真是看到的现线是,LED灯一直亮着,只是占空比25%时灯光最暗,其次是50%,最亮是75%。这是因为我们人眼的视觉暂留现线,我们会觉得LED灯一直亮着,但是25%占空比时,一个周期内灯光灭的时间长,因此,其亮度最暗,因此,我们利用PWM脉宽调制,可以模拟出任何数值的电压。
二、ESP32控制LED灯亮度
根据上一节提到的PWM脉宽调制原理,我们利用ESP32,控制输出脉冲的占空比,就可以控制LED灯的亮度。
ESP32的PWM(ledc)接口介绍:
- LEDC 配置函数:ledc_channel_config();
函数原型 | esp_err_t ledc_channel_config ( const ledc_channel_config_t* ledc_conf ) |
函数功能 | LEDC配置函数 |
参数 | [in] ledc_conf: ledc 配置结构体 ledc_channel_config_t uint32_t duty; //占空比 |
返回值 | ESP_OK:成功 ESP_ERR_INVALID_ARG : 参数错误 |
- LEDC 渐变安装函数:ledc_fade_func_install();
函数原型 | esp_err_t ledc_fade_func_install ( int intr_alloc_flags ) |
函数功能 | LEDC 渐变安装使能函数 |
参数 | [in] intr_alloc_flags:分配中断标记 |
返回值 | ESP_OK:成功 ESP_ERR_INVALID_ARG : 参数错误 |
LEDC 渐变函数:ledc_set_fade_with_time();
函数原型 | 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 ) |
函数功能 | LEDC 渐变函数 |
参数 | [in] speed_mode:速度 [in] channel:通道 [in] target_duty:目标占空比 [in] max_fade_time_ms:到达目标占空比需要的时间 |
返回值 | ESP_OK:成功 ESP_ERR_INVALID_ARG : 参数错误 ESP_ERR_INVALID_STATE:LEDC 渐变未安装 ESP_FAIL:LEDC 渐变安装失败 |
- LEDC 渐变开始函数:ledc_fade_start();
函数原型 | esp_err_t ledc_fade_start ( ledc_mode_t speed_mode, ledc_channel_t channel, ledc_fade_mode_t wait_done ) |
函数功能 | LEDC 渐变开始函数 |
参数 | [in] speed_mode:速度 [in] channel:通道 [in] wait_done:是否等待 |
返回值 | ESP_OK:成功 ESP_ERR_INVALID_ARG : 参数错误 ESP_ERR_INVALID_STATE:LEDC 渐变未安装 |
三、程序编写:
PWM控制LED亮度.h文件内容:
#ifndef COMPONENTS_MYPWM_INCLUDE_MYPWM_H_
#define COMPONENTS_MYPWM_INCLUDE_MYPWM_H_
#include <stdio.h>
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#define LED_R 2 //LED的GPIO接口
#define LEDC_MAX_DUTY (8191) //2的13次方-1(13位PWM)
#define LEDC_FADE_TIME (1000) //渐变时间(ms)
//LEDC配置结构体
extern ledc_channel_config_t g_ledc_ch_R;
void ledc_init(void);
#endif /* COMPONENTS_MYPWM_INCLUDE_MYPWM_H_ */
PWM控制LED亮度.c文件内容:
#include "mypwm.h"
ledc_channel_config_t g_ledc_ch_R; //PWM配置结构体
/*
* void ledc_init(void):定时器0用在PWM模式,输出3通道的LEDC信号
* @param[in] void :无
* @retval void :无
*/
void ledc_init(void){
/**
* 定时器参数配置结构体
*
*/
ledc_timer_config_t ledc_timer={
.duty_resolution = LEDC_TIMER_13_BIT, //PWM分辨率
.freq_hz = 5000, //频率
.speed_mode = LEDC_HIGH_SPEED_MODE, //速度
.timer_num = LEDC_TIMER_0, //选择定时器
};
ledc_timer_config(&ledc_timer); //设置定时器PWM模式
//PWM通道0配置->IO2->红色灯
g_ledc_ch_R.channel = LEDC_CHANNEL_0; //PWM通道
g_ledc_ch_R.duty = 0; //占空比
g_ledc_ch_R.gpio_num = LED_R; //IO映射
g_ledc_ch_R.speed_mode = LEDC_HIGH_SPEED_MODE; //速度
g_ledc_ch_R.timer_sel = LEDC_TIMER_0; //选择定时器
ledc_channel_config(&g_ledc_ch_R); //配置PWM
//使能ledc渐变功能
ledc_fade_func_install(0); //注册LEDC服务,参数标志是否容许中断
}
PWM控制LED亮度main.c函数内容:
void app_main(void)
{
ledc_init();
while(1){
//ledc 红灯渐变至100%,时间LEDC_FADE_TIME
ledc_set_fade_with_time(g_ledc_ch_R.speed_mode,
g_ledc_ch_R.channel,
LEDC_MAX_DUTY,
LEDC_FADE_TIME);
//渐变开始
ledc_fade_start(g_ledc_ch_R.speed_mode,
g_ledc_ch_R.channel,
LEDC_FADE_NO_WAIT);
//延时LEDC_FADE_TIME,给LEDC控制时间
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
//ledc 红灯 渐变至0%,时间LEDC_FADE_TIME
ledc_set_fade_with_time(g_ledc_ch_R.speed_mode,
g_ledc_ch_R.channel,
0,
LEDC_FADE_TIME);
//渐变开始
ledc_fade_start(g_ledc_ch_R.speed_mode,
g_ledc_ch_R.channel,
LEDC_FADE_NO_WAIT);
//延时LEDC_FADE_TIME,给LEDC控制时间
vTaskDelay(LEDC_FADE_TIME / portTICK_PERIOD_MS);
}
}
四、结束
本文介绍了PWM脉宽调制的内容,并通过PWM实现LED呼吸灯。