引言
在微控制器编程中,PWM(脉冲宽度调制)是一种重要的技术,它可以用于模拟模拟信号,控制LED亮度,驱动电机,以及生成音频信号等。ESP32是内置了一个高级LEDC(LED PWM Controller)硬件,用于产生高精度的PWM信号。本文将详细介绍ESP32的LEDC功能,包括其工作原理,编程方法,以及应用实例。
LEDC基础
LEDC是ESP32特有的一个硬件PWM控制模块。与传统的PWM不同,LEDC提供更高的分辨率,更多的通道,以及更灵活的频率控制。ESP32支持高速模式和低速模式两种LEDC运行模式,分别有8个独立的通道。
LEDC的特点
- 独立通道:ESP32提供高速模式和低速模式,每种模式8个通道。
- 高分辨率:支持高达14位的分辨率,即16384个不同的占空比水平。
- 灵活的频率调节:频率范围从几赫兹到几十兆赫兹。
- 波形生成:可用于生成复杂的波形序列。
LEDC的工作模式
在ESP-IDF开发框架中,LEDC模块可以工作在两种模式下:
- 高速模式:高速模式通常用于需要高频PWM信号的应用,如LED照明。在这种模式下,PWM信号的频率可以非常高,可以达到数十甚至数百千赫兹(kHz)。这对于LED照明是非常理想的,因为高频率确保了人眼无法察觉到任何闪烁,从而提供平滑的光线调整和更好的视觉体验。在高速模式下,LEDC模块使用ESP32的高速定时器。这些定时器的时钟来源于APB_CLK,这是一个较高的系统时钟频率,由此可以生成较高频率的PWM信号。
例子:初始化一个LEDC通道在高速模式ledc_timer_config_t ledc_timer = { .duty_resolution = LEDC_TIMER_13_BIT, // 分辨率 .freq_hz = 5000, // 频率 .speed_mode = LEDC_HIGH_SPEED_MODE, // 高速模式 .timer_num = LEDC_TIMER_0 // 定时器 }; ledc_timer_config(&ledc_timer); ledc_channel_config_t ledc_channel = { .channel = LEDC_CHANNEL_0, .duty = 0, .gpio_num = 18, .speed_mode = LEDC_HIGH_SPEED_MODE, .timer_sel = LEDC_TIMER_0 }; ledc_channel_config(&ledc_channel);
- 低速模式:低速模式适用于不需要很高频率PWM信号的场合,例如在电机控制等应用中。在这种模式下,PWM信号的频率较低,可以是几赫兹到几千赫兹。对于许多类型的电机控制,这种较低的频率是足够的,且有助于降低系统的功耗。在低速模式下,LEDC模块使用ESP32的低速定时器。这些定时器可以选择APB_CLK或RTC_CLK(来自RTC模块的慢速时钟)作为时钟源。使用RTC_CLK时,甚至在深度睡眠模式下,PWM也可以继续运作,对于低功耗场景非常有用。
#include "driver/ledc.h" // 定义LED连接的GPIO引脚 #define LED_PIN 2 // 初始化LEDC定时器的配置结构体 ledc_timer_config_t ledc_timer = { .speed_mode = LEDC_LOW_SPEED_MODE, // 低速模式 .duty_resolution = LEDC_TIMER_13_BIT, // 分辨率为13位 .timer_num = LEDC_TIMER_0, // 定时器编号 .freq_hz = 1000, // PWM信号的频率, 例如1000 Hz .clk_cfg = LEDC_AUTO_CLK, // 自动选择时钟源 }; // 初始化LEDC通道的配置结构体 ledc_channel_config_t ledc_channel = { .gpio_num = LED_PIN, .speed_mode = LEDC_LOW_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0, .duty = 0, // 初始占空比为0 .hpoint = 0 }; // 初始化LEDC定时器 ledc_timer_config(&ledc_timer); // 初始化LEDC通道 ledc_channel_config(&ledc_channel); // 设置通道的占空比到一定的值,例如50% ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, 1 << (ledc_timer.duty_resolution - 1)); ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);
1. ESP-IDF中的LEDC使用步骤:
ESP-IDF为LEDC提供了一系列的API函数,用于配置和控制LEDC。以下是使用LEDC进行编程的基本步骤:
2. 配置LEDC
使用LEDC之前,需要配置LEDC通道和定时器。每个通道都必须关联一个定时器,定时器定义了PWM信号的频率和分辨率。
ledc_timer_config_t ledc_timer = {
.duty_resolution = LEDC_TIMER_8_BIT, // 分辨率
.freq_hz = 5000, // 频率
.speed_mode = LEDC_HIGH_SPEED_MODE, // 速度模式
.timer_num = LEDC_TIMER_0 // 定时器编号
};
ledc_timer_config(&ledc_timer);
3. 配置LEDC通道
每个LEDC通道可以连接到一个GPIO引脚,并且可以独立控制PWM信号的占空比。
ledc_channel_config_t ledc_channel = {
.channel = LEDC_CHANNEL_0,
.duty = 0,
.gpio_num = 18,
.speed_mode = LEDC_HIGH_SPEED_MODE,
.timer_sel = LEDC_TIMER_0
};
ledc_channel_config(&ledc_channel);
4. 控制PWM信号
配置好LEDC通道和定时器后,就可以使用API函数来控制PWM信号了。
// 设置PWM占空比
ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, duty);
ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel);
arduino环境中的LEDC
虽然analogWrite函数现在已经支持ESP32使用,但是LEDC更加的灵活准确:
- LEDC是针对ESP32微控制器的一组硬件特定功能,它利用ESP32的LED PWM控制器来实现精确的PWM输出。它允许用户对频率和占空比进行更精细的控制,并可以在高速模式和低速模式之间选择。
- LEDC提供了更多的配置选项,如不同的定时器分辨率、不同的频率设置以及独立的通道管理,这使得它在需要精确控制PWM信号时更为有用。
- LEDC允许用户选择不同的分辨率,例如从1位到16位,我们可以产生精度从2级到65536级的PWM信号。
舵机实例
下面我们来详细的分析一下舵机控制的代码#include <Arduino.h> // 定义LEDC通道、GPIO引脚和分辨率 #define LEDC_CHANNEL 0 #define LEDC_PIN 12 #define LEDC_RESOLUTION 10 // 设置分辨率为10位 void setup() { // 初始化LEDC通道 ledcSetup(LEDC_CHANNEL, 50, LEDC_RESOLUTION); // 设置频率为50Hz,分辨率为10位 ledcAttachPin(LEDC_PIN, LEDC_CHANNEL); // 将GPIO引脚与LEDC通道关联 } void loop() { int dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.075; // 使用10位分辨率计算占空比值 ledcWrite(LEDC_CHANNEL, dutyCycle); // 延迟1秒 delay(1000); dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.0875; // 使用10位分辨率计算占空比值 ledcWrite(LEDC_CHANNEL, dutyCycle); delay(1000); dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.075; // 使用10位分辨率计算占空比值 ledcWrite(LEDC_CHANNEL, dutyCycle); delay(1000); dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.0625; // 使用10位分辨率计算占空比值 ledcWrite(LEDC_CHANNEL, dutyCycle); delay(1000); }
这段代码是为Arduino编写的,用于在ESP32微控制器上配置LEDC(LED PWM控制器)以产生PWM信号。以下是详细解析:
包含库
#include <Arduino.h>
这行代码包含Arduino基础库,提供了使用Arduino函数和宏的基础。
预处理器指令
#define LEDC_CHANNEL 0
#define LEDC_PIN 12
#define LEDC_RESOLUTION 10
这些行为LEDC通道、GPIO引脚和PWM分辨率定义了常量。这里定义了将要使用的通道为0,PWM信号输出的GPIO引脚为12,PWM分辨率为10位(意味着PWM值的范围是0到1023)。
setup函数
void setup() {
ledcSetup(LEDC_CHANNEL, 50, LEDC_RESOLUTION);
ledcAttachPin(LEDC_PIN, LEDC_CHANNEL);
}
在setup
函数中,首先调用ledcSetup
函数来初始化LEDC通道0,设置PWM频率为50Hz,分辨率为10位。然后调用ledcAttachPin
函数将GPIO引脚12与LEDC通道0关联起来,这样PWM信号就会输出到这个引脚。
loop函数
void loop() {
int dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.075;
ledcWrite(LEDC_CHANNEL, dutyCycle);
delay(1000);
// ...更多类似操作
}
在loop
函数中,使用ledcWrite
函数来设置LEDC通道0的占空比。这里有四个不同的占空比值(7.5%、8.75%、7.5%、6.25%),每个都通过计算得到一个10位分辨率下的duty cycle值,然后这个值被用来设置PWM的占空比。每次设置占空比后,代码暂停1000毫秒(1秒)。
波形分析:
通过上述的程序我们实现生成了一个频率为50hz即周期20ms的pwm,并通过pwm控制了舵机的左右旋转
下面我们通过示波器观察pwm的波形
观察上图,我们可以看到pwm波按照我们设定的占空比依次变化。
电机调速实例
#include <Arduino.h>
// 定义LEDC通道、GPIO引脚和分辨率
#define LEDC_CHANNEL 0
#define LEDC_PIN 12
#define LEDC_RESOLUTION 10 // 设置分辨率为10位
void setup() {
// 初始化LEDC通道
ledcSetup(LEDC_CHANNEL, 50, LEDC_RESOLUTION); // 设置频率为50Hz,分辨率为10位
ledcAttachPin(LEDC_PIN, LEDC_CHANNEL); // 将GPIO引脚与LEDC通道关联
}
void loop() {
// 设置占空比为25%
int dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.075; // 使用10位分辨率计算占空比值
ledcWrite(LEDC_CHANNEL, dutyCycle);
// 延迟2秒
delay(1000);
dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.0875; // 使用10位分辨率计算占空比值
ledcWrite(LEDC_CHANNEL, dutyCycle);
delay(1000);
dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.075; // 使用10位分辨率计算占空比值
ledcWrite(LEDC_CHANNEL, dutyCycle);
delay(1000);
dutyCycle = (pow(2, LEDC_RESOLUTION) - 1) * 0.0625; // 使用10位分辨率计算占空比值
ledcWrite(LEDC_CHANNEL, dutyCycle);
delay(1000);
}
#include <Arduino.h>
// 定义电机A的控制针脚
#define MOTOR_A_PWM 18 // 用于速度控制的PWM针脚
#define MOTOR_A_IN1 17 // 方向控制针脚1
#define MOTOR_A_IN2 5 // 方向控制针脚2
// 定义电机B的控制针脚
#define MOTOR_B_PWM 15 // 用于速度控制的PWM针脚
#define MOTOR_B_IN1 4 // 方向控制针脚1
#define MOTOR_B_IN2 2 // 方向控制针脚2
// 定义Standby针脚
#define MOTOR_STBY 16
void setup() {
// 初始化电机控制针脚为输出模式
pinMode(MOTOR_A_PWM, OUTPUT);
pinMode(MOTOR_A_IN1, OUTPUT);
pinMode(MOTOR_A_IN2, OUTPUT);
pinMode(MOTOR_B_PWM, OUTPUT);
pinMode(MOTOR_B_IN1, OUTPUT);
pinMode(MOTOR_B_IN2, OUTPUT);
pinMode(MOTOR_STBY, OUTPUT);
// 禁用Standby模式
digitalWrite(MOTOR_STBY, HIGH);
}
void drive(int motorAPower, int motorBPower) {
if (motorAPower >= 0) {
digitalWrite(MOTOR_A_IN1, HIGH);
digitalWrite(MOTOR_A_IN2, LOW);
} else {
digitalWrite(MOTOR_A_IN1, LOW);
digitalWrite(MOTOR_A_IN2, HIGH);
motorAPower = -motorAPower; // 取绝对值
}
if (motorBPower >= 0) {
digitalWrite(MOTOR_B_IN1, HIGH);
digitalWrite(MOTOR_B_IN2, LOW);
} else {
digitalWrite(MOTOR_B_IN1, LOW);
digitalWrite(MOTOR_B_IN2, HIGH);
motorBPower = -motorBPower; // 取绝对值
}
// 设置PWM值来控制速度
analogWrite(MOTOR_A_PWM, motorAPower);
analogWrite(MOTOR_B_PWM, motorBPower);
}
void loop() {
// 向前移动
drive(100, 200); // 设置两个电机的速度
delay(2000); // 持续2秒钟
drive(-200, -200); // 设置两个电机的速度
delay(2000); // 持续2秒钟
}
相比舵机控制实例,这个电机调速实例我们直接使用了analogWrite函数生成pwm,在一般的应用场景,比如在电机调速这种对pwm精度及频率不敏感的情况下,直接使用analogWrite也是不错的选择
评论(0)
您还未登录,请登录后发表或查看评论