cancel
Showing results for 
Search instead for 
Did you mean: 

Any code or documentation references for I2S Full Duplex using Cube-based firmware?

maggie
Associate III
Posted on August 06, 2014 at 10:47

Hi - Lately, it seems that there are 2 versions of STM32 firmwares. These firmwares appear in different sites in ST, but to put it simply, these 2 types are:

  • STM32Cube-based firmwares
  • Non-STM32Cube-based firmwares

Basically Cube-based ones are those which uses the

new

version of STM32 firmware architecture, where peripheral/hal drivers used are those which start withstm32f3xx_hal_ppp*. Non-Cube-based ones are those which uses the

old

version ofSTM32 firmware architecture,where peripheral/hal drivers used are those which start withstm32f30x_ppp*. Now, I would like to acquire a Cube-based firmware (or at least any documentation) that shows how to properly

configure

,

initialize

and

start

I2S in Full Duplex mode using DMA. I was only able to configure and initialize I2S in Full Duplex mode, but I don't know how to start it. In Non-Cube-based codes, it can be ''started'' via:

/* Enable the DMA_CHANNEL_RX transfer complete */ 
DMA_ITConfig(DMA_CHANNEL_RX, DMA_IT_TC, ENABLE);
/* Enable the DMA_CHANNEL_TX transfer complete */ 
DMA_ITConfig(DMA_CHANNEL_TX, DMA_IT_TC, ENABLE);
/* Enable the DMA channel Rx */
DMA_Cmd(DMA_CHANNEL_RX, ENABLE);
/* Enable the DMA channel Tx */
DMA_Cmd(DMA_CHANNEL_TX, ENABLE); 
/* Enable the I2Sext RX DMA request */
SPI_I2S_DMACmd(I2Sext, SPI_I2S_DMAReq_Rx, ENABLE); 
/* Enable the I2S TX DMA request */
SPI_I2S_DMACmd(SPI, SPI_I2S_DMAReq_Tx, ENABLE);
/* Enable the SPI Master peripheral */
I2S_Cmd(SPI, ENABLE);
/* Enable the I2Sext peripheral */
I2S_Cmd(I2Sext, ENABLE);

But above codes do not exist anymore in Cube-based ones. I can only find these 2 functions which might do the same as above snippet, but I'm not really sure

how

and

when

to call them:

HAL_I2S_Transmit_DMA @stm32f3xx_hal_i2s.c
HAL_I2S_TransmitReceive_DMA @stm32f3xx_hal_i2s_ex.c

I already understood this mechanism in Non-Cube-based one, inI2S_FullDuplexDataExchangeDMA example available in STSW-STM32108 package. Unfortunately, Cube-based firmwares and Non-Cube-based firmwares are totally different. I am using an STM32F303x-based board (I have both the Discovery and the Eval), but I will also appreciate samples not using F3 (ie, F4 will do) -I really just want to know the sequence of I2S, DMA calls. Any clue, reference code, or documentation that will help me implement this using the Cube-based libraries is/are very much appreciated. Thanks, #stm32cube-i2s-full-duplex
5 REPLIES 5
Posted on September 30, 2014 at 12:27

Hi maggie.roxas,

Unfortunately, there is no STM32Cube based example that demonstrates the use of I2S IP in full duplex DMA mode, till now.

We'll let our development team know about your suggestion.

Regards,

Heisenberg.

maggie
Associate III
Posted on October 14, 2014 at 09:12

To Heisenberg

 - please let us know once the development team released this in the future versions of STM32CubeFX examples, via posting to this thread.

I know it would be a hassle (sorry), but I'm saying for the benefit of all those who browses this forum, so that we/they can link the answer via your future post (ie ''this is already released as part of STM32CubeFX vX.X...'').

To all other users

 - if you were able to make a firmware successfully with this setup, please still feel free to post.

Thank you very much!

james2399
Associate II
Posted on November 17, 2014 at 09:09

Bumping this.

I'm trying to work with the HAL I2S/DMA and things are not always working as expected. The HAL documentation completely lacks implementation details and figuring some of this stuff out is taking far longer than it should.

qwer.asdf
Senior
Posted on November 17, 2014 at 13:30

Did you read the chapters 25-26 of the UM1725[1] document?

 

If it doesn't help I think your best bet would be reading the source code of the HAL drivers implementation of I2S, until more robust documention is ready.

[1] - http://www.st.com/st-web-ui/static/active/en/resource/technical/document/user_manual/DM00105879.pdf
james2399
Associate II
Posted on November 18, 2014 at 04:11

(forum software ate my reply - trying again)

I have read the 'F4 HAL documentation, and the usage seems fairly straight forward. In Master Transmit mode, I've been able to get HAL_I2SEx_TransmitReceive() and HAL_I2SEx_TransmitReceive_DMA() working, but not without issues. Here's the initialization code, as generated by STM32CubeMX 4.4.0: main.c

