cancel
Showing results for 
Search instead for 
Did you mean: 

Why SPI returns busy after a while of sending correctly?

LBaum.3
Associate II

Hello ST community,

I use a STM32F767ZI-Nucleo. There is a timer generating interrupts every 20ms. If a timer interrupt occurs I use HAL_SPI_Transmit_DMA to send some data. I monitor the return value of this function and after some time of sending without any failure the return value is HAL_BUSY (I toggle an GPIO pin if this occurs). I also use the HAL_SPI_TxCpltCallback where I toggle another GPIO to monitor when the SPI transfer is completed.

I noticed that sometimes the HAL_SPI_TxCpltCallback is not reached after a transmission. At the next trasmission I get a HAL_BUSY and also the data is corrupted.

In pic1.png you can see what I monitored with a logic analyzer.

pic2.png shows the SPI trasmission where no HAL_SPI_TxCpltCallback is reached.

pic3.png is the next trasmission after that which returns HAL_BUSY and contains the corrupted data. There you can see anohter phenomenon. The TxCpltCallback occurs during the transmission.

In pic4.png you can see how a normal or correct transmission looks like.

Is there anyone who has an idea why my SPI is sometimes busy and therefore the data is corrupted?

12 REPLIES 12
TDK
Guru

If the previous transaction is not yet complete, it will return busy if you try another one.

Looks like you're using the SPI as a slave, yes? How are you synchronizing it such that the master sends clocks every 20ms exactly? Note that clocks drift with respect to each other, which will cause issues.

If you feel a post has answered your question, please click "Accept as Solution".

See also errata, BSY bit may stay high at the end of a data transfer in Slave mode.

JW

[EDIT] Ah now I see you're talking about HAL_BUSY, that may be entirely unrelated then.

Ok, but why does it work most of the time and sometimes not? What is the difference at the few transactions where it doesn't work?

Yes you're right. The controlle is in slave mode.

For synchronization: Every 20ms a timer generates an interrupt and a GPIO pin is toggled (Channel INT_Tim3 at pic1.png). The master is triggered by the toggled GPIO pin and starts reading from the SPI bus.

Can the difting clocks cause the data to stay the same for the whole transaction?

> Ok, but why does it work most of the time and sometimes not? What is the difference at the few transactions where it doesn't work?

Most likely a bug in your program logic somewhere. Show the relevant portions of slave and master side code if you want.

My guess is a bug in the synchronization, or the master sending data before the slave is ready.

If you feel a post has answered your question, please click "Accept as Solution".

Yes, no problem.

If the following code is horrible to read there will be pictures from it. Maybe the code is easier to read at the two pictures.

Here is the code at the slave side:

The variables sentDataSize and TimSentFirstHalf are initialized with zero and only manipulated in this code section.

HALFBUFFERSIZE is 10

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
  HAL_StatusTypeDef ret=0xff;
 
  if(htim == &htim3){
    HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_2);
    if(sentDataSize){
      if(TimSentFirstHalf){
        ret = HAL_SPI_Transmit_DMA(&hspi3, &dacData[SINETABLESIZE/2], HALFBUFFERSIZE/2);
        TimSentFirstHalf = 0;
      }else{
        ret = HAL_SPI_Transmit_DMA(&hspi3, dacData, HALFBUFFERSIZE/2);
        TimSentFirstHalf = 1;
      }
      if(ret == HAL_BUSY){
        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2);
        HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_2);
      }
    }else{
      HAL_SPI_Transmit_DMA(&hspi3, bufferByteNumber, 1);	//transmit 1*16Bit
      sentDataSize = 1;
    }
  }
}

The code for the master side:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
  if(GPIO_Pin == GPIO_PIN_2){		//interrupt from GPIO PD2
    if(sentDataSize==1){
      uint8_t data[bufferByteNumber16*2];
      uint8_t dummy[bufferByteNumber16];
 
      for(uint16_t i=0; i<bufferByteNumber16; i++)
        dummy[i] = 0;
 
      if(sentFirstHalf){
        HAL_SPI_TransmitReceive_DMA(&hspi3, dummy, &data[bufferByteNumber16], bufferByteNumber16);	//bufferByteNumber/2 * 16Bit gets sent
        sentFirstHalf = 0;
      }else{
        HAL_SPI_TransmitReceive_DMA(&hspi3, dummy, data, bufferByteNumber16);	//bufferByteNumber/2 * 16Bit gets sent
        sentFirstHalf = 1;
      }
    }else if(sentDataSize==0){
      uint8_t dummy[2]={0};
      HAL_SPI_TransmitReceive_DMA(&hspi3, dummy, bufferByteNumber, 2);	//1*16Bit gets sent
      bufferByteNumber16 = bufferByteNumber[0] << 8 | bufferByteNumber[1];
      sentDataSize = 1;
    }
  }
}

Code at the slave:

Code at the master:

      uint8_t dummy[2]={0};
      HAL_SPI_TransmitReceive_DMA(&hspi3, dummy, bufferByteNumber, 2);	//1*16Bit gets sent
      bufferByteNumber16 = bufferByteNumber[0] << 8 | bufferByteNumber[1];

You're reading data back immediately, despite the transfer not being complete yet. You're treating HAL_SPI_TransmitReceive_DMA as a blocking function.

You're also using local variables for non-blocking transfers. This is not smart as the variables will likely go out of scope before the transfer completes and it will send out random data.

You don't show what bufferByteNumber is on the slave side, but presumably it might match up.

Possibly other stuff.

If you feel a post has answered your question, please click "Accept as Solution".

Ok thanks. So I should wait for the end of transmission before I use bufferByteNumber build bufferByteNumber16?

I'll change the variables to global and then try it again.

bufferByteNumber at the slave side is 10.