2025-07-04 4:08 AM - last edited on 2025-07-04 7:42 AM by Amel NASRI
when i make the macro LUDWIG_SAI_DMA_MODE 0 i can hear audio in continous loop without noise because it runs in blocking mode.when give 1 to LUDWIG_SAI_DMA_MODE, I can able to hear the audio for sometimes after that i hear noise. When i hear noise i suspend the task it will point to while(updatePointer==-1).
/*
* audio.c
*
* Created on: Jun 30, 2025
* Author: KannikaMMallu
*/
#include "audio.h"
#include "sai.h"
#include "tx_api.h"
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
__IO int16_t UpdatePointer = -1;
extern int16_t PlayBuff[PLAY_BUFF_SIZE];
/* USER CODE END PTD */
TX_THREAD audio_thread;
UCHAR audio_thread_stack[4096];
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* USER CODE BEGIN 3 */
void audio_thread_entry(ULONG thread_input)
{
Audio();
}
void Audio_ThreadX_Init(void)
{
// Create the audio thread
tx_thread_create(&audio_thread, "Audio Thread", audio_thread_entry, 0,
audio_thread_stack, sizeof(audio_thread_stack),
10, 10, TX_NO_TIME_SLICE, TX_AUTO_START);
}
/* USER CODE END 3 */
/* Private variables ---------------------------------------------------------*/
int Audio(void)
{
/* USER CODE BEGIN 1 */
//check audio buffer is loaded to internal memory
/* Check if the buffer has been loaded in flash */
SAI_check_audio_buffer();
/* USER CODE END 1 */
#if LUDWIG_SAI_DMA_MODE
SAI_DMA_circular_link_create();
// Pre-fill the entire buffer before starting playback
SAI_read_audio_buffer_from_mem((uint16_t *)PlayBuff, PLAY_BUFF_SIZE);
// Start DMA transfer to I2S
SAI_send_audio_buffer_to_i2s((uint8_t *)PlayBuff, PLAY_BUFF_SIZE);
#endif
while (1)
{
#if LUDWIG_SAI_DMA_MODE
/* Wait a callback event */
while (UpdatePointer == -1);
if(UpdatePointer == 0) //half of the buffer is complete
{
SAI_read_audio_buffer_from_mem((uint16_t *)&PlayBuff[0], PLAY_BUFF_SIZE / 2);
}
else
{
SAI_read_audio_buffer_from_mem((uint16_t *)&PlayBuff[PLAY_BUFF_SIZE / 2], PLAY_BUFF_SIZE / 2);
}
UpdatePointer = -1;
#else
//Read audio buffer of 64 samples
SAI_read_audio_buffer_from_mem((uint16_t *)PlayBuff, PLAY_BUFF_SIZE);
//Send the process data to I2S in blocking mode
SAI_send_audio_buffer_to_i2s((uint8_t *)PlayBuff, PLAY_BUFF_SIZE);
#endif
} //end of while
}
/*
* sai.c
*
* Created on: May 6, 2025
* Author: Mahesha
*/
#include "stm32wbaxx_it.h"
#include "sai.h"
#include "linked_list.h"
extern DMA_QListTypeDef SAIQueue;
int16_t PlayBuff[PLAY_BUFF_SIZE];
extern __IO int16_t UpdatePointer;
static uint32_t PlaybackPosition = PLAY_HEADER;
void SAI_check_audio_buffer(void)
{
/* Check if the buffer has been loaded in flash */
if(*((uint64_t *)AUDIO_FILE_ADDRESS) != 0x017EFE2446464952 )
Error_Handler();
/* Associate the DMA handle */
//__HAL_LINKDMA(&hsai_BlockB1, hdmatx, handle_GPDMA1_Channel1);
}
void SAI_DMA_circular_link_create(void)
{
/* Associate the DMA handle */
__HAL_LINKDMA(&hsai_BlockB1, hdmatx, handle_GPDMA1_Channel1);
MX_SAIQueue_Config();
/* Set queue circular mode for sai queue */
HAL_DMAEx_List_SetCircularMode(&SAIQueue);
/* Link SAI queue to DMA channel */
HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel1, &SAIQueue);
}
void SAI_send_audio_buffer_to_i2s(uint8_t *sendPlayBuff, uint16_t Size)
{
#if LUDWIG_SAI_DMA_MODE
if(HAL_OK != HAL_SAI_Transmit_DMA(&hsai_BlockB1, (uint8_t *)sendPlayBuff, Size))
{
Error_Handler();
}
#else
if(HAL_OK != HAL_SAI_Transmit(&hsai_BlockB1, (uint8_t *)sendPlayBuff, Size,5000))
{
Error_Handler();
}
#endif
}
void SAI_read_audio_buffer_from_mem(uint16_t *readPlayBuff, uint16_t Size)
{
/* check the end of the file */
for (uint16_t i = 0; i < Size; i++)
{
if ((PlaybackPosition + Size*2) >= AUDIO_FILE_SIZE)
PlaybackPosition = PLAY_HEADER;
readPlayBuff[i] = *(uint16_t *)(AUDIO_FILE_ADDRESS + PlaybackPosition);
PlaybackPosition += 2;
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/**
* @brief GPDMA1 Initialization Function
* None
* @retval None
*/
void MX_SAI_GPDMA_Init(void)
{
/* USER CODE BEGIN GPDMA1_Init 0 */
/* USER CODE END GPDMA1_Init 0 */
/* Peripheral clock enable */
__HAL_RCC_GPDMA1_CLK_ENABLE();
/* GPDMA1 interrupt Init */
HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 5, 5);
HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
/* USER CODE BEGIN GPDMA1_Init 1 */
/* USER CODE END GPDMA1_Init 1 */
handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
handle_GPDMA1_Channel1.InitLinkedList.Priority = DMA_HIGH_PRIORITY;
handle_GPDMA1_Channel1.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
handle_GPDMA1_Channel1.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel1.InitLinkedList.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;
handle_GPDMA1_Channel1.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel1) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN GPDMA1_Init 2 */
/* USER CODE END GPDMA1_Init 2 */
}
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/**
* @brief Tx Transfer Half completed callbacks
* hsai : pointer to a SAI_HandleTypeDef structure that contains
* the configuration information for SAI module.
* @retval None
*/
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
UpdatePointer = 0;
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET);
}
/**
* @brief Tx Transfer completed callbacks.
* hsai : pointer to a SAI_HandleTypeDef structure that contains
* the configuration information for SAI module.
* @retval None
*/
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
UpdatePointer = PLAY_BUFF_SIZE/2;
}
void HAL_SAI_ErrorCallback(SAI_HandleTypeDef *hsai)
{
printf("SAI ERROR! ErrorCode=0x%08lX\n", (unsigned long)hsai->ErrorCode);
}
2025-07-08 1:31 AM
Hello @Kannika_Mallu
For audio playback (or record), the audio buffer (PlayBuff in your case) contains only a part of the audio file. You fill it initially with the first PCM samples of the audio file. During playback, you absolutely need DMA callbacks (half-transfer and transfer-complete) to refill the audio buffer with new PCM samples from the file.
When the half-transfer callback occurs, you need to fill the first half of the audio buffer with new PCM samples. When the transfer-complete callback occurs, you need to fill the second half of the audio buffer with new PCM samples. This guarantees correct playback without noise.
Therefore, you cannot perform audio playback without using circular DMA and its half-transfer and transfer-complete callbacks