/* I2S2 init function */
void
MX_I2S2_Init(
void
)
{
hi2s2.Instance = SPI2;
hi2s2.Init.Mode = I2S_MODE_MASTER_TX;
hi2s2.Init.Standard = I2S_STANDARD_PHILLIPS;
hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
hi2s2.Init.MCLKOutput = I2S_MCLKOUTPUT_ENABLE;
hi2s2.Init.AudioFreq = I2S_AUDIOFREQ_16K;
hi2s2.Init.CPOL = I2S_CPOL_LOW;
hi2s2.Init.ClockSource = I2S_CLOCK_PLL;
hi2s2.Init.FullDuplexMode = I2S_FULLDUPLEXMODE_ENABLE;
HAL_I2S_Init(&hi2s2);
}
/**
* Enable DMA controller clock
*/
void
MX_DMA_Init(
void
)
{
/* DMA controller clock enable */
__DMA1_CLK_ENABLE();
/* DMA interrupt init */
HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn);
HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn);
}

stm32f4xx_hal_msp.c

void
HAL_I2S_MspInit(I2S_HandleTypeDef* hi2s)
{
GPIO_InitTypeDef GPIO_InitStruct;
if
(hi2s->Instance==SPI2)
{
/* USER CODE BEGIN SPI2_MspInit 0 */
/* USER CODE END SPI2_MspInit 0 */
/* Peripheral clock enable */
__SPI2_CLK_ENABLE();
/**I2S2 GPIO Configuration 
PB12 ------> I2S2_WS
PB13 ------> I2S2_CK
PB14 ------> I2S2_ext_SD
PB15 ------> I2S2_SD
PC6 ------> I2S2_MCK
*/
GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF6_I2S2ext;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* Peripheral DMA init*/
hdma_spi2_tx.Instance = DMA1_Stream4;
hdma_spi2_tx.Init.Channel = DMA_CHANNEL_0;
hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_spi2_tx.Init.Mode = DMA_NORMAL;
hdma_spi2_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_spi2_tx);
__HAL_LINKDMA(hi2s,hdmatx,hdma_spi2_tx);
hdma_i2s2_ext_rx.Instance = DMA1_Stream3;
hdma_i2s2_ext_rx.Init.Channel = DMA_CHANNEL_3;
hdma_i2s2_ext_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_i2s2_ext_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_i2s2_ext_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_i2s2_ext_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_i2s2_ext_rx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_i2s2_ext_rx.Init.Mode = DMA_NORMAL;
hdma_i2s2_ext_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_i2s2_ext_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
HAL_DMA_Init(&hdma_i2s2_ext_rx);
__HAL_LINKDMA(hi2s,hdmarx,hdma_i2s2_ext_rx);
HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI2_IRQn);
/* USER CODE BEGIN SPI2_MspInit 1 */
/* USER CODE END SPI2_MspInit 1 */
}
}

And here's the snippets of loopback code that I'm using to test:

uint16_t I2SBufferA[I2SBUFFER_SIZE];
uint16_t I2SBufferB[I2SBUFFER_SIZE];
uint16_t *pI2S_in, *pI2S_out;
void
AudioLoopbackTest()
{
for
(;;) {
HAL_I2SEx_TransmitReceive(&hi2s2, I2SBufferA, I2SBufferB, I2SBUFFER_SIZE, 1000);
HAL_I2SEx_TransmitReceive(&hi2s2, I2SBufferB, I2SBufferA, I2SBUFFER_SIZE, 1000);
}
}
void
AudioLoopbackTest_DMA()
{
pI2S_in = I2SBufferA;
pI2S_out = I2SBufferB;
for
(;;) {
// Wait for I2S ready state
while
(HAL_I2S_GetState(&hi2s2) != HAL_I2S_STATE_READY);
// Swap Buffers
pI2S_in = (pI2S_in == I2SBufferA) ? I2SBufferB : I2SBufferA;
pI2S_out = (pI2S_out == I2SBufferA) ? I2SBufferB : I2SBufferA;
HAL_I2SEx_TransmitReceive_DMA(&hi2s2, pI2S_out, pI2S_in, I2SBUFFER_SIZE);
}
}

The polling version of TransmitReceive works fine. The DMA version stalls after the first transaction: DMA interrupts stop firing, and the I2S state is stuck at HAL_I2S_STATE_BUSY_TX_RX. The I2S clock is no longer being generated. The only interrupt still going is SysTick. The DMA version DOES work if, during a debug session, after the first run, I pause, reset, and run again. This is with Atollic TrueSTUDIO & ST-LINK/V2. It does not work without the debugger attached, ie. pressing the reset button. My initial thought is that something is not being initialized properly. I haven't yet gone through the I2S HAL source to verify. Maybe I've missed something? It would be nice to have a working sample to compare against.

EDIT2

I think I misunderstood how the DMA functions should be used. With circular buffer mode and the half/full callbacks, the I2SEx_TransmitReceive() function only needs to be called once. Insight provided by this thread: https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https%3a%2f%2fmy%2est%2ecom%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fSTM32F427VIT%20I2S%20strange%20desync%20problem&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A7... This forum software is painful :(