cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F407 I2S High speed trouble

alexandr
Associate II
Posted on March 09, 2013 at 17:28

Hello peolpes,

maybe somebody can help me. For a testing I use two I2S - one(I2S3) for transmitting, second (I2S2) - for receiving, phisicaly connected at the PCB. At both I use Double Buffer DMA. I fill the sending buffer with the constant, and looking for a receiving buffer to get the same values. I use external clock at PC9 49MHz (by the way - what is the maximum alloud frequency?). At all speeds up to 96K it works fvery goood, but at 192K it totaly not work! Everytime I get a failed values in the receiving buffer, also frame error at I2S2 (receiver). I'm doing everithing according to the datasheet and errata sheet, but I thin something wrong in initialisation (maybe I forget to clear some flags?)

// this is receiver r init:
void I2S2_RCV_Init(void)
{
I2S_InitTypeDef I2S2_InitStructure;
//------------------------------------------------
 I2SGPIO_Init(); 
//
I2S_StructInit(&I2S2_InitStructure);
I2S2_InitStructure.I2S_AudioFreq =I2S_AudioFreq_Default;
I2S2_InitStructure.I2S_CPOL = I2S_CPOL_Low;
I2S2_InitStructure.I2S_Mode = I2S_Mode_SlaveRx;
I2S2_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
I2S2_InitStructure.I2S_Standard = I2S_Standard_MSB;
 I2S2_InitStructure.I2S_DataFormat = I2S_DataFormat_32b;
// wait for WS=1 the for WS=0, i.e. \_ front
WaitForWCLK(1));
WaitForWCLK(0));
I2S_Init(SPI2, &I2S2_InitStructure);
I2S2_DMA_Init();
DMA_Cmd( Rx_DMAStream, ENABLE );
I2S_Cmd(SPI2, ENABLE);
}
// WS waiting function:
static u8 WaitForWCLK(u8 edge)
{
TOTmr1ms = WCLK_TIMEOUT; // TOTmr1ms is decremented in SysTick interrupt
if(edge>0){ // wait for WCLK=1
while (0==(GPIOB->IDR & GPIO_Pin_12)){
if(TOTmr1ms==0) 
return 0;
} 
return 1;
}else{ // wait for WCLK=0
while (0!=(GPIOB->IDR & GPIO_Pin_12)){
if(TOTmr1ms==0) 
return 0;
} 
return 1;
}
}

DMA Function:

static __INLINE void I2S2_DMA_Init( void ) // ******* D M A RECEIVER ****************
{
DMA_InitTypeDef DMA_InitStruct;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_Cmd( Rx_DMAStream, DISABLE );
while( DMA_GetCmdStatus( Rx_DMAStream ) == ENABLE ); // wait for the last DMA end
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
// From:
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&(SPI2->DR); // I2S Data
// To:
DMA_InitStruct.DMA_Memory0BaseAddr = (u32)&DMARCVBUF[0][0]; // memory start
DMA_InitStruct.DMA_BufferSize = DMARCVBUF_SIZE; // length
// With parameters:
DMA_InitStruct.DMA_Channel = DMA_Channel_0; 
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory; // direction
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; 
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 bit per
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 16 bit memory
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; // circ
DMA_InitStruct.DMA_Priority = DMA_Priority_High; // prority
DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; 
DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init( Rx_DMAStream, &DMA_InitStruct ); // Init New
DMA_ITConfig( DMA1_Stream3, DMA_IT_TC, ENABLE ); 
DMA_DoubleBufferModeConfig(Rx_DMAStream, (uint32_t)RCVBuffer_2, DMA_Memory_0 );
DMA_DoubleBufferModeCmd( Rx_DMAStream, ENABLE );
SPI_I2S_DMACmd( SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
}

P.S. CPU - STM32F407VG #stm32f4xx-i2s-dma
14 REPLIES 14
Posted on March 15, 2013 at 12:19

It took me some time to get to setting this up.

Put out some helper signal onto PB14, see modified source below (btw.

d=(uint32_t)SPI3->DR; // to clear RXNE bit

I know it was an act of desperation, but SPI3???).

Turns out the problem is that you spend too much time between detecting the WCLK edge (see picture - red cursor A) and the I2S is enabled (blue cursor B). Pure luck the gcc I use compiled a similar sequence than your Keil... :)

Simply place the ''wait for WCLK edge'' just before the I2S enable - and don't forget to decrease the latencies by disabling interrupts.

JW

------

//----------------------------------------------

