Introduction
Serial Peripheral Interface (SPI) is a synchronous serial communication protocol widely used to connect microcontrollers to sensors, displays, and memory chips. In this tutorial, we’ll build an SPI loopback test using the Velxio simulator. The loopback test sends bytes via SPI and reads back the response, confirming that the protocol works correctly. Since there’s no physical slave device, the simulator returns the sent byte, making it a perfect way to learn SPI without any hardware.
Circuit Walkthrough
Let’s start by setting up the circuit. The only component we need is an Arduino Uno, which we’ll place on the canvas.

To add the Arduino Uno, open the component picker by clicking the ”+” icon on the canvas toolbar.

Search for “Arduino Uno” and place it on the canvas. The Arduino Uno has dedicated SPI pins:
- MOSI (Master Out Slave In) – pin 11
- MISO (Master In Slave Out) – pin 12
- SCK (Serial Clock) – pin 13
- SS (Slave Select) – pin 10 (we’ll use this as the chip select)
In this loopback test, we don’t need any external wiring because the SPI signals are internal to the Arduino. However, we must ensure that the SS pin (pin 10) is configured as an output and driven high initially, then low during communication. The code handles this.
Code Walkthrough
Now let’s examine the code. Open the code editor by clicking the “Code” tab in the editor toolbar.

The code is written in Arduino C++ and uses the built-in SPI.h library. No external libraries are needed.
// SPI Loopback Test
// Sends bytes via SPI and logs the exchange.
// Without a physical slave, the emulator returns the sent byte.
#include <SPI.h>
#define SS_PIN 10
void setup() {
Serial.begin(9600);
Serial.println("========================");
Serial.println(" SPI Protocol Test");
Serial.println("========================");
Serial.println();
pinMode(SS_PIN, OUTPUT);
digitalWrite(SS_PIN, HIGH);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV16);
Serial.println("SPI initialized.");
Serial.print("Clock divider: 16 (");
Serial.print(F_CPU / 16);
Serial.println(" Hz)");
Serial.println();
// Send test pattern
Serial.println("Sending test pattern via SPI:");
byte testData[] = {0xAA, 0x55, 0xFF, 0x00, 0x42, 0xDE, 0xAD, 0xBE};
digitalWrite(SS_PIN, LOW); // Select slave
for (int i = 0; i < sizeof(testData); i++) {
byte sent = testData[i];
byte received = SPI.transfer(sent);
Serial.print(" TX: 0x");
if (sent < 16) Serial.print("0");
Serial.print(sent, HEX);
Serial.print(" RX: 0x");
if (received < 16) Serial.print("0");
Serial.print(received, HEX);
if (sent == received) {
Serial.println(" (loopback OK)");
} else {
Serial.println();
}
}
digitalWrite(SS_PIN, HIGH); // Deselect slave
Serial.println();
Serial.println("SPI test complete.");
}
void loop() {
delay(1000);
}
Let’s break down the key parts:
#include <SPI.h>: Includes the Arduino SPI library.#define SS_PIN 10: Defines the slave select pin as pin 10.setup(): Initializes serial communication at 9600 baud, sets SS pin as output and drives it high, then callsSPI.begin()to initialize the SPI peripheral. The clock divider is set to 16, giving a clock frequency ofF_CPU / 16(1 MHz for a 16 MHz Arduino).- Test pattern: An array of 8 bytes (
0xAA, 0x55, 0xFF, 0x00, 0x42, 0xDE, 0xAD, 0xBE) is sent one by one. digitalWrite(SS_PIN, LOW): Selects the slave (in loopback, this is just a logical step).SPI.transfer(sent): Sends a byte and simultaneously receives a byte. In loopback, the received byte equals the sent byte.- Serial output: Each transmission is printed in hex format, and if the sent and received bytes match, “(loopback OK)” is appended.
digitalWrite(SS_PIN, HIGH): Deselects the slave after the transfer.loop(): Empty except for a delay, as the test runs only once insetup().
Key Concepts
- SPI Protocol: SPI is a synchronous, full-duplex protocol with four wires: MOSI, MISO, SCK, and SS. The master generates the clock and controls the slave select.
- Loopback: Connecting MOSI to MISO (or using a simulator that echoes) allows testing the SPI hardware without an external slave.
- Clock Divider: The
SPI.setClockDivider()function sets the SCK frequency relative to the system clock. Lower dividers mean faster communication. SPI.transfer(): This function simultaneously sends and receives a byte. It’s the core of SPI communication.
Common Pitfalls and Debugging Tips
- SS Pin Configuration: The SS pin must be configured as an output; otherwise, the SPI hardware may automatically switch to slave mode. Always set
pinMode(SS_PIN, OUTPUT)and drive it high when idle. - Baud Rate Mismatch: Ensure the Serial Monitor baud rate matches
Serial.begin(9600). In Velxio, the serial monitor opens automatically. - Clock Polarity and Phase: The default SPI mode (mode 0) uses SCK idle low and data sampled on the rising edge. If your slave expects a different mode, use
SPI.setDataMode(). - No External Wiring: In this loopback test, no wires are needed. If you were connecting an actual slave, double-check the connections: MOSI to MOSI, MISO to MISO, SCK to SCK, and SS to SS.
Suggested Extensions
- Add an External Slave: Simulate a sensor or display by adding a second Arduino as a slave and writing a slave sketch.
- Change Clock Speed: Experiment with different clock dividers (e.g.,
SPI_CLOCK_DIV2,SPI_CLOCK_DIV128) and observe the effect on transmission speed. - Send Larger Data: Modify the test pattern to send a string or a sensor reading.
- Use Interrupts: Implement SPI communication with interrupts for non-blocking transfers.
Try It Yourself
Ready to dive in? Open the live example in Velxio and run the simulation. You’ll see the serial output showing each transmitted and received byte, confirming the loopback.
Open the SPI Loopback Test in Velxio
Happy simulating!