cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 SPI DMA Double Buffer issue

lukas2
Associate II
Posted on February 17, 2014 at 16:07

Hi,

I'm using the F407 (Discovery) as a SPI-Slave with DMA enabled and a ''data ready'' signaling line. I'm currently trying to transfer data in dual buffer mode (the host cpu might also send configuration data at random times), because the slave has to respond very quickly when a CS-Edge arrives, so there is no time to service a regular CS-EXTI-IRQ in order to set up the DMA in single buffer mode with the correct data pointer (which might be an empty message or data/status frames). So I choose dual buffer mode with a half-transfer completion interrupt to setup the next memory location to handle continous transfers. Everything runs smooth, as long as the host requests data and only receives empty messages which means that I do not modify the dma. But when I start a transfer by stopping the DMA, selecting the proper memory region for buffer 0, choosing buffer 0 as next to transfer and re-enabling the DMA stream, the SPI frames are shifted by one byte. The first byte is always either ''0'' or equal to the last byte of the previous frame which was transmitted by the DMA. I'm using direct-mode, so no FIFO involved here... The receive is in standard circular mode nd works nicely... Any clue/solution on this problem? Init for the Tx Transfer:


DMA_DeInit(DMA1_Stream4);

DMA_InitStructure.DMA_Channel = DMA_Channel_0;

DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; 

DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)SPI_EmtpyBuffer;

DMA_InitStructure.DMA_BufferSize = (uint16_t)SPI_BUFFER_SLOTLENGTH;

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

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;

DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;

DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;

DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;


//Setup the Dual Buffer mode for the Tx DMA

DMA_DoubleBufferModeConfig(DMA1_Stream4, (uint32_t)SPI_EmtpyBuffer, DMA_Memory_0);

DMA_DoubleBufferModeCmd(DMA1_Stream4, ENABLE);

DMA_Init(DMA1_Stream4, &DMA_InitStructure);

My routine if my slave wants to send new data the host:


void SPI_Tx_Start(volatile uint8_t *pBuf)

{

//Check if there is no ongoing transfer. No need to do anything if still transfering since HT-IRQ will check if data left to send

if(SPI_CheckTxInProgress() == 0)

{

DMA_Cmd(DMA1_Stream4, DISABLE);

//Setup the current DMA memory to the desired buffer

if(DMA_GetCurrentMemoryTarget(DMA1_Stream4)== 0)

{

DMA_MemoryTargetConfig(DMA1_Stream4,(uint32_t)pBuf,DMA_Memory_0);

}

else

{

DMA_MemoryTargetConfig(DMA1_Stream4,(uint32_t)pBuf,DMA_Memory_1);

}

//Set the current data pointer to the entered buffer

ptr_SPI_Tx_Current = pBuf;

DMA_Cmd(DMA1_Stream4, ENABLE);


//Increment current open pointer to storage

SPI_Increment_TxOpen();


//Signal that data is available

SPI_Raise_IRQ();

}

}

#stm32f4-dma-double-buffer-spi
8 REPLIES 8
Posted on February 17, 2014 at 16:49

Suspending/restarting the DMA is not as simple as clearing/setting DMA_SxCR.EN (or, in the ''library'' gibberish

DMA_Cmd(DMA1_Stream4, DISABLE)/DMA_Cmd(DMA1_Stream4, ENABLE)

)

Note, that _NDTR and the address registers are in fact two registers, one is written and the other is incremented/decremented by hardware and read; and the first is copied into the other when CR.EN is set.

Read chapter 3.14 of RM0

JW

lukas2
Associate II
Posted on February 17, 2014 at 17:19

Ok, so the correct flow should be (I'm obviously using the disable with no later on restart):

1. Disable

2. Wait unitl disabled (<- Missing right now)

3. Setup M0/M1

3. Setup NTDR (<- Missing right now)

4. Enable

?

lukas2
Associate II
Posted on February 17, 2014 at 17:32

void
SPI_Tx_Start(volatile uint8_t *pBuf)
{
//Check if there is no ongoing transfer. No need to do anything if still transfering since HT-IRQ will check if data left to send
if
(SPI_CheckTxInProgress() == 0)
{
DMA_Cmd(DMA1_Stream4, DISABLE);
//Wait until disabled
while
(DMA_GetCmdStatus(DMA1_Stream4) == ENABLE)
{}
//Re-Setup the number of data items
DMA1_Stream4->NDTR = SPI_BUFFER_SLOTLENGTH;
//Setup the current DMA memory to the desired buffer
if
(DMA_GetCurrentMemoryTarget(DMA1_Stream4)== 0)
{
DMA_MemoryTargetConfig(DMA1_Stream4,(uint32_t)pBuf,DMA_Memory_0);
}
else
{
DMA_MemoryTargetConfig(DMA1_Stream4,(uint32_t)pBuf,DMA_Memory_1);
}
//Set the current data pointer to the entered buffer
ptr_SPI_Tx_Current = pBuf;
DMA_Cmd(DMA1_Stream4, ENABLE);
//Increment current open pointer to storage
SPI_Increment_TxOpen();
//Signal that data is available
SPI_Raise_IRQ();
}
}

but the issue still persists...after the first run of

SPI_Tx_Start()

, each frame is shifted by one byte at the SPI...is the (re-)init still somewhat wrong?

Posted on February 17, 2014 at 17:55

I don't know, but I don't understand what are you trying to accomplish either... I'd expect the dual-buffer mode is exactly in order to avoid stopping/restarting DMA, and I'd expect you change the opposite memory to the one being currently used, for example. But I did not spend much time trying to understand it.

JW
uprogc
Associate II
Posted on March 30, 2014 at 18:23

Hi !

I'm using I2S2 DMA with double buffer and circular mode config. I don't get DMA interrupt.

I have cheched the I2S2 with SPI2_IRQHandler, and it worked fine.

void DMAInit( uint32_t addr0, uint32_t addr1, uint32_t buffsize /*1536 x2 x2*/) 

{

    DMA_InitTypeDef DMA_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

 DMA_StructInit(&DMA_InitStructure); 

    // DMA 1 clock enable

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Stream3);

    /*

     * DMA configuration

     * SPI2_RX (=I2S_RX): DMA1 channel 0, Stream 3 -- The ref manual say it's DMA1/Ch0/Stream3 for SPI2_RX

     * Double Buffer and Circular mode enabled

     */

    DMA_InitStructure.DMA_Channel = DMA_Channel_0;

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 

    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // circular mode 

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

    DMA_InitStructure.DMA_PeripheralBaseAddr = I2S_PeripheralBaseAdd; //

    DMA_InitStructure.DMA_Priority = DMA_Priority_High;

    DMA_InitStructure.DMA_BufferSize = buffsize;

    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) addr0;  // 0. puffer addr

 

 

 //The Double Buffer mode can be used only when Circular mode is enabled. 

 /*

  The Double Buffer mode allows to set two different Memory addresses from/to which

  the DMA controller will access alternatively (after completing transfer to/from target

  memory 0, it will start transfer to/from target memory 1).

 */

 

    DMA_DoubleBufferModeConfig(DMA1_Stream3, (uint32_t) addr1 /*next address*/, DMA_Memory_0); 

    // Double buffer mode activated

    DMA_DoubleBufferModeCmd(DMA1_Stream3, ENABLE);

    DMA_Init(DMA1_Stream3,&DMA_InitStructure); 

    // Transfer complete interrupt enabling

    DMA_ITConfig(DMA1_Stream3, DMA_IT_TC, ENABLE);

   /* Configure and enable DMA interrupt */

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream3_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

SPI_I2S_DMACmd(SPI2,SPI_I2S_DMAReq_Rx,ENABLE); 

    I2S_Cmd(SPI2, ENABLE); 

}

volatile uint32_t k=0;

void DMA1_Stream3_IRQHandler()

{

 

    // Stream3 transfer complete interrupt?

    if(DMA_GetITStatus(DMA1_Stream3, DMA_IT_TCIF3)) // Transfer Complete event 

    {

        // clear pending interrupt

        DMA_ClearITPendingBit(DMA1_Stream3, DMA_IT_TCIF3); 

 

        if(DMA_GetCurrentMemoryTarget(DMA1_Stream3)) //

        {        

        }

        else

        {   

        }

    }

if(k==0)

{

GPIO_SetBits(GPIOD, GPIO_Pin_12); //green led

k=1;

}

else

{

k=0;

//GPIO_ResetBits(GPIOD, GPIO_Pin_12); //green led

}

}

Thanks

uprog

Posted on March 30, 2014 at 20:13

DMA_InitStructure.DMA_PeripheralBaseAddr = I2S_PeripheralBaseAdd; //??? BASE

Try:

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; // THE DATA REGISTER

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
uprogc
Associate II
Posted on March 30, 2014 at 20:19

Hi Clive !

Thanks for your reply ! 

The I2S_PeripheralBaseAdd is an define :

#define I2S_PeripheralBaseAdd ((uint32_t)&SPI2->DR)

uprogc
Associate II
Posted on March 30, 2014 at 21:35

The problem is solved.

I forgot :

DMA_Cmd(DMA1_Stream3, ENABLE); //!