2025-11-06 6:30 PM
I am new to STM32 programming.
I am doing an stm32 project with STM32F407VET6 MCU, I want to implement a music player that can read a list of wav files from sd card and stream the music data to a external DAC chip (PCM5012a) using double buffering apporach. I have configured SDIO and FATFS with DMA enabled for perfrming the sd card interfacing and read files, which should be working normally. But the main problem is comming from I2S3 with DMA, as it doesnt continue stream subsequent audio chunks after the 1st one. Here is my i2s3 ioc configurations:
I tested the playback in my main.c file, this is an abridged version:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "fatfs.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "lcd.h"
#include "touch.h"
#include <stdio.h>
#include "sd_interface.h"
/* USER CODE END Includes */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define AUDIO_BUF_SIZE 256
/* USER CODE END PD */
/* Private variables ---------------------------------------------------------*/
I2S_HandleTypeDef hi2s3;
DMA_HandleTypeDef hdma_spi3_tx;
SD_HandleTypeDef hsd;
DMA_HandleTypeDef hdma_sdio_rx;
DMA_HandleTypeDef hdma_sdio_tx;
SPI_HandleTypeDef hspi2;
UART_HandleTypeDef huart1;
SRAM_HandleTypeDef hsram1;
/* USER CODE BEGIN PV */
FIL wav_file;
WAV_Header_t wav;
static uint8_t audio_buf[AUDIO_BUF_SIZE * 2] __attribute__((aligned(4)));
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_FSMC_Init();
MX_SDIO_SD_Init();
MX_SPI2_Init();
MX_FATFS_Init();
MX_I2S3_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
printf("System Started\n");
printf("I2S Audio Player Debug Test\n");
LCD_Init();
Touch_Init();
Touch_Load_Calibration();
if (!Touch_Is_Calibrated()) {
Touch_Calibrate(); // This will automatically save to flash
}
else {
LCD_Fill(LCD_COLOR_WHITE);
LCD_DrawString(0,0, "Touch calibrate complete", LCD_COLOR_GREEN, LCD_COLOR_WHITE);
}
LCD_Fill(LCD_COLOR_BLACK);
if (SD_Mount() == FR_OK) {
SD_ScanMusicFiles();
if (num_music_files > 0) {
SD_OpenFile(music_files[0], &wav_file);
SD_GetWAVHeader(&wav_file, &wav);
f_close(&wav_file);
}
}
if (SD_OpenFile(music_files[0], &wav_file) != FR_OK) {
LCD_DrawString(10, 70, "Open FAIL", LCD_COLOR_RED, LCD_COLOR_BLACK);
while (1);
}
WAV_Header_t wav_hdr;
if (SD_GetWAVHeader(&wav_file, &wav_hdr) != FR_OK ||
wav_hdr.sampleRate != 44100 ||
wav_hdr.bitsPerSample != 16 ||
wav_hdr.numChannels != 2) {
LCD_DrawString(10, 90, "Unsupported WAV", LCD_COLOR_RED, LCD_COLOR_BLACK);
f_close(&wav_file);
while (1);
}
char txt[64];
sprintf(txt, "%s %luHz %dbit", music_files[0],
wav_hdr.sampleRate, wav_hdr.bitsPerSample);
LCD_DrawString(10, 110, txt, LCD_COLOR_GREEN, LCD_COLOR_BLACK);
f_lseek(&wav_file, sizeof(WAV_Header_t));
LCD_DrawString(10, 130, "Playing...", LCD_COLOR_YELLOW, LCD_COLOR_BLACK);
Start_I2S_DMA();
UINT total_read = 0;
while (total_read < wav_hdr.subchunk2Size) {
HAL_Delay(100); /* UI heartbeat */
/* You can update a progress bar here if you like */
}
HAL_I2S_DMAStop(&hi2s3);
f_close(&wav_file);
LCD_DrawString(10, 150, "Finished", LCD_COLOR_GREEN, LCD_COLOR_BLACK);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_Delay(100);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
static void FillHalfBuffer(uint8_t half_idx) {
UINT br;
FRESULT res;
uint8_t *p = &audio_buf[half_idx * AUDIO_BUF_SIZE]; /* pointer to the half */
res = f_read(&wav_file, p, AUDIO_BUF_SIZE, &br);
if (res != FR_OK || br == 0) {
/* EOF → silence */
memset(p, 0, AUDIO_BUF_SIZE);
}
}
static void Start_I2S_DMA(void) {
FillHalfBuffer(0);
FillHalfBuffer(1);
if (HAL_I2S_Transmit_DMA(&hi2s3,
(uint16_t *)audio_buf,
AUDIO_BUF_SIZE / 2) != HAL_OK) {
Error_Handler();
}
}
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s) {
if (hi2s == &hi2s3) {
FillHalfBuffer(1);
}
}
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s) {
if (hi2s == &hi2s3) {
FillHalfBuffer(0);
}
}
With my understanding, Start_I2S_DMA() should trigger I2S3's transmission of streaming PCM data to the DAC chip, and it should repeatedly invoke callbacks to HAL_I2S_TxHalfCpltCallback and HAL_I2S_TxCpltCallback, and the audio buffer should be filled as the MCU f_reads the current wav file.
But with only one single audio chunk being played, it seem to me that the callbacks are not invoked repeatedly.
Has anoye know how to solve/debug the issue? Thanks!