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

STM32F4+FreeRTOS+FATFS录音卡顿带噪声问题求助

I2S音频录制卡顿噪声问题求助

免责声明:我的I2S接收代码实现大量参考Stack Overflow上的相关方案,这是我最接近可行方案的来源。

基于STM32F405RGT6开发,从ICS43432(MSB优先,32位帧中传输24位数据)接收音频。

I2S硬件连接示意图

使用循环DMA(双缓冲)读取音频数据。经过3天调试,解决大量内存损坏和栈溢出问题后,终于实现无内存损坏的数据流。

实现逻辑如下:

  • UItask处理按键输入并切换系统状态;
  • StartWriteAudioTask()启动DMA并创建.wav文件,同时将systemState设置为STATE_RECORDING;
  • DMA填充缓冲区后释放信号量给MicTask,该任务负责字节序转换并写入WAV数据块;
  • 为防止DMA在写入完成前覆盖缓冲区,实现了音频缓冲区的拷贝。

通过调试器确认状态机逻辑、启停流程、文件初始化均正常,生成的文件可播放,但音频时序异常,整体卡顿且伴有噪声。

mic.c代码

#include "mic.h"
#include "sd.h"
#include "stm32f4xx_hal.h"
#include "freertos.h"
#include "cmsis_os.h"
#include "ui.h"
extern I2S_HandleTypeDef hi2s3;
extern osSemaphoreId RxAudioSemHandle;
extern SemaphoreHandle_t xAudioReady;
extern SemaphoreHandle_t xRecordStop;
extern SemaphoreHandle_t xAudioBlock;
#define I2S_DATA_WORD_LENGTH    (24)                // 24-bit MSB
#define I2S_FRAME               (32)                // bits per sample
#define READ_SIZE               (512)               // samples to read from I2S
#define BUFFER_SIZE             (READ_SIZE*I2S_FRAME/16) // number of uint16_t elements expected
#define WRITE_SIZE_BYTES        (BUFFER_SIZE*2)     // bytes to write
#define SAMPLE_RATE             (44100)
#define BITS                    (24)
#define CHANNELS                (1)
uint16_t aud_buf[2*BUFFER_SIZE];            // Double buffering
static volatile uint16_t *BufPtr;
static uint16_t localBuf[BUFFER_SIZE];

static FIL      file;
static uint32_t totalBytesWritten = 0;
void convert_endianness(uint32_t *array, uint16_t Size) {
    for (int i = 0; i < Size; i++) {
        array[i] = __REV(array[i]);
    }
}
void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
    BufPtr = aud_buf;
    osSemaphoreRelease(RxAudioSemHandle);
}

void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
    BufPtr = &aud_buf[BUFFER_SIZE];
    osSemaphoreRelease(RxAudioSemHandle);
}

void StartMicTask(void const *argument){
    for(;;){
        osSemaphoreWait(RxAudioSemHandle, osWaitForever);
        if (systemState != STATE_RECORDING){
            continue;
        }
        memcpy(localBuf, (void*)BufPtr, WRITE_SIZE_BYTES);
        convert_endianness((uint32_t *)localBuf, READ_SIZE);
        if (SD_WriteWavBlock(&file, (int32_t *)localBuf, READ_SIZE, WRITE_SIZE_BYTES) == 0)
            totalBytesWritten += WRITE_SIZE_BYTES;
    }

}

void StartWriteAudioTask(void const *argument){
    for(;;){
        switch(systemState){
        case STATE_START_RECORDING:
            totalBytesWritten = 0;

            if (SD_CreateWavFile(&file, 0, SAMPLE_RATE, BITS, CHANNELS) != 0) {
                    SetState(STATE_MAIN_MENU);
                    break;
                }

            HAL_I2S_Receive_DMA(&hi2s3, aud_buf, READ_SIZE);
            SetState(STATE_RECORDING);
            break;
        case STATE_RECORD_STOP:
            HAL_I2S_DMAStop(&hi2s3);
            osSemaphoreWait(RxAudioSemHandle, osWaitForever);
            SD_FinalizeWavFile(&file, totalBytesWritten, SAMPLE_RATE, BITS, CHANNELS);
            totalBytesWritten = 0;
            SetState(STATE_MAIN_MENU);
            break;

        default:
            osDelay(50);
            break;
        }
    }
}

sd.c代码

#include "sd.h"
#include "fatfs_platform.h"
#include <string.h>
#include <stdlib.h>
#include "main.h"
#define SUB_DIR "/REC_DIKTOFON"

extern SD_HandleTypeDef hsd;
static FATFS   sdFatFs;
static uint8_t sdMounted = 0;
SDStatus sdStatus = SD_STATUS_MISSING;
extern osMutexId fsMutex;

void SD_Unmount(void){
    f_mount(NULL, "", 0);
    sdMounted = 0;
    sdStatus  = SD_STATUS_MISSING;
}

