Skip to main content
john gassel
Associate II
January 2, 2018
Question

I2S Master has Full Duplex DMA L/R Swapped after Restart

  • January 2, 2018
  • 8 replies
  • 4491 views
Posted on January 02, 2018 at 22:53

I have an application using an STM32F429ZI processor and I'm seeing a strange problem where the L/R audio output gets swapped after changing I2S frequencies.

My procedure to change frequencies is as follows:

HAL_I2S_DMAStop(&hi2s3);

HAL_I2S_DeInit(&hi2s3);

hi2s3.Init.AudioFreq = sampling_rate;

HAL_StatusTypeDef result = HAL_I2S_Init( &hi2s3);

I2SEx_TransmitReceive_DMA_DB();

//my own function above is called, which is similar to the following but instead has call to 

//HAL_DMAEx_MultiBufferStart_IT(&hi2s3, &DummyAudioOutBuf, &DummyAudioOutBuf, AUDIO_OUT_BUF_SIZE);

It doesn't always happen.  I'd say that it gets swapped about 10% of the time.  I've seen people who have similar problems when running a slave, but with the ST configured as the master, I'm surprised to see this.

Here is my I2S3 configuration:

hi2s3.Instance = SPI3;

hi2s3.Init.Mode = I2S_MODE_MASTER_TX;

hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;

hi2s3.Init.DataFormat = I2S_DATAFORMAT_16B;

hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_DISABLE;

hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_16K;

hi2s3.Init.CPOL = I2S_CPOL_LOW;

hi2s3.Init.ClockSource = I2S_CLOCK_PLL;

hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;

#swap #stm32f4 #i2s #dma
This topic has been closed for replies.

8 replies

waclawek.jan
Super User
January 2, 2018
Posted on January 02, 2018 at 23:27

hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;

I've seen people who have similar problems when running a slave,

There's no duplex I2S module in STM32; it's two simplex modules tied together internally. Thus, there's always at least one slave.

I wonder whether Cube/HAL deinits/inits both I2S and I2SExt.

Which one of them is the receiver, btw?

JW

john gassel
Associate II
January 3, 2018
Posted on January 03, 2018 at 03:55

Waclawek.Jan

‌ that's an interesting idea! You might be onto something there.

I2S3_SD (PC12) = TX

I2S3_ext_SD (PB4) = RX

What should I look for in the de-init HAL routine?

waclawek.jan
Super User
January 3, 2018
Posted on January 03, 2018 at 10:15

Well... You say that output data are swapped. That won't indicate problems with the slave restart as the Ext unit is always the slave and you use it as Rx, so input data corruption would indicate problems of that nature.

Basically, you want to get things onto the starting point. You perhaps also may want to reset the codec or whatever is on the other side, although normally codecs are 'self-correcting' as they normally do follow correctly the LRCK (aka WS) signal.

At this point I'd look at your transmit function and/or the function called from there. Does it start from the correct point in the buffers (including the indicator of the current buffer in DMA)?

