cancel
Showing results for 
Search instead for 
Did you mean: 

I2S bits rotated

max_verem
Associate

Hi

I am working on project that receive several I2S streams from MEMS microphones. I found that "audio level" from second microphones pair had higher level then from other. After some analysis and SD lines swapping i found that I2S data received by second I2S is multiplied by 2 factor (binary shifted).

I built minimal system with on STM32F411CEUx (Black Pill board) to reproduce it.

I configured:
* I2S1: Half-Duplex Master Receive
* I2S2: Half-Duplex Slave Receive
* I2S4: Half-Duplex Slave Transmit

All I2S has same other configuration values:
* I2S Philips
* 32 Bits Data on 32 Bits Frame
* 16 KHz

GPIO pins connected that way:

/*

I2S1 GPIO I2S2 GPIO I2S4 GPIO
============== =============== ================
PA4 -> I2S1_WS <=> PB10 -> I2S2_CK <=> PA1  -> I2S4_SD
PA5 -> I2S1_CK <=> PB15 -> I2S2_SD <=> PB12 -> I2S4_WS
PA7 -> I2S1_SD <=> PB9  -> I2S2_WS <=> PB13 -> I2S4_CK

*/

I also set a maximal speed for all I2S pins:

    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

 

I use STM32Cube FW_F4 V1.28.1

All I2S configured with Circular DMA

I2S4 used to transmit data:

static const uint32_t demo[4] = {
	0xBADC0FFE,
	0xE0DDF00D,
	0xBADC0FFE,
	0xE0DDF00D
}

[...]

HAL_I2S_Transmit_DMA(&hi2s4, (uint16_t*)demo, 2);

I2S1 and I2S2 used to receive data:

static int32_t i2s_buffer_1[4 * I2S_DMA_BUF_LENGTH] __attribute__((__aligned__(16))) = {0};
static int32_t i2s_buffer_2[4 * I2S_DMA_BUF_LENGTH] __attribute__((__aligned__(16))) = {0};

[..]

  HAL_I2S_RegisterCallback(&hi2s1, HAL_I2S_RX_HALF_COMPLETE_CB_ID, i2s_dma_cb_half_1);
  HAL_I2S_RegisterCallback(&hi2s1, HAL_I2S_RX_COMPLETE_CB_ID, i2s_dma_cb_full_1);
  HAL_I2S_RegisterCallback(&hi2s2, HAL_I2S_RX_HALF_COMPLETE_CB_ID, i2s_dma_cb_half_2);
  HAL_I2S_RegisterCallback(&hi2s2, HAL_I2S_RX_COMPLETE_CB_ID, i2s_dma_cb_full_2);
  HAL_I2S_Receive_DMA(&hi2s1, (uint16_t*)i2s_buffer_1, I2S_DMA_BUF_LENGTH);
  HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)i2s_buffer_2, I2S_DMA_BUF_LENGTH);

 

In ideal case i expect that i2s buffers and source buffers should be the same, but it not.

Datas in i2s receiving buffers shifted (rotated):

/*
demo[0]          0xbadc0ffe |          101110101101110000001111 11111110
i2s_buffer_1[0]  0xfebadd0f | 11111110 101110101101110100001111
i2s_buffer_2[0]  0xebadd0ff |     1110 101110101101110100001111 1111
                                                      ^

demo[1]          0xe0ddf00d |          111000001101110111110000 00001101
i2s_buffer_1[1]  0x0de0dcf0 | 00001101 111000001101110011110000
i2s_buffer_2[1]  0xde0dcf00 |     1101 111000001101110011110000 0000
*/

I already checked:
- I2S weird bit shift problem : https://community.st.com/t5/stm32-mcus-products/i2s-weird-bit-shift-problem/td-p/757051/page/2
- STM32F42xx SPI errata handling with 70pF load : https://community.st.com/t5/stm32-mcus-products/stm32f42xx-spi-errata-handling-with-70pf-load/td-p/243053
https://www.st.com/resource/en/errata_sheet/es0287-stm32f411xcxe-device-errata-stmicroelectronics.pdf

But none of solutions works.

I also did a check with osciliscope and found no suspected signal deviations

Test output pattern 0xAAAAAAAA (SD and CK lines)
i2s_SD-0xAAAAAAAA_CK.png

 

Test output pattern 0xAAAAAAAA (SD and WS lines)

 

i2s_SD-0xAAAAAAAA_WS-fall.png

 

Test output pattern 0xAAAAAAAA (SD and WS lines)

i2s_SD-0xAAAAAAAA_WS-rise.png

 

WS fall and CK:

i2s_WS-fall_CK.png

 

WS rise and CK:

i2s_WS-rise_CK.png

 

It looks like delayed WS latch

Anybody seen that before?

P.S.

APB1 periferal clock 48MHz
APB2 periferal clock 96MHz / 48MHz

Clock configuration: 
i2s_clock_configuration_.png

