SPI Communication issues Mega - nano

I have an arduino Mega (Master) and a Series of arduino nano (Slaves) communicating via SPI.
Each slave SS pin is wired to a digital pin on the master. I called the pins Ports.
In my project I'll connect 13 Ports but the hardware is set up to allow up to 30 Ports. My idea is that ports can be dynamic, that means that if I connect the slave, the master recognize it and reads and writes data, if I disconnect the slave, the master sends a state value = -1.

Regarding the spi communication, initially I was using Strings to send and receive data, so for example, if the slave was connected and responding, it sent this "<id,state,value>".
On the master i concatenated all ports strings so I got a big String like this:

"<port,id,state,value><port,id,state,value>...<port,id,state,value>"

For example:

"<2,1,1,20><3,1,1,0>...<10,-1,-1,0><11,-1,-1,>" 

In that situation ports 2 and 3 where transmitting while ports 10 and 11 did not.
If I needed to send data from the master to a slave, I did the following:

String dataSend = "<value,value...,value>";
SPI.transfer(1); //Send a value indicating transmission started
loop(){SPI.transfer(dataSent.toBytes[index]};
SPI.transfer("-"); // Value that markes the end of the transmission.

That approach was working great and smoothly , I got data from slaves and was able to sent data to them. The problem arose when after a while, slaves stopped transmitting so they got blocked. I read online that using Strings was not a good idea because it generates memory overflow, so I decided to switch to a more robust structure using bytes and packaged structures instead of Strings.

I created two SPI libraries: Master and Slave.
On the master library, I have two functions sendData and readData. in readData, I got data coming from the slave. To achieve that, I have a dummy array that sends two markers: START_TRANSMISSION and END_TRANSMISSION and a dummy payload. The data coming from the slave, structured as START_PAYLOAD PAYLOAD CRC END_PAYLOAD, is stored in the array txBuffer. I put a small delay of 20us between every transmission to give the slave time to receive the data and send it back.

The problem I'm encountering is that I have a lot of wrongs readings from the slave and no mater what I change, I always got the same behavior, whereas when I was using Strings, wrong readings were almost inexistent.

For debug purposes, I'm only testing the port 22, so only one slave is connected. Regarding timing, I'm starting a new transmission every 20ms max 30ms. Speed is important in my project so higher than that will be too slow.
When I was working with Strings I got times of even 8ms per transmission.

This is what I'm getting from slave:

09:14:45.036 -> Port = 22 FF FF C0 1 1 0 0 C1 41 51 C4 C4...
09:14:45.036 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4...
09:14:45.070 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4... 
09:14:45.070 -> Port = 22 FF FF FF 1 1 0 0 10 41 51 C4 C4...
09:14:45.070 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4...
09:14:45.070 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4...
09:14:45.108 -> Port = 22 C0 C3 1 1 FF 0 10 FF FF C4 C4 C4...

The correc value must be

FF C3 1 1 0 0 10 41 51 C4 C4 C4...

Another strange thing is that if I connect the slave to the PC usb port, the readings became more stable even though no serial communication is enabled.

like this:

09:14:45.322 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4 ...
09:14:45.354 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4 ...
09:14:45.354 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4 ...
09:14:45.354 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4 ...
09:14:45.354 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4 ...
09:14:45.354 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4 ...
09:14:45.388 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4 ...
09:14:45.388 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4 ...

Here the code of the master and slave's libraries.
Regarding the slave, the function sendReceiveData() is called from the interrupt ISR, from the slave sketch, like this:

ISR(SPI_STC_vect) {
  spi_transfer.sendReceiveData();
}

the function getReceivedPacket() process the data coming from master after the transmission has finished
The function setDataToSend() preloads the data to send to the buffer only if nothing is being transmitted.

#ifndef SPITRANSFER_MASTER_H
#define SPITRANSFER_MASTER_H

#include <SPI.h>
#include <D:\OneDrive\Industrial Automation\SOFTWARE\DeviworksApp\Arduino\SPICommunication\DataValidation\DataValidation.h>

class SPITransfer_Master {
private:
  static const int bufferSize = 30;  //must match the slave's buffer size
  static const int8_t START_TRANSMISSION = 0xC0;
  static const int8_t END_TRANSMISSION = 0xC1;
  static const int8_t START_PAYLOAD = 0xC3;
  static const int8_t END_PAYLOAD = 0xC4;

  int startPort = 2;
  int finalPort = 10;
  bool transferParametersSet = false;

public:
  void setTransferParameter(int _startPort = 2, int _finalPort = 10) {
    if (transferParametersSet) return;
    startPort = _startPort;
    finalPort = _finalPort;

    for (int i = startPort; i <= finalPort; i++) {
      pinMode(i, OUTPUT);
      digitalWrite(i, HIGH);  // Deselect all slaves
    }

    SPI.begin();
    SPI.setClockDivider(SPI_CLOCK_DIV8);
    transferParametersSet = true;
  }
    bool readData(int port, void* rxData,
                const void* rxdefaultNotTransmitting, const void* rxdefaultInvalidData, size_t payloadlength) {
    int frameBytes = 3;
    size_t fullFrameLength = payloadlength + frameBytes;
    if (port < startPort || port > finalPort || fullFrameLength > bufferSize) return;
    int8_t dummyTx[bufferSize];
    int8_t rxBuffer[bufferSize];
    for (int i = 0; i < bufferSize; i++) {
        if(i<fullFrameLength)  dummyTx[i] = -1;
        else dummyTx[i] = END_TRANSMISSION;;
    }
    // Frame the message
    dummyTx[1] = START_TRANSMISSION;
    dummyTx[fullFrameLength-1] = 0XD1; //Fake CRC value
  


    //start transmission
    digitalWrite(port, LOW);
    delay(1);  // ensure slave ready

    // Send and receive bufferSize bytes
    for (int i = 0; i < bufferSize; i++) {
      rxBuffer[i] = SPI.transfer(dummyTx[i]);
      delayMicroseconds(50);
    }

    digitalWrite(port, HIGH);
    delay(1);  // ensure slave ready
    //end transmission

    //Copy just the payload (skip START and END)
    //printBuffer(port,rxBuffer);
    DataValidation Validator;
    DataValidation::DataStatus status = Validator.validateData(START_PAYLOAD,END_PAYLOAD,rxBuffer,bufferSize,payloadlength,true);
    switch (status) {
      case DataValidation::notTransmitting: memcpy(rxData, rxdefaultNotTransmitting, payloadlength); break;
      case DataValidation::dataCorrupted: memcpy(rxData, rxdefaultInvalidData, payloadlength); break;
      case DataValidation::dataOk: memcpy(rxData, &rxBuffer[Validator.GetStartIndex() + 1], payloadlength); break;
    }
  }
  bool sendData(int port, const void* txData, size_t payloadlength) {
    printBuffer(port,txData,payloadlength);
    int frameBytes = 3;
    size_t fullFrameLength = payloadlength + frameBytes;
    if (port < startPort || port > finalPort || fullFrameLength > bufferSize) return false;
    int8_t checkByte;
    bool payloadReceived = false;
    int8_t txBuffer[bufferSize];
    // Frame the message
    txBuffer[0] = START_TRANSMISSION;
    txBuffer[payloadlength + 1] = DataValidation::computeCRC(reinterpret_cast<const int8_t*>(txData), payloadlength);
    txBuffer[payloadlength+2] = END_TRANSMISSION;

    //start transmission
    digitalWrite(port, LOW);
    delay(1);  // ensure slave ready

    // Send and receive bufferSize bytes
    for (size_t i = 0; i < bufferSize; i++) {
      checkByte = SPI.transfer(txBuffer);
      if (checkByte == END_PAYLOAD) payloadReceived = true;
      delayMicroseconds(15);
    }

    digitalWrite(port, HIGH);
    delay(1);  // ensure slave ready
               //end transmission
    return payloadReceived;
  }

  int getStartPort() const {
    return startPort;
  }
  int getFinalPort() const {
    return finalPort;
  }
  void printBuffer(int port, const int8_t* buffer)
  {
    Serial.print("Port = " + String(port) + " ");
    for(int i=0;i<bufferSize;i++)
    {
        Serial.print((uint8_t)buffer[i],HEX);
        Serial.print(" ");
    }
    Serial.println();
  }
};

#endif

#ifndef SPITRANSFER_SLAVE_H
#define SPITRANSFER_SLAVE_H
#define SS_PIN 10
#include <SPI.h>
#include <D:\OneDrive\Industrial Automation\SOFTWARE\DeviworksApp\Arduino\SPICommunication\DataValidation\DataValidation.h>

class SPITransfer_Slave {
private:
    static const int bufferSize = 30;
    static const int8_t START_TRANSMISSION = 0xC0;
    static const int8_t END_TRANSMISSION = 0xC1;
    static const int8_t START_PAYLOAD = 0xC3;
    static const int8_t END_PAYLOAD = 0xC4;

    volatile int8_t txBuffer[bufferSize];
    volatile int8_t rxBuffer[bufferSize];
    volatile size_t frameLength = 0;
    volatile bool isPackageReady = false;
    volatile bool isBusy = false;
    volatile int byteIndex = 0;

    bool isSSLow() {
    return digitalRead(SS_PIN) == LOW;}

public:
    void initialize() {
        pinMode(MISO, OUTPUT);
        SPCR |= _BV(SPE);   // Enable SPI
        SPCR |= _BV(SPIE);  // Enable interrupt
        resetBuffer();
    }

    void sendReceiveData() {
        uint8_t received = SPDR;
        isBusy = true;
        if(byteIndex > bufferSize || received == END_TRANSMISSION) 
        {
            isPackageReady = true;
            return;
        }
        if(byteIndex < bufferSize) 
        {
            rxBuffer[byteIndex] = received;        
            SPDR = txBuffer[byteIndex];
        }
        byteIndex++;
    } 

    bool getReceivedPacket(void* dest, size_t length) {
        bool result = false;
        //if (isSSLow() && isPackageReady) {
        if (isPackageReady) {
            isPackageReady = false;
            DataValidation Validator;
            if (Validator.validateData(START_TRANSMISSION, END_TRANSMISSION, rxBuffer, bufferSize, length, true) == DataValidation::dataOk) {
                memcpy(dest, &rxBuffer[Validator.GetStartIndex() + 1], length); 
                result = true;
            }
            isBusy = false;
            resetBuffer();
        }
        return result;
    }

    void setDataToSend(const void* src, size_t length) {
        if(length > bufferSize-3) return;
        //if(!isSSLow() && !isBusy)
        if(!isBusy)
        {
            resetBuffer();
            frameLength= length;
            txBuffer[0] = START_PAYLOAD;
            memcpy(&txBuffer[1], src, length);
            txBuffer[length + 1] = DataValidation::computeCRC(reinterpret_cast<const int8_t*>(src), length);
            txBuffer[length + 2] = END_PAYLOAD;

            // Fill remaining buffer with -1
            for (size_t i = length + 2; i < bufferSize; ++i) {
                txBuffer[i] = END_PAYLOAD;
            }
        }
        else if(!isPackageReady && !isSSLow())isPackageReady = true;
    }

    void resetBuffer() {
        for (int i = 0; i < bufferSize; ++i) {
            rxBuffer[i] = -1;
            txBuffer[i] = -1;
        }
        SPDR =  -1; //preload first byte of SPDR
        byteIndex = 0;
    }
  
    void printBuffer(const int8_t* buffer, size_t length)
   {
      for(int i=0;i<length;i++)
      {
        Serial.print((uint8_t)buffer[i],HEX);
        Serial.print(" ");
      }
      Serial.println();
  }
};

#endif

How is the slave powered othewise?
How is it connected?

That sound slike a power sag issue.

Be careful with SPI transfers within the context of an interrupt, especially if you are also doing SPI transfers outside of the interrupt context. This probably explains better than I can, but generally an interrupt should be as short as possible:

Should I retrieve SPI data in interrupt routine? - Stack Overflow

Thank you for your reply.
The hole system is powered up by a 600W power supply. Every device is directly wired to the power supply to avoid problems due to voltage drop.
MISO MOSI AND SCK for any slave have single cable directly wired to the master pins.

I'm not doing any transfer outside the interrupt context and the function spi_transfer.sendReceiveData(); is really fast as it doesn't perform complex operations. Maybe I could make the variable uint8_t received global to save some time, but I don't think the problem is because of that.

    void sendReceiveData() {
        uint8_t received = SPDR;
        isBusy = true;
        if(byteIndex > bufferSize || received == END_TRANSMISSION) 
        {
            isPackageReady = true;
            return;
        }
        if(byteIndex < bufferSize) 
        {
            rxBuffer[byteIndex] = received;        
            SPDR = txBuffer[byteIndex];
        }
        byteIndex++;
    } 

All I ever do in an ISR is set a bool flag that gets interrogated in the main loop state machine or millis-driven code per several tutorials.

If I read the SPDR inside the ISR, I know the exactly moment when the byte has arrived.
If I move the reading function to the main loop, how can I be sure that I'm reading the correct value from SPDR?

If your code is done according to the many tutorials, it will be within a few clock cycles.
See post #2, especially the link re SPI

Arriving at the ISR, you set a flag and then test the flag in the loop() function to read the value of SPDR.

void loop()
{
    if(flag == true)
    {
          myData = SPDR;
          flag = false;
    }
}

ISR(SPI_STC_vect)
{
    flag = true;
}

However, I prefer to perform read/write operations with SPDR register in the ISR.

ISR(SPI_STC_vect)
{
  myData = SPDR; //read operation
  SPDR = 0x45;    //write operation
  flag = true;
}

This is exactly what I'm doing
in my sketch I have

ISR(SPI_STC_vect) {
  spi_transfer.sendReceiveData();
}

and then

void sendReceiveData() {
        uint8_t received = SPDR;
        isBusy = true;
        if(byteIndex > bufferSize || received == END_TRANSMISSION) 
        {
            isPackageReady = true;
            return;
        }
        if(byteIndex < bufferSize) 
        {
            rxBuffer[byteIndex] = received;        
            SPDR = txBuffer[byteIndex];
        }
        byteIndex++;
    }

which is the same to this:

ISR(SPI_STC_vect) {
          uint8_t received = SPDR;
        isBusy = true;
        if(byteIndex > bufferSize || received == END_TRANSMISSION) 
        {
            isPackageReady = true;
            return;
        }
        if(byteIndex < bufferSize) 
        {
            rxBuffer[byteIndex] = received;        
            SPDR = txBuffer[byteIndex];
        }
        byteIndex++;
    }
}

