cancel
Showing results for 
Search instead for 
Did you mean: 

Issues with Playing Audio on STM32G431RBT6

Kapil Kumar
Associate

Hello ST Community,

I am trying to play audio file on STM32G431RBT6, but I am not able to successfully play the audio. I shall be thankful if you can provide any suggestions/ feedback to help me debug the cause of noise in my output.

Also I would like to mention that my audio is slowed than actual (for example, I am a playing a file of 10 seconds, then the STM32 will continue on producing the audio till 18 seconds. )

Setup Details:

  1. My MCU is running at 170Mhz.
  2. I am using timer 6 as Trigger for DMA transfer to my DAC peripheral
  3. The audio file is stored in SD card.
  4. The audio file I am trying to play has the following specifications
    1. File Format = .wav
    2. Sampling Frequency = 44100Hz
    3. Channels = 2
    4. Bits per Sample = 16
  5. My DMA is configured for word transfers (Even though I am transferring only 12 bits at a time) as configuring it for Half word transfer results in DMA Underrun error.

Following is the code

#include "main.h"
#include "app_fatfs.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
// Define the structure of the WAV file header
typedef struct {
	uint8_t  riff[4];
	uint32_t size;
	uint8_t  wave[4];
	uint8_t  fmt[4];
	uint32_t fmtSize;
	uint16_t audioFormat;
	uint16_t numChannels;
	uint32_t sampleRate;
	uint32_t byteRate;
	uint16_t blockAlign;
	uint16_t bitsPerSample;
} WAV_HeaderTypeDef;
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define AUDIO_BUFFER_SIZE 4096
 
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
 
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
DAC_HandleTypeDef hdac1;
DMA_HandleTypeDef hdma_dac1_ch1;
 
SPI_HandleTypeDef hspi3;
 
TIM_HandleTypeDef htim6;
 
/* USER CODE BEGIN PV */
WAV_HeaderTypeDef wav_header;
FATFS fs;
FIL wav_file;
UINT bytesread;
uint16_t audio_buffer[2][AUDIO_BUFFER_SIZE];
uint8_t flg_dma_done = 1;
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_SPI3_Init(void);
static void MX_DAC1_Init(void);
static void MX_TIM6_Init(void);
/* USER CODE BEGIN PFP */
bool mount_SD_card( void );
void playWAVFile(const char* filename);
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
 
/**
  * @brief  The application entry point.
  * @retval int
  */
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_SPI3_Init();
  if (MX_FATFS_Init() != APP_OK) {
    Error_Handler();
  }
  MX_DAC1_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */
	if(mount_SD_card()){
		playWAVFile("1.wav");
	}
	else{
		//Unable to mount SD Card
	}
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1)
	{
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
	}
  /* USER CODE END 3 */
}
 
bool mount_SD_card(void)
{
	FATFS       FatFs;                 //Fatfs handle
	if (f_mount(&FatFs, "", 1) != FR_OK)
	{
		//No SD Card found
		return false;
	}
	//SD Card Mounted Successfully!!!
	return true;
}
 
void playWAVFile(const char* filename)
{
	// Open the WAV file
	if (f_open(&wav_file, filename, FA_READ) != FR_OK) {
		// Error opening file
	}
	// Read the WAV file header
	if (f_read(&wav_file, &wav_header, sizeof(WAV_HeaderTypeDef), &bytesread) != FR_OK || bytesread != sizeof(WAV_HeaderTypeDef)) {
		// Error reading header
		f_close(&wav_file);
	}
 
 
 
	// Calculate prescaler and period for Timer 6 to achieve the desired sample rate
	uint32_t audio_sample_rate = wav_header.sampleRate;
	uint32_t timer_freq = HAL_RCC_GetPCLK1Freq();
	uint32_t timer_period = (timer_freq / audio_sample_rate) - 1;
	uint32_t timer_prescaler = 0;
	while (timer_period > 0xFFFF)
	{
		timer_period /= 2;
		timer_prescaler++;
	}
 
	htim6.Init.Prescaler = timer_prescaler;
	htim6.Init.Period = timer_period;
	if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
	{
		Error_Handler();
	}
 
 
	// Calculate the offset of the audio data within the file
	f_lseek(&wav_file, 12); // Rewind to the beginning of the first chunk that is fmt file
	uint32_t audio_start = 0;
 
	// Read the chunk ID and size
	uint32_t chunk_id, chunk_size;
	uint32_t dataSize;
	while (1) {
		f_read(&wav_file, &chunk_id, sizeof(chunk_id), &bytesread);
		f_read(&wav_file, &chunk_size, sizeof(chunk_size), &bytesread);
		// Check if this is the audio data chunk
		if (chunk_id == 0x61746164) { // 'data'
			audio_start = f_tell(&wav_file);
			dataSize = chunk_size;
			break;
		}
		// Seek to the next chunk
		f_lseek(&wav_file, f_tell(&wav_file) + chunk_size);
	}
 
	// Seek to the beginning of the audio data
	f_lseek(&wav_file, audio_start);
 
	uint32_t bytes_to_read = dataSize;
	uint32_t bytes_read = 0;
	uint32_t bytes_left = 0;
	uint8_t bank = 0;
 
	// Scale audio samples to fit within DAC range
	const float DAC_RANGE = (1 << 12); // 12-bit DAC has 4096 levels
	const float AUDIO_RANGE = (1 << wav_header.bitsPerSample); // 16-bit audio has a range of [-32768, 32767]
	const float scaling_factor = DAC_RANGE / AUDIO_RANGE;
 
	// Start Timer 6 to trigger the DMA transfer
	HAL_TIM_Base_Start(&htim6);
 
	while(bytes_to_read > 0) {
		// Calculate bytes to read for this iteration
		bytes_left = bytes_to_read > AUDIO_BUFFER_SIZE ? bytes_to_read - AUDIO_BUFFER_SIZE : 0;
		bytes_to_read = bytes_to_read > AUDIO_BUFFER_SIZE ? AUDIO_BUFFER_SIZE : bytes_to_read;
		// Read audio data from file
		f_read(&wav_file, audio_buffer[bank], bytes_to_read, (void *)&bytes_read);
 
		for (uint16_t i = 0; i < bytes_to_read; i++) {
			uint16_t scaled_value = (audio_buffer[bank][i] + (1 << (wav_header.bitsPerSample - 1))) * scaling_factor;
			audio_buffer[bank][i] = scaled_value;
		}
		// wait for DMA complete
		while(!flg_dma_done) {
			__NOP();
		}
 
		flg_dma_done = 0;
		HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*)audio_buffer[bank], bytes_to_read, DAC_ALIGN_12B_R);
		bank = !bank;
		bytes_to_read = bytes_left;
	}
 
	HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
	f_close(&wav_file);
}
 
