3
\$\begingroup\$

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.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ 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). \$\endgroup\$
    – Ron Beyer
    Oct 27 '20 at 22:22
  • \$\begingroup\$ 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)... ;) \$\endgroup\$
    – syam
    Oct 27 '20 at 23:04
  • \$\begingroup\$ 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. \$\endgroup\$
    – syam
    Oct 27 '20 at 23:26
  • \$\begingroup\$ 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. \$\endgroup\$
    – Ron Beyer
    Oct 27 '20 at 23:43
  • 1
    \$\begingroup\$ How is the MCLK configured? Is it 256 x FS? \$\endgroup\$
    – Justme
    Oct 28 '20 at 5:26
1
\$\begingroup\$

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];
  HAL_I2S_Receive_DMA(&I2S_RCV, data, sizeof(data) / sizeof(*data));
  HAL_I2S_Transmit_DMA(&I2S_SND, data, sizeof(data) / sizeof(*data));

The main loop, just dumping asynchronously the buffer's content on UART, confirmed the coherence of the data.

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.

I for one won't research this issue further since I need DMA anyway in the final product, so those issues specific to the synchronous API are moot. But if someone has an idea of the why I'd still be interested to hear it.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.