I just moved the code to another file to reuse it in another sketches.

Are you saying that these operations can block the ISR?

        if(byteIndex > bufferSize || received == END_TRANSMISSION) 

        if(byteIndex < bufferSize) 

        byteIndex++;

I don't think it is as easy as that. I noticed that if you don't put a small delay between every byte transmission, your master reads faster than you slave can process an you get, on the master side, the same value it sent as a result. So for that I put a delay of 50us, here.

    for (int i = 0; i < bufferSize; i++) {
      rxBuffer[i] = SPI.transfer(dummyTx[i]);
      delayMicroseconds(50);
    }

Your sendReceiveData() apparently contains too much codes which may not be permissible in an ISR(). To keep the ISR() as short as possible, save your data items in an array and then process them in the loop() function.

If possible, can you post a simple sketch between your Master and Slave-1?

That is exaclty what I'm doing. It seems to be complex, but it's actually code moved from the master or slave sketches to another file. It's just because I need to reuse the SPI functions in different sketches, so instead of duplicate code every time, I created those libraries.

Here a simplified version of the master and the slave sketches
Master

const int startPort = 2, endPort = 30;
SPITransfer_Master spi;

struct __attribute__((packed)) SlaveStatus {
  int8_t id;
  int8_t state;
  float value;
};
SlaveStatus slaveStatusCache[31];  // Ports 0–30
SlaveStatus invalidData = {-1,-3,0};
SlaveStatus notTransmitting = {-1,-1,0};
void setup() {
  spi.setTransferParameter(startPort, endPort);
}

