Skip to main content
jvavra
Associate III
February 13, 2014
Question

SPI RX Buffer via DMA Too Big

  • February 13, 2014
  • 15 replies
  • 2613 views
Posted on February 13, 2014 at 14:07

I've set up my DMA to transfer 106 half-words via the SPI to a memory buffer. I am quite confident that my buffer size is correct. However, when the transfer actually occurs, it is twice the size I expect (and is consequently stepping on some global variables). Because the transfer is all 0xFFs, I can't tell if I'm getting 212 half-words, 106 full-words, or two 106 half-word ''packets'' back-to-back and somehow the destination pointer is getting scrambled. If I look at the NDTR register for that stream in my IDE just before it transfers, it says I have 106 data items, leading me to believe the case is 106 full-words, but then again it also says the memory and peripheral data sizes are set to half-words so I don't know which to believe.

Any thoughts are appreciated. Sample DMA/SPI setup:

void SPI_Configuration()

{

   StopSPI();                                                                   //Stop DMA, SPI

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

   //     SPI1               //

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

   SPI1_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

   SPI1_InitStructure.SPI_Mode = SPI_Mode_Master;

   SPI1_InitStructure.SPI_DataSize = SPI_DataSize_16b;

   SPI1_InitStructure.SPI_CPOL = SPI_CPOL_Low;

   SPI1_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;

   SPI1_InitStructure.SPI_NSS = SPI_NSS_Soft;

   SPI1_InitStructure.SPI_BaudRatePrescaler = SPI1SCALER;

   SPI1_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

   SPI1_InitStructure.SPI_CRCPolynomial = 7;

   SPI_Init(SPI1, &SPI1_InitStructure);

  

   // Rx Channel

   DMA_DeInit(DMA2_Stream2);

   DMA_StructInit(&SPI1RX_DMA_InitStructure);

   SPI1RX_DMA_InitStructure.DMA_Channel = DMA_Channel_3;

   SPI1RX_DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(SPI1->DR);

   SPI1RX_DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&g_spi1_adc_data;

   SPI1RX_DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;

   SPI1RX_DMA_InitStructure.DMA_BufferSize = g_chain1_buffer_size;

   SPI1RX_DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

   SPI1RX_DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

   SPI1RX_DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

   SPI1RX_DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

   SPI1RX_DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

   SPI1RX_DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;

   DMA_Init(DMA2_Stream2, &SPI1RX_DMA_InitStructure);

   // Enable DMA Stream Transfer Complete interrupt

   DMA_ITConfig(DMA2_Stream2, DMA_IT_TC, ENABLE);

  

   // Tx Channel

   DMA_DeInit(DMA2_Stream3);

   DMA_StructInit(&SPI1TX_DMA_InitStructure);

   SPI1TX_DMA_InitStructure.DMA_Channel = DMA_Channel_3;

   SPI1TX_DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(SPI1->DR);

   SPI1TX_DMA_InitStructure.DMA_Memory0BaseAddr = g_spi1_config_buf_base_ptr;

   SPI1TX_DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;

   SPI1TX_DMA_InitStructure.DMA_BufferSize = g_chain1_buffer_size;

   SPI1TX_DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

   SPI1TX_DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

   SPI1TX_DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

   SPI1TX_DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

   SPI1TX_DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

   SPI1TX_DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;  

   DMA_Init(DMA2_Stream3, &SPI1TX_DMA_InitStructure);

   // Enable SPI1

   SPI_Cmd(SPI1, ENABLE);

 

   // Enable DMA request

   SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE);

}

#dma #stm32 #spi
This topic has been closed for replies.

15 replies

Tesla DeLorean
Guru
February 13, 2014
Posted on February 13, 2014 at 14:20

g_chain1_buffer_size = number of 16-bit words to transfer

The DMA shouldn't overflow it's described buffer. You could make yourself a large buffer, put a fill character in it, and then observe how much gets over written. Avoid DMA to auto/local variables.
Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
jvavra
jvavraAuthor
Associate III
February 13, 2014
Posted on February 13, 2014 at 14:24