Disclaimer (although it's obvious): I don't Cube.

JW

john gassel
Associate II
January 3, 2018
Posted on January 03, 2018 at 15:33

Basically, you want to get things onto the starting point. You perhaps also may want to reset the codec or whatever is on the other side, although normally codecs are 'self-correcting' as they normally do follow correctly the LRCK (aka WS) signal.

Yeah, I've already looked into that.  I make sure that the codec is in shutdown before making any changes to the I²S configuration in the STM.  Once all is configured, I re-enable the codec.  It's running in PLL mode and should auto-sync to the incoming I²S stream.

At this point I'd look at your transmit function and/or the function called from there. Does it start from the correct point in the buffers (including the indicator of the current buffer in DMA)?

Yeah, I believe it does.  I'll review it again today.  Interestingly, when I look at the DMA output buffers in the Live Watch view of IAR the data is always oriented properly.  That is, LRLRLR starting from address 0 of the buffer.  This is true in both cases: swapped and correct.  So it's behaving as if the WS output from the processor is wrong.

I really wish I had access to probe the I²S lines on my board, but unfortunately, they're buried (BGA to BGA) so I'm running a bit blind in this case.  

john gassel
Associate II
January 3, 2018
Posted on January 03, 2018 at 22:16

HAL aside, does anyone know what should be shut down/reset to get a clean starting point?

waclawek.jan
Super User
January 3, 2018
Posted on January 03, 2018 at 23:04

It should be enough to disable both I2S and I2SExt (in ), and both DMA channels tied to them.

You may want to try that on a Discovery or similar board, where the signals are exposed.

JW

john gassel
Associate II
January 9, 2018
Posted on January 09, 2018 at 15:53

Waclawek.Jan

‌ and others:

I was able to remove the codec off a board sucessfully. This confirmed my suspicion that the problem is in the ST microcontroller, not the codec.

See two screen captures below. I'm playing the exact same tone in both cases with left audio only.

In this case, data output when WS=1

0690X00000609OaQAI.png

In this case, data output when WS=0

0690X00000609OuQAI.png

The data in my output buffers never changes. I always observe that the even indices of the buffer have non-zero values and the odd bits are zero.

I also found out something very interesting...the RX data has the same swapping issue!

I'm still stumped, but happy to be ruling some things out. If you have any input or suggestions to try, please let me know.

Cheers,

John

waclawek.jan
Super User
January 9, 2018
Posted on January 09, 2018 at 16:32

even bits of the buffer have non-zero values and the odd bits are zero.

bits? or did you mean words?

john gassel
Associate II
January 9, 2018
Posted on January 09, 2018 at 16:56

Correct.  What I actually meant are 

indices, not bits, but words are also true as it's a int array.
alemv
Associate
January 31, 2019

I don’t know the reasons, but I can confirm that trying to synchronize with channel "0" before calling dma like

while (SPI2->SR & SPI_SR_CHSIDE);

while (!SPI2->SR & SPI_SR_CHSIDE);

does not work, and I have to reset and re-init the I2S interface.

waclawek.jan
Super User
January 31, 2019

Can you please give us more context of your particular setup/software?

JW

JVini
Visitor II
August 28, 2019

Hi Guys!

Sorry for this late answer, but I had this problem last week. I guess that the point here is the follow: in any STM32F4 manual with I2S we can see a description about the transmission that says “The transmission sequence begins when a half-word is written into the Tx buffer.�? So, when you suddenly stop a transmission, the last data that was put in the buffer is pending when the I2S is reinitialized. In this case, this pending data assumes the first position in a new transmission that corresponds to first channel (WS low in my case) and as a consequence our data is swapped.

I could confirm this writing a dummy in the DR register in every transmission. I saw that this byte assumed the first position and the swapped stopped (but this is not a solution). I was unable to find a way to empty this pending data with some register and I am using the same approach for while (HAL_I2S_DMAStop(&hi2n), __HAL_RCC_SPIn_FORCE_RESET(), __HAL_RCC_SPIn_RELEASE_RESET().

roger239955_stm1
Associate III
December 26, 2021

Was this problem ever resolved.

I've got the same problem and would like to know how to fix it

Piranha
Principal III
December 29, 2021

Implement the stopping of transmission in such a way that it always waits and stops only when both channels of the current frame are transmitted.

roger239955_stm1
Associate III
December 29, 2021

Thanks.

We ended up leaving the transfer running, but filling the final buffer with 0x00 so that its sending silence when there is no real audio.

Also changed the ISR's to return immediatly if no real data needs to be transferred.

FYI

Starting the I2S slave in sync with the master was also problematic.

But we finally read an Errata which indicated that you must wait for WS to be high before the Slave is started.

We wait for WS to toggle from low to high before calling the HAL API function, and this seems to reliably start the slave in sync with the master

i.e we use GPIO ReadPin ( WS ) even though in theory the WS pin is assigned to its alternate function as part of the I2S interface.

But its common knowledge that both GPIO Write and Read seem to be possible even when pins are assigned to alternate uses, even USB.

Piranha
Principal III
December 30, 2021

Just a note for everyone - if SAI is available, it is highly recommended to use that instead of I2S. It has more features, less (almost none) problems, is more flexible and simultaneously is also easier to use.