passing an array between 2 arduinos using SPI

Hi

Im currently trying to pass an 3x3 array of values from the master arduino and recreating the array on the slave using SPI. for the most part my code is working (thanks to Nick Gammons great web page on the subject) however the results are not as expected as the array on my slave when printed to serial is giving me the values every 100ms:
9 1 2
3 4 5
6 7 8

where as on my master i am sending the following to the slave every 100ms
1 2 3
4 5 6
7 8 9

so my array has shifted 1 place. this has me baffled as to where i have gone wrong.
if ayone can assist would be much appreciated.
Thanks

master

//Arduino SPI Master
//com 5

#include <SPI.h>
byte rectArray [3][3] = { {1,2,3},
                          {4,5,6},
                          {7,8,9}}; // [number of rectangles] [number of variables (x, y, h, w)] 


void setup (void)
{
  Serial.begin (9600);
  Serial.println ("Master");
  
  digitalWrite(SS, HIGH);  // ensure SS stays high for now

  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();

  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  
}  // end of setup
void loop (void)
{
  byte b;

  // enable Slave Select
  digitalWrite(SS, LOW); 
  delayMicroseconds (200);
  SPI.transfer (0xCD); //are you there
  delayMicroseconds(20); //give the slave time to process
  byte x =SPI.transfer (0); //get response
  delayMicroseconds(20); //give the slave time to process
  //Serial.println(x);
  if (x == 0xEF){
    for ( byte i = 0 ; i < 3 ; i++){
      for (byte j = 0 ; j < 3 ; j++){
        b =(rectArray[i][j]);
        SPI.transfer (b);
        delayMicroseconds(20); //give the slave time to process
        //Serial.print(rectArray[i][j]);
      }
      //Serial.println("");
    }
  }
 digitalWrite(SS, HIGH); // disable Slave Select
 delay(100);

}  // end of loop

slave

//arduino SPI Slave
//com 11

byte i = 0;
byte j = 0;
byte rectArray [3][3];

void setup (void)
{
  Serial.begin (9600);
  // 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);


}  // end of setup

ISR (SPI_STC_vect)
{
  byte c = SPDR;
  //Serial.println(c);
 // SPDR = 0;
  if (c == 0xCD){SPDR = 0xEF;}
  else{
  if (j == 3){
    j=0;
    ++i;
    }
  if (i == 3){i=0;}
  rectArray[i][j]=c;
  ++j;
  }
}  // end of interrupt service routine (ISR) SPI_STC_vect

void loop (void)
{
 if (digitalRead(SS)==LOW){
   for ( byte k = 0 ; k < 3 ; k++){
        for (byte m = 0 ; m < 3 ; m++){
          Serial.print(rectArray[k][m]);
      }
    Serial.println("");
    //Serial.print(rectArray[0][1]);
    //Serial.println("");    
    }
    i=0;
    j=0;
  }  
}

not sure if this is going to fixe your problem but I hope that works for you. This I how I would have coded the slave ISR:

ISR (SPI_STC_vect)
{
  byte c = SPDR;
  if (c == 0xCD) {
    SPDR = 0xEF;
    i = 0;
    j = 0;
  }
  else if (i < 3 && j < 3) {
    rectArray[i][j] = c;
    ++i;
    ++j;
    SPDR = 0xFF;
  }
}  // end of interrupt service routine (ISR) SPI_STC_vect

Hope that helps…

Hi Sherzaad
thanks for the reply. unfortuanatly it hasent fixed the issue as the else if statment does not step thru the 9 positions of the array. so it returns :
000
010
002

Roscop:
Hi Sherzaad
thanks for the reply. unfortuanatly it hasent fixed the issue as the else if statment does not step thru the 9 positions of the array. so it returns :
000
010
002

Apologies… looking at the code I posted I now realise my mistakes. What I should have posted is something like this:

