ESP32双WAV文件切换播放异常问题求助
ESP32 I2S WAV播放切换异常排查与解决
问题描述
基于ESP32的I2S WAV播放教程修改代码后,实现默认WAV循环播放,当MPU-6050陀螺仪数据满足阈值时切换播放单次目标WAV文件。但首次切换到目标文件正常,切回默认文件后播放异常:出现杂音、播放中断,串口日志显示播放几次后无输出。
核心问题排查点
- 资源未正确释放:切换播放时,前一次的WAV文件句柄、I2S缓冲区未关闭/清空,导致资源冲突
- 播放状态逻辑混乱:切换时未重置播放状态,默认循环的状态被破坏
- I2S驱动未重置:切换文件时未停止I2S传输、重置驱动参数,后续播放的音频格式不匹配
- 任务优先级冲突:MPU-6050采样任务抢占资源,导致播放线程被阻塞
修复方案
1. 统一播放控制逻辑,添加状态锁
- 定义播放状态枚举,避免并发修改
- 使用互斥锁锁定播放状态,确保切换操作原子性
typedef enum { PLAY_STATE_DEFAULT, PLAY_STATE_TARGET, PLAY_STATE_SWITCHING } PlayState; PlayState current_state = PLAY_STATE_DEFAULT; SemaphoreHandle_t play_state_mutex; // 初始化互斥锁(在setup中调用) play_state_mutex = xSemaphoreCreateMutex();
2. 切换时强制释放前一次播放资源
切换播放前,必须清理上一次的资源:
void switch_to_target_wav() { xSemaphoreTake(play_state_mutex, portMAX_DELAY); current_state = PLAY_STATE_SWITCHING; // 停止I2S传输,清空缓冲区 i2s_stop(I2S_NUM_0); i2s_zero_dma_buffer(I2S_NUM_0); // 关闭当前打开的文件 if (current_wav_fp != NULL) { fclose(current_wav_fp); current_wav_fp = NULL; } // 播放单次目标文件 current_wav_fp = fopen("/spiffs/target.wav", "r"); if (current_wav_fp != NULL) { play_wav_once(current_wav_fp); // 自定义单次播放函数 fclose(current_wav_fp); current_wav_fp = NULL; } // 切回默认循环播放 current_wav_fp = fopen("/spiffs/default.wav", "r"); if (current_wav_fp != NULL) { fseek(current_wav_fp, 0, SEEK_SET); // 重置文件指针到开头 i2s_start(I2S_NUM_0); current_state = PLAY_STATE_DEFAULT; } xSemaphoreGive(play_state_mutex); }
3. 重置默认播放的循环状态
在默认循环播放任务中,检测文件播放完毕后强制重置指针:
void play_default_loop_task(void *arg) { uint8_t wav_buffer[1024]; size_t bytes_written; while(1) { xSemaphoreTake(play_state_mutex, portMAX_DELAY); if (current_state == PLAY_STATE_DEFAULT && current_wav_fp != NULL) { size_t bytes_read = fread(wav_buffer, 1, sizeof(wav_buffer), current_wav_fp); if (bytes_read == 0) { // 检测文件读取错误或播放完毕 if (ferror(current_wav_fp)) { fclose(current_wav_fp); current_wav_fp = fopen("/spiffs/default.wav", "r"); } else { fseek(current_wav_fp, 0, SEEK_SET); bytes_read = fread(wav_buffer, 1, sizeof(wav_buffer), current_wav_fp); } } i2s_write(I2S_NUM_0, wav_buffer, bytes_read, &bytes_written, portMAX_DELAY); } xSemaphoreGive(play_state_mutex); vTaskDelay(pdMS_TO_TICKS(10)); } }
4. 优化任务优先级
将音频播放任务的优先级设为高于MPU-6050采样任务,避免播放被频繁打断:
// 播放任务优先级设为5,采样任务设为3 xTaskCreate(play_default_loop_task, "play_default", 4096, NULL, 5, NULL); xTaskCreate(mpu_sampling_task, "mpu_sample", 2048, NULL, 3, NULL);
5. 串口日志排查补充
在关键节点添加日志,定位异常点:
// 切换时打印状态 Serial.printf("Switching to target state, current fp: %p\n", current_wav_fp); // 默认播放时打印读取字节数 Serial.printf("Read %d bytes from default wav\n", bytes_read);
内容的提问来源于stack exchange,提问作者Pommesfreak
相关产品推荐
相关产品推荐