uint8_t SD_Init(void)
{
    if(BSP_PlatformIsDetected() == SD_PRESENT){
        f_mount(NULL, "", 0);

        HAL_SD_Init(&hsd);
        if(f_mount(&sdFatFs, "", 1) != FR_OK)
        {
            sdMounted = 0;
            sdStatus  = SD_STATUS_MISSING;
            return 0;
        }

        sdMounted = 1;
        sdStatus  = SD_STATUS_OK;
        return 1;
    }
    return 0;
}


static uint8_t IsWavFile(const char *name)
{
    const char *dot = strrchr(name, '.');

    if(!dot)
        return 0;

    return (!strcasecmp(dot, ".wav"));
}


typedef struct
{
    char     ChunkID[4];
    uint32_t ChunkSize;
    char     Format[4];
    char     Subchunk1ID[4];
    uint32_t Subchunk1Size;
    uint16_t AudioFormat;
    uint16_t NumChannels;
    uint32_t SampleRate;
    uint32_t ByteRate;
    uint16_t BlockAlign;
    uint16_t BitsPerSample;
    char     Subchunk2ID[4];
    uint32_t Subchunk2Size;

} WAVHeader;

static void WAV_FillHeader(WAVHeader *hdr,
                           uint32_t dataSize,
                           uint32_t sampleRate,
                           uint16_t bits,
                           uint16_t channels)
{
    memcpy(hdr->ChunkID,     "RIFF", 4);
    memcpy(hdr->Format,      "WAVE", 4);
    memcpy(hdr->Subchunk1ID, "fmt ", 4);
    memcpy(hdr->Subchunk2ID, "data", 4);

    hdr->ChunkSize     = 36 + dataSize;
    hdr->Subchunk1Size = 16;
    hdr->AudioFormat   = 1;
    hdr->NumChannels   = channels;
    hdr->SampleRate    = sampleRate;
    hdr->BitsPerSample = bits;

    hdr->ByteRate   = sampleRate * channels * (bits / 8);
    hdr->BlockAlign = channels * (bits / 8);

    hdr->Subchunk2Size = dataSize;
}
static uint8_t GetNextRecIndex(uint16_t *nextIndex)
{
    DIR dir;
    FILINFO fno;

    int16_t maxIndex = -1;

    if (f_opendir(&dir, SUB_DIR) != FR_OK)
        return 1;

    while (1)
    {
        if (f_readdir(&dir, &fno) != FR_OK || fno.fname[0] == 0)
            break;

        if (fno.fattrib & AM_DIR)
            continue;

        // otsi RECXX.wav
        if (strncasecmp(fno.fname, "REC", 3) == 0 && IsWavFile(fno.fname))
        {
            int idx = 0;
            int i = 3;
            while (fno.fname[i] >= '0' && fno.fname[i] <= '9')
            {
                idx = idx * 10 + (fno.fname[i] - '0');
                i++;
            }

            if (idx > maxIndex)
                maxIndex = idx;
        }
    }

    f_closedir(&dir);

    *nextIndex = maxIndex + 1;
    return 0;
}
uint8_t SD_CreateWavFile(FIL *file,
                         uint32_t dataSize,
                         uint32_t sampleRate,
                         uint16_t bits,
                         uint16_t channels){
    UINT bw;
    WAVHeader hdr;
    osMutexWait(fsMutex, osWaitForever);

    uint16_t idx;
    char path[64];

    if (GetNextRecIndex(&idx) != 0) {
        osMutexRelease(fsMutex);
        return 1;
    }

    // REC00.wav stiil
    snprintf(path, sizeof(path),
             SUB_DIR "/REC%02u.wav", idx);

    if (f_open(file, path, FA_CREATE_ALWAYS | FA_WRITE) != FR_OK){
        osMutexRelease(fsMutex);
        return 1;
    }

    WAV_FillHeader(&hdr, dataSize, sampleRate, bits, channels);
    f_write(file, &hdr, sizeof(hdr), &bw);
    f_sync(file); // Kirjuta päis kohe kettale
    osMutexRelease(fsMutex);
    return 0;
}

uint8_t SD_WriteWavBlock(FIL *file, int32_t *data, uint32_t samples, uint32_t bytes)
{
    UINT bw;
    osMutexWait(fsMutex, osWaitForever);
    FRESULT res = f_write(file, data, bytes, &bw);
    osMutexRelease(fsMutex);
    return (res == FR_OK && bw == bytes) ? 0 : 1;
}
uint8_t SD_FinalizeWavFile(FIL *file, uint32_t dataSize, uint32_t sampleRate,
        uint16_t bits,
        uint16_t channels)
{
    UINT bw;
    osMutexWait(fsMutex, osWaitForever);

    uint32_t filesize = f_size(file);
    uint32_t data_len  = filesize - 44;
    uint32_t total_len = filesize - 8;

    f_lseek(file, 4);
    f_write(file, &total_len, 4, &bw);
    f_lseek(file, 40);
    f_write(file, &data_len, 4, &bw);
    f_close(file);

    osMutexRelease(fsMutex);
    return 0;
}

恳请各位提供技术帮助!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 21:18:10