void loop() {

  for (int port = startPort; port <= endPort; ++port) {
    SlaveStatus status =slaveStatusCache[port];
    invalidData.id = status.id;
    spi.readData(port, &status, &notTransmitting, &invalidData, sizeof(SlaveStatus));
    slaveStatusCache[port] = status;   
  }
delay(20);
}

Slave

struct __attribute__((packed)) SlaveStatusPacket {
  int8_t id;
  int8_t state;
  float rpm;
};

SPITransfer_Slave spi_transfer;

ISR(SPI_STC_vect) {
//send and receive data and stores it to be processed later by spi_transfer.getReceivedPacket()
  spi_transfer.sendReceiveData();
}

void setup() {
  spi_transfer.initialize();
}

void loop() {
  SlaveCommandPacket commandPacket;
//Receive the data only after the full transmission has finished. Not inside the ISR
  if (spi_transfer.getReceivedPacket(&commandPacket, sizeof(commandPacket))) {
      doTasks();
  }
  updateStatus();
}

void updateStatus() {
  SlaveStatusPacket statusPacket;
  statusPacket.id = id;
  statusPacket.state = isRunning ? 5 : 1;
  statusPacket.rpm = rpm;
//Prepare data to be sent when required by the Master
  spi_transfer.setDataToSend(&statusPacket, sizeof(statusPacket)); 
}
1 Like

