cancel
Showing results for 
Search instead for 
Did you mean: 

Receiving SPI data through DMA - STM32 internal hardware error ?

kn7
Associate II
Posted on January 10, 2011 at 22:55

Receiving SPI data through DMA - STM32 internal hardware error ?

#spi-dma
16 REPLIES 16
kn7
Associate II
Posted on May 17, 2011 at 14:21

Hi ANN;

I solved the problems with the receiving. I tried to receive data through DMA which memory size was set to 8-bit while SPI data word size was set to 16-bit. This is why it didn't work. Now it seems it works correctly.

Can you please mention the steps you have taken in order to get the receiving part of SPI work without DMA. I am also trying to do that. But it is not working properly.

 

 

I need to receive in slave mode.

 

 

I've never used slave mode without DMA but i will present the code that works well without DMA for master mode. I think it should also work in slave mode with slight modification.

Sending a byte in master mode:

void Send_SPI_Byte(u8 b)

  while (SPI1->SR & SPI_SR_BSY);

  while (!(SPI1->SR & SPI_SR_TXE));      

  SPI1->DR = b;

  while (SPI1->SR & SPI_SR_BSY);

  while (!(SPI1->SR & SPI_SR_RXNE)); 

  tmp8 = SPI1->DR;

}

The above function may look strange especially because of the flags SPI_SR_BSY that are checked twice. However, the functions NEVER worked when i writte them in other way.

The ''tmp8'' variable must be declared as global (otherwise the compiler will remove the code for '' tmp8 = SPI1->DR'' from you program)

As for slave mode i think the receiving function may look like this:

u8 ReadSlave_SPI_Byte(void)

  while (SPI1->SR & SPI_SR_BSY);

  while (!(SPI1->SR & SPI_SR_TXE));

            

  return (u8) SPI1->DR;    

}

nanayakkaraan
Associate II
Posted on May 17, 2011 at 14:21

Thanks Robert.

Will look into it.

leszek2
Associate II
Posted on May 17, 2011 at 14:21

You should use return SPIx->DR, not tmp8 return to global variable. Its not efficient and the global variable consumes memory for whole program run. Local variable are better because they are pushed on stack only for the needed time and then poped.

kn7
Associate II
Posted on May 17, 2011 at 14:21

You should use return SPIx->DR, not tmp8 return to global variable.

The tmp8 global variable consumes only one byte so this is not a problem for 20KB memory. Besides if tmp8 is declared as a local variable the compiler removes the code for the instruction: tmp8 = SPIx->DR because it detects the tmp8 is never used again in the function. This is due to optimization compiler does when compiling a program.

jpa
Associate II
Posted on May 17, 2011 at 14:21

I am also having trouble with DMA and SPI, on STM32F100. First time I receive a data block after reset, I get only 0x00. The second time I get the correct data except one byte missing from beginning.

Here is my code:

// Transmit 0xFF and receive to buf.

static void dma_rx(BYTE *buf, UINT count)

{

    char txvalue = 0xff;

   

    // Transmit channel

    DMA1_Channel3->CPAR = (int) &(SPI1->DR);

    DMA1_Channel3->CMAR = (int) &txvalue;

    DMA1_Channel3->CNDTR = count;

    DMA1_Channel3->CCR = (1 << 😎 | DMA_CCR1_DIR; // Low priority

   

    // Receive channel

    DMA1->IFCR = DMA_IFCR_CGIF2;

    DMA1->IFCR = 0;

    DMA1_Channel2->CPAR = (int) &(SPI1->DR);

    DMA1_Channel2->CMAR = (int) buf;

    DMA1_Channel2->CNDTR = count;

    DMA1_Channel2->CCR = (1 << 12) | (1 << 😎 | DMA_CCR1_MINC | DMA_CCR1_TCIE; // Medium priority

   

    NVIC_EnableIRQ(DMA1_Channel2_IRQn);

    NVIC_SetPriority(DMA1_Channel2_IRQn, 14);

   

    always_read(SPI1->DR);

    always_read(SPI1->SR);

    DMA1_Channel2->CCR |= DMA_CCR1_EN;

    DMA1_Channel3->CCR |= DMA_CCR1_EN;

    SPI1->CR2 |= SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN;

   

    vTaskDelay(1);

    xSemaphoreTake(mmc_dma_semaphore, 10000);

   

    SPI1->CR2 &= ~(SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN);

    DMA1_Channel3->CCR &= ~DMA_CCR1_EN;

    DMA1_Channel2->CCR &= ~DMA_CCR1_EN;

}

Posted on May 17, 2011 at 14:21

Not really enough information to work with here.

What are you connecting it too, and how are you initializing the system?

Have you attached a scope/analyzer to the bus to confirm it's not sending zeros? If it is then the DMA is not the problem, but rather the peripheral.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jpa
Associate II
Posted on May 17, 2011 at 14:21

Never mind, got it fixed after all. It was a bunch of overlapping problems. Most importantly, I wasn't reading back the data after transmission-only requests, leaving one byte in DR.

For anyone else who might need it: To clear the SPI RXNE and overflow flags before starting the DMA transfer, I use this:

#define always_read(x) asm(''''::''r''(x))

while (!(SPI1->SR & SPI_SR_TXE)); // Wait for bus free

while (SPI1->SR & SPI_SR_BSY);

always_read(SPI1->DR); // Clear RX flags

always_read(SPI1->SR);