Skip to main content
SLasn.1
Associate III
August 31, 2020
Question

SAI - how to synchronize the data transmit between 2 blocks

  • August 31, 2020
  • 10 replies
  • 3943 views

Hi!

I am still playing with the SAI of my STM32F446RE. It has one SAI (SAI1) which has two blocks, A and B.

The idea is to use the two blocks synchronously in TDM mode to transmit in total 8 slots, 4 slots for each block.

I can get it to work fine, though I noticed the two blocks do not necessarily send the data at the same time - i.e. as soon as I put something in the DR registers it gets sent. I went through the datasheet and a few PDF presentations multiple times, but I still do not get how I can get the 2 blocks to send the data at the same time (for example I put 4 bytes in each block, then say "go" and the 8 bytes are sent together using the 8 slots - i.e. one frame).

I guess it is something to do with the FIFIO thresholds? I tried setting them to Full (in my case half full will be 4 bytes, so it will be perfect) but it does not change anything.

Could someone explain to me how I am supposed to configure the SAI blocks?

Thank you in advance!

Simon

PS: I am using CubeMX and the HAL lib.

This topic has been closed for replies.

10 replies

SLasn.1
SLasn.1Author
Associate III
August 31, 2020

Note that ideally I want to do this with DMA. Maybe there is a way to do it on the DMA level, with the DMA FIFO?

SLasn.1
SLasn.1Author
Associate III
September 1, 2020

Anyone? :)

waclawek.jan
Super User
September 1, 2020

Note that there's only a handful of users answering questions here; I don't remember many threads with SAI/TDM. (I used a single block SAI for I2S and then other for SPDIF-Tx, and all this was quite some time ago, I don't really remember the details).

So, without looking into the manual, IMO the blocks are master-slave, and slave won't transmit before master does. So, what I'd do is, start DMA into the slave's data register, make sure the FIFO is full, and then start DMA into the master's data register. They should start transmitting simultaneously when data arrive to master; DMA is there to top up the FIFO as transmitter in SAI drains it.

JW

SLasn.1
SLasn.1Author
Associate III
September 3, 2020

Hi Jan

Yes it's more than fair, I did not want to push anyone, just thinking the post passed through the nets a bit :)

Thanks for the tip I'll try that and write what I get here :)

SLasn.1
SLasn.1Author
Associate III
September 9, 2020

I tested the DMAPause/Resume and I can confirm it gets out of sync when resuming. BUT it turns out that within one frame the data is still where it should be - so that the data is still sent to the correct audio output (see below).

Though the slave and master can still be one frame out of sync, and can get more and more out of sync each time the HAL_SAI_DMAPause/Resume functions are called.

Notice that in terms of audio one frame out of sync means some audio channels will be 1/fs out of phase, f.x. 0.02ms (with fs=44.1kHz) - I am thinking this should not be audible really?

Here is my test setup:

  • I am sending 8 samples with DMA set to Half-Word Circular
  • I start transmitting with DMA in the main()
  • In the loop I call Pause/Resume when I press a button

The code:

// test data - dummy data very visual on the logic analyzer
static const uint16_t data[8] = { 0b1, 0b101, 0b10101, 0b1010101, 0b101010101, 0b10101010101, 0b1010101010101, 0b101010101010101 };
 
int main(void) {
 
 // [...]
 
 HAL_SAI_Transmit_DMA(SAI_BlockSlave, (uint8_t*)data, 8); // slave first
 HAL_SAI_Transmit_DMA(SAI_BlockMaster, (uint8_t*)data, 8);
 
 while (1) {
 
 // [...]
 
 if (buttonPressed) {
	 if (paused) {
		 HAL_SAI_DMAResume(SAI_BlockSlave); // slave first
		 HAL_SAI_DMAResume(SAI_BlockMaster);
	 }
 	 else {
		 HAL_SAI_DMAPause(SAI_BlockMaster); // master first
		 HAL_SAI_DMAPause(SAI_BlockSlave);
	 }
 
 // [...]
	}
 }
}

When I start the SAIs I get that:

0693W000003RBmqQAG.png

So far so good, you can see both data lines are in sync, and also that the data is in the right place in the frame (since this is TDM, the DAC will send the samples to different outputs according to their position in the frame).

Then I press the button (calling HAL_SAI_DMAPause) and it does not (always) stop at the end of the frame, nor does the two data lines stop at the time (again: not always).

0693W000003RBoiQAG.png

When I press again (calling HAL_SAI_DMAResume), and it seems the DMA/SAI make sure that the data within each frame is at its place, but since one SAI starts transmitting a bit before the other, then the other come one frame later sometimes, like here:

0693W000003RC4LQAW.png

SLasn.1
SLasn.1Author
Associate III
September 9, 2020

If I am not mistaken, here stopping the DMA simply means the FIFO is empty and is contantly in "underrun" until the DMA is resumed?

As the UM explains:

0693W000003RCNSQA4.png

I guess I am doing something wrong here - I keep reading the UM, and even going through the registers it seems the only way to stop sending data from the FIFO (sending zeros if it is empty as shown in the screenshot above) is to disable the SAI with SAIEN=0. Problem is when I disable the master then the Master Clock stops being output.

waclawek.jan
Super User
September 9, 2020

I have no idea what could be DMAPause/Resume. I don't use Cube/HAL and don't intend to debug it; and I also doubt anybody does.

Why don't just simply stop, and start afresh?

Normally, pause in audio is to be performed by filling the Tx buffer with a neutral value (zeros), while clocks and DMA keeps running; and simply ignoring the incoming data.

JW

SLasn.1
SLasn.1Author
Associate III
September 9, 2020

My bad I thought everybody used HAL - the HAL_SAI_DMAPause/Resume functions simply reset/set the DMAEN bit in the CR register.

And yes I agree it seems the best option is to simply keep the DMA/SAI running constantly and fill the Tx buffer with zeros when paused or stopped - that would work with me. But it also means the DMA is still constantly moving the zeros from the buffer to the FIFO, and that the SAI is constantly sending zeros from the FIFO to the data lines... isn't that waste of energy consumed? (I do not want to run on a battery or something but I am still thinking it sounds like a lot of work for sending zeros)

jean
Senior
March 28, 2024

Hi @SLasn.1 !

Just found your very interesting thread about SAI and dual-line transmit. I'm new to SAI and it appears to be not the largely used library of STM32 users :)

Have you found a better solution to the zeros buffers?

PS: I've posted a related question in this thread, maybe you can confirm this idea can work, it seems quite similar to your use? https://community.st.com/t5/stm32-mcus-products/sai-transmission-to-external-dac-more-serial-data-outputs/m-p/655246 

Thanks!