You get wrong data; because, Master and Slave go out-of-sync. Therefore, you may try using redundant synchronization pattern (courtesy @J-M-L).

Example:
Create sketches for both Master and Slave to send/receive a structure type data from Master to Slave using redundant sync pattern.

#include <SPI.h>

const uint8_t sensorPin = A0;

struct __attribute__((packed, aligned(1))) Payload
{
  uint16_t sensorValue;
  unsigned long timeStamp;
};

const uint8_t syncHeader[] = {0xDE, 0xAD, 0xBE, 0xEF}; // Synchronization header

Payload data;
uint8_t *dataPtr = reinterpret_cast<uint8_t *>(&data);//reinterpret_cast<uint8_t*>(&data);

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  data.sensorValue = analogRead(sensorPin);
  Serial.println(data.sensorValue);
  data.timeStamp = millis();
  Serial.print("Sending: ");
  printPayload(data);

  digitalWrite(SS, LOW);  // enable Slave Select
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0)); // provide settings 1Mb/s

  // Send synchronization header
  for (size_t i = 0; i < sizeof syncHeader; i++)
  {
    SPI.transfer(syncHeader[i]);
    delayMicroseconds (10);
  }
  // Send payload data
  for (size_t i = 0; i < sizeof(data); i++)
  {
    SPI.transfer(dataPtr[i]);
    delayMicroseconds (10);
  }
  SPI.endTransaction();
  digitalWrite(SS, HIGH);  // disable Slave Select
  delay(1000);  //tst interval
}

