Skip to main content
Visitor
October 13, 2026
Question

Syncing SAI and DMA for I2S

Hello,


I have a STM32U585. I’m trying to use SAI to transmit and receive I2S audio as a slave. The I2S master is an external codec that’s configured for stereo 16bit 16kHz audio. Mic/input from codec->MCU and speaker/output from MCU->codec. The first stereo sample received on the codec is routed to the left speaker and second to the right.
 
This works as expected for the mic. For the speakers, sometimes even samples will play on the right side and odd on the left. I’ve loaded the DMA buffer with dummy values 0xAAAA and 0x0000 to demonstrate. MISO is the signal of interest here. I’d like 0xAAAA to be on Ch 1 consistently.

Expected Samples
Unexpected Samples

This occurred about 50% of the time. It’s observable for the entire runtime and doesn’t correlate with how long the software has been running. I suspected a sync issue SAI/DMA so I implemented a quick hack to poll the GPIO to start the DMA transfer after the falling edge of WS to help sync. This improves reliability from 50% to 90%. I’ve included a simplified snippet of the code.

HAL_SAI_Abort(rx);
HAL_SAI_Abort(tx);

while (HAL_GPIO_ReadPin(WS_PORT, WS_PIN) == GPIO_PIN_RESET);
while (HAL_GPIO_ReadPin(WS_PORT, WS_PIN) == GPIO_PIN_SET);

HAL_SAI_Receive_DMA(rx, rx_data, sizeof(rx_data));
HAL_SAI_Transmit_DMA(tx, tx_data, sizeof(tx_data));

 

I am using SAI1 block A for async Tx and SAI1 block B for sync Rx both with DMA.

 

Appreciate your help, thanks.

2 replies

LCE
Principal III
October 13, 2026

​​​​​​What else is going on with in your code?

If possible, ​try to increase both DMA priorities. If the channel swap happens while everything is running, this might be the cause.

And the DMA buffers: big enough and same size?

And how’s the buffer handling? Does the U5 have DMA double buffer mode (DBM)? If yes, use that. If not, implement some smart DMA “transfer complete / half-complete” buffer handling.

AND - as I recently learned - after each stop (or before each start) of the SAIs / DMA, flush the FIFO.

 

Associate II
October 13, 2026

I can explain prerequisites in terms of SPI not SAI or I2C.

As I understand it WS is a framing signal and it differentiates between left ch and right ch.  I’m assuming WS timing is good enough to ensure differentiation.

Your DMA is circular meaning the linked list (with probably one entry) does not terminate it keeps reloading the destination rings and  source rings. This is simplest to do using four DMA channels

a) transfer from SAI (Left) to Ring_In_Left

b) transfer  from SAI (Right) to Ring_In_Right

c) transfer to SAI (Left) from Ring_Out_Left

d) transfer  to SAI (Right) from Ring_Out_Right

With SPI I set it to a word length of 32 bit and what’s in the FIFO gives me a trigger for the DMA so the input ring buffer is continuously filling.

Let’s assume your input ring buffers are say 4KB each and your output ring buffers are also 4KB each and lets assume your app reads the inputs processes (FIR filter FFT etc) and populates the output ring buffers.  What’s then vital is that you are reading the input rings behind the input DMA pointer and writing the output rings ahead of the output DMA pointer.

Also that all four rings are separate - no way they can overlap (that’s the advantage of 4 DMA channels doing the job). If you are using two DMA channels it can be done but the linked list and trigger event becomes more involved I think.

Summing up its likely that what you are seeing is a trigger event problem and if you are using WS for your trigger events WS has to be OK.  

For DMA there are a huge number of choices for trigger events to find out about.  

Hope that helps, I could give you working DMA examples of DMA running with SPI but they are in assembler (not popular on here).