Skip to main content
alexandr
Associate II
March 9, 2013
Question

STM32F407 I2S High speed trouble

  • March 9, 2013
  • 14 replies
  • 2297 views
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
This topic has been closed for replies.

14 replies

martinmartin9129
Associate II
March 11, 2013
Posted on March 11, 2013 at 09:50

Nobody use this?

waclawek.jan
Super User
March 11, 2013
Posted on March 11, 2013 at 10:38

> I use external clock at PC9 49MHz (by the way - what is the maximum alloud frequency?).

While I could not find any relevant data (the whole I2S timing section in datasheet is still TBD and the table does not contain an entry for external clock either), I believe the value for min EXTINT pulse = 10ns is somewhat pertinent for this value too (i.e. 50MHz should be a relatively safe limit).

> At all speeds up to 96K it works fvery goood, but at 192K it totaly not work!

Please be more specific - in what way it ''totaly not work''? All received values are incorrect, or just some of them? In what way are they incorrect - whole word shifted, or inversed bits?

I don't use the I2S as slave but running a full-duplex I2S (i.e. the respective I2SExt is the slave), at 187.5ksps and did not encounter troubles of this kind so far.

JW

alexandr
alexandrAuthor
Associate II
March 11, 2013
Posted on March 11, 2013 at 22:21

Yes, ALL received values are incorrect, and FRE flag is always set.

waclawek.jan
Super User
March 12, 2013
Posted on March 12, 2013 at 08:20

And I thought it's only the management types who can't answer more than one question per post... ;)

Okay, so once again:

 

In what way are they incorrect - whole word shifted, or inversed bits?

You might also try to use an internal clock source, does the problem persist?

You might also post a minimal but complete, compilable example which exhibits the problem, so that it could be reproduced elsewhere.

JW

martinmartin9129
Associate II
March 12, 2013
Posted on March 12, 2013 at 10:16

I send:  0xA1A25354  and 0x1B2C3D4E

I got:  0xAA0DD129 and 0xA750961E

or 0x6A83344A and 0xA9D46587

Why you suggst to use internal clock? Receiver is not use it.

Example I will be able to send later - I need to change it, tha it can work not at my PCB only.

waclawek.jan
Super User
March 12, 2013
Posted on March 12, 2013 at 12:28

While you did not provide enough information again (do you transmit only these two words circularly infinitely, are the received data always one of those couples of words repeating infinitely, etc.), these are shifted versions of the transmitted words (and the answer to my first question above is ''no'', more different tx data follow).

Seems like your ''wait for sync'' does not work as it should.

Could you try simply removing the timeout from it?

> Why you suggst to use internal clock? Receiver is not use it.

Because it's easy and could reveal a potential problem you have mentioned too (the max. input frequency is not specified).

JW

martinmartin9129
Associate II
March 12, 2013
Posted on March 12, 2013 at 13:52

I send the constant values, circularly infinitely.

Yes, the received data always one of those couples of words repeating infinitely.

After every a reset - the received data ara constant, but can cahnged after another reset.

Of course, I understand that this is ''wait for sync'' problem, but I donn't understand what else I can do!

Without timeout - the same.

> Because it's easy and could reveal a potential problem you have mentioned too (the max. input frequency is not specified).

OK, but what the matter, if receiver does not use this signal? Tranmitting is OK, the sigmal at the receiver's input is OK - the same word as I send.

(I saw this with oscillocsope, that can decode the bus signals).

See the latest source in attachment.

martinmartin9129
Associate II
March 12, 2013
alexandr
alexandrAuthor
Associate II
March 13, 2013
Posted on March 13, 2013 at 22:56

Now I add I2S interrupt on FRE, where I make 2 things: read I2S2->SR and ->DR to clear FRE, and set the programming flag.

In the main loop I check this flag, and if it is set I clere it and re-init I2S.

So, re-init will be up to FRE dissapeared.

This helps, a little bit -now 192K works, but after the first init it takes 200ms-1s (!) to stabilize, which means  than 192K is still unusable :(

waclawek.jan
Super User
March 15, 2013
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