cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L151CBT6 SPI Full-Duplex as slave: Problems understanding SPI DMA and how to use it

Chrizzly
Associate III

Hello, in my setup the STM32L151CBT6 (32MHz) is configured as slave of a PLD Master, which provides a 4MHz Clk for the SPI communication.

After a short "handshake" between slave and PLD where the slave sends and receives some bytes (with HAL_SPI_Transmit and HAL_SPI_Receive in a while loop until a specific handshake value is received) for confirmation that it had a successfull "start-up" phase the master then starts sending 64Byte Data packages in a cyclic manner.

In previous tests I (with the help of the forum Einstein TDK) came to the conclusion that the HAL_SPI_TransmitReceive function is to slow. So now I want to use the HAL_SPI_TransmitReceive_DMA function. When I just use the HAL_SPI_TransmitReceive_DMA function in a while loop in my main function I see (on a digital oscilloscope) that my master and slave send the right Dummy values in correct order.

My slave should have a 400µs window between each received package to manipulate data.

To test if my slave receives the right data during the operational phase I wanted it to copy the received data and send it out in the next package cycle.

I think the problem lies in the lack of understanding how to use the HAL_SPI_TransmitReceive_DMA function properly. Maybe someone can explain how I get my test setup working and how to use the HAL_SPI_TransmitReceive_DMA function the right way (and even if I can use it for my use case)?

I posted the simple main while loop for my Test setup, which does not work. It aborts the slave sending part afer the 1st byte.

  while (1)
  {
 
 
   HAL_SPI_TransmitReceive_DMA(&hspi1, Configuration_Values_Tx, Configuration_Values_Rsx, 64);
    
   for (iter_i =0; iter_i < sizeof(Configuration_Values_Rsx); iter_i ++)
    {
      Configuration_Values_Tx[iter_i] = Configuration_Values_Rsx[iter_i];
    }
   
    
  }

12 REPLIES 12

HAL_SPI_TransmitReceive_DMA() is not blocking, so you have to wait until DMA finished transfer, before manipulating the data in buffers. I don't use Cube, don't know the incantations to do that; surely it's described in Cube/HAL's manual.

JW

Chrizzly
Associate III

Thank you for your answer. I undestand it. I tried this and it kind of works, but the Data is wrong and corrupted.

I am not sure if this is the right solution to my problem. Maybe someone who is familiar with HAL can provide answers to my questions?

 while (1)
 
 {
 
 
  HAL_SPI_TransmitReceive_DMA(&hspi1, Configuration_Values_Tx, Configuration_Values_Rsx, 64);
 
 
  HAL_SPI_TxRxCpltCallback(&hspi1);
 
 
 }
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi)
{
  for (iter_i =0; iter_i < sizeof(Configuration_Values_Rsx); iter_i ++)
  {
    Configuration_Values_Tx[iter_i] = Configuration_Values_Rsx[iter_i];
  }
}

You are not supposed to call HAL_SPI_TxRxCpltCallback() in your main(); it's Cube which calls it.

JW

MM..1
Chief II

Hi Chrizzly,

maybe you dont understand how purpose is DMA. DMA is for offload MCU , and MCU can do other code in time of transfer data.

Both your codes is bad. When your application dont do other things intime, use simply HAL_SPI_TransmitReceive, but you need next code speed to less 400us.

If you need offload setup DMA circullar , but properly.

__IO global bool data_rec=FALSE   //or uint8
 
HAL_SPI_TransmitReceive_DMA(&hspi1, Configuration_Values_Tx, Configuration_Values_Rsx, 64);
 
  while (1)
  {
   
if(data_rec) {
data_rec=FALSE;    
HAL_SPI_TransmitReceive_DMA(&hspi1, Configuration_Values_Tx, Configuration_Values_Rsx, 64); 
   for (iter_i =0; iter_i < sizeof(Configuration_Values_Rsx); iter_i ++)
    {
      Configuration_Values_Tx[iter_i] = Configuration_Values_Rsx[iter_i];
    }
 
  if(data_rec) echo your code is slow
 
  }
}
 
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi)
{
datarec=TRUE;
}

