stm32interruptspislave

STM32 SPI slave response to master lags for several bytes


I'm trying to implement SPI slave on STM32H7 that responds with different data based on command bytes from master. Because the number of bytes that will be received by the slave is unknown I don't use DMA. I use interrupts.

Data frame is like this: (the gray bytes are send by slave = my board) Read command structure

First two bytes are command bytes that I have to decode and based on them I should send different data back as slave. Next two bytes (PEC0 and PEC1) are command CRC but I'm not checking it RN (not needed for testing purposes, will be added later).

Next bytes should be the slave response (master provides the clock signal). The number of bytes is unknown to the slave upfront as is depends on the command or until the master stops the communication. Usually 6 bytes should be sent from slave but could be as high as ca 300 bytes.

Here is the problem: In the slave SPI interrupt I just count the received bytes and when I get four (two command and two CRC bytes) I compare the received bytes to some value to verify that correct command was received (in the example below are just dummy values "ABCD"). If it is correct, then the slave will respond with bytes in TXbuffer by writing to the TXDR register.

SPI IRQ handler

Problem is that the response is delayed and instead of responding immediately (during 5th byte) the slave sends it later, sometimes as 6th, 7th or even 8th byte. Note that no other code is running on the device, main loop is just "while(1) {}".

Here is a scope screen image where it lags 3 bytes behind: Yellow is clock, green is MISO, blue is MOSI and magenta is CS (not used by the slave, you can ignore it, also please ignore the cursors, sorry for them) SPI communication The green line shows the problem, it is shifted by three bytes. It looks like that writing to the TXDR is not instant as it behaves as FIFO (I was not able to disable it). Perhaps there is simply not enough time between writing to the register and the master clock for the 5th byte so it is sent during next bytes.

I know that speed could be the issue here, master is clocking the SPI at 2 MHz but even when I simulated it with lower clock (100 kHz) the issue was still there. Note that the STM32H7 slave core is running at 300 MHz. SPI wiring is not the problem here as the communication is working, noise on the scope is due to bad ground connection because of the PCB constrains.

One solution I tried and kind of worked was turning off and on the SPI slave peripheral (bit 0 in CR1 register) after receiving four bytes and then immediately write data to the TXDR and this worked - but only during low clock frequency (100 kHz) and with pauses between each bytes with simulated master. Without enough time for enabling it simply lost synchronization and started to listen randomly in the middle of some byte which lead to received garbage data.

I even tried using the DMA instead of the interrupts but the delay was still present or even worse at it takes more time to setup the DMA.

After looking on forums all I got was that you should prepare the data in the slave before the communication takes place but this is simply not possible here as the slave here has to react to the received data and I cannot change the protocol.

So, finally my question. Is it possible to provide faster (immediate) response, so the slave responses correctly during 5th byte, no later? Is there some way to disable the SPI TXDR FIFO (hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA; is not working) or to bypass it? Or can you think of some different solution (e.g. use the DMA somehow)?

Sorry for a long post, thanks for any idea!


Solution

  • So, I figured it out. Basically you cannot let the TX FIFO (writing done via TXDR) run out of data, otherwise it will stop transmitting new data when refilled and you have to disable-enable it again. Reason for this is unknown.

    What I did was loading four data bytes (with value = 0) to the TX FIFO prior to the communication because the first four command + CRC bytes will always be present from master. (Transmitting zeros is as nothing is transmitted...). After reception of the first two bytes (command) the command is processed and the requested data are immediately loaded to the TX FIFO by writing to the TXDR. This is done while receiving the command CRC - this gives us theoretical time of 8 us at 2 MHz SPI clock for parsing the command and preparation of the requested data bytes. After reception of the command CRC, it is checked and if it matches, then loading of another bytes to the TXDR continues. If the CRC is wrong, the SPI peripheral is disabled so no data are being sent to the master and the system is waiting for the next falling edge on the SS pin to enable the SPI again.

    This works but to be honest it is quite hard to process the command in that quite a short time. I tried to place as much code as possible to the RAM but it only helped a little - but helped. Conclusion is that for time-constrained stuff like this it is probably better to use FPGA or multi-core processors.