SPI communication between 2 Uno's

Hi,

I have written the codes below to use SPI between 2 arduino UNOs as a first trial based on Nick Gammon’s tutorial.

I send commands ‘i’ and ‘o’ to the master over serial. Slave receives commands over SPI. ‘i’ turns ON and LED. ‘o’ turns the LED OFF.

I’ve set up this protocol for the SPI communitcation:
SPI MASTER_SLAVE Communication Setup.PNG

While the code I’ve written does work, it really bothers me that it seems that you need to have that “delayMicroseconds (20)” inbetween transfers else things just don’t work properly and stop responding!

the SPDR register should be already updated by the time SPI.transfer is complete. so why do we need to wait 20us before returning the value?

And to make things more fun, if I use Serial.print to try and debug, it work fine! :o

Any ideas why we need that delay, anyone please?

MASTER:

#include <SPI.h>

volatile char req[9];
volatile uint8_t a, i = 0;

byte transferAndWait (byte what)
{
  byte x = SPI.transfer (what);
  delayMicroseconds (20); //wait for slave to process and reply
  return x;
}

void setup (void)
{
  Serial.begin (9600);

  digitalWrite(SS, HIGH);  // ensure SS stays high for now

  SPI.begin ();

  SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));

  Serial.println ("READY");
}

void loop (void)
{
  uint8_t j;

  if (Serial.available()) {
    req[i] = Serial.read();
    ++i;
  }


  if (req[i - 1] == ';') {
    if (req[0] == 'o') { //turn LED OFF
      // enable Slave Select
      digitalWrite(SS, LOW);

      a = transferAndWait(0); //start SPI COMMS
      //Serial.println (String(a,HEX));
      a = transferAndWait('o'); //send command
      //Serial.println (String(a,HEX));
      a = transferAndWait(0); //end SPI COMMS

      if (a == 'O') {
        Serial.print("LED OFF!");
        Serial.println (char(a));
      }
      else {
        Serial.print("Failed! ");
        Serial.println (String(a, HEX));
      }

      // disable Slave Select
      digitalWrite(SS, HIGH);
    }
    else if (req[0] == 'i') { //turn LED ON
      // enable Slave Select
      digitalWrite(SS, LOW);

      a = transferAndWait(0); //start SPI COMMS
      //Serial.println (String(a,HEX));
      a = transferAndWait('i'); //send command
      //Serial.println (String(a,HEX));
      a = transferAndWait(0); //end SPI COMMS

      if (a == 'I') {
        Serial.print("LED ON!");
        Serial.println (char(a));
      }
      else {
        Serial.print("Failed! ");
        Serial.println (String(a, HEX));
      }

      // disable Slave Select
      digitalWrite(SS, HIGH);
    }

    i = 0;
  }

}

SLAVE:

#define SPI_TIMEOUT 25

#include <SPI.h>

volatile uint8_t start = 0, command = 0;

// SPI interrupt routine
ISR (SPI_STC_vect)
{
  byte spi = SPDR;

  if (spi == 0 && start == 0) { //start of SPI comms
    if (command != 0xA3) {
      start = 1;
      SPDR = 0xA4; //acknowledge start of comms
    }
    else {
      command = 0;
      SPDR = 0;
    }
  }
  else if (start > 0) {

    if (start == 1) {
      start = 2;
      command = spi;
    }

    if (command == 'o') {
      digitalWrite(2, LOW);
      command = 0xA3; //end of command
      start = 0; //end of comms
      SPDR = 'O';
    }
    else if (command == 'i') {
      digitalWrite(2, HIGH);
      command = 0xA3; //end of command
      start = 0; //end of comms
      SPDR = 'I';
    }
    else { //reset spi comms
      start = 0;
      command = 0;
    }
  }

  timeout = micros();
}

void setup (void)
{

  pinMode(2, OUTPUT); //LED output
  digitalWrite(2, LOW);

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);

  // turn on SPI in slave mode
  SPCR |= _BV(SPE);

  // turn on interrupts
  SPCR |= _BV(SPIE);

  SPDR = 0; //initialise SPI register

}  // end of setup

void loop (void)
{
  //re-initialise SPI variables on timeout
  if (SPDR != 0 && (micros() - timeout) > SPI_TIMEOUT) {
    start = 0;
    command = 0;
    SPDR = 0;
  }
}