cancel
Showing results for 
Search instead for 
Did you mean: 

Is there any reason why I should miss received bytes when using HAL_SPI_TransmitReceive_IT()?

Mike xx
Associate III

I'm sending and trying to receive 5 bytes. If I run the peripheral at 3MHz all is well. If I run at 6MHz I miss receive bytes.

If I break and inspect hspi4.RxXferSize I can see this will be stuck at 2. The TxXferCount has counted down to 0, and so there are no more clocks being sent to collect any further received bytes. So we become stuck with hspi->State = HAL_SPI_STATE_BUSY_TX_RX

I've looked at the errata and can't find details of an issue like this. The datasheet claims 50Mb/s and obviously I'm nowhere near this.

This is reliable and repeatable. Run at 3MHz and all works, 6MHz and it doesn't. Is this a bug in the libraries or just me? I'm conscious I am a little sparse on details but I was wondering if anyone had some insight?

1 ACCEPTED SOLUTION

Accepted Solutions
Mike xx
Associate III

I forgot about the TXE interrupt and that it should fire immediately after the first write to DR.

I put a logic analyser on the SPI clock and a GPIO that goes high when entering the SPI interrupt and low when exiting.

I'm running the MCU at full smoke of 100MHz.

One thing to note that only one flag is considered at any time for 5 bytes so the interrupt is being called 10 times. Each time it is called the interrupt the GPIO is active for 1.5us. Presumably executing something close to 150 ARM instructions. If I look at the code (no optimisation) I can see possibly 60 or so ARM instructions so feel I'm a factor or 2 out if I assume 1 clock = 1 instruction.

The conclusion is that this is a 'softare' timing issue; where reads are being missed.

I feel the best way forward is to consider DMA.

Many thanks to TDK and waclawek.jan for their replies.

View solution in original post

6 REPLIES 6
TDK
Guru

SPI master?

What chip?

More likely to be a software bug than a hardware issue.

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

Its a STM32F412 and I'm using SPI4.

I fully accept this is likely to be a software bug.

One thing I have noticed is that at the higher speed when the interrupt is initially triggered only the SPI_FLAG_TXE flag is set. And only after 2 bytes are received is the SPI_FLAG_RXNE set.

I'm wondering if the writing of a byte to the DR register destroys the RXNE flag. The alternative would be to wait for the RXNE bit to be set and read the DR before writing the next byte.

Because reading DR resets the RXNE flag it is difficult to debug as having "hspi4" in the variable or expression tabs will clear this flag every time the MCU is suspended.

TDK
Guru

> I'm wondering if the writing of a byte to the DR register destroys the RXNE flag.

Writing clears the TXE flag. Reading clears the RXNE flag.

> The alternative would be to wait for the RXNE bit to be set and read the DR before writing the next byte.

If you're writing two bytes before reading any, you have a race condition where two bytes are coming in. If you don't read DR after the first byte, then it gets overwritten and it looks like only one byte is received. Sounds like this could be exactly what is happening.

Alternative is to use DMA, or polling mode. For so few bytes at such a fast speed, polling isn't a huge delay. The CPU might be blocked during the process anyway with all the interrupts happening.

> Because reading DR resets the RXNE flag it is difficult to debug as having "hspi4" in the variable or expression tabs will clear this flag every time the MCU is suspended.

In peripheral view, you can choose which registers are monitored. Not monitoring DR will prevent flags getting cleared. Although probably of limited use due to have fast things are happening on the bus.

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

When you have an empty SPI both the Tx shift register and holding register are empty (and so are both Rx shift and holding registers). So when enabling interrupts, first write goes into the Tx holding register but that is immediately transferred to the shift register, so holding register is empty again and throws the TXE interrupt immediately. First byte hasn't left the shift register by now, so there's no RXNE interrupt so far.

So, this is normal.

What you see is probably result of using the bloated Cube/HAL functions, probably combined with switched off compiler optimization, leading to Rx overrun. Ditch Cube, or resort to DMA.

JW

Mike xx
Associate III

I forgot about the TXE interrupt and that it should fire immediately after the first write to DR.

I put a logic analyser on the SPI clock and a GPIO that goes high when entering the SPI interrupt and low when exiting.

I'm running the MCU at full smoke of 100MHz.

One thing to note that only one flag is considered at any time for 5 bytes so the interrupt is being called 10 times. Each time it is called the interrupt the GPIO is active for 1.5us. Presumably executing something close to 150 ARM instructions. If I look at the code (no optimisation) I can see possibly 60 or so ARM instructions so feel I'm a factor or 2 out if I assume 1 clock = 1 instruction.

The conclusion is that this is a 'softare' timing issue; where reads are being missed.

I feel the best way forward is to consider DMA.

Many thanks to TDK and waclawek.jan for their replies.

Mike xx
Associate III

I've gone the DMA route and running at a bit rate of 12.5MHz. I'm limited by the FPGA attached and it's clock speed.

In the MX application I found the data was corrupted unless I ticked "Use FIFO". It's now running smoothly.