AnsweredAssumed Answered

STM32L476 SAI frame synchronization is incorrect swapped left right if HAL_SAI_Transmit_DMA is called 4 us after rising edge of WS

Question asked by RokitanskyAschoff on Oct 20, 2016
Latest reply on Oct 20, 2016 by RokitanskyAschoff
Hi All,

I have an STM32L476RE connected to an audio codec. The audio codec is the master.SAI1_Block_A is configured as SAI_MODESLAVE_TX, SAI_ASYNCHRONOUS, SAI_I2S_STANDARD, SAI_PROTOCOL_DATASIZE_16BIT, SAI_STEREOMODE with 2 channels, left and right.

My project mostly works. That is, I can play and record audio.

However, under certain conditions the frame synchronization gets swapped around so that the left channel data gets played over the right channel instead. As far as I can tell this only happens on the first time that audio is played after a power up.

To investigate the cause I created a test case where the STM32L4 powers on, initializes the audio, starts playing a sine tone on the left channel and zero on the right channel, delays for some time and then resets itself with NVIC_SystemReset() so that the test repeats and I can listen to the audio and see how often the problem occurs by playing the left channel tone on the right channel. The frame synchronization was wrong about 1/10 to 1/50 of the tests.

Anyway, after trying various things like making sure the FIFO was empty etc I eventually decided to monitor the level of the LRCLK (Left Right CLocK) or Frame Select (FS) or Word Select (WS) or whatever you want to call it, the I2S signal that specifies if the channel is Left (0) or Right (1). 

I did this because I have also used devices like the STM32F407 for audio, and know that the I2S errata specifies that the I2S should only be enabled when the WS is high.

// Synchronise the call of HAL_SAI_Transmit_DMA to the FS:
while(hal_gpio_pin_read(&FS_INPUT) == 1) {}
while(hal_gpio_pin_read(&FS_INPUT) == 0) {}
uint32_t k = 0;
for(k = 0; k != 40; k ++) {}
hal_gpio_pin_on(&LED6);
 
HAL_SAI_Transmit_DMA(&hsai_BlockA1, (uint8_t*)(&dac_buf[0]), AUDIO_BUFFER_SIZE);
 
hal_gpio_pin_off(&LED6);

This means that the call of HAL_SAI_Transmit_DMA is synchronised to the FS. This seems to fix the frame synchronization, that is, I have not seen an incorrect synchronization yet.

Using the signal on LED6 I found that the frame synchronization is swapped when HAL_SAI_Transmit_DMA is called approximately 4 us after the rising edge of FS. If I adjust the delay in the for loop to achieve this 4 us, then the frame synchronization is mostly wrong: it is wrong in about 8/10 of the tests (compare to above).

Is this a known problem? The STM32L476 has no errata on the SAI? To me this looks like it could be a hardware problem, I could reduce my project to a minimal version and post the code if you think this could be a software problem. 

The reference manual RM0351 says:
In slave mode, the audio frame starts when the audio block is enabled and when a start of
frame is detected.
In Slave TX mode, no underrun event is possible on the first frame after the audio block is
enabled, because the mandatory operating sequence in this case is:
1. Write into the SAI_xDR (by software or by DMA).
2. Wait until the FIFO threshold (FLH) flag is different from 000b (FIFO empty).
3. Enable the audio block in slave transmitter mode.

I have tried this sequence by pulling __HAL_SAI_ENABLE out of the HAL_SAI_Transmit_DMA function and first waiting for the fifo threshold to be not empty, but it waits for ever as I suppose the SAI does not send DMA requests before it is enabled, so I don't understand how the above sequence is supposed to work This actually does work, the problem was that I was waiting for the FIFO to be full but FTH was configured for half full.

Outcomes