Chrizzly
Associate III

Thank you guys for your answers.

I think I understand the basics of DMA and what it is used for. I think I am struggeling to understand how to use it properly so there is no conflict of accessing Data.

Unfortunatly the HAL_SPI_TransmitReceive function is to slow (see the description of my problem).

With the code below and two problems occured:

  • Data is not received properly / corrupted by my software (See attached Files Package_1 to Package_3)
  • After the 1st Package the transmission gets stuck at 64 (See attached Files 64_Problem)

Either one of the scenarios happen whenever I trigger the communication.

0693W00000GY2sbQAD.png0693W00000GY2rsQAD.png0693W00000GY2rJQAT.png0693W00000GY2rEQAT.png 

0693W00000GY2lpQAD.png0693W00000GY2laQAD.png0693W00000GY2kOQAT.png0693W00000GY2kIQAT.png0693W00000GY2kDQAT.png0693W00000GY2jyQAD.png

while (1)
  {
       HAL_SPI_TransmitReceive_DMA(&hspi1, Configuration_Values_Tx, Configuration_Values_Rsx, 64);
       
  }
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi)
{
  for (iter_i =0; iter_i < sizeof(Configuration_Values_Rsx); iter_i ++)
  {
    Configuration_Values_Tx[iter_i] = Configuration_Values_Rsx[iter_i];
  }
}

You dont read ? Your code is still same bad. You in while 1 start crazy frog init DMA transfer and totaly dont control is ready or done.

_DMA say non blocking init , this line dont wait for data and repeat... Configure circular DMA and change code to

       HAL_SPI_TransmitReceive_DMA(&hspi1, Configuration_Values_Tx, Configuration_Values_Rsx, 64);
 
while (1)
  {
       
  }

Need only once init !!!

and use my previous code without call any other HAL_SPI_TransmitReceive_DMA

For end you need call STOP.

You must not call HAL_SPI_TransmitReceive_DMA() in main() repeatedly, without checking whether the started process hasn't finished.

As MM said above, that function is a non-blocking call, just sets up DMA and exits. It takes time until DMA finishes its job and calls HAL_SPI_TxRxCpltCallback(); only after that you may call HAL_SPI_TransmitReceive_DMA() again.

JW

Chrizzly
Associate III

Thank you so much for your answers and your patience MM and JW. I really appreciate your help.

I set up SPI DMA as circular in CubeMX. SPI1_RX DMA1 Channel 2 Priority High and SPI1_TX DMA1 Channel 3 Priority Medium.

How can I get a feedback wether my code is to slow or not? Is it realistic that my code is to slow when my CPU runs at 32MHz?

The problem that data is not received properly / corrupted by my software (See attached Files Package_1 to Package_3) still exists.

bool data_rec = false;
HAL_SPI_TransmitReceive_DMA(&hspi1, Configuration_Values_Tx, Configuration_Values_Rsx, 64);
  
  while (1)
  {
    
    if(data_rec){
      data_rec=false;
      for (iter_i =0; iter_i < sizeof(Configuration_Values_Rsx); iter_i ++)
      {
        Configuration_Values_Tx[iter_i] = Configuration_Values_Rsx[iter_i];
      }
    }
 
  }
 

void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef * hspi)
{
 data_rec = true;
}

HAL_SPI_TransmitReceive_DMA(&hspi1, Configuration_Values_Tx, Configuration_Values_Rsx, 64);
  
  while (1)
  {
    
    if(data_rec){
      data_rec=false;
      for (iter_i =0; iter_i < sizeof(Configuration_Values_Rsx); iter_i ++)
      {
        Configuration_Values_Tx[iter_i] = Configuration_Values_Rsx[iter_i];
      }
    }
              if(data_rec){
                   //place here code and breakpoint to handle if is here TRUE your code is slower as need and you can speed code up or create bigger pause in sender
               }
  }

 but for your design when you send what you receive you simply swap buffers .

Set DMA to normal mode and start it with swapped buffers no copy, but i dont see what purpose your code then have.