cancel
Showing results for 
Search instead for 
Did you mean: 

Using single I2S to read from two external stereo ADC ICs (2+2 channels)

tmtlib
Associate II
Posted on July 04, 2017 at 09:28

Hello! Is it possible to use single I2S port to read from two external stereo audio ADC ICS?

I want to obtain 4-channel audio signal without any time offset.

My setup is:

1) STM32F407 x 1pcs

2) PCM1808 24bit ADC 2 channels x 2pcs

My idea is to configure 

I2S2 as master full-duplex receiver with master clock generation.

Connect all clock signals from mater I2S2 to two slaves PCM1808 in parallel

Then configure 

I2S2_EXT as I2S_MODE_SLAVE_RX

(by default STM32HAL firmware makes 

I2S2_EXT =- 

TX if I2S2 is set to RX)

Is there any problem with this approach?

Reference manual rm0090 and HAL i2s driver source code states that 'Both I2Sx and I2Sx_ext can be configured as transmitters or receivers.'

Another way is to use two I2S ports, but i am unsure if they will run synchronously. Maybe I can use I2S2 master with clock generation and I2S3 as slave. Then all I2S2 clock signals will be connected to pair of PCM1808 ICs and I2S3 input.

Please tell me which way is better. Currently I prefer first solution, although I did not tested it (using I2S2 receiver with one additional line I2S2_EXT as I2S_MODE_SLAVE_RX).

Thank for your attention.

6 REPLIES 6
tmtlib
Associate II
Posted on July 13, 2017 at 19:22

I am still trying to solve this problem. I2S2 and I2S2ext must simultaneously receive data to two DMA buffers, without transmitting. Here is preliminary code copy/pasted altered from HAL library to accomplish this task:

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_ENABLE;

GPIO_InitTypeDef GPIO_InitStruct;

__HAL_RCC_SPI2_CLK_ENABLE();

GPIO_InitStruct.Pin = GPIO_PIN_2;

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

GPIO_InitStruct.Alternate = GPIO_AF6_I2S2ext;

HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_6;

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(GPIOC, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_12;

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);

hdma_spi2_rx.Instance = DMA1_Stream3;

hdma_spi2_rx.Init.Channel = DMA_CHANNEL_0;

hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;

hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE;

hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE;

hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

hdma_spi2_rx.Init.Mode = DMA_CIRCULAR;

hdma_spi2_rx.Init.Priority = DMA_PRIORITY_LOW;

hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

__HAL_LINKDMA(&hi2s2,hdmarx,hdma_spi2_rx);

hdma_i2s2_ext_tx.Instance = DMA1_Stream4;

hdma_i2s2_ext_tx.Init.Channel = DMA_CHANNEL_2;

hdma_i2s2_ext_tx.Init.Direction =

DMA_PERIPH_TO_MEMORY

;//DMA_MEMORY_TO_PERIPH;

hdma_i2s2_ext_tx.Init.PeriphInc = DMA_PINC_DISABLE;

hdma_i2s2_ext_tx.Init.MemInc = DMA_MINC_ENABLE;

hdma_i2s2_ext_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

hdma_i2s2_ext_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

hdma_i2s2_ext_tx.Init.Mode = DMA_CIRCULAR;

hdma_i2s2_ext_tx.Init.Priority = DMA_PRIORITY_LOW;

hdma_i2s2_ext_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