Clive,

Thanks for the reply. I have confirmed that g_chain1_buffer_size is the number that I want (106). And without creating a larger buffer, I can tell pretty easily what size it's actually writing by viewing the memory starting at the destination pointer in my IDE. It's actually sending exactly twice what I expect (212 16-bit integers, instead of 106).

bpacan
Associate II
February 13, 2014
Posted on February 13, 2014 at 14:59

Please try enable only DMA TX request - last line. Configured as full-duplex should also trigger a DMA RX, I hope.

jvavra
jvavraAuthor
Associate III
February 13, 2014
Posted on February 13, 2014 at 15:24

Thank you for the reply. So I changed the last line to:

   // Enable DMA request

   SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);

However, now I get NO data transferred at all. I think you may be on to something here: I just noticed that before I made this change, when I see this ''too large'' DMA transfer, both the RX stream and TX stream (which each have the same size buffer) have their NDTR registers cleared. Can you elaborate on why you say ''Configured as full-duplex should also trigger a DMA RX, I hope.''? I would not expect to receive anything when only the TX DMA transfer is enabled, but I may be missing something important.

Thanks!

 

francescatodiego
Associate III
February 13, 2014
Posted on February 13, 2014 at 16:01

DMA TX transfer is enabled ?

DMA_Cmd (DMA2_stream3,  ENABLE);

You can post also the DMA handler procedure called when transfer is complete

jvavra
jvavraAuthor
Associate III
February 13, 2014
Posted on February 13, 2014 at 16:29

Yes, it's when I actually enable the DMA that I see this overrun transfer:

DMA2_Stream3->CR |= (u32)DMA_SxCR_EN;

I also notice that I get a FIFO error for this TX stream; in searching it appears that getting one of these error flags is normal since I enable the SPI before enabling the DMA. Can anyone confirm?

jvavra
jvavraAuthor
Associate III
February 13, 2014
Posted on February 13, 2014 at 16:59

I am thoroughly cinfused now: I changed the buffer size on the TX side of the config, trying to verify if it was indeed the TX stream contributing to the ovverun. Changed like this:

   // Tx Channel

   DMA_DeInit(DMA2_Stream3);

   DMA_StructInit(&SPI1TX_DMA_InitStructure);

   SPI1TX_DMA_InitStructure.DMA_Channel = DMA_Channel_3;

   SPI1TX_DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&(SPI1->DR);

   SPI1TX_DMA_InitStructure.DMA_Memory0BaseAddr = g_spi1_config_buf_base_ptr;

   SPI1TX_DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;

   SPI1TX_DMA_InitStructure.DMA_BufferSize = (g_chain1_buffer_size - 20);

 

   SPI1TX_DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

   SPI1TX_DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

   SPI1TX_DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

   SPI1TX_DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

   SPI1TX_DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

   SPI1TX_DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;  

   DMA_Init(DMA2_Stream3, &SPI1TX_DMA_InitStructure);

I expected I might see the total transfer to decrease by 20. Instead, it decreased by 40. It's as if the TX stream ONLY is writing to memory, instead of sending from memory to my peripheral. 

Does this make sense to anyone? 

Tesla DeLorean
Guru
February 13, 2014
Posted on February 13, 2014 at 18:42

Does this make sense to anyone? 

I'm confused too, present a complete test case that illustrates the problem. There are other things going on here, and I lack context.
Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
jvavra
jvavraAuthor
Associate III
February 13, 2014
Posted on February 13, 2014 at 20:09

The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6e2&d=%2Fa%2F0X0000000bsN%2FH2zZCIvrAbIGNqJ8Sui91UDnnMSRjyty5o2TglYZpO0&asPdf=false
Tesla DeLorean
Guru
February 13, 2014
Posted on February 13, 2014 at 21:04

So you're sure you don't need to reset the addresses being used when you restart the DMA? Most probable issue from skimming the code is it's simply continuing where it left off.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..