void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
	flg_dma_done = 1;
}
void HAL_DAC_ConvHalfCpltCallbackCh1(DAC_HandleTypeDef *hdac)
{
	flg_dma_done = 1;
}
void HAL_DAC_ErrorCallbackCh1(DAC_HandleTypeDef *hdac)
{
	printf("DMA ERROR\n");
}

1 REPLY 1
Kapil Kumar
Associate

All the Initialization functions (generated by CubeMx) are added here because the original question was being too long

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);
 
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV4;
  RCC_OscInitStruct.PLL.PLLN = 85;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  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_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    Error_Handler();
  }
}
 
/**
  * @brief DAC1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_DAC1_Init(void)
{
 
  /* USER CODE BEGIN DAC1_Init 0 */
 
  /* USER CODE END DAC1_Init 0 */
 
  DAC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN DAC1_Init 1 */
 
  /* USER CODE END DAC1_Init 1 */
 
  /** DAC Initialization
  */
  hdac1.Instance = DAC1;
  if (HAL_DAC_Init(&hdac1) != HAL_OK)
  {
    Error_Handler();
  }
 
  /** DAC channel OUT1 config
  */
  sConfig.DAC_HighFrequency = DAC_HIGH_FREQUENCY_INTERFACE_MODE_AUTOMATIC;
  sConfig.DAC_DMADoubleDataMode = DISABLE;
  sConfig.DAC_SignedFormat = DISABLE;
  sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_ENABLE;
  sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;
  sConfig.DAC_Trigger2 = DAC_TRIGGER_NONE;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
  sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_EXTERNAL;
  sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
  sConfig.DAC_SampleAndHoldConfig.DAC_SampleTime = 0;
  sConfig.DAC_SampleAndHoldConfig.DAC_HoldTime = 0;
  sConfig.DAC_SampleAndHoldConfig.DAC_RefreshTime = 0;
  if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN DAC1_Init 2 */
 
  /* USER CODE END DAC1_Init 2 */
 
}
 
/**
  * @brief SPI3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI3_Init(void)
{
 
  /* USER CODE BEGIN SPI3_Init 0 */
 
  /* USER CODE END SPI3_Init 0 */
 
  /* USER CODE BEGIN SPI3_Init 1 */
 
  /* USER CODE END SPI3_Init 1 */
  /* SPI3 parameter configuration*/
  hspi3.Instance = SPI3;
  hspi3.Init.Mode = SPI_MODE_MASTER;
  hspi3.Init.Direction = SPI_DIRECTION_2LINES;
  hspi3.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi3.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi3.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi3.Init.NSS = SPI_NSS_SOFT;
  hspi3.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  hspi3.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi3.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi3.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi3.Init.CRCPolynomial = 7;
  hspi3.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi3.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  if (HAL_SPI_Init(&hspi3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI3_Init 2 */
 
  /* USER CODE END SPI3_Init 2 */
 
}
 
/**
  * @brief TIM6 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM6_Init(void)
{
 
  /* USER CODE BEGIN TIM6_Init 0 */
 
  /* USER CODE END TIM6_Init 0 */
 
  TIM_MasterConfigTypeDef sMasterConfig = {0};
 
  /* USER CODE BEGIN TIM6_Init 1 */
 
  /* USER CODE END TIM6_Init 1 */
  htim6.Instance = TIM6;
  htim6.Init.Prescaler = 85;
  htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim6.Init.Period = 23;
  htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM6_Init 2 */
 
  /* USER CODE END TIM6_Init 2 */
 
}
 
/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{
 
  /* DMA controller clock enable */
  __HAL_RCC_DMAMUX1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Channel4_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel4_IRQn);
 
}
 
/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
 
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET);
 
  /*Configure GPIO pin : PA15 */
  GPIO_InitStruct.Pin = GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
}