zoologist 发表于 2021-4-9 08:45:25

FireBeetle 直接放音(PWM篇)

本帖最后由 zoologist 于 2021-4-9 08:52 编辑

前面介绍了FireBeetle通过 DAC 来播放音频,除此之外,还可以使用 PWM 方式来播放音频。关于 PWM动力老男孩在“Arduino系列教程之 – PWM的秘密(上)”【参考1】有介绍,对于我们来说,能用到的就是下面这一段:PWM是用占空比不同的方波,来模拟“模拟输出”的一种方式。靠,这个太拗口了,简而言之就是电脑只会输出0和1,那么想输出0.5怎么办呢?于是输出01010101….,平均之后的效果就是0.5了。早这么说就了然了嘛。比如,当前最高电压是5V,如果输出50%的PWM信号,可以当作 2.5V 的信号输出。对于 ESP32来说,有对 PWM的直接支持【参考2】。
Arduino core for the ESP32 并没有一般Arduino 中用来输出 PWM 的analogWrite(pin, value) 方法,取而代之的 ESP32 有一个 LEDC ,设计是用来控制 LED 。ESP32 的 LEDC 总共有16个路通道(0 ~ 15),分为高低速两组,高速通道(0 ~ 7)由80MHz时钟驱动,低速通道(8 ~ 15)由 1MHz 时钟驱动。
对于我们来说,用到的函数有下面3个:
ledcSetup(uint8_t channel, double freq, uint8_tresolution_bits)
分别设定使用的通道(Channel),PWM 的频率, PWM 的分辨率。比如,我们设定1Hz 的频率,然后分辨率为4Bit,那么就可以设置从 0 到15,一共16个PWM值。可以看出,分辨率越高,可以细分出更多的 PWM值。
ledcWrite(uint8_t channel, uint32_t duty)对通道设定当前的占空比(duty)
ledcAttachPin(uint8_t pin, uint8_t channel)
将 LEDC 通道绑定到指定IO 口上这样,我们就得到了一个和之前 DAC 很像的代码:
#include "audio\SoundData.h"



int freq = 8000*256;    // 频率

int channel = 0;    // 通道

int resolution = 8;   // 分辨率



const int led = 25;

void setup() {

ledcSetup(channel, freq, resolution); // 设置通道

ledcAttachPin(led, channel);// 将通道与对应的引脚连接‘

Serial.begin(115200);

}



void loop() {

for (unsigned int i=0;i<2527766;i++) {

   ledcWrite(channel, WarOfWorldsWav);

   delayMicroseconds(120);

}

}



测试结果表示和 DAC 的音质无差别。
前面提到了,PWM支持更高的分辨率,因此,我们可以尝试播放16Bits的音频。最简单的想法,直接将频率设定为8000Hz,然后PWM信号分辨率为16位。但是实际测试下来这样无法工作,经过研究,频率和分辨率之间有一定的限制关系【参考3】。在 8000Hz 下能够达到最高分辨率是13bits。最终实验表明使用12Bits 分辨率8000Hz 可以接收,再高噪音会较大。此外,16bits的 WAV 和 8Bits 的还有一个很大的区别在于:前者是有符号数值,后者是无符号数值。比如:0x8001实际上表示的是 -1。因此代码中取出数值后需要加上 0x8000 再做处理。另外,因为 16Bits 相对于 8Bits 数据量是直接翻倍了,导致无法在 Flash 中放下全部文件,为此,16Bits音频数据只是部分歌曲。
#include "audio\SoundData.h"

int freq = 8000*4;    // 频率

int channel = 0;    // 通道

int resolution = 12;   // 分辨率

const int led = 25;

void setup() {

ledcSetup(channel, freq, resolution); // 设置通道

ledcAttachPin(led, channel);// 将通道与对应的引脚连接

}



void loop() {

   int tmp;

    for (unsigned int i=0;i<2831155;i=i+2) {

      tmp=(int)(WarOfWorldsWav+(WarOfWorldsWav<<8));

      tmp=tmp+0x8000;

   ledcWrite(channel, tmp>>4);

   delayMicroseconds(120);

    }

}
参考:1.http://www.diy-robots.com/?p=814
2.https://blog.csdn.net/weixin_434 ... &utm_term=ledcWrite
3.https://forum.micropython.org/viewtopic.php?t=3717
The maximum PWM frequency with the currently used ledc dutyresolution of 10 bits in PWM module is 78.125KHz.The duty resolution can be lowered down to 1 bit in whichcase the maximum frequency is 40 MHz, but only the duty of 50% is available.For duty resolution of 8 buts, the maximal frequency is312.5 kHz.The available duty levels are (2^bit_num)-1, where bit_num canbe 1-15.The maximal frequency is 80000000 / 2^bit_numIn my MicroPython implementation, I'm currently working onenabling user selectable and/or automatic duty resolution and higher maxumumfrequencies.
4.手册上有描述

zoologist 发表于 2021-4-9 08:49:51

播放 8Bits 的代码和数据


播放 16Bits 的代码和数据


可以看到 16Bits 相对于 8Bits 数据量直接翻倍。
页: [1]
查看完整版本: FireBeetle 直接放音(PWM篇)