2023-08-19 11:52 AM
Hello,
I have an STM32F373 and setup a master uC and slave uC to communicate over SPI with DMA enabled. I send a fixed packet size of 10bytes and have set the DMA size to 20bytes in case I need a double buffer for my solution. I enabled a SS/CS output pin on the master which goes active low when sending data to the slave. I enabled an EXTI input pin on the slave to detect the master setting the SS/CS pin low or high, with the interrupt enabled for rising and falling edge in case I need those for my solution.
Currently, I don't use the EXTI interrupts since I use the DMA half-transfer interrupt to detect when a full 10 byte packet has been received. For now, I plan on clearing the SPI DMA in this interrupt so the second half of the DMA buffer will never really be used unless I implement a double buffer.
I see a flaw in my design; What if the slave's SPI DMA potentially fills with a few bytes due to noise? Let's say the SPI gets 1 byte of noise, the DMA half-transfer interrupt would then be triggered at 9bytes of the packet received which is a problem. The SPI DMA may be out-of-sync for any following packet, always triggering the Half-Transfer or Transfer-Complete interrupts at 9bytes of packet received. I need to reset the DMA pointer somehow.
I was thinking of enabling the SPI when the SS pin goes active low, resetting the DMA buffer pointer, and then disabling the SPI when the SS pin goes high. This may not work since the slave may start receiving SPI data from the master before it gets a change to enable SPI in the EXTi interrupt.
I thought of adding some type of watch-dog timer to reset the SPI DMA rx pointer after 1ms or so, but realize I may be creating another problem.
Any suggestions on how to best handle this? Is there a good way to handle all error conditions and retries? ( I can add a request-select pin going from slave to master if needed )
Thank you!
Solved! Go to Solution.
2023-08-19 12:07 PM
One common way is to enable SPI when CS goes HIGH, in anticipation of the next time it goes low. In this event, you don't require time between CS going low and clocks starting, but you do require a "setup time" between CS going high and the next time it goes low.
> Is there a good way to handle all error conditions and retries?
Can't solve everything in software. You can add a CRC check, but if the lines are excessively noisy, you're going to have issues. CRC checks still have a nonzero chance of a false positive.
The SPI master needs to be aware of the limitations of the slave, if any. Resetting/resyncing on a rising CS edge and having a CRC data check solves most issues.
2023-08-19 12:07 PM
One common way is to enable SPI when CS goes HIGH, in anticipation of the next time it goes low. In this event, you don't require time between CS going low and clocks starting, but you do require a "setup time" between CS going high and the next time it goes low.
> Is there a good way to handle all error conditions and retries?
Can't solve everything in software. You can add a CRC check, but if the lines are excessively noisy, you're going to have issues. CRC checks still have a nonzero chance of a false positive.
The SPI master needs to be aware of the limitations of the slave, if any. Resetting/resyncing on a rising CS edge and having a CRC data check solves most issues.
2023-08-19 12:39 PM - edited 2023-08-19 01:48 PM
I had not thought of that, thanks!
I my case, there is no wire, it's all PCB traces so less chance for noise.
I'll also add CRC. What happens if the slave gets a bad CRC checksum? In my case, the master is sending 'events', and the slave needs to be aware of the potentially missed event and cannot 'drop' or disregard the bad packet ( if it was a packet )...
UPDATE: Now that I think about it, what happens if the SS pin goes high, and the slave enables the SPI anticipating the next packet, but the current packet is still being received? The master sends using DMA and would set the SS pin low on DMA TransmitComplete interrupt which may happen before the slave has had a change to read the SPI DMA.
UPDATE2: Ok, I think I have it figured out. I'll have the master DMA TransferComplete interrupt wait for SPI bsy to be 0 to ensure the last byte has been transmitted before I toggle the SS pin.
2023-08-19 01:49 PM
Failed CRC means the data isn't trustworthy and you should ignore it. You can implement a two-way interface for the slave to request the data again if required.
CS going high indicates the end of the transaction, not the beginning. Data can't still be flowing. If the slave detects CS high and it isn't done sending, something went wrong and you should reset the interface.
But those should be exceptions and should rarely or never happen. Just added those since you asked:
> Is there a good way to handle all error conditions
2023-08-19 02:53 PM - edited 2023-08-19 03:36 PM
Ok thanks.
The case I envision is if the master sends 10 bytes via SPI's DMA buffer, the DMA transfer-complete interrupt will trigger on the master before the master has finished sending the last byte. I'll have the master DMA transfer-complete interrupt poll the ->BSY flag for the SPI tx to ensure it has finished sending the last byte. If the master toggles the SS when the DMA transfer-complete interrupt triggers, the slave our handle the interrupt and 'enable SPI' but it may still be reading the last byte being sent by the master no? ( sorry if I"m missing something )
Also, when the SS pin is set to high by the master, the slave already has its SPI enabled as it is receiving the last byte of data and handling the DMA half-transfer interrupt. I'm thinking maybe the EXTi interrupt could run before the DMA half-transfer.
Maybe I can just disable and enable SPI when EXTI triggers in order to 'reset' the DMA pointer?
UPDATE: Ok, I think I got it. I had DMA in circular mode. I now see how re-enabling SPI when SS goes high makes sense.