cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 I2S master receive clock problem

M_Squared
Associate

Hi everyone,

I am working on an STM32H7A3ZI and I'm trying to to exchange audio data with an SGTL5000 audio codec. Because i want to both send and receive data from the codec i configured the I2S peripheral on the STM32 to Full-Duplex Master mode in Cube MX.

My problem:

Transmitting data using the HAL I2S functions (HAL_I2S_Transmit, or HAL_I2S_Transmit_DMA) works fine, but when i try to receive data (also using the HAL functions) the bit- and word-select-clock won't start. If i first transmit data and then try to receive everything works fine, because the clocks stay on. If i change the I2S config to Half-Duplex Master Receive mode, just receiving also starts the clocks.

So in Full-Duplex Master mode i have to transmit data first (or at least write some data into the transmit data register, which starts the clocks), before i can receive data.

My question:

This seems to be intended behavior, but i couldn't find any documentation on it. Why is this implemented like that?

1 ACCEPTED SOLUTION

Accepted Solutions
AScha.3
Super User

Hi,

>This seems to be intended behavior, but i couldn't find any documentation on it. 

Well, its not "intended" , its just physically caused : the I2S is variant of a SPI , so only transmit generates the clock and receive happens same time with this clock.

You cannot separate transmit and receive, if you want happen both; it has to be at same time, with the clock and WS clock. You only can do it separate, if you also want to only receive OR transmit .

So use it together, if you want both:   > TransmitReceive <  is you friend here.

from hell aaa HAL:

/**
  * @brief  Full-Duplex Transmit/Receive data in non-blocking mode using DMA
  *   hi2s pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  *   pTxData a 16-bit pointer to the Transmit data buffer.
  *   pRxData a 16-bit pointer to the Receive data buffer.
  *   Size number of data sample to be sent:
  * @note   When a 16-bit data frame or a 16-bit data frame extended is selected during the I2S
  *         configuration phase, the Size parameter means the number of 16-bit data length
  *         in the transaction and when a 24-bit data frame or a 32-bit data frame is selected
  *         the Size parameter means the number of 16-bit data length.
  * @note   The I2S is kept enabled at the end of transaction to avoid the clock de-synchronization
  *         between Master and Slave(example: audio streaming).
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2SEx_TransmitReceive_DMA(I2S_HandleTypeDef *hi2s, const uint16_t *pTxData, uint16_t *pRxData,
                                                uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;

+

use circular DMA , with callbacks, so you get continuous streams IN and OUT , and just handle the data in the callbacks , full and half , for putting data and reading a block.

(I have the buffers about 4K x samples size, so about every 40ms get+put a 4K block.)

+

Just start the streams with DMA after program start and never stop them, as I2S is intended for continuous streams.

For doing "stop or pause" just write the send data block full with 0x0000, so output is "mute", zero, without any problems or click noise etc.

+

If any problems with data > 16b , i recommend using the SAI , set to I2S format, not the basic I2S ; the SAI on H7 working perfect . 

(With I2S i got problems, if set to 24 or 32b format; if only 16b data used, I2S also was ok.)

 

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

View solution in original post

3 REPLIES 3
AScha.3
Super User

Hi,

>This seems to be intended behavior, but i couldn't find any documentation on it. 

Well, its not "intended" , its just physically caused : the I2S is variant of a SPI , so only transmit generates the clock and receive happens same time with this clock.

You cannot separate transmit and receive, if you want happen both; it has to be at same time, with the clock and WS clock. You only can do it separate, if you also want to only receive OR transmit .

So use it together, if you want both:   > TransmitReceive <  is you friend here.

from hell aaa HAL:

/**
  * @brief  Full-Duplex Transmit/Receive data in non-blocking mode using DMA
  *   hi2s pointer to a I2S_HandleTypeDef structure that contains
  *         the configuration information for I2S module
  *   pTxData a 16-bit pointer to the Transmit data buffer.
  *   pRxData a 16-bit pointer to the Receive data buffer.
  *   Size number of data sample to be sent:
  * @note   When a 16-bit data frame or a 16-bit data frame extended is selected during the I2S
  *         configuration phase, the Size parameter means the number of 16-bit data length
  *         in the transaction and when a 24-bit data frame or a 32-bit data frame is selected
  *         the Size parameter means the number of 16-bit data length.
  * @note   The I2S is kept enabled at the end of transaction to avoid the clock de-synchronization
  *         between Master and Slave(example: audio streaming).
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_I2SEx_TransmitReceive_DMA(I2S_HandleTypeDef *hi2s, const uint16_t *pTxData, uint16_t *pRxData,
                                                uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;

+

use circular DMA , with callbacks, so you get continuous streams IN and OUT , and just handle the data in the callbacks , full and half , for putting data and reading a block.

(I have the buffers about 4K x samples size, so about every 40ms get+put a 4K block.)

+

Just start the streams with DMA after program start and never stop them, as I2S is intended for continuous streams.

For doing "stop or pause" just write the send data block full with 0x0000, so output is "mute", zero, without any problems or click noise etc.

+

If any problems with data > 16b , i recommend using the SAI , set to I2S format, not the basic I2S ; the SAI on H7 working perfect . 

(With I2S i got problems, if set to 24 or 32b format; if only 16b data used, I2S also was ok.)

 

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

Thanks for the quick reply, it didn't occur to me, that this could stem from I2S being based on SPI.

The thing is, I never need to send and receive at the same time and also relatively rarely (2048 samples, roughly every half second, compared to the 44kHz sampling frequency). So continuously writing mostly unused received data into memory, seems kind of unnecessary. On the other hand, reconfiguring the peripheral to Half-Duplex Transmit or Receive each time also doesn't seem to make much sense...

Is there an elegant solution to this, or is it best to just continuously read in the data, even tough most of it is never used?

> Is there an elegant solution to this, or is it best to just continuously read in the data .. ?

This IS the "elegant" solution. Because never problems from the chips with synchronization on start/stop/start...

and it wont hurt the ram, to be used. :)

Its not a flash or SSD , its just RAM. Continuous write and read is its "standard business" .

When nothing to send, just write the send buffer to zero, 0x00, only once , then it is on "mute" state running.

When no receive, just dont use/copy the incoming data in buffer. Anytime you can begin to use it - or not.

Up to you, to set a IN+OUT buffer size for your needs. I have 4K sampels, because also flac or MP3 decoder running and read from SD or USB , and this can make up 5...20ms delay, so some size of buffer makes sense here.

Some have the buffers only 16 sampels "long" , for doing real time sound effects or something like this, you dont want long delays added between in and out of the sound (= data ).

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