void printPayload(Payload &p)
{
  Serial.print(p.timeStamp);
  Serial.print('\t');
  Serial.println(p.sensorValue);
}

Slave Sketch:
// RECEIVER CODE
#include <SPI.h>

struct __attribute__((packed, aligned(1))) Payload
{
  uint16_t sensorValue;
  unsigned long timeStamp;
};

Payload data;
volatile uint8_t *dataPtr = reinterpret_cast<uint8_t *>(&data);
volatile bool payloadIsReady = false;

void setup()
{
  Serial.begin(115200);
  pinMode(MISO, OUTPUT);  // pin on which to send info out
  pinMode(SS, INPUT_PULLUP);
  SPCR |= bit(SPE);  // turn on SPI in slave mode
  SPI.attachInterrupt();
  Serial.println(F("\nReady to receive data"));
}

void loop()
{
  if (payloadIsReady)
  {
    // critical section
    noInterrupts();
    Payload dataCopy = data;
    payloadIsReady = false;
    interrupts();
    // end of critical section
    printPayload(dataCopy);
  }
}

ISR(SPI_STC_vect)
{
  static const uint8_t syncHeader[] = {0xDE, 0xAD, 0xBE, 0xEF}; // Synchronization header
  static uint8_t syncPos = 0;
  static byte pos = 0;

  uint8_t receivedByte = SPDR;

  if (syncPos < sizeof syncHeader)
  {
    if (receivedByte == syncHeader[syncPos])
    {
      syncPos++;
    }
    else
    {
      syncPos = 0;
    }
  }
  else
  {
    dataPtr[pos++] = receivedByte;
    if (pos >= sizeof(Payload))
    {
      pos = 0;
      syncPos = 0;
      payloadIsReady = true;
    }
  }
}

