# I2S input (ADC) yields garbage, output (DAC) works fine

Setup:

• STM32F407 (discovery board) used in I2S master mode
• I2S in/out module, 24 bit over 32 bits frame, used in I2S slave mode
• using CubeMX's HAL for the prototype (this might not be relevant)

I2S output is connected to I2S3 (MCLK=PC7, WS=PA15, CK=PC10, SD=PB5) and works fine. I can hear a test wave.

I2S input is connected to I2S2 (MCLK=PC6, WS=PB12, CK=PB13, SD=PB15). All I get is garbage data with amplitudes way beyond the expected 24 bits (IOW the 4th byte is also garbage, but even when masked the resulting data is garbage). My first reaction was "noise on the line" (since it's a "hairy" circuit for now) but when I disconnect only MCLK from the module (not from the µproc, so MCLK can still interfere) I get straight zeros, which I think invalidates the "noise on the line" option.

Of course, being paranoid, I checked multiple times that everything is soldered at the right place and connects to the right pin.

Now I'm stumped. Any idea what could be wrong, or at least where should I look?

For the record, here is the (autogenerated) configuration and my test code:

/* I2S2 init function */
void MX_I2S2_Init(void)
{

hi2s2.Instance = SPI2;
hi2s2.Init.Mode = I2S_MODE_MASTER_RX;
hi2s2.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s2.Init.DataFormat = I2S_DATAFORMAT_24B;
hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_8K;
hi2s2.Init.CPOL = I2S_CPOL_LOW;
hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
if (HAL_I2S_Init(&hi2s2) != HAL_OK)
{
Error_Handler();
}

}
/* I2S3 init function */
void MX_I2S3_Init(void)
{

hi2s3.Instance = SPI3;
hi2s3.Init.Mode = I2S_MODE_MASTER_TX;
hi2s3.Init.Standard = I2S_STANDARD_PHILIPS;
hi2s3.Init.DataFormat = I2S_DATAFORMAT_24B;
hi2s3.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
hi2s3.Init.AudioFreq = I2S_AUDIOFREQ_8K;
hi2s3.Init.CPOL = I2S_CPOL_LOW;
hi2s3.Init.ClockSource = I2S_CLOCK_PLL;
hi2s3.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_DISABLE;
if (HAL_I2S_Init(&hi2s3) != HAL_OK)
{
Error_Handler();
}

}

void HAL_I2S_MspInit(I2S_HandleTypeDef* i2sHandle)
{

GPIO_InitTypeDef GPIO_InitStruct = {0};
if(i2sHandle->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspInit 0 */

/* USER CODE END SPI2_MspInit 0 */
/* I2S2 clock enable */
__HAL_RCC_SPI2_CLK_ENABLE();

__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**I2S2 GPIO Configuration
PB12     ------> I2S2_WS
PB13     ------> I2S2_CK
PB15     ------> I2S2_SD
PC6     ------> I2S2_MCK
*/
GPIO_InitStruct.Pin = I2S_RCV_WS_Pin|I2S_RCV_CK_Pin|I2S_RCV_SD_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

GPIO_InitStruct.Pin = I2S_RCV_MCK_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(I2S_RCV_MCK_GPIO_Port, &GPIO_InitStruct);

/* USER CODE BEGIN SPI2_MspInit 1 */

/* USER CODE END SPI2_MspInit 1 */
}
else if(i2sHandle->Instance==SPI3)
{
/* USER CODE BEGIN SPI3_MspInit 0 */

/* USER CODE END SPI3_MspInit 0 */
/* I2S3 clock enable */
__HAL_RCC_SPI3_CLK_ENABLE();

__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**I2S3 GPIO Configuration
PC7     ------> I2S3_MCK
PA15     ------> I2S3_WS
PC10     ------> I2S3_CK
PB5     ------> I2S3_SD
*/
GPIO_InitStruct.Pin = I2S_SND_MCK_Pin|I2S_SND_CK_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

GPIO_InitStruct.Pin = I2S_SND_WS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
HAL_GPIO_Init(I2S_SND_WS_GPIO_Port, &GPIO_InitStruct);

GPIO_InitStruct.Pin = I2S_SND_SD_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
HAL_GPIO_Init(I2S_SND_SD_GPIO_Port, &GPIO_InitStruct);

/* USER CODE BEGIN SPI3_MspInit 1 */

/* USER CODE END SPI3_MspInit 1 */
}
}

  // SEND (WORKS FINE)
static constexpr int32_t amplitude = 80000;
int32_t wave[] = { 0, 0,
amplitude / 2, amplitude / 2,
amplitude, amplitude,
amplitude / 2, amplitude / 2,
0, 0,
-amplitude / 2, -amplitude / 2,
-amplitude, -amplitude,
-amplitude / 2, -amplitude / 2,
};
while (true) {
HAL_I2S_Transmit(&I2S_SND, (uint16_t*)wave, sizeof(wave) / 4, 500);
}

  // RECEIVE (GARBAGE)
int32_t data[2];
while (true) {
if (HAL_OK == HAL_I2S_Receive(&I2S_RCV, (uint16_t*)data, sizeof(data) / 4, 500)) {
debug("%08x, %08x\n", data[0], data[1]);
}
}


And a sample of the data I get:

00000000, 00000000
00000000, 00000000
bf00085b, fb000848
030007d2, 110007c2
0729f600, 071ca400
95000676, 8c000683
8b0005d2, b40005db
05363b00, 0540c600
04acb200, 04b4eb00
9700042b, 54000423
03afd600, 03a97300
030e8c00, 03074700
800002d3, 150002d8
71000276, 1b00027a
021f2500, 02213700
f00001a2, 400001a0


Note that the first samples are 0 before they go wild. Please also note that I get the same kind of results with an open line (nothing connected), with a microphone connected, or even looping the wave I generate with the DAC into the ADC.

Lastly, before any remarks: I use separate I2S ports because the STM32F407 is not the final target (which is STM32F730) which doesn't support full duplex I2S. This shouldn't change anything past cabling.

Edit: I don't have an oscilloscope at hand, but I nevertheless tried to observe the signals with a mere voltmeter. All 3 clocks are perfectly stable (1.46V) as is to be expected with symmetric signals. However the data signal (taken directly on the module, disconnected from the µproc so the wire doesn't interfere) varies wildly in the 0.5-1.7V range. So the problem appears to come from the module itself, which is doubtful. There has to be something I missed.

