ESP32-Arduino PWM驱动

1 ESP32上的PWM资源

ESP32本身提供了两种PWM输出,分别是

1.1 LED PWM 控制器 (LEDC)

ESP32上提供最高16路 LEDC的PWM输出,其中8路为高速PWM,8路为低速PWM,而高低速通道都分别含有4个对应的高/低速时钟
在这里插入图片描述

该PWM输出主要针对LED的驱动(但实际应用中不限制)。这个针对是因为其预定义了类似逐渐增加占空比等操作,可以在不占用处理器资源的情况下,实现对亮度(幅值)和颜色(频率)的调控。

1.2 电机控制脉宽调制器 (MCPWM)

ESP32提供6个该PWM输出,含有两个MCPWM外设,每个外设可以提供3个PWM通道。
该PWM个人理解是包含更多高级的功能,但对于PWM波形的控制则相对没有LEDC的精细。
结构如下图所示,可以看到除了分频器、计时器、和操作器外,还包含了错误检测专用捕获模块(例如捕获电机转速等)
在这里插入图片描述

2 ESP32-Arduino使用PWM资源

首先,上述的两个资源并不都在ESP32-Arduino上提供支持,仅LEDC提供支持(查询支持的外设)。
因此我们下文主要集中在LEDC中。但参考上一篇blogESP32+Arduino+VS code开发环境搭建+BLINK,若确实需要MCPWM,可以考虑在程序中调用ESP-IDF的语法去使用,这应该是可能的。

2.1 LEDC的频率与解析度

与Arduino Uno板的8位固定PWM解析度不同,ESP32的PWM解析度是动态的,与设定相关。
解析度:即输出PWM波输出中,对时间的离散化精度,对应的结果,若是等频率,改变占空比以改变等效幅值的应用中,会得到更精细的等效电压输出。例如,对于UNO,8位的解析度,则最小的变化电压是1/256*3.3V=0.01289V,而若是10位的解析度,则为0.0032V。
解析度的解释
对于ESP32的LEDC输出,其最高解析度可以根据下式计算并向下取整,具体原因可参照下一节的解析。
[ l o g 2 ( 80 M H z P W M 输 出 频 率 ) ] f l o o r [log_2(\frac{80MHz}{PWM输出频率})]_{floor} [log2(PWM80MHz)]floor

2.2 LEDC的结构

其中一路高速时钟+高速通道的结构如下所示,下面参照这一路对PWM波形产生的过程进行解释。
在这里插入图片描述

  1. 时钟输入
    在高速分频器中,可以选择REF_TICK或APB_CLK,REF_TICK是APB_CLK经过分频后再得到的,因此小于APB_CLK。
    默认情况下(即不对CPU的时钟进行修改),APB_CLK=80MHz。

一般来说,CPU在低功耗时,会采用RTC8M_CLK时钟工作,LEDC的低速通道可以采用REF_TICK或SLOW_CLOCK进行工作,而SLOW_CLOCK时钟可以选择采用RTC8M_CLK工作。
即当ESP32进入低功耗时所有的外设都会应APB_CLK关闭而不能工作,而低速LEDC则可以正常工作,可以实现待机亮灯的操作。

  1. 信号频率配置
    时钟信号会依次经过分频器和计数器,产生供后续比较用的信号。
    计数器的周期直接决定了最后PWM的周期,即,如下图所示,从0到溢出值的过程对应了PWM波形的一个周期。
    而频率的计算则如下式所示:
    f P W M = f c l o c k 分 频 系 数 ∗ 溢 出 值 f_{PWM}=\frac{f_{clock}}{分频系数*溢出值} fPWM=fclock
    分频系数由LEDC_CLK_DIV_NUM_HSTIMERx决定
    溢出为 2 L E D C _ H S T I M E R x _ D U T Y _ R E S 2^{LEDC\_HSTIMERx\_DUTY\_RES} 2LEDC_HSTIMERx_DUTY_RES
    在这里插入图片描述
  2. PWM波形的产生
    以下述图为例,上部分为经过分频器产生的计数信号,而下面则为实际输出的PWM波形。通过比较计数信号与hpoint,lpoint的值,决定了输出高电平的区间。
    在这里插入图片描述
    在这里对上述解析度的计算进行解释。从上图我们可以看出,在一个周期内,即从0到溢出值的过程,溢出值的大小直接决定了区间内脉冲的最小时间宽度。例如溢出值为256,则,最小的时间精度为1/256*一个周期时间。
    因此,为了得到最高的解析度,首先肯定不希望对时钟进行分频,因为:
    溢 出 值 = 时 钟 频 率 输 出 频 率 溢出值=\frac{时钟频率}{输出频率} =
    即若80MHz的时钟,为了输出40MHz的信号,则计数器只能在0,1之间变化,此时占空比只能为50%,解析度为1。而若输出20MHz的信号,则计数器可以在0,1,2,3间变化,则可以输出25%,50%,75%的信号,解析度为2。
    这样,便可以计算出在当前时钟下可以实现的最大解析度了,默认情况下可以用上面的log式子进行计算(假设了时钟为80Hz)

