STM32F1 - sending WAV file using I2S problem (SD, FatFs, DMA, I2S, DAC)
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.
When I checked LRCK i SDATA signals - reason is clear.

Below 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?