ISR (SPI_STC_vect)
{
  byte c = SPDR;
  if (c == 0xCD) {
    SPDR = 0xEF;
    i = 0;
    j = 0;
  }
  else if (i < 3) {
    rectArray[i][j] = c;
    ++j;
    if(j==3){
      j=0;
      ++i;
    }
    SPDR = 0xFF;
  }
}

1. The following diagram (Fig-1) indicates that the SPI Protocol is a circulating buffer system; where, the current content of SPDR Register of Master will enter into SPDR Register of Slave; at the same time, the current content of SPDR Register of Slave will enter into SPDR Register of Slave. The data byte (or be replaced value by slave) that has gone now to Slave would be received by Master in the next transfer cycle. This means that there is an one byte offset feature in SPI Protocol.
spi328x.png
Figure-1:

2. In order to to take care of the one byte offset feature, we need to read one byte more (10 read operations instead of 9 for 3x3 array) and then re-structure the received data into 3x3 array (or whatever it is) ignoring the 1st item of the array and taking into account the last 9 items of the array (see Fig-3).

(1) Master Code (yours one)

//Arduino SPI Master
//com 5

#include <SPI.h>
byte rectArray [3][3] = 
{ {1, 2, 3},
  {4, 5, 6},
  {7, 8, 9}
}; // [number of rectangles] [number of variables (x, y, h, w)]


void setup (void)
{
  Serial.begin (9600);
  Serial.println ("Master");

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

  // Put SCK, MOSI, SS pins into output mode
  // also put SCK, MOSI into LOW state, and SS into HIGH state.
  // Then put SPI hardware into Master mode and turn SPI on
  SPI.begin ();

  // Slow down the master a bit
  SPI.setClockDivider(SPI_CLOCK_DIV8);

}  // end of setup
void loop (void)
{
  byte b;

  // enable Slave Select
  digitalWrite(SS, LOW);
  delayMicroseconds (200);
  byte z = SPI.transfer (0xCD); //are you there
  delayMicroseconds(20); //give the slave time to process
  byte x = SPI.transfer (0); //0x00 is pumped to get response byte
  delayMicroseconds(20); //give the slave time to process
  //Serial.println(x);
  if (x == 0xEF)
  {
    for ( byte i = 0 ; i < 3 ; i++)
    {
      for (byte j = 0 ; j < 3 ; j++)
      {
        b = (rectArray[i][j]);
        byte y = SPI.transfer (b);
        delayMicroseconds(20); //give the slave time to process
       Serial.print(rectArray[i][j]);
      // while(1);
      }
      //Serial.println("");
    }

  }
  digitalWrite(SS, HIGH); // disable Slave Select
  delay(2000);
  Serial.println();
  Serial.println("=================");

}  // end of loop

(2) Slave Codes (my version)

#include<SPI.h>  //to get the meaning of MISO
volatile bool flag1 = false;
volatile bool flag2 = false;
volatile byte c;

byte i = 0;
volatile byte j = 0;
byte rectArray[10];// [3][3];

void setup (void)
{
  Serial.begin (9600);
  // 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);
}  // end of setup

void loop (void)
{
  if (flag2 == true)//digitalRead(SS) == HIGH)
  {
    for (int k = 0; k < 10; k++)
    {
      Serial.print(rectArray[k]);
    }
    Serial.println();
    Serial.println("================");
    flag1 = false;
    flag2 = false;
    j = 0;
  }
}


ISR (SPI_STC_vect)
{
  c = SPDR;
  if (flag1 == false)
  {
    if (c == 0xCD)
    {
      SPDR = 0xEF;
      flag1 = true;
    }
  }
  else
  {
    rectArray[j] = c;
    j++;
    if (j == 10)
    {
      flag2 = true;
    }
  }
}

(3) Master Monitor
spi1MSM.png
Figure-2:

(4) Slave Monitor
spi1Ssm.png
Figure-3:

3. Note
(1) From where the zero (0) comes as the first data byte of the receiver? It has come from the argument of this instruction of your Master: byte x = SPI.transfer (0); which has been used to receive the response byte 0xEF from the slave against 0xCD.

