Issues with Playing Audio on STM32G431RBT6
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:
- My MCU is running at 170Mhz.
- I am using timer 6 as Trigger for DMA transfer to my DAC peripheral
- The audio file is stored in SD card.
- The audio file I am trying to play has the following specifications
- File Format = .wav
- Sampling Frequency = 44100Hz
- Channels = 2
- Bits per Sample = 16
- 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");
}