if (HAL_DMA_Init(&hdma_i2s2_ext_tx) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

__HAL_LINKDMA(&hi2s2,hdmatx,hdma_i2s2_ext_tx);

i removed some HAL functions here and directly write to registers to ensure both channels are RX

uint32_t aa;

aa=0x00000317; // prescaler

WRITE_REG(hi2s2.Instance->I2SPR,aa);

aa=0x00000B03; //

master receive

WRITE_REG(hi2s2.Instance->I2SCFGR,aa);

aa=0x00000002;

WRITE_REG(I2SxEXT(hi2s2.Instance)->I2SPR,aa);

aa=0x00000903; //

slave receive

WRITE_REG(I2SxEXT(hi2s2.Instance)->I2SCFGR,aa);

two buffers i2s_rx1/2

(10 32-bit elements each).

hi2s2.pTxBuffPtr = (uint16_t *)&i2s_rx2;

hi2s2.pRxBuffPtr = (uint16_t *)&i2s_rx1;

hi2s2.TxXferSize = 20;

(20 because receiving 24bit over 32bit)

hi2s2.TxXferCount = 20;

ALTHOUGH it is called TXfer i use it for RX (HAL generated names)

hi2s2.RxXferSize = 20;

hi2s2.RxXferCount = 20;

hi2s2.ErrorCode = HAL_I2S_ERROR_NONE; // seems have no effect

hi2s2.State = HAL_I2S_STATE_BUSY_RX;

__HAL_I2S_CLEAR_OVRFLAG(&hi2s2);

HAL_DMA_Start_IT(hi2s2.hdmarx, (uint32_t)&hi2s2.Instance->DR, (uint32_t)&i2s_rx1, hi2s2.RxXferSize);

SET_BIT(hi2s2.Instance->CR2,SPI_CR2_

RX

DMAEN);

HAL_DMA_Start_IT(hi2s2.hdmatx, (uint32_t)&I2SxEXT(hi2s2.Instance)->DR,(uint32_t)&i2s_rx2, hi2s2.TxXferSize);

SET_BIT(I2SxEXT(hi2s2.Instance)->CR2,SPI_CR2_

RX

DMAEN);

if((hi2s2.Instance->I2SCFGR &SPI_I2SCFGR_I2SE) != SPI_I2SCFGR_I2SE)

{

__HAL_I2SEXT_ENABLE(&hi2s2);

__HAL_I2S_ENABLE(&hi2s2);

}

Unfortunately, only I2S2 RX buffer is filled.  I2S2_EXT buffer remains intact.

Surprisingly if i set TX DMA ENABLE (

SPI_CR2_TX

DMAEN) for one of the channels, both DMA buffers are filled, but data is corrupted. Reference manual says that I2Sx_EXT can be used for tx/rx, so it seems there is no limitation to TX/RX mode or RX/TX mode. In my opinion TX/TX or RX/RX modes should be possible. Although i am unsure how to achieve that.

Posted on July 14, 2017 at 06:23

I found the problem. 

SPI2_RX and I2S2_EXT_RX share single DMA Stream 3 and could not be used simultaneously.

Posted on July 17, 2017 at 01:59

Good catch. Still possible with two SPI/I2S units with bit and word clocks connected externally, or with I2S3/I2S3Ext.

JW

tmtlib
Associate II
Posted on July 17, 2017 at 06:26

Important note: SPI2_RX and I2S2_EXT_RX share single DMA Stream 3 and could not be used simultaneously only in DMA mode.

Without DMA both channels could be configured as receivers! Up to 4 stereo ADCs/DACs could b e connected to 2 SPI ports, resulting in 8 audio channels. So relatively cheap audio DAC/ADCs could be used to make 3D audio recorder/player using 8 channels.

Posted on July 17, 2017 at 11:27

Yes, the I2SxExt units are are just a crippled-down version of the same SPI/I2S IP block, and they are entirely independent from their respective SPIx/I2Sx units as far as setup and operation goes; their only connection is that their WS and CK are connected at the GPIO level.

With I2S2/Ext in STM32F405/407/415/417 there's one more

https://community.st.com/0D50X00009XkaBPSAZ

, in the larger packages this clock connection is missing at the PI pins, see also in errata.

JW

tmtlib
Associate II
Posted on August 13, 2017 at 21:10

If someone interested I want to confirm it works. I have 4 channels of 24-bit audio using single I2S channel, PCM1808 connected in parallel as slaves,perfectly synchronized with excellent dynamic range. I think I can connect up to 4 PCMxxx stereo ADC/DAC chips, providing 8-channel 24-bit audio. Using STM32F407.