2020-01-29 07:04 AM
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
Solved! Go to Solution.
2020-01-29 09:03 AM
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.
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.
2020-01-29 07:53 AM
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.
2020-01-29 08:47 AM
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..
2020-01-29 09:03 AM
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.
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.
2020-01-31 12:44 AM
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.
2020-01-31 03:45 AM
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.