2025-03-25 2:26 AM
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)
Test output pattern 0xAAAAAAAA (SD and WS lines)
Test output pattern 0xAAAAAAAA (SD and WS lines)
WS fall and CK:
WS rise and CK:
It looks like delayed WS latch
Anybody seen that before?
P.S.
APB1 periferal clock 48MHz
APB2 periferal clock 96MHz / 48MHz
Clock configuration:
2025-03-25 3:55 AM
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
2025-03-25 3:58 AM
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.
2025-03-25 11:21 AM
@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:
but i have no idea how to implement it...
2025-03-25 11:27 AM
>> 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
2025-03-25 1:10 PM
>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;
2025-03-25 1:29 PM
@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).
2025-03-25 2:02 PM
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
2025-03-25 2:44 PM
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);
[...]
2025-03-25 4:06 PM
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