You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32通过I2S录制WAV音频卡顿失真问题排查求助

STM32F446RE I2S立体声录音卡顿失真问题排查与解决

问题现象

使用STM32F446RE通过I2S协议从两个INMP441 MEMS麦克风采集立体声音频,基于FreeRTOS+HAL库录制到micro SD卡的WAV文件时,出现卡顿失真(类似达斯·维达音效),Audacity中可见真实音频数据间插入持续噪声。已尝试增大消息队列、更换帧大小与采样率、调整通道数、单麦克风录制等操作,均未解决。

现有固件架构与代码

RTOS任务架构

  • UART中断接收启停命令任务
  • WAV写入状态机任务
  • I2S音频数据处理任务

WriteWavFileTask代码

switch(audio_state)
{
case STATE_START_RECORDING:

    sprintf(filename, "%saud_%03d.wav", SDPath, count++);

    do
    {
        res = f_open(&file_ptr, filename, FA_CREATE_ALWAYS|FA_WRITE);
    }
    while(res != FR_OK);

    res = fwrite_wav_header(&file_ptr, I2S_SAMPLE_FREQUENCY, I2S_FRAME, 2);

    HAL_I2S_Receive_DMA(&hi2s2, aud_buf, READ_SIZE);
    audio_state = STATE_RECORDING;
    break;

case STATE_RECORDING:
    osDelay(50);
    break;

case STATE_STOP:
    HAL_I2S_DMAStop(&hi2s2);
    while(osMessageQueueGetCount(AudioQueueHandle)) osDelay(1000);

    filesize = f_size(&file_ptr);
    data_len = filesize - 44;
    total_len = filesize - 8;
    f_lseek(&file_ptr, 4);
    f_write(&file_ptr, (uint8_t*)&total_len, 4, bw);
    f_lseek(&file_ptr, 40);
    f_write(&file_ptr, (uint8_t*)&data_len, 4, bw);
    f_close(&file_ptr);

    audio_state = STATE_IDLE;
    break;

case STATE_IDLE:
    osThreadSuspend(WAVHandle);
    audio_state = STATE_START_RECORDING;
    break;

default:
    osDelay(50);
    break;
}

代码宏定义

#define I2S_DATA_WORD_LENGTH    (24)                // industry-standard 24-bit I2S
#define I2S_FRAME               (32)                // bits per sample
#define READ_SIZE               (128)               // samples to read from I2S
#define WRITE_SIZE              (READ_SIZE*I2S_FRAME/16) // half words to write
#define WRITE_SIZE_BYTES        (WRITE_SIZE*2)      // bytes to write

#define I2S_SAMPLE_FREQUENCY    (16000)             // sample frequency

I2S数据处理任务代码

void convert_endianness(uint32_t *array, uint16_t Size) {
    for (int i = 0; i < Size; i++) {
        array[i] = __REV(array[i]);
    }
}
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
    convert_endianness((uint32_t *)aud_buf, READ_SIZE);
    osMessageQueuePut(AudioQueueHandle, aud_buf, 0L, 0);

    HAL_I2S_Receive_DMA(hi2s, aud_buf, READ_SIZE);
}
void pvrWriteAudioTask(void *argument)
{
  /* USER CODE BEGIN pvrWriteAudioTask */
    static UINT *bw;
    static uint16_t aud_ptr[WRITE_SIZE];

    /* Infinite loop */
    for(;;)
    {
        osMessageQueueGet(AudioQueueHandle, aud_ptr, 0L, osWaitForever);

        res = f_write(&file_ptr, aud_ptr, WRITE_SIZE_BYTES, bw);
    }
  /* USER CODE END pvrWriteAudioTask */
}

硬件配置

已提供I2S与时钟配置截图。

问题根源与解决方法

问题根源

音频数据缓冲方式不合理:ISR中执行字节序转换+消息队列推送操作开销过大,导致DMA接收中断响应不及时;同时单缓冲模式下,DMA传输完成后重新启动的间隙会丢失数据,最终表现为卡顿与噪声插入。

优化方案

  1. 降低ISR开销:将字节序转换操作从ISR移至后台数据处理任务,减少中断响应时间
  2. 实现DMA循环双缓冲:配置I2S DMA为循环双缓冲模式,避免DMA重启间隙的数据丢失,确保音频数据连续采集

优化后代码及DMA配置截图已附上。

内容的提问来源于stack exchange,提问作者Wonky

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 16:51:38