并且,支持如下所示的渐变占空比的设定,并且增大减小的方向,与每次增大减小的步长都可以进行调整。
相关寄存器:
LEDC_DUTY_INC_HSCHn:决定方向;
LEDC_DUTY_SCALE_HSCHn:决定每次渐变的步长;
LEDC_DUTY_NUM_HSCHn:决定渐变的步数;
在这里插入图片描述

2.3 LEDC的使用

输出一个5kHz,60%工作周期(40%占空比)的PWM讯号
基本逻辑:配置PWM通道–>指定输出引脚–>输出

double ledcSetup(uint8_t channel, double freq, uint8_t resolution_bits);//返回值为设定频率,若放回0表示配置失败
void ledcWrite(uint8_t chan, uint32_t duty);
uint32_t ledcRead(uint8_t chan);读取设定duty
void ledcAttachPin(uint8_t pin, uint8_t chan);
void ledcDetachPin(uint8_t pin);
uint32_t brightness=0;

void setup(){
    ledcSetup(0, 5000, 10); //通道0, 5KHz,10位解析度
    ledcAttachPin(25, 0); //pin25定义为通道0的输出引脚
}

void loop(){
    ledcWrite(0, brightness); // 通道0输出, PWM输出0~100%(0~2^10=1024)
    printf("%u\n", brightness);
    if (!(++brightness % 1024)){
        brightness=0;
    }
}

调控PWM讯号的频率-让扬声器发出固定音量,不同音调的声音

ESP32不支持Arduino的tone()函数

double ledcWriteTone(uint8_t chan, double freq); // 设定LEDC通道以50%占空比发出指定频率声音,返回freq,若返回0则存在错误;
double ledcWriteNote(uint8_t chan, note_t note, uint8_t octave); // 按照音名、音阶来指定发声。

参考音阶、音名
在这里插入图片描述

void setup(){
	ledcSetup(0, 20000, 10);
	ledcAttachPin(25, 0);
}
void loop(){
	ledcWriteTone(0, 262); // 在通道0产生262Hz的频率
	ledcWriteNote(0, Note_C, 4); // 在通道里产生C4音(即262Hz),等效

	ledcWriteTone(0, 0); // 停止输出
}

附一个无源蜂鸣器驱动的程序

typedef struct data{
    uint32_t pitch; // 音高频率
    uint16_t interval; // 间隔时间
} note;

note tones[] = {    
    {659, 1000},
    {440, 1000}
};

byte toneSize = sizeof(tones) / sizeof(note);

void alarmSnd(){
    static uint8_t i = 0;
    ledcWriteTone(0, tones[i].pitch);
    delay(tones[i].interval);
    if (++i % toneSize==0){
        i = 0;
    }
}

void setup(){
    ledcSetup(0, 20000, BITS);
    ledcAttachPin(BUZZER_PIN, 0);
}

void loop(){
    alarmSnd();
}

参考资料
《ESP32 超图解》-赵英杰
《ESP32 技术规格书》-乐鑫官方

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值