2025-06-06 10:12 PM
Hi,
I'm using the STM32H743, with TLV320AIC3254 audio codec to send and receive audio using the SAI peripheral in I2S mode, 16 bits (extended mode, i.e 32 bit slot), master, stereo.
I'm using DMA to send/receive the data, but have come across an issue where the data transmitted by the SAI seems to have the left and right data swapped. As I understand, the even samples in my buffer should be for the left channel, and the odd samples for the right channel.
As a test, I have written specific values (0x0033 , 0x0088) to the first to the first two int_16 of the output buffer. And I expect to see the first sample output with FS low, then then next with FS high. However checking the data output with a logic analyzer, shows that 1. There is an initial sample that is 0, which is not in the output buffer. 2. The left and right data are swapped.
I've tried changing the FS polarity, which will fix the transmitted samples, but then the received samples are swapped.
I guess it could be possible to change the FS polarity only for the TX SAI sub-block, but ideally there shouldn't be this issue in the first place.
Does anyone know why this is happening?
Thanks!
I've included parts of the code below.
SAI and DMA setup:
hsai_BlockA1.Instance = SAI1_Block_A;
hsai_BlockA1.Init.AudioMode = SAI_MODEMASTER_RX;
hsai_BlockA1.Init.Synchro = SAI_ASYNCHRONOUS;
hsai_BlockA1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockA1.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE;
hsai_BlockA1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockA1.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_48K;
hsai_BlockA1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockA1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockA1.Init.CompandingMode = SAI_NOCOMPANDING;
if (HAL_SAI_InitProtocol(&hsai_BlockA1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BITEXTENDED, 2) != HAL_OK) //SAI_PROTOCOL_DATASIZE_16BIT
{
Error_Handler();
}
hsai_BlockB1.Instance = SAI1_Block_B;
hsai_BlockB1.Init.AudioMode = SAI_MODESLAVE_TX;
hsai_BlockB1.Init.Synchro = SAI_SYNCHRONOUS;
hsai_BlockB1.Init.OutputDrive = SAI_OUTPUTDRIVE_DISABLE;
hsai_BlockB1.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_EMPTY;
hsai_BlockB1.Init.SynchroExt = SAI_SYNCEXT_DISABLE;
hsai_BlockB1.Init.MonoStereoMode = SAI_STEREOMODE;
hsai_BlockB1.Init.CompandingMode = SAI_NOCOMPANDING;
hsai_BlockB1.Init.TriState = SAI_OUTPUT_NOTRELEASED;
if (HAL_SAI_InitProtocol(&hsai_BlockB1, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BITEXTENDED, 2) != HAL_OK)
{
Error_Handler();
}
---------------------------------------------------------------------
hdma_sai1_a.Instance = DMA1_Stream0;
hdma_sai1_a.Init.Request = DMA_REQUEST_SAI1_A;
hdma_sai1_a.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_sai1_a.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sai1_a.Init.MemInc = DMA_MINC_ENABLE;
hdma_sai1_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_sai1_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_sai1_a.Init.Mode = DMA_CIRCULAR;
hdma_sai1_a.Init.Priority = DMA_PRIORITY_LOW;
hdma_sai1_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_sai1_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_sai1_a.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_sai1_a.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_sai1_a) != HAL_OK)
{
Error_Handler();
}
.....
hdma_sai1_b.Instance = DMA1_Stream1;
hdma_sai1_b.Init.Request = DMA_REQUEST_SAI1_B;
hdma_sai1_b.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_sai1_b.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_sai1_b.Init.MemInc = DMA_MINC_ENABLE;
hdma_sai1_b.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_sai1_b.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_sai1_b.Init.Mode = DMA_CIRCULAR;
hdma_sai1_b.Init.Priority = DMA_PRIORITY_LOW;
hdma_sai1_b.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_sai1_b.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
hdma_sai1_b.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_sai1_b.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_sai1_b) != HAL_OK)
{
Error_Handler();
}
/* Several peripheral DMA handle pointers point to the same DMA handle.
Be aware that there is only one channel to perform all the requested DMAs. */
__HAL_LINKDMA(saiHandle,hdmarx,hdma_sai1_b);
__HAL_LINKDMA(saiHandle,hdmatx,hdma_sai1_b);
Setup output buffer and start transfer:
memset(playbuff,0x55,AUDIO_BUFF_SIZE);
playbuff[0] = 0x33;
playbuff[1] = 0x88;
...
__HAL_SAI_ENABLE(&hsai_BlockA1);
__HAL_SAI_ENABLE(&hsai_BlockB1);
HAL_StatusTypeDef stat = HAL_OK;
if((stat = HAL_SAI_Transmit_DMA(&hsai_BlockB1, (uint8_t *)playbuff, AUDIO_BUFF_SAMPLES)) != HAL_OK)
{
printf("DMA TX ERROR: %d\n\r",stat);
printf("SAI ERROR: %d DMA ERROR: %d \n\r",hsai_BlockB1.ErrorCode,hdma_sai1_b.ErrorCode);
//Error_Handler();
}
if((stat = HAL_SAI_Receive_DMA(&hsai_BlockA1, (uint8_t *)readbuff, AUDIO_BUFF_SAMPLES)) != HAL_OK)
{
printf("DMA RX ERROR: %d\n\r",stat);
printf("SAI ERROR: %d DMA ERROR: %d \n\r",hsai_BlockA1.ErrorCode,hdma_sai1_a.ErrorCode);
//Error_Handler();
}