【问题标题】:Trouble playing WAV on ESP32 using I2S with internal DAC使用带有内部 DAC 的 I2S 在 ESP32 上播放 WAV 时出现问题
【发布时间】:2019-07-28 14:59:01
【问题描述】:

我正在尝试使用 I2S 和内部 DAC 在 Heltec WiFi LoRa 32 V2 上使用 Arduino IDE 播放来自 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 使用。谁能帮帮我?

【问题讨论】:

    标签: audio esp32


    【解决方案1】:

    首先,看看你是如何设置你的 pin 的

    #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 的音频相对应的串行数据。

    .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 驱动程序,也不需要延迟外部函数,因为 write 函数会写入以指定采样率消耗的缓冲区。

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

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

    #define SAMPLE_SIZE     4000
    

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

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

    应该做的伎俩。 我没有测试过这段代码,但希望我的解释能给你一些调试代码的指导。

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

    【讨论】:

    • 多么彻底的答案!谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-11
    • 2021-11-23
    • 1970-01-01
    • 2019-10-30
    • 1970-01-01
    • 2015-08-08
    相关资源
    最近更新 更多