cancel
Showing results for 
Search instead for 
Did you mean: 

SPI RX Buffer via DMA Too Big

jvavra
Associate III
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
15 REPLIES 15
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 Venmo
Up vote any posts that you find helpful, it shows what's working..
jvavra
Associate III
Posted on February 13, 2014 at 22:26

I've never heard of having to reset the source/destination address. Plus, it doesn't really explain why the size of the buffer to be TX'd affects the size of the buffer RX'd.

Edit: Just realized a stupid mistake, though my larger problem still remains: the correct number of bytes were getting transferred originally: I set the buffer size to 106 half-words, and was seeing 212 bytes getting written to memory. Wasn't thinking; obviously, 106 half-words IS 212 bytes. However, two questions still remain:

1) Why would the size of the TX buffer affect the size of the data received in the RX buffer?

2) Why would my linker place this RX buffer where it could overrun other data?  

Posted on February 13, 2014 at 22:38

SPI is symmetrical, you get as much data back as you clock out. In the MASTER context, the RX operation does not generate clocks, the clocks are generated by TX.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jvavra
Associate III
Posted on February 13, 2014 at 23:25

Wow. That was a fundamental misunderstanding of a full-duplex SPI that I had. Makes sense now. You can only receive in as many bytes as you send out.

So the only remaining question is why my linker keeps forcing global data into locations it should know will get stepped on by the DMA's receive buffer.

jvavra
Associate III
Posted on February 13, 2014 at 23:25

Wow. That was a fundamental misunderstanding of a full-duplex SPI that I had. Makes sense now. You can only receive in as many bytes as you send out.

So the only remaining question is why my linker keeps forcing global data into locations it should know will get stepped on by the DMA's receive buffer.

Posted on February 14, 2014 at 00:55

Does the .MAP file suggest the linker is putting anything there?

The stack and heap can collide on Keil if the stack is too small, which in most examples is usually the case.

You might want to memset() your upper memory region outside the scope of the linker, you could shrink the RAM size in the scatter file / linker script. You could space your hard coded addresses further apart.

The DMA won't restart with initial settings, the address incrementer just counts up from where it starts. Generally it is safer to reinitialize the DMA controller each time around. If that works properly you can try decomposing the initialization to the minimum required steps.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..