Serial Peripheral Interface (SPI) is a communication protocol used to transfer data between microcontrollers and peripheral devices like sensors, SD cards, displays, and more.
It is faster than I2C and uses a master-slave architecture.
This tutorial explains how SPI works, its implementation on Arduino, and provides practical examples.
Table of Contents
1. What Is SPI?
SPI is a synchronous serial communication protocol that uses four main lines:
- MOSI (Master Out Slave In): Data sent from master to slave.
- MISO (Master In Slave Out): Data sent from slave to master.
- SCK (Serial Clock): Clock signal controlled by the master.
- SS (Slave Select): Selects which slave device to communicate with.
Key Features:
- Full-duplex: Data can be sent and received simultaneously.
- Fast Communication: Suitable for high-speed data transfer.
- Multiple Slaves: Can connect several slave devices with unique SS pins.
2. How Does SPI Work?
- The master generates a clock signal on the SCK line.
- Data is sent via MOSI (from master) or MISO (from slave).
- The master selects the target slave device using the SS line.
- The master and slave exchange data bits on every clock pulse.
3. Setting Up SPI on Arduino
Master Device
The master initiates communication and controls the clock.
Slave Device
The slave responds to the master’s requests and sends data when addressed.
4. Arduino SPI Library
The SPI library simplifies SPI communication in Arduino. You can include it in your project as follows:
#include <SPI.h>
4.1 SPI.begin()
- Initializes the SPI bus.
- Use on the master device.
4.2 SPI.transfer(data)
- Sends data from the master to the slave and simultaneously receives data from the slave.
- Returns the received byte.
4.3 SPI.end()
- Disables the SPI bus.
5. SPI Pin Configuration
Board | MOSI | MISO | SCK | SS |
---|---|---|---|---|
Arduino Uno | 11 | 12 | 13 | 10 |
Arduino Mega | 51 | 50 | 52 | 53 |
Arduino Nano | 11 | 12 | 13 | 10 |
6. Practical Examples
6.1 Communicating Between Two Arduinos
Master Code
#include <SPI.h> const int ssPin = 10; void setup() { pinMode(ssPin, OUTPUT); digitalWrite(ssPin, HIGH); // Ensure slave is not selected SPI.begin(); // Initialize SPI as master Serial.begin(9600); } void loop() { digitalWrite(ssPin, LOW); // Select slave byte receivedData = SPI.transfer(42); // Send data and receive response digitalWrite(ssPin, HIGH); // Deselect slave Serial.print("Received: "); Serial.println(receivedData); delay(1000); }
Slave Code
#include <SPI.h> const int ssPin = 10; void setup() { pinMode(ssPin, INPUT); SPI.begin(); // Initialize SPI as slave SPI.setBitOrder(MSBFIRST); // Set bit order (default) } void loop() { if (digitalRead(ssPin) == LOW) { byte data = SPI.transfer(0); // Respond with dummy data SPI.transfer(data + 1); // Return data incremented by 1 } }
6.2 Interfacing with an SPI Sensor
Use an SPI temperature sensor (e.g., MCP3008).
#include <SPI.h> const int csPin = 10; void setup() { pinMode(csPin, OUTPUT); digitalWrite(csPin, HIGH); // Deselect sensor SPI.begin(); Serial.begin(9600); } void loop() { digitalWrite(csPin, LOW); // Select sensor SPI.transfer(0b00000001); // Start bit byte highByte = SPI.transfer(0b10000000); // Send configuration bits byte lowByte = SPI.transfer(0); // Read data digitalWrite(csPin, HIGH); // Deselect sensor int value = ((highByte & 0x03) << 8) | lowByte; // Combine bytes Serial.print("Sensor Value: "); Serial.println(value); delay(500); }
6.3 Controlling an SPI Display
Control an SPI OLED or LCD display (e.g., SSD1306).
- Install Adafruit SSD1306 Library:
- Go to Tools > Manage Libraries and search for “Adafruit SSD1306.”
- Connect the Display:
- Connect MOSI, SCK, CS, and DC to appropriate pins.
- Code Example:
#include <SPI.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define CS_PIN 10 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_RESET, DC, CS_PIN); void setup() { if (!display.begin(SSD1306_I2C_ADDRESS, CS_PIN)) { Serial.println("OLED initialization failed"); while (1); } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("Hello, SPI Display!"); display.display(); } void loop() { // Empty }
7. Best Practices for Using SPI
- Use Pull-Up Resistors:
- Use pull-up resistors on the SS lines to prevent floating states.
- Select the Correct Mode:
- SPI devices operate in different modes (clock polarity and phase). Refer to the device datasheet to configure the correct mode using:
SPI.setDataMode(SPI_MODE0);
- SPI devices operate in different modes (clock polarity and phase). Refer to the device datasheet to configure the correct mode using:
- Handle Shared Buses:
- If multiple slaves share the SPI bus, ensure only one slave is active at a time by managing the SS lines.
- Monitor Clock Speed:
- Use SPI.setClockDivider() to adjust the speed based on the slave device’s requirements.
- Debug with Serial Monitor:
- Print transmitted and received data to verify communication.
Conclusion
SPI is a versatile and high-speed communication protocol, ideal for connecting Arduino with various peripherals.
By using the built-in SPI library, you can simplify communication and focus on building your project.
This tutorial provided an introduction to SPI, its functions, and practical examples like Arduino-to-Arduino communication, interfacing with a sensor, and controlling a display.
For more details, visit the official Arduino SPI reference.