cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F1 - sending WAV file using I2S problem (SD, FatFs, DMA, I2S, DAC)

MLemb.2
Associate II

Hello!

After somet tests on Nucleo and Discovery I wan't to make some more complicated and practice project. I'm trying to make WAV file player from microSD card file.

Hardware is based on STM32F103RCT6 connected with microSD card socket and DAC (PCM1780).

Firstly - using FatFs I'm reading WAV file from microSD card to buffer,

Secondly - I'm sending buffer to DAC using DMA in circular mode. When half of buffer is sended I load it from microSD (while second part of buffer is palyed). When second part of buffer is palayed I load first part, etc.

Sound is deformed and too slow.

When I connect oscilloscope I can see that audio signal is generated only in 1/3 time.


_legacyfs_online_stmicro_images_0693W00000bj3nJQAQ.pngWhen I checked LRCK i SDATA signals - reason is clear.


_legacyfs_online_stmicro_images_0693W00000bj3nYQAQ.png
_legacyfs_online_stmicro_images_0693W00000bj3nTQAQ.pngBelow you can see the most important code:

main.c

/* Private variables ---------------------------------------------------------*/
I2S_HandleTypeDef hi2s2;
DMA_HandleTypeDef hdma_spi2_tx;
 
SPI_HandleTypeDef hspi1;
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_SPI1_Init(void);
static void MX_I2S2_Init(void);
/* USER CODE BEGIN PFP */
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s);
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s);
/* USER CODE END PFP */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
  HAL_I2S_MspInit(&hi2s2);
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_FATFS_Init();
  MX_SPI1_Init();
  MX_I2S2_Init();
  MX_SPI3_Init();
  /* USER CODE BEGIN 2 */
  
  SDMount();
  WAVPlayerFileSelect("test1.wav");
  WAVPlayerPlay(&hi2s2);
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  WAVPlayerProcess();
    /* USER CODE END WHILE */
  }
}
 
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
 
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2S2;
  PeriphClkInit.I2s2ClockSelection = RCC_I2S2CLKSOURCE_SYSCLK;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/**
  * @brief I2S2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2S2_Init(void)
{
  hi2s2.Instance = SPI2;
  hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
  hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
  hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
  hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
  hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_44K;
  hi2s2.Init.CPOL = I2S_CPOL_LOW;
  if (HAL_I2S_Init(&hi2s2) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/**
  * @brief SPI1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI1_Init(void)
{
  /* SPI1 parameter configuration*/
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{
 
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Channel5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
 
}
 
/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
 
  // REMAP NJTRST pin to use PB4 as normal GPIO
  __HAL_RCC_AFIO_CLK_ENABLE();
  __HAL_AFIO_REMAP_SWJ_NONJTRST();
 
/* USER CODE END MX_GPIO_Init_1 */
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();
 
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
 
  /*Configure GPIO pin : PA4 */
  GPIO_InitStruct.Pin = GPIO_PIN_4;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
 
/* USER CODE BEGIN 4 */
 
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	WAVPlayerBufferState(2);
}
 
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
	WAVPlayerBufferState(1);
}
 

wavPlayer.c

#define SPI_TIMEOUT 500
 
#include <wavPlayer.h>
#include "fatfs.h"
 
FATFS fatFs;			// file system
FIL wavFile;			// file
FIL file;
FRESULT fresult;		// to store the result
 
UINT br, bw;
 
/* capacity related variables */
FATFS *pfatFs;
DWORD fre_clust;
uint32_t total, free_space;
 
uint8_t bufferState = 0;
 
int16_t audioBuffer[AUDIO_BUFFER_SIZE];
 
extern SPI_HandleTypeDef hspi3;
 
bool SDMount(void)
{
  /* Mount SD Card */
  if (f_mount(&fatFs, "", 0) == FR_OK)
  {
	  return true;
  }
  else
  {
	  return false;
  }
}
 
bool WAVPlayerFileSelect(const char* filePath)
{
	[...]
}
 
void WAVPlayerPlay(I2S_HandleTypeDef* i2s)
{
	// Fill buffer first time
	f_read(&wavFile, &audioBuffer, AUDIO_BUFFER_SIZE, &br);
	// Start circular DMA
	HAL_I2S_Transmit_DMA(i2s, (uint16_t *)audioBuffer, AUDIO_BUFFER_SIZE);
}
void WAVPlayerBufferState(uint8_t bs)
{
	bufferState = bs;
}
 
void WAVPlayerProcess(void)
{
	if(bufferState == 1)
	{
		f_read(&wavFile, &audioBuffer[0], AUDIO_BUFFER_SIZE / 2, &br);
		bufferState = 0;
	}
	if(bufferState == 2)
	{
		f_read(&wavFile, &audioBuffer[AUDIO_BUFFER_SIZE / 2], AUDIO_BUFFER_SIZE / 2, &br);
		bufferState = 0;
	}
}

 wavPlayer.h

#include "main.h"
#include "stm32f1xx_hal.h"
 
#define AUDIO_BUFFER_SIZE		4096
#define WAV_FILE_HEADER_SIZE	44
 
#define CHUNK_ID_CONST			0x46464952
#define FORMAT_CONST			0x45564157
#define CHANNEL_STEREO			2
 
bool SDMount(void);
bool WAVPlayerFileSelect(const char* filePath);
void WAVPlayerPlay(I2S_HandleTypeDef* i2s);
void WAVPlayerBufferState(uint8_t bs);
void WAVPlayerProcess(void);

What can be the reason of this I2S behavior?

1 ACCEPTED SOLUTION

Accepted Solutions
MLemb.2
Associate II

Thank you for suggesions. Debugging with sinewave was helpfull 🙂

Finally I founded 2 problems:

1) I have int16_t type buffer (2 bytes), but function f_read need amount of data to read in bytes. When I sended AUDIO_BUFFER_SIZE / 2 it read only 1/4 buffer. I change it to AUDIO_BUFFER_SIZE to fill half buffer.

2) I change sampling frequency of file and I2S configuration to 22kHz.

After that sound is playing without deforming and at the right speed.

View solution in original post

2 REPLIES 2

I'd probably opt for a larger buffer, and align the data reads

The header read will knock it out of alignment, so perhaps you can just zero it out, or figuring out a different buffering scheme.

Benchmark the reads, make sure you have the bandwidth.

Check I2S with a sinewave you generate.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
MLemb.2
Associate II

Thank you for suggesions. Debugging with sinewave was helpfull 🙂

Finally I founded 2 problems:

1) I have int16_t type buffer (2 bytes), but function f_read need amount of data to read in bytes. When I sended AUDIO_BUFFER_SIZE / 2 it read only 1/4 buffer. I change it to AUDIO_BUFFER_SIZE to fill half buffer.

2) I change sampling frequency of file and I2S configuration to 22kHz.

After that sound is playing without deforming and at the right speed.