12. SPI
12.1 What Is SPI?
SPI, Serial Peripheral Interface, is a synchronous serial communication protocol used for data transfer between microcontrollers and external devices. It consists of one master device, usually a microcontroller or MCU, and one or more slave devices, forming a one-master, multiple-slave structure. It is usually used for short-distance, high-speed, full-duplex communication and is widely used in embedded systems and electronic devices.
12.2 SPI Hardware Interface
SPI mainly uses four wires: clock line, SCLK; Master Out Slave In, MOSI; Master In Slave Out, MISO; and chip select, CS.
| Communication Line | Description |
|---|---|
| SCLK | Clock line, also called SCK. The master generates the clock signal. |
| MOSI | Master output, slave input line, also called SDO. It means the master sends data to the slave. |
| MISO | Master input, slave output line, also called SDI. It means the master receives data from the slave. |
| CS | Chip select line, also called NSS. It is the slave enable signal controlled by the master. When the master controls a slave, it pulls that slave's chip select pin low or high. |
- The master sends data to the slave through the MOSI line. In each clock cycle, the master sends one bit onto MOSI, and the slave reads that bit in the next clock cycle.
- The slave sends data to the master through the MISO line. In each clock cycle, the slave sends one bit onto MISO, and the master reads that bit in the next clock cycle.
- Data transfer can be full duplex, meaning the master and slave can send and receive data at the same time.
- The length of data transfer can be variable, usually in bytes.
- Data transfer can be unidirectional, meaning the master only sends data or only receives data.
- Data transfer can support multiple masters, meaning multiple masters can communicate with multiple slaves.
The master selects the slave it wants to communicate with through the chip select line. Each slave has a chip select line. When the chip select line is low, the slave is selected. Some devices are active high, so check the datasheet. The master controls the clock line level to synchronize data transfer. The rising and falling edges of the clock line are used to control data transfer and sampling.
SPI master-slave wiring is similar to serial port wiring: transmit and receive must be cross-connected.
12.3 SPI Mode Selection
The SPI protocol defines multiple transmission modes, also called SPI modes or timing modes, to control the data transfer order and data sampling method under the clock signal. SPI transmission mode is mainly determined by two parameters: clock polarity, CKPL, and phase, CKPH. Clock polarity, CKPL, defines the level of the clock signal in the idle state. CKPL = 0: the clock signal is low in the idle state. CKPL = 1: the clock signal is high in the idle state. Clock phase, CKPH, defines which clock edge is used for data sampling and updating. CKPH = 0: data is sampled on the first clock edge and updated on the second edge. CKPH = 1: data is sampled on the second clock edge and updated on the first edge. The following are common SPI modes:
Mode 0 (CKPL=0, CKPH=0):
- Clock polarity is 0, meaning the clock idle state is low level.
- Clock phase is 0, meaning data is sampled and stabilized on the first edge of the clock signal, the rising edge.
Mode 1 (CKPL=0, CKPH=1):
- Clock polarity is 0, so the clock idle state is low level.
- Clock phase is 1, meaning data is sampled and stabilized on the second edge of the clock signal, the falling edge.
Mode 2 (CKPL=1, CKPH=0):
- Clock polarity is 1, so the clock idle state is high level.
- Clock phase is 0, meaning data is sampled and stabilized on the first edge of the clock signal, the falling edge.
Mode 3 (CKPL=1, CKPH=1):
- Clock polarity is 1, so the clock idle state is high level.
- Clock phase is 1, meaning data is sampled and stabilized on the second edge of the clock signal, the rising edge.
The decision for selecting an SPI mode usually depends on the slave device specifications and communication protocol. Different devices may use different modes, so before communicating with a specific slave device, you must understand the SPI mode required by that slave. If no SPI mode is clearly specified, you can usually try the most common Mode 0 or Mode 3 according to the slave datasheet or communication protocol. In addition, pay attention to SPI mode clock frequency limits to ensure the clock frequency matches between the master and slave devices.
12.4 Basic SPI Parameters
The SPI protocol defines a set of parameters that are very important for correctly configuring communication parameters and implementing SPI communication. Basic SPI parameters include:
Clock polarity (CPOL): specifies the signal level of the clock line in the idle state. There are two states: high level when idle, CPOL=1, or low level when idle, CPOL=0.Clock phase (CPHA): specifies when data is sampled. There are two states: sample data after the clock rising edge, CPHA=0, or sample data before the clock falling edge, CPHA=1.Number of data bits: specifies how many bits each SPI data packet contains, usually 8 bits, but smaller or larger values can also be configured.Transfer mode: determines how data is transmitted on the SPI bus, such as full-duplex, half-duplex, or unidirectional mode.Clock rate: specifies the SPI bus clock rate in bits per second, bps.Master/slave mode: determines whether the device is a master or slave on the SPI bus.Transfer order: specifies the bit transfer order, MSB, most significant bit, first or LSB, least significant bit, first. These parameters can be configured through SPI control registers to ensure correct communication between SPI devices. When determining these parameters, consider the actual hardware setup and communication requirements. If SPI communication is required, make sure these parameters are configured correctly.
12.5 Software SPI and Hardware SPI
Similar to IIC, SPI can be divided into software SPI and hardware SPI. Software SPI is not covered here. This chapter focuses on hardware SPI.
The ESP32-S3 chip integrates four SPI controllers:
- SPI0
- SPI1
- General-purpose SPI2, or GP-SPI2
- General-purpose SPI3, or GP-SPI3
SPI0 and SPI1 controllers are mainly used internally to access external flash and PSRAM. We can only use SPI2 and SPI3. Hardware SPI supports the following features:
12.6 SPI Usage Flow
Using SPI, Serial Peripheral Interface, in Arduino requires the following steps:
12.6.1 Include the SPI Library
First, include the SPI library in your Arduino code. Use the following line to add the SPI library reference:
#include <SPI.h>12.6.2 Initialize SPI
In the setup() function, initialize SPI. Call SPI.begin() to start SPI communication. You can set parameters to configure SPI mode, master/slave mode, data bit order, and clock frequency. For example, the following code configures Arduino as an SPI master:
void setup() {
// Initialize SPI
SPI.begin();
// Set SPI mode and clock frequency
SPI.setClockDivider(SPI_CLOCK_DIV4);
SPI.setDataMode(SPI_MODE0);
}2
3
4
5
6
7
12.6.3 Transfer Data
Send and receive data through SPI. Use SPI.transfer() to send and receive one byte of data. For example, the following code sends one byte and waits to receive one byte of response:
void loop() {
byte dataToSend = 0x55;
byte receivedData;
// Transfer data
digitalWrite(CS_PIN, LOW); // Select the SPI device
receivedData = SPI.transfer(dataToSend); // Send and receive data
digitalWrite(CS_PIN, HIGH); // Deselect the SPI device
// Process the received data
// ...
delay(1000);
}2
3
4
5
6
7
8
9
10
11
12
13
14
In the loop() function, use digitalWrite() to select the SPI device, for example by setting CS low, then use SPI.transfer() to send and receive data. After completion, deselect the SPI device.
12.7 SPI-Related Functions
SPI initialization
cSPI.begin();1The SPI interface defaults to VSPI. The interface frequency is 1,000,000. Data uses MSB format by default, most significant bit first. Clock mode: SPI_MODE0, SCLK idle is 0, sampling on the SCLK rising edge.
Set transfer order
cSPI.setBitOrder(LSBFIRST);1Parameter:
bitOrder: transfer order. Options:LSBFIRST, least significant bit first, orMSBFIRST, most significant bit first.Set frequency
cSPI.setFrequency(1000000);1Parameter:
freq: frequencySet clock mode
cSPI.setDataMode(SPI_MODE0);1Parameter:
dataMode: clock mode. Possible values are:
| Mode | Description |
|---|---|
| SPI_MODE0 | SCLK idle is low, sample on rising edge (default) |
| SPI_MODE1 | SCLK idle is low, sample on falling edge |
| SPI_MODE2 | SCLK idle is high, sample on rising edge |
| SPI_MODE3 | SCLK idle is high, sample on falling edge |
Start SPI communication according to the
settingconfiguration This function can replace the three functions above.cSPI.beginTransaction(setting);1Parameter:
setting. It is an object of typeSPISettingsand has three properties:_bitOrder,_clock, and_dataMode. Example:csetting1._bitOrder = LSBFIRST; setting1._clock = 1000000; setting1._dataMode = SPI_MODE0; SPI.beginTransaction(setting1);1
2
3
4End SPI communication
cSPI.endTransaction();1Function: ends SPI communication.
Receive/send one byte of data
cuint8_t SPIClass::transfer(uint8_t data)1Parameter:
data: data to send Return value: received data You can also choose the length of data to send, as shown below:
SPI.transfer(0x01);
SPI.transfer16(0x0102);
SPI.transfer32(0x01020304);
uint8_t byte1;
uint16_t bytes2;
uint32_t bytes3;
byte1 = SPI.transfer();
bytes2 = SPI.transfer16();
bytes3 = SPI.transfer32();2
3
4
5
6
7
8
9
10
12.8 SPI Verification
Use a 0.96-inch OLED screen with an SPI interface as the test case. First, install the U8G2 library.
Code example:
#include <Arduino.h>
#include <U8g2lib.h>
// Construct the object
U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=d0=*/8, /* data=d1=*/9,
/* cs=*/12, /* dc=*/11, /* reset=*/10);
void setup(void)
{
// Initialize the OLED object
u8g2.begin();
// Enable UTF-8 character set support
u8g2.enableUTF8Print();
}
void loop(void)
{
// Set font
u8g2.setFont(u8g2_font_unifont_t_chinese2);
// Set font direction
u8g2.setFontDirection(0);
u8g2.clearBuffer();
u8g2.setCursor(0, 15);
u8g2.print("Hello LCKFB!");
u8g2.setCursor(0, 40);
u8g2.print("LCSC-Openkits");
u8g2.sendBuffer();
delay(1000);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
12.9 SPI Effect
Static display: Hello LCKFB! LCSC-Openkits