2023-03-23 04:53 AM
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:
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");
}
2023-03-23 04:54 AM
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);
}