Wrong bytes being sent through SPI

Hi, this is my first post on this forum. Im not sure how to add code to the post other than copy/paste.

I am trying to have a slave device send 3 bytes of data to a master device (both Arduinos).

The first byte of data in the packet always comes out wrong and sometimes the first byte of the second packet as well. I'm not sure why, but i think the Serial monitor may be the issue?

Below is my Master code, slave code and the Master output on the Serial monitor.

#include <SPI.h>
/************************************************MASTER TEST FILE: USED TO FIGURE OUT HOW TO SEND 3 BYTES TO THE MASTER FROM THE SLAVE******************************/
int del= 15;
byte b[3];
unsigned long message = 0;

void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV128);
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
pinMode(MISO, INPUT);
pinMode(SS, OUTPUT);
digitalWrite(SS, HIGH);  // ensure SS stays high for now

}
 
void loop() 
{
// put your main code here, to run repeatedly:
digitalWrite(SS, LOW);  //TURN SPI TRANSFER ON

b[2] = SPI.transfer(0x50);    //0x50
    delay(del);   
Serial.println(b[2], HEX);

b[1] = SPI.transfer(0x02);    //0x00
    delay(del);
Serial.println(b[1], HEX);

b[0] = SPI.transfer(0x03);    //0x00
    delay(del);      
Serial.println(b[0], HEX);   
                                   
digitalWrite(SS, HIGH); //TURN SPI TRANSFER OFF
}

/SLAVE CODE*********/

int messageCounter = 0;
volatile byte command = 0;

String display[9] = {"0x000000", "0x400000", "0x100000", "0x002000", "0x000100", "0x000080", "0x000040", "0x000008", "0x000004"};
long int myMessages[9] = {0x123456, 0x020000, 0x030000, 0x042000, 0x050100, 0x060080, 0x070040, 0x080008, 0x090004};  //TEST MESSAGES

//long int myMessages[9] = {0x000000, 0x400000, 0x100000, 0x002000, 0x000100, 0x000080, 0x000040, 0x000008, 0x000004};  //REAL MESSAGES

byte b[3];
/**********************************************************Slave Receive Function***************************************************/
byte SPI_SlaveReceive(void)
{
  /* Wait for reception complete */
  while(!(SPSR & (1<<SPIF)))
  ;
  /* Return Data Register */
  return SPDR;
}

void setup() 
{
  pinMode(MISO, OUTPUT);
  pinMode(SS, INPUT);
                                                                  
  SPCR |= _BV(SPE); //turn on SPI in slave mode
  //SPCR |= _BV(SPIE);
}

void loop() 
{
  b[2] = (myMessages[messageCounter] >> 16);  //MSB
  b[1] = (myMessages[messageCounter] >> 8);
  b[0] = (myMessages[messageCounter]);        //LSB

  //spi sending happens here
  if (digitalRead (SS) == LOW)
  {
    command = SPI_SlaveReceive(); 
    if (command == 0x50)
    {
      for (int i=2; i>=0; i--)
      {
        SPDR = b[i];
        while(!(SPSR & (1<<SPIF)));
      }
    }
    
    else
    {
      //do nothing
    }
  }
  else
  {
    // do nothing
  }
}

AVR controllers are almost unusable as SPI slaves due to hardware problems. One of the problems is: the first byte has to be placed into the SPDR before a transaction. So don't expect valid data on transmission of the command byte, only with the next 3 transmissions.

And use SS to prepare the slave for receipt of the next command and to abort any pending transmissions.

Try it this way where the three received bytes are all on the same output line to get a better feel for what is happening:

void loop()
{
  // put your main code here, to run repeatedly:
  digitalWrite(SS, LOW);  //TURN SPI TRANSFER ON

  b[2] = SPI.transfer(0x50);    //0x50
  delay(del);
  Serial.print(b[2], HEX);
  Serial.print(' ');

  b[1] = SPI.transfer(0x02);    //0x00
  delay(del);
  Serial.print(b[1], HEX);
  Serial.print(' ');

  b[0] = SPI.transfer(0x03);    //0x00
  delay(del);
  Serial.println(b[0], HEX);

  digitalWrite(SS, HIGH); //TURN SPI TRANSFER OFF
}

Note: A typical SPI command and response would look like this:

