2025-09-10 12:45 AM - last edited on 2025-09-10 12:58 AM by mƎALLEm
Post edited by ST moderator to be inline with the community rules especially for sharing a code. Please read this post.
I’m working on playing audio on the TAS2110 codec using the STM32U5G9J-DK1 board via SAI.
I tested with generated sine and square waves, and I can hear output on the speaker.
However, when I try to play audio data stored in memory, I don’t get any sound.
To store the audio file, I created a .s file as shown below:
.section .wav_data, "a"
.global _wav_data
_wav_data:
.incbin "C:/Work/Project/Project/audio_codec/Core/Src/wav/audio.wav"
.global _wav_data_end
_wav_data_end:
I can parse the WAV data using my parser API. Parsing works fine, and I copy the samples into a buffer, then send it to SAI via DMA.
Could you please help me identify what I might be missing? Why do I not get audio output from the speaker when streaming from the WAV file?
My GPDMA and SAI configurations are set up accordingly.
In the linker script
.wav_data :
{
. = ALIGN(4);
_swav_data_start = .;
KEEP(*(.wav_data))
_swav_data_end = .;
} >FLASH
Parse API
int parse_wav_file(uint8_t *wav_data, uint32_t wav_size, wav_info_t *info) {
if (wav_size < 44) return -1; // Too small for header
// Check RIFF header
if (strncmp((char *)wav_data, "RIFF", 4) != 0) return -1;
if (strncmp((char *)(wav_data + 8), "WAVE", 4) != 0) return -1;
// Find fmt chunk
uint32_t offset = 12;
while (offset + 8 < wav_size) {
if (strncmp((char *)(wav_data + offset), "fmt ", 4) == 0) {
break;
}
offset += 8 + *(uint32_t *)(wav_data + offset + 4);
}
if (offset + 8 >= wav_size) return -1;
// Parse fmt chunk
uint16_t audio_format = *(uint16_t *)(wav_data + offset + 8);
info->num_channels = *(uint16_t *)(wav_data + offset + 10);
info->sample_rate = *(uint32_t *)(wav_data + offset + 12);
info->bits_per_sample = *(uint16_t *)(wav_data + offset + 22);
if (audio_format != 1 || info->num_channels != 1 || info->bits_per_sample != 16) {
return -1; // Only support mono, 16-bit PCM
}
// Find data chunk
offset += 8 + *(uint32_t *)(wav_data + offset + 4);
while (offset + 8 < wav_size) {
if (strncmp((char *)(wav_data + offset), "data", 4) == 0) {
info->data_size = *(uint32_t *)(wav_data + offset + 4);
info->data_start = wav_data + offset + 8;
return 0;
}
offset += 8 + *(uint32_t *)(wav_data + offset + 4);
}
return -1;
}
Filling audio as per below API
#define AUDIO_BUFFER_SIZE 4096
uint32_t wav_index = 0;
void fill_audio_buffer(void) {
uint32_t samples_to_copy = AUDIO_BUFFER_SIZE;
if (wav_index + samples_to_copy > wav_info.data_size / 2) {
samples_to_copy = (wav_info.data_size / 2) - wav_index;
}
memcpy(audio_buffer,
(int16_t *)(wav_info.data_start + wav_index * 2),
samples_to_copy * sizeof(int16_t));
wav_index += samples_to_copy;
}
Audio Play API
static int audio_play(uint16_t *paudioBuff, uint32_t len)
{
if(HAL_OK != HAL_SAI_Transmit_DMA(saiptr,(uint8_t*)paudioBuff, AUDIO_BUFFER_SIZE))
{
return -1;
}
return 0;
}
int wavPlayer_play(tas2110_data_t *tas_data)
{
uint32_t wav_size = _swav_data_end - _swav_data_start;
file_size = wav_size;
if (parse_wav_file(_wav_data, wav_size, &wav_info) != 0)
{
return -1;
}
tas2110_Configuration(tas_data);
fill_audio_buffer();
if(audio_play((uint16_t *)&audio_buffer[0], AUDIO_BUFFER_SIZE) != 0)
{
return -1;
}
return 0;
}
Callback APIs:
void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai)
{
fill_audio_buffer();
HAL_SAI_Transmit_DMA(hsai, (uint8_t*)audio_buffer, AUDIO_BUFFER_SIZE);
}
void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai)
{
fill_audio_buffer();
HAL_SAI_Transmit_DMA(hsai, (uint8_t*)audio_buffer, AUDIO_BUFFER_SIZE);
}
Solved! Go to Solution.
2025-09-15 4:02 AM
Hello @Jinal
Why did you set pNodeConfig.Init.DestInc to DMA_DINC_INCREMENTED ? In your case (DMA memory to peripheral), it should be set to DMA_DINC_FIXED.
Please review the example I provided earlier for reference.
2025-09-10 7:03 AM
Hello @Jinal
Please refer to the example below:
2025-09-11 2:46 AM - edited 2025-09-11 4:21 AM
Hi @Saket_Om
Thanks for the response.
I’m using the same example, but the DMA callback is not triggered in Linked-List mode.
If I disable Linked-List and switch to List + Standard Request mode, I only get one buffer of output on the speaker (sounds like noise), and the callback is called only once.
Attached are the .c and .ioc files for reference.
Please advise if I’m missing something in the configuration.
Thank you
Jinal
2025-09-11 3:33 AM
Hello @Jinal
You are not enabling the DMA interrupt in your code.
please add the instruction below in MX_GPDMA1_Init()
HAL_NVIC_EnableIRQ(GPDMA1_Channel12_IRQn);
2025-09-11 10:18 AM
Hi @Saket_Om
Thanks for your response.
Now I’m able to get the DMA callback, but it only triggers once. My audio file size is about 680 KB, and I expect the half-transfer callback at every ~2048 bytes and the full-transfer callback at ~4096 bytes, repeating until the end of the file.
However, HAL_NVIC_EnableIRQ(GPDMA1_Channel12_IRQn); is still not firing when I enable circular transfer mode in DMA.
For your reference, I’ve attached the .ioc, main.c, and wav_player.c files. Could you please check if I’m missing something in the configuration?
Could you please check if I’m missing something in the configuration?
Thanks,
Jinal
2025-09-12 3:00 AM
Hello @Jinal
According to your code attached in main.c you are not initializing the DMA;
In the MX_GPDMA1_Init() you are juste enabling the DMA clock and setting the interrupt priorities.
Please refer to the code below to initialise your DMA.
static void MX_GPDMA1_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_Channel12_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel12_IRQn);
/* USER CODE BEGIN GPDMA1_Init 1 */
/* USER CODE END GPDMA1_Init 1 */
handle_GPDMA1_Channel12.Instance = GPDMA1_Channel12;
handle_GPDMA1_Channel12.InitLinkedList.Priority = DMA_HIGH_PRIORITY;
handle_GPDMA1_Channel12.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
handle_GPDMA1_Channel12.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel12.InitLinkedList.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;
handle_GPDMA1_Channel12.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel12) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel12, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN GPDMA1_Init 2 */
/* USER CODE END GPDMA1_Init 2 */
}
2025-09-12 5:23 AM
Hi @Saket_Om
As mentioned earlier, when I configure the channel in Linked List mode instead of Standard Request mode, I do not receive any DMA callbacks.
I have tried the same GPDMA configuration as suggested previously. For now, I am focusing only on the Linked List mode, but I am not getting any interrupts. Could you please advise how to resolve this issue?
Below is my updated MX_GPDMA1_Init function:
static void MX_GPDMA1_Init(void)
{
/* Peripheral clock enable */
__HAL_RCC_GPDMA1_CLK_ENABLE();
/* GPDMA1 interrupt Init */
HAL_NVIC_SetPriority(GPDMA1_Channel12_IRQn, 0, 0);
handle_GPDMA1_Channel12.Instance = GPDMA1_Channel12;
handle_GPDMA1_Channel12.InitLinkedList.Priority = DMA_HIGH_PRIORITY;
handle_GPDMA1_Channel12.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
handle_GPDMA1_Channel12.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
handle_GPDMA1_Channel12.InitLinkedList.TransferEventMode = DMA_TCEM_LAST_LL_ITEM_TRANSFER;
handle_GPDMA1_Channel12.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel12) != HAL_OK)
{
Error_Handler();
}
if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel12, DMA_CHANNEL_NPRIV) != HAL_OK)
{
Error_Handler();
}
}
Could you please guide me on why callbacks are not triggered in Linked List mode, and what additional configuration might be missing?
2025-09-12 6:30 AM
Could you share your whole project please?
2025-09-15 12:09 AM - edited 2025-09-15 2:34 AM
2025-09-15 4:02 AM
Hello @Jinal
Why did you set pNodeConfig.Init.DestInc to DMA_DINC_INCREMENTED ? In your case (DMA memory to peripheral), it should be set to DMA_DINC_FIXED.
Please review the example I provided earlier for reference.