cancel
Showing results for 
Search instead for 
Did you mean: 

Overrun flag when using SPI in interrupt mode (HAL_SPI_Receive_IT())

ikrosoft
Associate II

Hello,

I'm reading data from an external device using SPI in Receive Only Master mode. For this, I'm using the HAL function HAL_SPI_Receive_IT(), which as its name suggests doesn't use DMA, but is non-blocking and calls HAL_SPI_RxCpltCallback() when it's done and my buffer holds the number of bytes I asked for.

Everything runs fine at 10Mbits/s, but when I increase the speed to 12Mbits/s (the external device supports up to 20Mbits/s SPI, and my STM32H750 runs at 400MHz SYSCLK), I start getting overruns (HAL_SPI_ErrorCallback() is called and I see that the OVR flag in the SPI SR is set). I'm always waiting until HAL_SPI_RxCpltCallback() or HAL_SPI_ErrorCallback() get called before starting a new transfer, and hspi1.State is HAL_SPI_STATE_READY when I start it. I don't understand what could cause an overrun.

From the STM32H750 reference manual:

"An overrun condition occurs when data are received by a master or slave and the RxFIFO

has not enough space to store these received data. This can happen if the software or the

DMA did not have enough time to read the previously received data (stored in the RxFIFO)."

As mentioned I'm not using DMA. And the buffer I give to HAL_SPI_Receive_IT() is allocated big enough to hold the data. What can cause this overrun?

Thank you for your help

1 ACCEPTED SOLUTION

Accepted Solutions
berendi
Principal

Let's do some math first. You are receiving at 12 MBits/s, that's 1.5 MBytes/sec. No idea about the data width, assuming 8 bit data it's 1.5 million interrupts/second. At 400 MHz core speed it means that the interrupt handler has 266 cycles to complete its task, or an overrun will occur. That's not a lot, but there are a few things that you can do about it.

  • Don't use HAL, it's too slow. (I'm surprised that it managed to handle 10 Mbits.) Look at the source HAL_SPI_IRQHandler() function. Even if receiving is handled relatively early, it still checks half a dozen variables and registers to decide what to do before finally reading the data register. Reimplement the whole mess from scratch, hard-coding data width and other parameters. Do not ever check for anything that is known at development time (think about it every time before writing an if or switch). For starters,
void SPI1_IRQHandler(void) {
  uint32_t sr;
  uint8_t data;
  sr = SPI1->SR;
  if(sr & SPI_SR_RXP) {
    data = *(volatile uint8_t *)&SPI1->RXDR;
    // put data into a buffer or process it otherwise
  }
  if(sr & SPI_SR_TXP) {
    // generate data to be sent, or read it from the outgoing buffer
    *(volatile uint8_t *)&SPI1->RXDR = data;
  }
  // handle error conditions
}

Don't forget to declare as volatile everything that is used to communicate between the main program and the interrupt handler.

  • Depending on how you are processing the received data (if you don't have to act on every byte immediately), it might make sense to receive 16 or 32 bits at once (check SPI implementation and functional description in the reference manual), either by setting DSIZE or enabling the FIFO.
  • When receiving lots of data to be processed later, switch to DMA transfer.

View solution in original post

5 REPLIES 5
RMcCa
Senior II

Not using dma could cause this.

Try toggling a pin to check the isr timing.

Ditching hal and writing your own isr can help as well.​

KnarfB
Principal III

Overrun is a timing issue, not a space issue.

Keep in mind that an interrupt is processed (by HAL) after each byte received, even if the callback is called only once at the end of the buffer. You could check+adjust your interrupt priorities before switching to DMA, ditching HAL etc..

berendi
Principal

Let's do some math first. You are receiving at 12 MBits/s, that's 1.5 MBytes/sec. No idea about the data width, assuming 8 bit data it's 1.5 million interrupts/second. At 400 MHz core speed it means that the interrupt handler has 266 cycles to complete its task, or an overrun will occur. That's not a lot, but there are a few things that you can do about it.

  • Don't use HAL, it's too slow. (I'm surprised that it managed to handle 10 Mbits.) Look at the source HAL_SPI_IRQHandler() function. Even if receiving is handled relatively early, it still checks half a dozen variables and registers to decide what to do before finally reading the data register. Reimplement the whole mess from scratch, hard-coding data width and other parameters. Do not ever check for anything that is known at development time (think about it every time before writing an if or switch). For starters,
void SPI1_IRQHandler(void) {
  uint32_t sr;
  uint8_t data;
  sr = SPI1->SR;
  if(sr & SPI_SR_RXP) {
    data = *(volatile uint8_t *)&SPI1->RXDR;
    // put data into a buffer or process it otherwise
  }
  if(sr & SPI_SR_TXP) {
    // generate data to be sent, or read it from the outgoing buffer
    *(volatile uint8_t *)&SPI1->RXDR = data;
  }
  // handle error conditions
}

Don't forget to declare as volatile everything that is used to communicate between the main program and the interrupt handler.

  • Depending on how you are processing the received data (if you don't have to act on every byte immediately), it might make sense to receive 16 or 32 bits at once (check SPI implementation and functional description in the reference manual), either by setting DSIZE or enabling the FIFO.
  • When receiving lots of data to be processed later, switch to DMA transfer.
ikrosoft
Associate II

Thank you for your answers.

I didn't realize HAL was processing so many interrupts, but taking a closer look at the code it now makes sense. I'll stick with 10Mbits/s for now (which probably works because my data width is 16 bits, so that's still 2x 266 cycles per interrupt), but I'll definitely ditch HAL and write something along the lines of @berendi​ 's suggestion when I find the time.

Note that if it can't keep up with the data at 12 Mbits, then it is using more than 83.3% (10/12) of the available processing power​ at 10 Mbits.

Perhaps not all the time, there might be another interrupt with the same or higher​ priority that blocks the SPI handler from time to time.