cancel
Showing results for 
Search instead for 
Did you mean: 

I2S weird bit shift problem

baator025
Associate II

Hi,

I'm getting a problem in my project, where I tried to create a loopback between I2S3 and I2S4 on STM32F411 Disco.
It seems that only the least significant bit of 16-bit packets is somehow shifted to the next packet. The rest of the transmission is intact.
For example - I send {0x0001 0x0000 0x0000 0x0000} and I get {0x0000 0x0000 0x0001 0x0000}, but if I send {0xFFFE 0x0000 0x0000 0x0000}, I will receive {0x0000 0x0000 0x0000 0xFFFE}.
Packets order reversal is expected, but during analysis pay attention what happens with the least significant bit. Another example: transmitting {0x8001 0x0000 0x0000 0x0000} will end up with receiving {0x0000 0x0000 0x0001 0x8000} (I'd expect {0x0000 0x0000 0x000 0x8001}).
What is also interesting, readouts from logic analyzer seem to be correct.

I am not using HAL, I am configuring registers manually. I2S4 is configured as master receiver and uses DMA. I2S3 is configured as slave transmitter and I tried "feeding" it both with polling and interrupts. I always fill transmitter's buffer before initializing receiver with the first packet.

Since I've written my own abstraction layer, attaching code here may be difficult. Therefore I am uploading the link to repo: https://github.com/baator025/I2S_loopback . The most recent branch is rtos_integration.

I'd really appreciate help, because I've run out of ideas long ago.

Thanks,
Bartosz

27 REPLIES 27

Oh BTW: "since I am using MSB justified transmission, I should make sure the WS line should be low".
NO!!!
LRCK (WS) signal is there to indicate if left or right channel data. It does not have any relation to the protocol.
As I understand: I2S, Left or Right Justified... are always as "shift MSB first". Just: where the MSB (and LSB) is - in RELATION to SCLK (WS) signal depends on which protocol is used.

LRCK (WS) should always toggle (after word size (16bit) BCK) - if you use two channels (stereo). In Mono mode - than it should stay constant at one level.

Based on the waveforms you have attached: the very first cycle is "invalid", BCLK (WS) toggling for less than 16bit BCK. So, the startup (enable) has a "hick up", just reliable signals afterwards (after some cycles stable, keep running both in endless mode).

SCLK (WS) should toggle automatically in stereo mode (left and right channel indication, also indicating the word size, e.g. 16 BCK clocks for one channel). You cannot control and influence the SCLK (WS) behavior (just via Stereo or Mono config).

Sorry" "LRCK", a signal to indicate left of right channel (and channel word size) - anyway: generated automatically (nothing to control, except to configure the protocol and channel word size properly).

BTW: if you would use the LRCK (WS) signal as DATA input on the Receiver (connect LRCK out - here on your Master Receiver generating it, to the DATA in on your Master Receiver) - now you should see 0xFFFF and 0x0000 for the right channel (when LRCK is high) and left channel (when LRCK is low).

If this works (both channels have different values: 0xFFFF vs. 0x0000) - all should be ok.

If the channel values are a bit different (e.g. LSB is 0 vs. 1) - the I2S protocol is configured: I2S has the LSB on DATA when LRCK has changed already (for one BCK cycle). Otherwise (if all 0xFFFF and 0x0000) - the Left or Right Aligned protocol is used. These protocols just differs where the MSB and LSB is (in relation to LRCK).

I suggest:

  • configure as a I2S Master Transmitter (and an I2S Slave Receiver)
  • see I2S signals running endlessly and all looks reasonable (16 x BCK, SCLK toggles for 16 x BCK...)
  • send anything (and DATA out looks reasonable in relation to BCK (bit clock) plus channel 1 and 2 (LRCK) have different patterns (send different 16bit words for left and right)
  • add "flow control": just write a new stereo (2 channels) word(s) if Tx has completed the transmission
  • and scope looks ok

now you can focus on I2S Slave Receiver:

it should receive exactly what you have sent.
If so: BCK and LRCK generated by the Master Transmitter goes to Slave Receiver as inputs, DATA from Transmitter (out) goes to Slave Receiver (in).

Just watch for:

  • both sides with same protocol, same word size, same clock config (and I2s speed: PLL settings)
  • and "flow control" used (not to overwrite transmitter data register when it has not yet completed)

If you do not check for "transmission completed" and you would overwrite the Transmitter Data Register (when transmission is still inn process) - you can get also "strange" results.

First of all - sorry, I didn't have much time for changes in code as you suggested, probably will try to do this tomorrow.

Regarding other topics.

1. I get that WS/LRCK indicates which channel is currently streaming, I just understood the workaround from errata orders me to make sure WS is low when I start I2S peripherals. Finally did this by using pull down, which I forgot about. Nevertheless, it didn't change much.

2. Until now I tried two flow controls:

- I had a "feeder" task, when I checked TXE flag in I2S status register. If it indicated emptiness, I wrote data to DR.
- I used interrupts, triggered on TXE as well

I thought that maybe I am writing something until not all of data is shifted, (this constant shift of 16 bits could indicate so) but I don't get how this would be possible if I'm checking TXE.

3. Why I'm using this weird config - I wanted to make a "hello world" I2S project by interfacing PDM microphone. This required me to configure I2S as master receiver. Unfortunately, I encountered a problem with PDM2PCM library (which I wrote at this forum about). In the meantime, I thought that I can make sure, that I configured everything properly, so decided on implementing slave transmitter to simulate microphone and check if I'm not scrambling the packet order. 

Either way to sum up my initialization procedure:

  1. PLL configuration and start
  2. slave transmitter configuration (without starting)
  3. master receiver configuration (without starting as well)
  4. slave transmitter start
  5. I write first packet to transmitter DR
  6. this is optional point when I tried to wait 1s to check lines' behavior
  7. receiver DMA init
  8. start receiver
baator025
Associate II

 Btw, here is the start of transmission, where I made sure WS is pulled down low when I start both Tx and Rx. 

pull_down_wave.png

A pull-down does not help: if the LRCK signal is generated it is Totem Pole (or Push-Poll). It is actively driven (not Open Drain output), so a pull-up or -down does not have any effect.

PDM microphones? But is PDM not a completely different interface (protocol)? Or are you trying to connect I2S microphones?

PDM does not have a LRCK signal. Instead: the left and right is done via the falling and raising edge of the PDMCLK signal. And the PDMCLK is way faster (something between 1..3 MHz, depending on the PDM audio format, oversampling factor).

What was your problem with the PDM2PCM library? (I did a lot of tests with PDM mics and this library, on a different board: Portenta H7)

WS with pull-down does not fix, does not do anything (actively driven signals).

Your "problem" is: when all starts, the very first cycle is not correct (some BCK clocks but WS/LRCK changes - sure: now it goes to the 1st correct cycle).
This is due to the fact (for my understanding):

  • the output signals BCK and LRCK/WS are generated by the Master Receiver
  • so, you start Slave Transmitter first (which will wait for these signals, but never mind how long you wait: as long as the Master Receiver does not start - no signals)
  • afterwards you initialize and start Master Receiver (now starting to generate the signals)
  • and starting the Master Receiver starts with "strange" cycle, but Slave Transmitter is hot and sees this "strange" cycle

This creates a "kick-up" (incorrect first cycle). Your Slave Transmitter is confused and therefore the Master Receiver gets unpredictable Rx data.


What happens on the following cycles? Do not do all the time Start and Stop (let both run forever after enabled).

Have you tried to let both run forever and you pull-up or pull-down the DATA Rx input? What if you connect DATA Rx with LRCK/WS signal? (do you see 0xFFFF vs. 0x0000 in the two channels?)

I think, your issue is due to the very first cycle is an "unreliable" (unpredictable) cycle.
Even, you would change to a slightly different start sequence, e.g.:

  • enable Master Receiver first (which generates the "reference" signals, BCK, LRCK/WS) - but let it run forever
  • enable Slave Transmitter (which uses the generated signals) - and let it also run forever
  • and start sending something at least one I2S cycle later

it is still a potential "hick-up": the Slave Transmitter might start anywhere, to see the generated signals at any time (the start of both instances are not in sync). So, the started Slave Transmitter might not see a complete valid I2S cycle, a bit too short, on the very first iteration.

So, you have to wait a bit after Master Receiver was enabled (at least one complete I2S cycle), enable Slave Transmitter and wait also at least one complete I2C cycle). Now send something, but not immediately (Slave Transmitter might not be in sync yet with the I2S cycles).

All it just stable after one fully completed I2S cycle (a second I2S cycle needed to be completed). Send something a while after both I2S instances are launched and running in a stable state (on I2S cycles). You have to discard the results of the very first I2S cycle (which is potentially not a correct valid complete cycle).

Send something later and have the receiver hot (waiting for something received). This should match now. Potentially, you have to discard the result of the very first iteration.

Looking at your pin initialization code, you don't set OSPEEDR for the bit clock. Your problem thus may be the Corrupted last bit of data and/or CRC received in Master mode with delayed SCK feedback erratum. Try setting OSPEEDR to higher than the default lowest state.

JW