(2) You change the instruction to this: byte x = SPI.transfer (4);, you will get 4 as the first byte of the receiver.

Figure-4:

(3) This is not a problem; it is a feature of the SPI protocol and we must keep in mind of this feature while receiving/storing/processing received data bytes.

spi328x.png

spi1MSM.png

spi1Ssm.png

smSl.png

Wow. thank you GolamMastafa for your very detailed explaination. i have modified your solution of using a 1D array and converted to a 2D array (in the loop) and it all works as expected.

#include<SPI.h>  //to get the meaning of MISO
volatile bool flag1 = false;
volatile bool flag2 = false;
volatile byte c;

byte i = 0;
volatile byte j = 0;
byte holdingArray[10];// 
byte rectArray [3][3];

void setup (void)
{
  Serial.begin (9600);
  // 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);
}  // end of setup

void loop (void)
{
  byte n = 1;
  if (flag2 == true)//digitalRead(SS) == HIGH)
  {
    for (int k = 0; k < 10; k++)
    {
      Serial.print(holdingArray[k]);
    }
    Serial.println();
    Serial.println("================");
     for ( byte l = 0 ; l < 3 ; l++){
      for (byte m = 0 ; m < 3 ; m++){
        rectArray[l][m] = holdingArray[n];
        Serial.print(rectArray[l][m]);
        n++;
      }
      Serial.println("");
     }
    Serial.println("================");
    flag1 = false;
    flag2 = false;
    j = 0;
  }
}


ISR (SPI_STC_vect)
{
  c = SPDR;
  if (flag1 == false)
  {
    if (c == 0xCD)
    {
      SPDR = 0xEF;
      flag1 = true;
    }
  }
  else
  {
    holdingArray[j] = c;
    j++;
    if (j == 10)
    {
      flag2 = true;
    }
  }
}

i also tried doing it directly in the ISR while trying to “disregard” the value from “byte x = SPI.transfer (0)” while using a portion of Sherzaad code to tidy up my own. and this also works as expected.

//arduino SPI Slave
//com 11
#include <SPI.h>

volatile bool flag1 = false;
volatile bool flag2 = false;
volatile bool flag3 = false;
volatile byte c;


byte i = 0;
byte j = 0;
byte rectArray [3][3];

void setup (void)
{
  Serial.begin (9600);
  // 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);
}  // end of setup

ISR (SPI_STC_vect)
{
  c = SPDR;
  if (flag1 == false && flag3 ==false)
  {
    if (c == 0xCD)
    {
      SPDR = 0xEF;
      flag1 = true;
     }
  }
  else if (flag1 == true && flag3 == false)
  {
    flag3 = true;
  }
  else if (i < 3) {
    rectArray[i][j] = c;
    ++j;
    if(j==3){
      j=0;
      ++i;
    }
    if (i==3){flag2 = true;}
  }
} // end of interrupt service routine (ISR) SPI_STC_vect

void loop (void)
{
 if (flag2 == true){
   for ( byte k = 0 ; k < 3 ; k++){
        for (byte m = 0 ; m < 3 ; m++){
          Serial.print(rectArray[k][m]);
      }
    Serial.println("");
    }
    i=0;
    j=0;
    flag1 = false;
    flag2 = false;
    flag3 = false;
    
  }  // end of flag set
}

Big thankyou to the both of you for your assistance. this is truly a great forum

Roscop:
i also tried doing it directly in the ISR while trying to "disregard" the value from "byte x = SPI.transfer (0)" while using a portion of Sherzaad code to tidy up my own. and this also works as expected.

It is a pleasure to know that the solution has come as an assistance to you. However, I would suggest like many others that you spend minimum time in the ISR as there are many interrupt driven events that happen in the background of the Arduino IDE like millis() function to mention. Therefore, I am at the side of building the 2D array in the loop() function.

You have done a good job, which also has created jobs for others. (+Karma)