void printPayload(Payload &p)
{
  Serial.print(p.timeStamp);
  Serial.write('\t');
  Serial.println(p.sensorValue);
}

Thank you. I'll try that way and let you know.

As can be seen in the second line:

09:14:45.036 -> Port = 22 FF C3 1 1 0 0 10 41 51 C4 C4 C4...

Your code looks too complex to me. I only think that you missed that received bytes (MISO) are off by one to the just sent byte (MOSI). I.e. the first slave byte has to be set before the transmission, typically when the slave is selected. The SS also indicates the start of another transaction.

It simply is impossible to echo a just received byte (MOSI) in the same transmission (on MISO). This byte was already sent before the MOSI byte can be read.

1 Like

And how long is that wiring.
The default 4MHz for a Mega can't handle long wiring without seeing errors.
Leo..

I know that. Infact for the first transmission I preload SPDR = FF, the second byte is the start of the framed payload

About 300 mm. But I've changed the frequency even to 125Khz and no improvements seen.

SPI frequency of a Mega only works in divisions of the 16MHz clock, so 8, 4, 2, 1MHz etc.
<= 30cm seems OK for 4MHz.
Leo..

1 Like

SPI is a byte-oriented synchronous serial communication network. In theory, there is no need to send any preamble byte (like START byte) and postamble byte (like STOP byte). However, you might need to incorporate redundant syncronization pattern if you want to transmit struct type data (Example of post #12).

You send a byte using SPI.transfer(data8);, the data is received by the Slave after 8 SCK pulses.

After reception of the data byte, the Slave is interrupted, goes to the ISR(), reads/saves the data byte and then sends a byte (if any) to the Master via SPDR Register.

Once the whole message is received, the Slave (master) processes the data.

You follow the above protocol strictly and carefully, the SPI Netwrok is here to serve you.

Please, provide me set of data that you are trying to exchnage between Master-MEGA and Slave-NANO without errors.

based on what you said, it should work like this

//Master
uint8_t m_txBuffer[4] = {A0, A1, 0xA2, 0xA3};
uint8_t m_rxBuffer[4] = {0x00,0x00,0x00,0x00};
loop()
{
  for(...,m_index++;)
  {
    m_rxBuffer[m_index] = SPI.transfer(m_txBuffer[index]);   
  } 
}

//Slave
uint8_t s_txBuffer[4] = {0xB0, 0xB1, 0xB2, 0xB3};
uint8_t s_rxBuffer[4] = {0x00,0x00,0x00,0x00};
ISR(){
    s_rxBuffer[s_index] = SPDR;
    SPDR[s_index] = s_txBuffer;
    s_index++;
}

so I should see:

on Master
m_rxBuffer= {0xB0, 0xB1, 0xB2, 0xB3};

on Slave
s_rxBuffer = {A0, A1, 0xA2, 0xA3};

but what I actually see is

on Master
m_rxBuffer= {0x00, 0xB0, 0xB1, 0xB2};

on Slave
s_rxBuffer = {A0, A1, 0xA2, 0xA3};

the first byte in the master rxBuffer is wrong and the other ones have moved one step forward.
What I understood you need to do, on slave, is preload the next byte to SPDR, like this:

//Slave
const int bufferSize = 4;
uint8_t s_txBuffer[bufferSize ] = {0xB0, 0xB1, 0xB2, 0xB3};
uint8_t s_rxBuffer[bufferSize ] = {0x00,0x00,0x00,0x00};
loop()
{
  if(!isBusy){
    SPDR = s_txBuffer[0]; //preload the first byte
  }
}
ISR(){
    isBusy = true;
    if(s_index  <= bufferSize-1){
           s_rxBuffer[s_index] = SPDR;
           if(s_index+1 <= bufferSize-1)
                  SPDR= s_txBuffer[s_index+1] ; // preload the next byte
    }
    else isBusy = false;
    s_index++;
}

My master rxBuffer will be

m_rxBuffer  = {0xB0, 0xB1, 0xB2, 0xB3};

That is what is working for me. Am I wrong?