我正在尝试使用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使用。谁能帮我吗?
最佳答案
首先,看一下如何设置图钉
#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驱动程序,也无需延迟外部功能,因为写入功能会写入以指定采样率消耗的缓冲区。
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有一些代码示例也可以检查。
关于audio - 使用带有内部DAC的I2S在ESP32上播放WAV时出现问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57242062/