使用带有内部DAC的I2S在ESP32上播放WAV时出现问题

时间:2019-07-28 14:59:01

标签: audio esp32

我正在尝试使用I2S和内部DAC通过Arduino IDE在Heltec WiFi LoRa 32 V2上播放来自SPIFF的WAV文件。

我有一个音频放大器和一个示波器连接到板上的DAC2(引脚25),但没有收到任何信号。我通过生成正弦波简化了问题(如ESP-IDF示例中所示)。这是代码:

#include <Streaming.h>
#include <driver/i2s.h>
#include "freertos/queue.h"

#define SAMPLE_RATE     (22050)
#define SAMPLE_SIZE     4000
#define PI              (3.14159265)
#define I2S_BCK_IO      (GPIO_NUM_26)
#define I2S_WS_IO       (GPIO_NUM_25)
#define I2S_DO_IO       (GPIO_NUM_22)
#define I2S_DI_IO       (-1)


size_t i2s_bytes_write = 0;
static const int i2s_num = 0;

int sample_data[SAMPLE_SIZE];

i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),                                  // Only TX
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,                           //2-channels
    .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags = 0,//ESP_INTR_FLAG_LEVEL1   
    .dma_buf_count = 8,
    .dma_buf_len = 64,
    .use_apll = false                           //Interrupt level 1
};

i2s_pin_config_t pin_config = {
    .bck_io_num = I2S_BCK_IO,
    .ws_io_num = I2S_WS_IO,
    .data_out_num = I2S_DO_IO,
    .data_in_num = I2S_DI_IO                                               //Not used
};

static void setup_sine_wave()
{
    unsigned int i;
    int sample_val;
    double sin_float;
    size_t i2s_bytes_write = 0;

    for (i = 0; i < SAMPLE_SIZE; i++)
    {
        sin_float = sin(i * PI / 180.0);
        sin_float *= 127;
        sample_val = (uint8_t)sin_float;
        sample_data[i] = sample_val;
        Serial << sample_data[i] << ",";
        delay(1);
    }
    Serial << endl << "Sine wave generation complete" << endl;
}

void setup() {
    pinMode(26, OUTPUT);
    Serial.begin(115200);
    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    //i2s_set_pin(I2S_NUM_0, NULL);
    i2s_set_pin(I2S_NUM_0, &pin_config);
    i2s_set_dac_mode(I2S_DAC_CHANNEL_RIGHT_EN);
    i2s_set_sample_rates(I2S_NUM_0, 22050); //set sample rates
    setup_sine_wave();
    i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
    i2s_write(I2S_NUM_0, &sample_data, SAMPLE_SIZE, &i2s_bytes_write, 500);
    i2s_driver_uninstall(I2S_NUM_0); //stop & destroy i2s driver
}

void loop() 
{
    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    i2s_write(I2S_NUM_0, &sample_data, SAMPLE_SIZE, &i2s_bytes_write, 500);
    delay(100);
    i2s_driver_uninstall(I2S_NUM_0);
    delay(10);
}

代码上载并运行正常,但是在第25针上仍然没有任何信号。我也查看了第26针(DAC1),但这似乎由LoRa_IRQ使用。有人可以帮我吗?

1 个答案:

答案 0 :(得分:0)

首先,看看如何设置图钉

#define I2S_BCK_IO      (GPIO_NUM_26)
#define I2S_WS_IO       (GPIO_NUM_25)
#define I2S_DO_IO       (GPIO_NUM_22)
#define I2S_DI_IO       (-1)

根据此规范,引脚26将输出时钟信号,引脚25将输出线路选择器(左或右),引脚22将输出与您要发送到DAC的音频相对应的串行数据。 / p>

.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,

现在,将采样率设置为22050Hz,并将位深度设置为16位。因此,在引脚26和22上,您应该获得22kHz的周期信号,在引脚25上得到22kHz / 16的周期信号。

现在解决您的问题。首先,ESP32开发板有两个8位内部DAC,它们将输出8位深度的模拟信号。因此,实际上,引脚22应该正在输出模拟信号。让我们看看您的代码:

i2s_set_pin(I2S_NUM_0, &pin_config);

I2s规范是用于音频通信的3线总线规范。由于您使用的是内部DAC,因此不需要这三行,并且设置它们的引脚将使驱动程序假定您要使用它们(这意味着DAC引脚将不会被激活)。

//i2s_set_pin(I2S_NUM_0, NULL);

取消注释,驱动程序将假定您要使用内部DAC。

.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,

同样,由于内部DAC每个样本仅占用8位,因此驱动器仅占用8个最高有效位。您可以将其设置为8位,以避免出现任何问题。

void setup() {
    pinMode(26, OUTPUT);
    Serial.begin(115200);
    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    //i2s_set_pin(I2S_NUM_0, NULL);
    i2s_set_pin(I2S_NUM_0, &pin_config);
    i2s_set_dac_mode(I2S_DAC_CHANNEL_RIGHT_EN);
    i2s_set_sample_rates(I2S_NUM_0, 22050); //set sample rates
    setup_sine_wave();
    i2s_set_clk(I2S_NUM_0, SAMPLE_RATE, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
    i2s_write(I2S_NUM_0, &sample_data, SAMPLE_SIZE, &i2s_bytes_write, 500);
    i2s_driver_uninstall(I2S_NUM_0); //stop & destroy i2s driver
}

在设置功能中,您正在安装I2s驱动程序,然后设置用于与外部DAC进行I2s通信的引脚,将采样率设置为22050,再次将其重置为22050,写入1个周期的正弦波,然后卸载驱动程序。卸载驱动程序后,没有任何输出。这是一种更合适的方法:

void setup() {
    Serial.begin(115200);
    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    i2s_set_pin(I2S_NUM_0, NULL);
    i2s_set_dac_mode(I2S_DAC_CHANNEL_BOTH_EN); // You also might be sending data to the wrong channel, so use both.
    setup_sine_wave();
}

现在循环功能:

void loop() 
{
    i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
    i2s_write(I2S_NUM_0, &sample_data, SAMPLE_SIZE, &i2s_bytes_write, 500);
    delay(100);
    i2s_driver_uninstall(I2S_NUM_0);
    delay(10);
}

您不需要安装和卸载I2s驱动程序,也无需延迟外部功能,因为写入功能会写入以指定采样率消耗的缓冲区。

void loop() 
{
    i2s_write(I2S_NUM_0, &sample_data, SAMPLE_SIZE, &i2s_bytes_write, 500);
}

理论上,这就是您所需要的。但是有一个大问题。您将音频缓冲区定义为一个int数组(一个32位带符号值的列表)。然后将这个缓冲区的大小定义为4000

#define SAMPLE_SIZE     4000

在写函数中,缓冲区大小参数期望缓冲区的大小(以字节为单位),就像使用malloc函数时一样。由于缓冲区中的每个样本都有4个字节,因此您只将1/4的缓冲区提供给了写函数。最后,您不是输出正弦波,而是输出正弦波的1/4。

void loop() 
{
    i2s_write(I2S_NUM_0, &sample_data, SAMPLE_SIZE * sizeof(int), &i2s_bytes_write, 500);
}

应该做到这一点。 我尚未测试此代码,但希望我的解释会给您一些调试代码的指导。

I2s driver documentation有一些代码示例,您也可以检查。