void loop()
{
  // put your main code here, to run repeatedly:
  digitalWrite(SS, LOW);  //TURN SPI TRANSFER ON

  SPI.transfer(0x50);    //0x50 (Command)

  b[2] = SPI.transfer(0x00);    //  First response byte
  delay(del);
  Serial.print(b[2], HEX);
  Serial.print(' ');

  b[1] = SPI.transfer(0x00);    // Second response byte
  delay(del);
  Serial.print(b[1], HEX);
  Serial.print(' ');

  b[0] = SPI.transfer(0x00);    // Third response byte
  delay(del);
  Serial.println(b[0], HEX);

  digitalWrite(SS, HIGH); //TURN SPI TRANSFER OFF
}

I've never used SPI for communication between Arduino's, but the example's in this library have always worked for me

SPI Transmitter sketch:

Receiver sketch:

Don't get confused by the "struct" in the example's. You can get rid of it and replace it with any variable, array, etc. of your choice.

@real_metrics

1. Please, read the following diagram (Fig-1, Fig-2) carefully and observe that the present command of the Master brings-in the "previous byte" from the Slave. It is due to back-to-back connection of SPDR Registers of the Master and Slave.


Figure-1:

spitxrx
Figure-2:

2. Say, you want to collect these three bytes data: 0x12, 0x34, and 0x56 from Slave.
(1) From Master, you execute --

byte y1 = SPI.transfer(0xAB);  //y1 = xx (unknown) data coming from SPDR of Slave.
Having received the above command, the Slave puts 0x12 into its SPDR

(2) Then, you execute --

byte y2 = SPI.transfer(0xCD);  //y2 = 12 data coming from SPDR of Slave.
Having received the above command, the Slave puts 0x34 into its SPDR

(3) Then, you execute --

byte y3 = SPI.transfer(0xEF);  //y3 = 34 data coming from SPDR of Slave.
Having received the above command, the Slave puts 0x56 into its SPDR

(4) Then, you execute --

byte y4 = SPI.transfer(0x00);  //y4 = 56 data coming from SPDR of Slave.
------------------------------------------------------------------------------------------------------

or
you execute --

byte y1 = SPI.transfer(0xAB);  //y1 = 56 data coming from SPDR of Slave.
------------------------------------------------------------------------------------------------------

3. The analysis of Step-2 could be framed by the following Master and Slave sketches; where, Master receives 3-byte data from Slave and shows on its Serial Monitors. Check that the reception sequence (values of y1, y2, and y3) agrees with Fig-2.
Master Sketch:

#include <SPI.h>

void setup ()
{
  Serial.begin(9600);
  SPI.begin();  //default speed: 4 MHz
  digitalWrite(SS, LOW);   //Slave is selceted
}

void loop()
{
  byte y1 = SPI.transfer(0xAB); //xx, 56
  delayMicroseconds(100);    //give time to Slave to process its data structure
  byte y2 = SPI.transfer(0xCD); //12
  delayMicroseconds(100);
  byte y3 = SPI.transfer(0xEF);  //34
  //-----------------------------------
  Serial.print("y1 = "); Serial.println(y1, HEX);
  Serial.print("y2 = "); Serial.println(y2, HEX);
  Serial.print("y3 = "); Serial.println(y3, HEX);
  Serial.println("//=========================");
  delay(2000);  //test interval
}

Slave Sketch:

#include<SPI.h> //include to get the meanings of SS, MISO, MOSI, SCK, etc.
byte txData[] = {0x12, 0x34, 0x56};
int i = 0;
volatile bool flag = false;

void setup()
{
  Serial.begin(9600);
  pinMode(SS, INPUT_PULLUP);  // ensure SS stays high for now
  pinMode(MISO, OUTPUT);     //must do
  pinMode(MOSI, INPUT);     //always do
  pinMode(SCK, INPUT);     //always do
  bitSet(SPCR, SPE);      //SPI Port is enabled
  bitClear(SPCR, MSTR); //Arduino is Slave
  SPDR = 0x98;    //known value loaded into SPDR
  SPI.attachInterrupt();   //interrupt logic is enabled
}

void loop()
{
  if (flag == true)
  {
    SPDR = txData[i]; //places 0x12, then 0x34, 0x56
    i++;
    if (i == 3)     //3-byte data are sent
    {
      i = 0;        //array pointer is reset
    }
    flag = false;
  }
}

ISR(SPI_STC_vect)
{
  flag = true;
}

4. Master's Serial Monitor:

y1 = 56
y2 = 12
y3 = 34
//=========================

5. From Step-4, you know which variable contains which data byte that is coming from Slave. The arrival sequence is in agreement with the hardware structure of the SPI Port.

6. The attached file may worth reading and practicing.
Ch-8SPILec.pdf (352.5 KB)
Expt-8(SPI).pdf (453.3 KB)

Thanks a lot. I didn't realize that sending the command byte doesn't get valid data.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.