SSP/SPI communication

12 Jan 2015

Hi all,

I want to accomplish the following task. I want to make two mcu's talk together through the SSP/SPI protocol ideally with interrupts.

Can someone help me with the pseudocode for the master and the slave?

I get totally confused when having to program two mcu's especially when factoring in also interrupts and I am hoping you could help me break it down into smaller steps.

Is there an easy way to think about implementing interrupts? E.g. I have heard someone say that thinking about it as a state machine is easier but I couldn't understand how that could help me.

Cheers everyone

12 Jan 2015

Here's a basic framework. It adds a start marker, end marker and length to each packet to help keep things in sync. This does assume that the message size will never be over 255 so we can use a single byte for length.

Data that comes in is handled by an interrupt and put into a temporary buffer. Once the whole message is received it is copied into a new buffer for the main background loop to process. This frees up the temporary buffer so that a new message can start to arrive while the first one is getting processed. All the main loop needs to do is check if there is a message waiting, it will only ever get complete messages+.

The main loop must flag that it has finished processing the message before the next one has finished arriving or the message will be lost and the overflow indicator set.

Message transmission is done by calling the transmit function with a char array and length. This automatically adds start, end and length information to the message when sending it.

+There is a chance of a garbled message getting through, adding a checksum would reduce this significantly.

// max size of a message we could want to send/receive must be under 254 since we only use 1 byte for length.
#define bufferSize 64

// messages should start and end with these as a sanity check that they are valid.
#define messageStartMarker 'S'
#define messageEndMarker 'E'

// serial port to use
serial dataconnection(tx,rx);

// variables to hold incoming messages that are ready for the main loop to look at.
char dataReadyBuffer[bufferSize]; // data buffer
volatile int dataReadySize = 0;       // size of data (-1 for an error in Rx), set to 0 to indicate buffer is free.
volatile bool overflow = false;          // data overflow indicator.

// data rx interrupt
void ondataRx(void) {

  static char dataInBuffer[bufferSize]; // buffer to put data into as it arrives

  static enum eRxState {waitingStart, waitingLen, inData, waitingEnd} rxState = waitingStart;  // current rx state
  static int expectedBytes = -1;  // expected message length
  static int dataCount = 0;           // current message length

  char byteIn; 

  while (dataconnection.readable()) {  // while there is data in the port
    byteIn = dataconnection.getc();      // read data

    switch (rxState) {                               // process data depending on the current state
      default:
      case waitingStart:
        if (byteIn  == messageStartMarker)
          rxState = waitingLen;
        break;
      case waitingLen:
        expectedBytes  = byteIn;
        rxState = inData;
        dataCount = 0;
        break;
      case byteIn:
        dataInBuffer[dataCount] = byteIn;
        dataCount++;
        if (dataCount == expectedBytes)  // got all the data
          rxState = waitingEnd
        break;
      case waitingEnd:
        if (byteIn == messageEndMarker) {
         if (dataReadySize == 0) {                   // main loops buffer is available
           memcpy(dataReadyBuffer,dataInBuffer,dataCount);  // copy the rx data to the main loops buffer.
           dataReadySize  = dataCount;                                         // indicate that the buffer has data in.
         } else                                                    // buffer isn't available
           overflow = true;
        } else {          // didn't get the expected message end marker
          dataReadySize  = -1;
        }
        rxState = waitingStart;
        break;
     } // end switch
  } //end while
}


// send a message. Automatically adds the start, length and end markers.
void sendMessage(char *data, int len) {
  dataconnection.putc(messageStartMarker);
  dataconnection.putc(len);
  for (int i = 0; i<len; i++) {
    dataconnection.putc(*(data+i));
  }
  dataconnection.putc(messageEndMarker);
}


main() {

  serial.attach(ondataRx); // attach rx interrupt.

  while (true) {

    if (dataCount != 0) {      // will be changed when there is data ready
      if (dataCount < 0) {     // -1 indicates an error (didn't get the end marker at the expected place)

    // rx error. handle (or ignore) 

      } else {                        // got a valid packet

    // dataReadyBuffer contains a valid message of size dataReadySize
    // process it in whatever way you see fit.
   
     }

     dataCount = 0; // indicate that we are done with the data and it can be over written.
     if (overflow) {
       // indication that a second message arrived and was lost while we were processing that one.
         overflow = false;
     }
  }
}