9 REPLIES 9

Do you observe the In I2S Slave mode, the WS level must be set by the external master when enabling the I2S erratum?

I don't know if there's any provision in Cube/HAL for this erratum, but I doubt there is. I don't use Cube.

JW

AScha.3
Chief III

Hi,

>Anybody seen that before?

Maybe...

I had DAC connected on I2S , on F411 ; as long as i was running it with 16b data, 2ch , all fine.

But then going to 32b sample size, i never got clean signal - or lets say reliable; something looked "puzzled" in the data, like data shifted or hi-lo nibble puzzled; then after some power cycles it was suddenly ok - so my settings correct, but next power cycle again wrong. So i decided: useless, with 32b data words.

And switched to H743 , on SAI with I2S mode, all perfect, also with 32b data.

 

So on F411 - try to use 16b words, at double frequency, should be same with pd micro signals.

If you feel a post has answered your question, please click "Accept as Solution".

@waclawek.jan wrote:

Do you observe the In I2S Slave mode, the WS level must be set by the external master when enabling the I2S erratum?

i seen that:

errata_2.11.6.png

but i have no idea how to implement it...

 

>> So on F411 - try to use 16b words, at double frequency, should be same with pd micro signals.

i seen it with 16 bits samples.

>> And switched to H743 , on SAI with I2S mode, all perfect, also with 32b data.

thanks for hint

>i seen it with 16 bits samples.

but you set 32b : > * 32 Bits Data on 32 Bits Frame

--- so what you set and tried ?

 

+

for connecting the mic : try half dup. master receive , so WS + bcl is generated by the I2S module,

with 16b in 16b frame, in PCM mode;

If you feel a post has answered your question, please click "Accept as Solution".

@AScha.3 wrote:

>i seen it with 16 bits samples.

but you set 32b : > * 32 Bits Data on 32 Bits Frame

--- so what you set and tried ?

16 bits samples was on a first iteration of development and i also noticed that mic connected to I2S2 had higher level because of data shift (i understand it later).

 

Well, as the errata says, in pure slave you would wait until WS is in the inactive state and only after that you enable the I2S slave(s). Timing matters of course, so do whatever you need (e.g. disable interrupts) to avoid this to be late.

For the case where the same STM32 acts also as master in some other I2S, WS is under your full control so timing is less tight. IIRC, the master, if enabled but has no data, remains in the inactive state, but better check on this, this is not explicitly clear from documentation (and it's been quite some time I did this the last time). If it's true, then the procedure is 1. enable master I2S but don't write data to it yet; 2. enable slave I2S(s); 3. start transmitting data to master I2S (writing to its data register or enable DMA, whichever you use).

JW


For the case where the same STM32 acts also as master in some other I2S, WS is under your full control so timing is less tight. IIRC, the master, if enabled but has no data, remains in the inactive state, but better check on this, this is not explicitly clear from documentation (and it's been quite some time I did this the last time). If it's true, then the procedure is 1. enable master I2S but don't write data to it yet; 2. enable slave I2S(s); 3. start transmitting data to master I2S (writing to its data register or enable DMA, whichever you use).


this is exactly how it works in test/demo project i did to reproduce weird behavior:

 [...]
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2S1_Init();
  MX_I2S2_Init();
  MX_I2S4_Init();
  /* USER CODE BEGIN 2 */

  HAL_I2S_RegisterCallback(&hi2s1, HAL_I2S_RX_HALF_COMPLETE_CB_ID, i2s_dma_cb_half_1);
  HAL_I2S_RegisterCallback(&hi2s1, HAL_I2S_RX_COMPLETE_CB_ID, i2s_dma_cb_full_1);
  HAL_I2S_RegisterCallback(&hi2s2, HAL_I2S_RX_HALF_COMPLETE_CB_ID, i2s_dma_cb_half_2);
  HAL_I2S_RegisterCallback(&hi2s2, HAL_I2S_RX_COMPLETE_CB_ID, i2s_dma_cb_full_2);
  HAL_I2S_Receive_DMA(&hi2s1, (uint16_t*)i2s_buffer_1, I2S_DMA_BUF_LENGTH);
  HAL_I2S_Receive_DMA(&hi2s2, (uint16_t*)i2s_buffer_2, I2S_DMA_BUF_LENGTH);
  HAL_I2S_Transmit_DMA(&hi2s4, (uint16_t*)demo, 2);
[...]

 

I don't think so.

In step 1, I am talking about the I2S master being *enabled*, i.e. SPI_I2SCFGR.I2SE=1.  I don't think this happens in your code before HAL_I2S_Transmit_DMA(). As I've said, I don't think Cube/HAL is adequate for this.

Also as I've said, I'm not quite sure this ensures WS being inactive, i.e. for I2S, high (right), you have to check this.

Alternatively, you can try simply to pull up WS somewhere in the initialization, although again, you have to ensure that subsequent Cube/HAL action won't void this in some way.

JW