Author Topic: STM32 - I2S2 - Full Duplex - Not triggering DMA  (Read 5227 times)

0 Members and 1 Guest are viewing this topic.

Offline YansiTopic starter

  • Super Contributor
  • ***
  • Posts: 3893
  • Country: 00
  • STM32, STM8, AVR, 8051
STM32 - I2S2 - Full Duplex - Not triggering DMA
« on: June 11, 2016, 09:44:55 am »
Hi,
normally I wouldn't create a thread like this, but I am really stuck and cannot find the issue. Can you help me find it?

I have STM32F407 and  using I2S2, trying to make a full duplex transmission with DMA. The I2S2 is in a Slave_Tx mode (the codec chip provides precise clock itself). The transmit side works fine, including the DMA in doublebuffer mode.  But I am struggling to make it work full duplex. The RX side I2S_RX_Ext is not working, DMA is not triggered (CNTDR stuck, memory untouched).

Here is a summary of the settings and configurations:

 + I2S2 uses pins PB12, PB13, PB14, PB15. (Should be fine, TX already works)
 + I2S2 is in Slave_TX mode. (Codec generates clocks, already working).
 + DMA1 Stream4 Channel0 is used for transmit (SPI2_TX selected, working fine)
 + I2S2 is configured Full duplex. (Not working!)
 + DMA1 Stream3 Channel3 is used for the receive side (I2S2_RX_Ext selected, not triggered!).

I see both TXDMAEN and RXDMAEN set in the SPI2_CR2 register.
I see all registers from the DMA1 Stream3 configured, seem also to be properly. CNTDR is stuck at default, no request coming.

Can you pleas help me to trace the issue? What did I do wrong? What should I check? I just can't see my mistake.

Here is a dump of the init code:

Code: [Select]
void Init_I2S2(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  I2S_InitTypeDef I2S_InitStructure;
  DMA_InitTypeDef DMA_InitStructure;
  NVIC_InitTypeDef NVIC_InitStruct;

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

  GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_SPI2);  /* WS */
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);  /* CK */
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2);  /* DIN */
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);  /* DOUT */

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_25MHz;
  GPIO_Init(GPIOB, &GPIO_InitStruct);

  I2S_StructInit(&I2S_InitStructure);
  I2S_InitStructure.I2S_Mode = I2S_Mode_SlaveTx;
  I2S_InitStructure.I2S_Standard = I2S_Standard_Phillips;
  I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16bextended;
  I2S_InitStructure.I2S_CPOL = I2S_CPOL_Low;
  I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
  I2S_Init(SPI2, &I2S_InitStructure);

  I2S_FullDuplexConfig(I2S2ext, &I2S_InitStructure);

  /* Enable DMA request for TX & RX */
  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);

  /* DMA1 Stream4 Channel0 for I2S2 TX side */
  DMA_InitStructure.DMA_Channel = DMA_Channel_0; 
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)DAC_Buffer0;
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
  DMA_InitStructure.DMA_BufferSize = BUFFER_DEPTH*2;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  DMA_Init(DMA1_Stream4, &DMA_InitStructure);

  DMA_DoubleBufferModeConfig(DMA1_Stream4, (uint32_t)DAC_Buffer1, DMA_Memory_0);
  DMA_DoubleBufferModeCmd(DMA1_Stream4, ENABLE);

  /* DMA1 Stream3 Channel3 for I2S2 RX side */
  DMA_InitStructure.DMA_Channel = DMA_Channel_3; 
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADC_Buffer0;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = BUFFER_DEPTH*2;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;         
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  DMA_Init(DMA1_Stream3, &DMA_InitStructure);

  DMA_DoubleBufferModeConfig(DMA1_Stream3, (uint32_t)ADC_Buffer1, DMA_Memory_0);
  DMA_DoubleBufferModeCmd(DMA1_Stream3, ENABLE);

  DMA_ITConfig(DMA1_Stream4, DMA_IT_TC, ENABLE);

  NVIC_InitStruct.NVIC_IRQChannel = DMA1_Stream4_IRQn;
  NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
  NVIC_Init(&NVIC_InitStruct);
 
  /* Enable DMA, RX & TX */
  DMA_Cmd(DMA1_Stream3, ENABLE);
  DMA_Cmd(DMA1_Stream4, ENABLE);

}

Thx for help!

//EDIT:  Calling I2S_Cmd() enable both for SPI2 and I2S2ext from main.
« Last Edit: June 11, 2016, 10:01:50 am by Yansi »
 

Offline YansiTopic starter

  • Super Contributor
  • ***
  • Posts: 3893
  • Country: 00
  • STM32, STM8, AVR, 8051
Re: STM32 - I2S2 - Full Duplex - Not triggering DMA
« Reply #1 on: June 11, 2016, 10:13:46 am »
Finaly found it.  |O |O |O :palm:

Half a day has gone into shit, cause of ST's policy of no usable examples.

For those who may encounter similar issues, here's the resolution:

instead of SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE); one must call

SPI_I2S_DMACmd(I2S2ext, SPI_I2S_DMAReq_Rx, ENABLE);

and change the data register in the DMA settings of course:

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&I2S2ext->DR;

... half a day has gone shit due to this.  |O :palm: The rest of the application (about 600 lines of code) was piece of cake.  :phew:

« Last Edit: June 11, 2016, 10:41:53 am by Yansi »
 

Online AndyC_772

  • Super Contributor
  • ***
  • Posts: 4224
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: STM32 - I2S2 - Full Duplex - Not triggering DMA
« Reply #2 on: June 11, 2016, 10:27:34 am »
Glad you found it. The only real issue I ever had getting DMA working (albeit on SPI rather than I2S) was the limited connectivity between the DMA controllers and peripherals. I was trying to use DMA1, where only DMA2 actually had the necessary connection to the SPI peripheral (or vice versa).

How have you generally found using the ST HAL? I ditched it and went straight to poking the hardware quite early on in my STM32 learning process, and I've not looked back since. It sounds as though it this case it's not really made your life easier; would you say it's been generally beneficial overall?

Offline YansiTopic starter

  • Super Contributor
  • ***
  • Posts: 3893
  • Country: 00
  • STM32, STM8, AVR, 8051
Re: STM32 - I2S2 - Full Duplex - Not triggering DMA
« Reply #3 on: June 11, 2016, 10:34:26 am »
As you can see, I use StdPeriph, that's how I like it. I am not afraid of poking straight into HW (and usualy I have to do it due to optimizations for speed where easily possible).

HAL is a piece of crapcode for arduino dummies. Simple and nice until one has to do some real work with it. Not trying to offend any arduino user, but that's my opinion. Saidly, the StdPeriph is discontinued for new product lines (I especially miss it for STM32F7).

I've been always a "HW guy" who is very curious how do things work, so really not afraid poking HW or RTFM. My first app for ARM was plain C direct register access and I have even tried pure assembly. I've written once a HD44780 display demo for F100 discovery, pure ASM. Just to get better understanding of the HW.
The software is today kind of "necessary evil" for a HW design engineer. So I am not a programming and software specialist.

But please don't make this thread another HAL x StdP war.
« Last Edit: June 11, 2016, 10:40:48 am by Yansi »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf