2014-02-17 07:07 AM
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
2014-02-17 07:49 AM
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 JW2014-02-17 08:19 AM
Ok, so the correct flow should be (I'm obviously using the disable with no later on restart):
1. Disable2. Wait unitl disabled (<- Missing right now)3. Setup M0/M13. Setup NTDR (<- Missing right now)4. Enable?2014-02-17 08:32 AM
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?
2014-02-17 08:55 AM
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.
JW2014-03-30 09:23 AM
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 }} Thanksuprog2014-03-30 11:13 AM
DMA_InitStructure.DMA_PeripheralBaseAddr = I2S_PeripheralBaseAdd; //??? BASE
Try: DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR; // THE DATA REGISTER2014-03-30 11:19 AM
Hi Clive !
Thanks for your reply ! The I2S_PeripheralBaseAdd is an define :#define I2S_PeripheralBaseAdd ((uint32_t)&SPI2->DR)2014-03-30 12:35 PM
The problem is solved.
I forgot :DMA_Cmd(DMA1_Stream3, ENABLE); //!