u8 InitI2S2(void){ // slave receiver

GPIO_InitTypeDef GPIO_InitStructure;

// NVIC_InitTypeDef NVIC_InitStructure;

I2S_InitTypeDef I2S2_InitStructure;

volatile u32 d;

RxDMADisable();

// RCC_I2SCLKConfig(RCC_I2S2CLKSource_Ext);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

// GPIO

GPIO_PinAFConfig(GPIOB,GPIO_PinSource12, GPIO_AF_SPI2); // WCLK

GPIO_PinAFConfig(GPIOB,GPIO_PinSource13, GPIO_AF_SPI2); // BCLK

GPIO_PinAFConfig(GPIOB,GPIO_PinSource15, GPIO_AF_SPI2); // SDATA

GPIO_StructInit(&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_15; // WCLK-BCLK-SDATA

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

GPIO_InitStructure.GPIO_Speed = RCV_Speed;

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_StructInit(&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

GPIO_InitStructure.GPIO_Speed = RCV_Speed;

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIOB->BSRRL = 1 << 14; // set high

//------------------------------------------------

I2S_StructInit(&I2S2_InitStructure);

I2S2_InitStructure.I2S_AudioFreq = I2S_AudioFreq_Default;

I2S2_InitStructure.I2S_CPOL = I2S_CPOL_Low;

I2S2_InitStructure.I2S_Mode = I2S_Mode_SlaveRx;

I2S2_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Disable;

I2S2_InitStructure.I2S_Standard = I2S_Standard_MSB;

I2S2_InitStructure.I2S_DataFormat = I2S_DataFormat_32b;

GPIOB->BSRRH = 1 << 14; // set low

if(0==WaitForWCLK(1)) return 0; // return if timeout

if(0==WaitForWCLK(0)) return 0; // ---//----

GPIOB->BSRRL = 1 << 14; // set high

I2S_Init(SPI2, &I2S2_InitStructure);

I2S2_DMA_Init();

DMA_Cmd( Rx_DMAStream, ENABLE );

GPIOB->BSRRH = 1 << 14; // set low

I2S_Cmd(SPI2, ENABLE);

GPIOB->BSRRL = 1 << 14; // set high

d=(uint32_t)SPI3->DR; // to clear RXNE bit

__ISB();

return 1;

}

//*********************************************************************************************************

________________

Attachments :

t.png : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006HtXz&d=%2Fa%2F0X0000000aRb%2FOX_zfcbhBdrT8jSs6ugYR1DJdxCyUkz.go08_C6xdTY&asPdf=false
alexandr
Associate II
Posted on March 15, 2013 at 13:28

> (btw.   d=(uint32_t)SPI3->DR;   // to clear RXNE bit  

>I know it was an act of desperation, but SPI3???).

Probably copy-paste error, when I cut the nesessary parts from all project.

btw, I don't know is it must or not to clear this bit here (of course, with actual SPIx :))

>Turns out the problem is that you spend too much time between detecting the WCLK edge (see picture - red cursor A) and the I2S is enabled (blue cursor B). Pure luck the gcc I use compiled a similar sequence than your Keil... :)

>Simply place the ''wait for WCLK edge'' just before the I2S enable - and don't forget to decrease the latencies by disabling interrupts.

>JW

Thank you very much Jan, I will try asap. I can't imagine that:

  I2S_Init(SPI2, &I2S2_InitStructure);

  I2S2_DMA_Init();

  DMA_Cmd( Rx_DMAStream, ENABLE );

are so long, in comparison to WCLK half-period!

Id describes many thinkg, like good operation (out of spec) at 384kbps - it was simly next-next WCLK period, and strange dependancy of the optimisation level and ''time'' switch.

Again, thank you! Let you know the results!

emalund
Associate III
Posted on March 15, 2013 at 15:34

just curious

never heard of using I²S for interprocessor communications, why did you choose that one

Erik
alexandr
Associate II
Posted on March 15, 2013 at 17:41

Erik, where did I say that I wish to use I2S for interprocessor communication?! SPI or UART are faster and simpler.

It was only for I2S-Slave testing, really I2S will come from external source.

Alex.

emalund
Associate III
Posted on March 15, 2013 at 18:48

Erik, where did I say that I wish to use I2S for interprocessor communication?! 

here:

For a testing I use two I2S - one(I2S3) for transmitting, second (I2S2) - for receiving, physically connected at the PCB.

you did not say ''For a testing something else''

anyhow, you satisfied my curiosity, I was wondering if someone had come up with some  new interprocessor comms method, never too old to learn

Erik