Arduino SPI Master Slave Communication

Many peripherals or external systems can communicate with microcontrollers with different communication interfaces. There are many different types of communication protocols divided in synchronous and asynchronous. The synchronous communication means there is a need for a clock for time reference. The Serial Peripheral Interface (SPI) is a synchronous serial communication interface specification used for short-distance communication, primarily in embedded systems. 

In this article we will look into the SPI communication between Arduinos.

More About SPI :

SPI devices communicate in full duplex mode using a master-slave architecture with a single master. Master initiates the communication by providing the clock to the slave. We can connect multiple slaves on the SPI bus but there can be only one master connected. And each Slave will be selected for communication by Slave/Chip select pin by making it active low.

Following are the pins required in the SPI Communication,

  1. CLK/SCLK – Clock for SPI communication
  2. MISO – Master In Slave Out
  3. MOSI – Master Out Slave In
  4. CS/SS – Chip Select / Slave Select

In the communication we have to consider the clock mainly. Both master and slave should be compatible with the selected Clock. Nowadays chips are using SPI interface with 100MHz clock.

SPI is a full duplex protocol that means on each clock pulse there is transmission and reception of 1 bit. And full duplex is achieved using shift registers. One shift register is on the master side and one on the slave side. They are connected in virtual ring topology. 

Data is usually shifted out with the most significant bit first. On the clock edge, both master and slave shift out a bit and output it on the transmission line to the counterpart. On the next clock edge, at each receiver the bit is sampled from the transmission line and set as a new least-significant bit of the shift register. 

After the register bits have been shifted out and in, the master and slave have exchanged register values. If more data needs to be exchanged, the shift registers are reloaded and the process repeats. Transmission may continue for any number of clock cycles. When complete, the master stops toggling the clock signal, and typically deselects the slave.

Connection:

Code:

Master Code

//Master Code
#include <SPI.h>

void setup() {
  Serial.begin(115200); //set baud rate to 115200 for usart
  digitalWrite(SS, HIGH); // disable Slave Select
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV8);//divide the clock by 8
}

void loop() {
  char c;
  digitalWrite(SS, LOW); // enable Slave Select
  // send test string
  for (const char * p = "Hello, world!\r" ; c = *p; p++) {
    SPI.transfer (c);
    Serial.print(c);
  }
  digitalWrite(SS, HIGH); // disable Slave Select
  delay(2000);
}

Slave Code

//Slave Code
#include <SPI.h>
char buff [50];
volatile byte indx;
volatile boolean process;

void setup() {
  Serial.begin (115200);
  pinMode(MISO, OUTPUT); // have to send on master in so it set as output
  SPCR |= _BV(SPE); // turn on SPI in slave mode
  indx = 0; // buffer empty
  process = false;
  SPI.attachInterrupt(); // turn on interrupt
}

ISR (SPI_STC_vect) {// SPI interrupt routine 
  byte c = SPDR; // read byte from SPI Data Register
  if (indx < sizeof buff) {
    buff [indx++] = c; // save data in the next index in the array buff
    if (c == '\r') //check for the end of the word
    process = true;
  }
}

void loop(){
  if (process) {
    process = false; //reset the process
    Serial.println (buff); //print the array on serial monitor
    indx= 0; //reset button to zero
  }
}

Code Explaination:

One board is configured as a master and one board configured as a slave. And as i mentioned in the communication master will provide the clock and master will initiate the communication. So, In the code here we are configuring the clock from master.

SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV8);//divide the clock by 8

While sending the data from master we have to select the slave. To select the slave we have to make the Slave Select pin as a LOW (Active Low) .

After that send the data using spi.transfer function call. and then after that release the slave select (Unselect the slave) my making the slave select pin as High.

Now we will configure the Slave device. Following is the code which turns Arduino into Slave device. We will be using interrupt for receiving the data. Interrupt is good choice to use it on the slave side to indication of start of SPI transfer.

  SPCR |= _BV(SPE); // Configure for Slave
  indx = 0;
  process = false;
  SPI.attachInterrupt(); // enable the interrupt

In the ISR routine we have set a flag which is being checked in the loop function.

ISR (SPI_STC_vect) {
  byte c = SPDR; // read byte from SPI Data Register
  if (indx < sizeof buff) {
    buff [indx++] = c; // save data in the next index of the array buff
    if (c == '\r') //check for the end of the word
    process = true;
  }
}

In the loop we will print the received data.

Leave a Reply

Your email address will not be published. Required fields are marked *