Arduino as SPI Master and Slave Demo Code

What a fun project this turned out to be! I feel like I just finished a good video game or something. I hope I'm not the only one who found the documentation for Arduino SPI challenging. For example the SPI library does not support SPI slave mode. This doesn't present too much of a problem but does require you to do a bit more of the "dirty work" yourself when working on the SPI slave sketch. I've attached my sketches for an Arduino SPI Master and Slave. The code is heavily documented and loaded with Serial.println output for debugging. Don't forget to make a Gnd connection between each Arduino. I'm hoping to have more Arduinos to work with and would enjoy testing a SPI master with two or more SPI slaves. My demo video can be seen on YouTube at Arduino SPI Serial Peripheral Interface Master & Slave Demo Tutorial - YouTube

If you would like to help fund this and other projects please consider a donation to my PayPal account at https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=U48JEW8HSQFM2. Thank you!

SPI_Master_Demo.ino (4.55 KB)

SPI_Slave_Demo.ino (3.66 KB)

On that page:

http://www.gammon.com.au/forum/?id=10892&reply=1#reply1 ("How to make an SPI slave")

Hey Nick! Great reference! Most of what I did was actually based on this article. You are even credited in the sketches I uploaded. I appreciated but chose not to use an Interrupt Service Routine in my solution. I must have been viewing your article from a mirror site since I didn't see any of the follow up posts. Looks like I've got some more reading to do.
One thing I feel that I feel is significant in my example is controlling the MISO pin. Many of the examples I found ignored this important requirement when dealing with multiple slaves. You've inspired me to try implementing the SS as an interrupt. The biggest challenge I found was ensuring the slave had written it's data to the DR before the master begins the SPI transfer. I thought about using SS to signal each transfer and this would be much easier as an ISR.
I'm looking forward to purchasing a sensor board and SPI based sensor modules to learn more about the communication protocols used by these devices.

The biggest challenge I found was ensuring the slave had written it's data to the DR before the master begins the SPI transfer. I thought about using SS to signal each transfer and this would be much easier as an ISR.

I don't understand that bit. The master controls the timing (it sends the clock pulses). Once an SPI.transfer call completes the data is transferred. There is no waiting.

If the master expects a reply from the slave, the slave has no way of knowing when the master is going start cycling the clock. It has to write each bit to the DR before the master tries to clock it out.

OK, I see what you mean. From page 171 of the datasheet:

Basically you set up (get the data ready) on the opposite edge to the sample one. An interesting question is: "when does the first bit get set up?". I presume the answer would be:

  • After SS is asserted; or
  • As soon as possible after a response is requested

Hy Guys,

First of all, thanks for all the info, very good work!

I'm trying to do the same, but with a Master Arduino Due, and Slave Arduino Uno.

Colud be a problem with the SS pin to set the logical state if Due works in 3.3v and Uno in 5v?

Regards

I've always seen a level shifter used in cases like this.

mr_pacheko:
Hy Guys,

First of all, thanks for all the info, very good work!

I'm trying to do the same, but with a Master Arduino Due, and Slave Arduino Uno.

Colud be a problem with the SS pin to set the logical state if Due works in 3.3v and Uno in 5v?

Regards

In SlaveInit(),shouldn't the MISO pin be declared as OUTPUT?

Adithyag91:
In SlaveInit(),shouldn't the MISO pin be declared as OUTPUT?

Sorry, old question, but I just happened to see it.

No, it should be initialized as INPUT. It is set to OUTPUT when the slave select line is recognize as active (LOW), and returned to INPUT when the slave select line is recognized as inactive (HIGH.) The reason is that there could multiple slaves on the SPI bus, and only one of them (the one with the active slave select) should be trying to drive the bus at a time. The others should have their MISO outputs at high impedance. On the Arduino, you set an output to high impedance by making it an input.

Hi, I need to comunicate two Arduino Leonardo by SPI, but the Leonardo doesn't have the SS pin available to conect outside. I tried to define other digital pin (pin 10) as a SS one in both Arduino, but i couldn´t comunicate them so far. I checked the code and i couldn´t find anything wrong, i suppose that i didn't configure the Slave Arduino well.
If you have any information it will be really appreciated.
Thank you all!

Hi all, is there any way to use the ShiftIn command for using Arduino as the SPI slave?

MahtaE:
Hi all, is there any way to use the ShiftIn command for using Arduino as the SPI slave?

No. ShiftIn() is a function that performs a software implementation (bit-banging) of a master device acting to shift data in on a pin. It generates the clock signal, and samples the input pin for the data.

Any slave implementation requires that the master generates the clock (and the slave select signal) while the slave listens for the select and shifts data out according to the clock signal.

Being an SPI master is easy, being the slave is a lot more complicated (hence the basis of the discussion here, if it were simple like using ShiftIn(), there wouldn't be this thread.)

Hi,
I tried using the code posted with proteus using 2 arduino uno, I did not have errors during the simulation but the diodes do not work by clicking on the button.

Here is my schema on ISIS.
If someone can help me I will be really grateful.

Chould you help me to make SPI comunication betwn 2 orduinoes to monitor value of ACD on Slave... please thank U...
this is my code but it's not succes ...

MASTER:
void initSPI_Master() {
DDRB |= (1<<2)|(1<<3)|(1<<5); // SCK, MOSI dan SS menjadi output
DDRB &= ~(1<<4); // MISO menjadi input
SPCR |= (1<<MSTR); // SPI sebagai master
SPCR |= (1<<SPR0)|(1<<SPR1); // Pembagi clock = 128
SPCR |= (1<<SPE); // Aktifkan SPI
}
void setup() {
initSPI_Master();
Serial.begin(9600);
pinMode(2, INPUT);pinMode(3, INPUT); pinMode(4, INPUT);
digitalWrite(2,HIGH);digitalWrite(3, HIGH); digitalWrite(4, HIGH);
}
void loop() {
//char adc = String(analogRead(0), DEC);
int adc=analogRead(A0);
if(digitalRead(2)==LOW)
kirimData(5);
else if(digitalRead(3)==LOW)
kirimData(10);
else if(digitalRead(4)==LOW)
kirimData(adc);
Serial.print(adc);

}
void kirimData(char data) {
SPDR = data; // Kirim data
while(!(SPSR & (1<<SPIF))); // Tunggu sampai pengiriman
delay(500);
}

SLAVE:

unsigned char data;
void initSPI_Slave() {
DDRB &= ~((1<<2)|(1<<3)|(1<<5)); // SCK, MOSI dan SS menjadi input
DDRB |= (1<<4); // MISO menjadi output
SPCR &= ~(1<<MSTR); // SPI sebagai slave
SPCR |= (1<<SPR0)|(1<<SPR1); // Pembagi clock = 128
SPCR |= (1<<SPE); // Aktifkan SPI
}
void setup() {
initSPI_Slave();
Serial.begin(9600);
}
void loop() {
while(!(SPSR & (1<<SPIF))); // Tunggu data masuk
data = SPDR;

// Menyimpan isi register SPDR ke variabel data
Serial.println("Data Receive ==> ");
Serial.println(data,HEX);
delay(1000);
}