2014-08-06 01:47 AM
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:
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 theold
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 properlyconfigure
,initialize
andstart
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
andwhen
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
2014-09-30 03:27 AM
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.2014-10-14 12:12 AM
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!2014-11-17 12:09 AM
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.2014-11-17 04:30 AM
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
2014-11-17 07:11 PM
(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 :(