I’m working on STM32F405RGT6, receiving audio from ICS43432 (MSB First, 24bit on 32 bit frame).
sing Circular DMA (double buffering) to read it in. After 3 days of debugging and millions of mem corruptions and stackoverflows later I finally managed to get the pipeline to work without MemCorruptions.
Basically my implementation:
I have a UItask, that handles button presses and changes the system states. The StartWriteAudioTask() starts the DMA and creates a .wav file. Also sets the systemState to STATE_RECORDING. DMA fills the buffer and gives semaphore to my MicTask, which basically does an endian conversion and writes a wav block. Just in case I also implemented a copy of the audiobuffer, because DMA might overwrite before I can manage to write it to a wav block. Using the debugger I see that my state machine logic and start/stop works fine, file initialization works as well and even the file appears and is playable. But the timing is 2x slower and of course the overall sound is really distorted.
Here is my mic.c code:
#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[WRITE_SIZE_BYTES]; // Double buffering
static volatile uint16_t *BufPtr;
static uint16_t localBuf[BUFFER_SIZE];
/* ── File handle & byte counter ─ */
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\]); }}
/* ── DMA callbacks — called from ISR context ─────────────── */
void HAL_I2S_RxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
BufPtr = aud_buf;
osSemaphoreRelease(RxAudioSemHandle);
}
void HAL_I2S_RxCpltCallback(I2S_HandleTypeDef *hi2s)
{
// BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BufPtr = &aud_buf[BUFFER_SIZE];
// xSemaphoreGiveFromISR(RxAudioSemHandle, &xHigherPriorityTaskWoken);
// portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
osSemaphoreRelease(RxAudioSemHandle);
}
void StartMicTask(void const *argument){
for(;;){
**int** i; **osSemaphoreWait**(RxAudioSemHandle, osWaitForever); **if** (systemState != *STATE_RECORDING*){ **continue**; } //memcpy(localBuf, (void\*)BufPtr, WRITE_SIZE_BYTES); **uint32_t** validSamples = READ_SIZE / 2; **for** (i = 0; i < validSamples; i++) { ((**uint32_t**\*)localBuf)\[i\] = ((**uint32_t**\*)BufPtr)\[i \* 2 + 1\]; } **convert_endianness**((**uint32_t**\*)localBuf, validSamples); **uint32_t** bytesToWrite = validSamples \* 3; **if** (**SD_WriteWavBlock**(&file, (**int32_t** \*)localBuf, validSamples, bytesToWrite) == 0) totalBytesWritten += bytesToWrite;}
}
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**; }}
}
and sd.c:
#include “sd.h”
#include “fatfs_platform.h”
#include <string.h>
#include <stdlib.h>
#include “main.h”
#define SUB_DIR “/REC_DIKTOFON”
/* ---------------- INTERNAL STATE ---------------- */
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;}
/* ---------------- INIT ---------------- */
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;
}
/* ---------------- WAV FILE FILTER ---------------- */
static uint8_t IsWavFile(const char *name)
{
**const** **char** \*dot = strrchr(name, '.'); **if**(!dot) **return** 0; **return** (!strcasecmp(dot, ".wav"));}
/* ---------------- FILE LIST ---------------- */
uint8_t SD_ListWavFiles(char list[][SD_FILENAME_LEN],
uint8_t maxFiles, uint8_t \*count){
DIR dir; FILINFO fno; \*count = 0; **if**(!sdMounted) { sdStatus = SD_STATUS_MISSING; **return** 1; } **if**(f_opendir(&dir, SUB_DIR) != FR_OK) { sdStatus = SD_STATUS_NO_FILES; **return** 1; } **while**(\*count < maxFiles) { **if**(f_readdir(&dir, &fno) != FR_OK || fno.fname\[0\] == 0) **break**; **if**(fno.fattrib & AM_DIR) **continue**; **if**(IsWavFile(fno.fname)) { strncpy(list\[\*count\], fno.fname, SD_FILENAME_LEN - 1); list\[\*count\]\[SD_FILENAME_LEN - 1\] = '\\0'; (\*count)++; } } **if**(BSP_PlatformIsDetected() == SD_NOT_PRESENT){
f_closedir(&dir); SD_Unmount(); **return** 1;}
f_closedir(&dir); sdStatus = (\*count > 0) ? SD_STATUS_OK : SD_STATUS_NO_FILES; **return** 0;}
/* ---------------- WAV HEADER ---------------- */
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;}
/* --------------- GET FILE INDEX ------------*/
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;}
/* ---------------- WAV WRITE ---------------- */
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;
// Pack 24-bit samples from 32-bit frames, drop the lowest 8 bits uint8_t packed\[samples \* 3\]; **for**(uint32_t i = 0; i < samples; i++){ int32_t sample = data\[i\]; // shift out the 8 empty LSB bits packed\[i\*3 + 0\] = (sample) & 0xFF; // byte 0 LSB packed\[i\*3 + 1\] = (sample >> 8) & 0xFF; // byte 1 packed\[i\*3 + 2\] = (sample >> 16) & 0xFF; // byte 2 MSB } osMutexWait(fsMutex, osWaitForever); FRESULT res = f_write(file, packed, samples \* 3, &bw); osMutexRelease(fsMutex); **return** (res == FR_OK && bw == samples \* 3) ? 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;}
/* ---------------- HOTPLUG WATCHER ---------------- */
void StartSDWatchTask(void const *argument)
{
uint8_t lastState = BSP_PlatformIsDetected(); **if** (lastState == SD_PRESENT) { **if** (SD_Init()) sdStatus = SD_STATUS_OK; **else** sdStatus = SD_STATUS_MISSING; } **for**(;;) { uint8_t nowState = BSP_PlatformIsDetected(); /\* trigger ONLY on removal edge \*/ **if**(lastState == SD_NOT_PRESENT && nowState == SD_PRESENT){ **if** (SD_Init()){ sdStatus = SD_STATUS_OK; } **else** { sdStatus = SD_STATUS_MISSING; } } **if**(lastState == SD_PRESENT && nowState == SD_NOT_PRESENT) { SD_Unmount(); HAL_SD_DeInit(&hsd);; sdStatus = SD_STATUS_MISSING; EventType evt = EVT_SD_REMOVED; xQueueSend(uiQueue, &evt, 0); } lastState = nowState; osDelay(50); }}
Would be very thankful for any help!