• I'm very suspect of this: sizeof(data) / 4 with regards to how much data you are receiving. You are passing in 2, but really the value should be 4 since you need to be specifying the number of 16-bit data length (16-bit fields in the data array). Oct 27 '20 at 22:22
• Actually I verified this in the HAL source: both HAL_I2S_Transmit and HAL_I2S_Receive expect the number of "words" as the size. Words being either 16 bits or 32 bits depending on the configuration. In fact, the HAL code first multiplies the size by 2 when in 24 or 32 bits mode before sending or receiving the 16 bits words. But you still were right to suspect it: the actual HAL code doesn't match its documentation, and it took me a moment to fix (originally I didn't have any output because of this)... ;)
– syam
Oct 27 '20 at 23:04
• Also, my latest measurements (even if with a mere voltmeter) show that the SDOUT pin varies wildly on the module itself. Looks like a hardware issue to me, not software.
– syam
Oct 27 '20 at 23:26
• SDOUT should vary wildly, a voltmeter isn't a good diagnostic tool there. The speed at which that line goes low/high can't be analyzed with a voltmeter. Oct 27 '20 at 23:43
• How is the MCLK configured? Is it 256 x FS? Oct 28 '20 at 5:26

After re-checking everything, including going through (again) the board schematics to rule out any pin incompatibility, I was fed up and decided to move on and try DMA transfers since I'll need them anyway (I tried the synchronous API first as a POC, thinking --erroneously-- that it would be simpler).

On first try it worked, with line-in being successfully copied to line-out (verified with PC -> board -> headphones):

  volatile int32_t data[4];

I tried to reproduce it using the synchronous calls HAL_I2S_Receive and HAL_I2S_Transmit, and again got incoherent input data. :(
My best guess right now is that HAL's synchronous functions HAL_I2S_Receive and HAL_I2S_Transmit are buggy, since DMA works out of the box.