ESP-IDF框架下ESP32无正常音频输出问题求助
问题描述
我尝试让Raspiaudio Muse Proto播放测试用正弦波,但仅听到非常微弱的脉动,和预期输出完全不符。以下是完整代码:
#include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_system.h" #include "esp_log.h" #include "driver/uart.h" #include "driver/i2s.h" #include <math.h> #include <vector> #include <sstream> #define I2S_BCLK 5 #define I2S_LRC 25 #define I2S_DOUT 26 #define I2S_DIN 35 /** This is necessary for the linker to find the firmware's entry point */ extern "C" { void app_main(void); } #define SAMPLE_RATE (44100) const i2s_port_t I2S_NUM = I2S_NUM_0; #define PI (3.14159265) #define FREQUENCY (440) #define SAMPLES (SAMPLE_RATE / FREQUENCY) typedef std::vector<int16_t> VectorBuffer; VectorBuffer generate_sine_wave( float sample_rate_Hz, float duration_s = 1.0f, float frequency = 440.0f, float amplitude = 1.0f ) { const auto samples_count = (int)(sample_rate_Hz * duration_s * 2); VectorBuffer buffer(samples_count); for (int i = 0; i < samples_count - 1; i++) { const auto sample = (int16_t)(amplitude * sin((2 * PI * i * frequency) / sample_rate_Hz)); buffer.at(i) = sample; buffer.at(i + 1) = sample; } return buffer; } // Should play a F note for half a second every second void i2s_task(void *pvParameter) { while (1) { auto buffer = generate_sine_wave( SAMPLE_RATE, 1.0f, 440.0f, floor(INT16_MAX / 3) ); ESP_LOGI("I2S_TASK", "Playing sine wave of %d samples", buffer.size()); size_t bytes_written; for (;;) { if (buffer.empty()) { ESP_LOGD("I2S_TASK", "Buffer is empty, good."); break; } esp_err_t e = i2s_write(I2S_NUM, buffer.data(), std::fmin(buffer.size(), 1024), &bytes_written, portMAX_DELAY); if (e != ESP_OK) { ESP_LOGE("I2S_TASK", "Error writing to I2S: %d", e); break; } if (bytes_written == 0) { // this should not happen ESP_LOGE("I2S_TASK", "No bytes written"); break; } ESP_LOGI("I2S_TASK", "Wrote %d bytes", bytes_written); /* Remove too verbose logging std::ostringstream oss; for (int i = 0; i < bytes_written; i++) { oss << buffer.at(i) << " "; } ESP_LOGD("I2S_TASK", "Buffer: %s", oss.str().c_str()); // */ buffer.erase(buffer.begin(), buffer.begin() + bytes_written); }; vTaskDelay(1000 / portTICK_PERIOD_MS); } } void hello_task(void *pvParameter) { while (1) { ESP_LOGI("HELLO_TASK", "hello"); vTaskDelay(500 / portTICK_PERIOD_MS); } } void app_main() { // UART configuration const uart_config_t uart_config = { .baud_rate = 115200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE }; uart_param_config(UART_NUM_0, &uart_config); uart_driver_install(UART_NUM_0, 1024 * 2, 0, 0, NULL, 0); const auto mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX); // I2S configuration i2s_config_t i2s_config = { mode, .sample_rate = SAMPLE_RATE, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S | I2S_COMM_FORMAT_STAND_MSB), .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 3, .dma_buf_len = 1024, .use_apll = false, .tx_desc_auto_clear = true, .fixed_mclk = -1, .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT, .bits_per_chan = I2S_BITS_PER_CHAN_16BIT, }; // I2S pin configuration i2s_pin_config_t pin_config = { .bck_io_num = I2S_BCLK, .ws_io_num = I2S_LRC, .data_out_num = I2S_DOUT, .data_in_num = I2S_DIN, // default Copilot was: I2S_PIN_NO_CHANGE // maybe 35 https://github.com/RASPIAUDIO/Simple_Bluetooth_Speaker_ESP32/blob/884c7f3700ec92dcf000dca75934860833559df7/speaker/speaker.ino#L171 }; // Install and start I2S driver const auto e_i = i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); if (e_i != ESP_OK) { ESP_LOGE("APP_MAIN", "Error installing I2S driver: %d", e_i); return; } const auto e_p = i2s_set_pin(I2S_NUM, &pin_config); if (e_p != ESP_OK) { ESP_LOGE("APP_MAIN", "Error setting I2S pin: %d", e_p); return; } const auto e_s = i2s_start(I2S_NUM); if (e_s != ESP_OK) { ESP_LOGE("APP_MAIN", "Error starting I2S: %d", e_s); return; } // Create the hello_task xTaskCreate(&hello_task, "hello_task", 2048, NULL, 5, NULL); // Create the i2s_task xTaskCreate(&i2s_task, "i2s_task", 2048, NULL, 5, NULL); }
排查思路
- 修正I2S写入的字节数处理逻辑
i2s_write的第三个参数是字节数,但你传入的是vector<int16_t>的元素个数,每个元素占2字节,导致实际写入数据只有预期的一半。正确写法应该是:
同时esp_err_t e = i2s_write(I2S_NUM, buffer.data(), std::fmin(buffer.size() * sizeof(int16_t), 1024), &bytes_written, portMAX_DELAY);buffer.erase时要把写入的字节数转换成元素个数:buffer.erase(buffer.begin(), buffer.begin() + (bytes_written / sizeof(int16_t))); - 修复正弦波生成的逻辑错误
当前循环条件i < samples_count - 1会导致最后一个数组位置未被赋值,且重复写入两次样本的逻辑会导致音频数据冗余。修正后的生成逻辑:const auto samples_per_channel = (int)(sample_rate_Hz * duration_s); VectorBuffer buffer(samples_per_channel * 2); // 双声道总样本数 for (int i = 0; i < samples_per_channel; i++) { const auto sample = (int16_t)(amplitude * sin((2 * PI * i * frequency) / sample_rate_Hz)); buffer[2*i] = sample; // 左声道 buffer[2*i + 1] = sample; // 右声道 } - 核对I2S引脚与设备匹配
你设置的data_in_num = 35但仅使用TX模式,应改为I2S_PIN_NO_CHANGE避免占用不必要的引脚。同时再次确认Raspiaudio Muse Proto的官方引脚定义,确保BCLK、LRC、DOUT的引脚编号正确。 - 调整I2S配置参数
开启use_apll = true,APLL能提供更精准的采样率,避免因采样率不匹配导致的音频失真;可尝试调整dma_buf_count = 6、dma_buf_len = 512优化缓存性能。 - 测试基础音频输出
暂时替换正弦波生成逻辑,直接写入固定幅值的音频数据(比如int16_t test_sample = INT16_MAX / 3;)填充整个buffer,若能听到稳定声音,说明问题出在正弦波生成逻辑;若仍微弱,排查I2S配置或硬件连接。 - 检查声道格式匹配
当前设置I2S_CHANNEL_FMT_RIGHT_LEFT双声道,若Muse Proto实际为单声道设备,可尝试切换为I2S_CHANNEL_FMT_ONLY_LEFT或I2S_CHANNEL_FMT_ONLY_RIGHT测试。
内容的提问来源于stack exchange,提问作者djfm
相关产品推荐
相关产品推荐

