PIC SPI sending stale data instead of what I want to send - Page 1
Forum section: electronics / general

author: perdrix
author_rank: Frequent Contributor
timestamp: January 15, 2022, 10:21:15 am
content: This is a dsPIC33FJ12MC201 (should that be relevant). I'm running a test that returns amongst other things the position of a servo motor which is controlled by the PIC running as an SPI slave.The SPI data (MISO) sent in response to the first of a sequence of 0xEBEB (Request_PID_Tune) messages should(for this test) always have the value 0x0046 - like this LA trace fragment:Code:Time ,Packet ID,MOSI,MISO4.836107562500000,0,0xEBEB,0x3004 <== response to previous message from master4.836192937500000,0,0xEBEB,0x0046 <== response to 1st 0xEBEB4.836286250000000,0,0xEBEB,0x9DEE4.836379625000000,0,0xEBEB,0x02C94.836475687500000,0,0xEBEB,0x01C74.836569000000000,0,0xFFFF,0x00A5Sometimes however I get this:Code:Time ,Packet ID,MOSI,MISO4.839946000000000,0,0xEBEB,0x11EE4.840031312500000,0,0xEBEB,0x11EE <== WHAT ON EARTH?4.840127375000000,0,0xEBEB,0x9EAD4.840220750000000,0,0xEBEB,0x02B04.840314062500000,0,0xEBEB,0x01D34.840407437500000,0,0xFFFF,0x00A3My code is simple enough:Code://==============================================================================// SPI1 Interrupt data has been received.//==============================================================================void _ISR _SPI1Interrupt(void){uint16_t reply = 0; // Reply:static uint8_t pidState = 0;:SPI_Received = SPI1BUF; // Read SPI input into buffer:if (Request_PID_Tune == SPI_Received){switch(pidState){case (0): // First PID tune msg receivedpollPosCount = posCount; // Save position counterreply = (pollPosCount & 0xFFFF0000) >> 16;++pidState;break;case (1):reply = pollPosCount & 0x0000FFFF;++pidState;break;case (2):reply = PWM_Output;++pidState;break;case (3):reply = currentSpeed;++pidState;break;case (4):reply = speedPID;blockLoop = false;pidState = 0;break;}::}//// Wait for the transmit buffer to be available//while(_SPITBF);SPI1BUF = reply;IFS0bits.SPI1IF = 0; // Clear the interrupt flag}What's wrong ?Thanks

author: NorthGuy
author_rank: Super Contributor
timestamp: January 15, 2022, 09:44:30 pm
content: There's a race condition between two events- You send your reply back- The master initates the next SPI transferIn the first case, the master gets your reply. In the second case, the master gets whatever was transmitted the last time - this is sometimes called "buffer underrun".You need to re-think your communication protocol.

author: perdrix
author_rank: Frequent Contributor
timestamp: January 16, 2022, 03:56:16 am
content: A bit more detail would help a lot. I don't have the code for the master, but AFAIK it only sends the second 0XEBEB when it has read the data from the slave.Questions:Is there anything the slave can do resolve this issue or does this need to be fixed at the master?Would this show up at the slave as a "receive overflow" (SPI1STATbits.SPIROV = 1)?What should a well mannered slave implementation do it if detects receive overflow?Are you suggesting a short pause between each 0xEBEB poll? Or,Should the master stop the SPI clock between frames?David

author: NorthGuy
author_rank: Super Contributor
timestamp: January 16, 2022, 07:39:18 pm
content: Quote from: perdrix on January 16, 2022, 03:56:16 amThere are two wires - MISO (master input / slave output) and MOSI (master output / slave input) which produce signals simultaneously and synchronous to the clock produced by the master. The master produces the clock at will. By the time the master starts to produce clocks you should have your data written to the BUF register, then the master will receive it.Quote from: perdrix on January 16, 2022, 03:56:16 amThe master usually makes pauses for the slave to respond. Read the master's datasheet. It should explain the protocol. Typically, master sends some sort of command in one transaction then initiates the next transaction to send data to the slave or to receive the response from the slave. When the master expects data, it may produce delay and/or extra bytes to give the slave more time.Quote from: perdrix on January 16, 2022, 03:56:16 amYou'll get if you don't read. There might be a small FIFO buffer in the module, so you may not get it right away.If you don't want to read, on some modules you can disable reading, others will allow you not to read and will continue to work even after the buffer overflow. However, some of the modules will hang if you don't read. Read the datasheet for your particular SPI module to find out.Quote from: perdrix on January 16, 2022, 03:56:16 amWell written slave writes/reads everything in time. Usually it is easy to do as SPI rates are typically slow.Quote from: perdrix on January 16, 2022, 03:56:16 amThere's no poll. Master decides when to transmit (unless there's some sort of external arrangement where the slave can request the transmission). The slave must adapt to whatever master is doing. This should be described in the datasheet. Transactions are not always bi-directional, might be read only or write only. In such one-sided transactions, one of the parties simply discards whatever was read. Thus, if the transaction is transmit-only for the slave, you must read and discard the BUF to remain in sync with the process.Quote from: perdrix on January 16, 2022, 03:56:16 amMost of the slow ones will. With faster transmissions clock is often continuous. Usually there's a SS (Slave Select) line also called SE (Slave Enable). The slave must monitor the line and discard all the signalling on MOSI and tri-state MISO while the SS line is high. This lets the master talk to multiple slaves.

author: perdrix
author_rank: Frequent Contributor
timestamp: January 17, 2022, 05:51:10 pm
content: 0XEBEB poll - my terminology for the sequence of five 0xEBEB messages followed by 0xFFFF - no intention on my part to suggest that SPI is a polling protocol.a 10uS pause between sending each of the 0xEBEB messages was added at the master, problem now fixed.Thanks for identifying what caused the issueDavid