SPI master: reliably detecting end of frame
Hi all:
I have an STM32F407 as main platform, as well as other types like STM32G0B1 and STM32F072, and I am trying to implement a robust SPI master for all of them.
I want to reliably detect the end of frame in order to toggle the Chip Select line, but it is well known that the BSY flag is unusable for that purpose:
STM32 gotchas
68. SPI_SR.BSY is unusable
http://www.efton.sk/STM32/gotcha/g68.html
The hardware NSS output, if available on the particular SPI, would fix the problem, but I have several SPI slaves, so that wouldn't work, would it? Or is there a way to check what the NSS output would be without sacrificing a pin?
The recommendation is to use RXNE instead ob BSY, even if you are only transmitting and do not care about the received data.
The idea is that, as soon as RXNE is set, the SPI frame has ended, as the last clock edge is what allows the master to finish reading the data from the slave, right?
After RXNE is set, I can disable the Chip Select straight away. I could do that even before reading the data from register SPI_DR, correct? Well, that is assuming the SPI slave does not impose some timing requirements, but I just want to understand the idea.
The trouble is, the web page referenced above states:
"however, its timing (which again is not something properly documented by ST) might not be suitable in all CPOL/CPHA combinations"
I am trying to understand which combinations may pose a problem. I thought that reading the last bit from the slave is the last operation that an SPI transfer performs, so how can RXNE possibly fail me?
Say I just sent the first byte after initialising the SPI. Can I assume that, if RXNE is set, TXE should also be set? The reasoning is: SPI has to generally "send" each bit first, and "receive" the corresponding bit afterwards. If I send just one byte, TXE should set before RXNE is set, right? Otherwise, this would be the full sequence for the first byte:
- Write to SPI_DR.
- First loop to wait for RXNE:
- Read SPI_SR once, as reading can have side effects like clearing error flags.
- At least in debug builds: check all error flags, to make sure everything is OK.
- If RXNE is not set, do another loop iteration.
- Read from SPI_DR, which will automatically clear RXNE.
- Second loop to wait for TXE:
- Read SPI_SR once, as reading can have side effects like clearing error flags.
- At least in debug builds:
- Check all error flags, to make sure everything is OK.
- Check also that RXNE is still reset.
- If TXE is not set, do another loop iteration.
- Third loop to wait for BSY, in case today this flag lags behind:
- Read SPI_SR once, as reading can have side effects like clearing error flags.
- At least in debug builds:
- Check all error flags, to make sure everything is OK.
- Check also that RXNE is still reset.
- Check also that TXE is still set.
- If BSY is set, do another loop iteration.
- Turn Chip Select off.
I could optimise the loops in this way: When the first loop detects that RXNE is set, it could also check whether TXE is set and BSY is not set, and then skip reading SPI_SR again.
But the most important question is whether I can optimise the TXE and the BSY loops away:
- If TXE is always set before RXNE, or at the same time, then the TXE loop is not necessary.
- If RXE is reliable enough, then the BSY loop is not necessary.
After these matters are clear, I am planning to discuss optimising sending and receiving more than 1 byte, which probably can go faster depending on whether the SPI has a FIFO. And how to use DMA and still reliably detect the end of the last frame.
I have searched the web and read quite a lot about SPI, BSY etc., but I still could not figured all the details out.
Thanks in advance, rdiez.
