1. Basics of SPI Protocol
SPI (Serial Peripheral Interface) is a high-speed, full-duplex synchronous serial communication protocol with the following core features:
-
Four-wire Communication:
- MOSI (Master Out Slave In): Master output, slave input
- MISO (Master In Slave Out): Master input, slave output
- SCLK: Serial clock generated by the master
- CS/SS: Chip select signal (active low)
-
Master-Slave Architecture: Supports one master and multiple slaves, each slave requires an independent CS pin
-
Full-Duplex Transmission: Data can be sent and received simultaneously
-
High-Speed Transmission: Typically reaches several MHz to tens of MHz
-
Four Operating Modes: Determined by the combination of CPOL (Clock Polarity) and CPHA (Clock Phase)
Typical Application Scenarios
- Display Drivers (OLED, TFT LCD)
- Storage Devices (SD Cards, SPI Flash)
- Wireless Communication Modules (nRF24L01, LoRa)
- Sensor Interfaces (High Data Rate Sensors)
2. ESP32 SPI Hardware Features
The ESP32 integrates four SPI controllers:
| Controller | Usage | Availability | Default Pins |
|---|---|---|---|
| SPI0 | Cache Flash | System Reserved | Non-configurable |
| SPI1 | Cache PSRAM | System Reserved | Non-configurable |
| HSPI (SPI2) | General SPI | User Available | MISO:12, MOSI:13, SCLK:14, CS:15 |
| VSPI (SPI3) | General SPI | User Available | MISO:19, MOSI:23, SCLK:18, CS:5 |
Key Features:
- Supports Master/Slave mode (typically used in Master mode)
- Clock frequency up to 80MHz
- Programmable clock polarity and phase
- Supports DMA transfer
- Pins can be flexibly mapped through IO matrix
3. ESP-IDF SPI Programming Interface
1. Bus Configuration Structure
typedef struct {
int mosi_io_num; // MOSI pin
int miso_io_num; // MISO pin
int sclk_io_num; // SCLK pin
int quadwp_io_num; // WP signal (for QSPI)
int quadhd_io_num; // HD signal (for QSPI)
int max_transfer_sz; // Maximum transfer size in bytes
int flags; // Special flags
} spi_bus_config_t;
2. Device Configuration Structure
typedef struct {
uint8_t command_bits; // Command bit length
uint8_t address_bits; // Address bit length
uint8_t dummy_bits; // Number of dummy cycles
spi_mode_t mode; // SPI mode (0-3)
uint32_t clock_speed_hz; // Clock frequency
int spics_io_num; // CS pin
uint32_t flags; // Special flags
int queue_size; // Transaction queue size
} spi_device_interface_config_t;
3. Common API Functions
| Function | Description |
|---|---|
<span>spi_bus_initialize()</span> |
Initialize SPI bus |
<span>spi_bus_add_device()</span> |
Add SPI device |
<span>spi_device_transmit()</span> |
Execute SPI transaction |
<span>spi_device_queue_trans()</span> |
Add transaction to queue |
<span>spi_device_get_trans_result()</span> |
Get transaction result |
<span>spi_bus_remove_device()</span> |
Remove SPI device |
<span>spi_bus_free()</span> |
Free SPI bus |
4. SPI Communication Examples
1. SPI Master Initialization
#include "driver/spi_master.h"
#include "esp_log.h"
#define SPI_HOST SPI3_HOST // Use VSPI
#define PIN_NUM_MISO 19
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK 18
#define PIN_NUM_CS 5
void spi_init() {
spi_bus_config_t buscfg = {
.miso_io_num = PIN_NUM_MISO,
.mosi_io_num = PIN_NUM_MOSI,
.sclk_io_num = PIN_NUM_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4096
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = 10 * 1000 * 1000, // 10MHz
.mode = 0, // SPI mode 0
.spics_io_num = PIN_NUM_CS,
.queue_size = 7,
};
// Initialize SPI bus
ESP_ERROR_CHECK(spi_bus_initialize(SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
// Add device
spi_device_handle_t spi;
ESP_ERROR_CHECK(spi_bus_add_device(SPI_HOST, &devcfg, &spi));
}
2. Data Sending Example
void spi_send_data(spi_device_handle_t spi, uint8_t* data, size_t len) {
spi_transaction_t t = {
.length = len * 8, // Bit count
.tx_buffer = data,
};
ESP_ERROR_CHECK(spi_device_transmit(spi, &t));
}
3. Data Transfer Example (Full Duplex)
void spi_transfer(spi_device_handle_t spi, uint8_t* tx_data, uint8_t* rx_data, size_t len) {
spi_transaction_t t = {
.length = len * 8,
.tx_buffer = tx_data,
.rx_buffer = rx_data,
};
ESP_ERROR_CHECK(spi_device_transmit(spi, &t));
}
5. Advanced Application Techniques
1. Reading Flash JEDEC ID
void read_flash_id(spi_device_handle_t spi) {
uint8_t tx_buf[4] = {0x9F, 0x00, 0x00, 0x00}; // JEDEC ID command
uint8_t rx_buf[4];
spi_transaction_t t = {
.length = 32,
.tx_buffer = tx_buf,
.rx_buffer = rx_buf,
};
ESP_ERROR_CHECK(spi_device_transmit(spi, &t));
ESP_LOGI("SPI", "Manufacturer ID: 0x%02X", rx_buf[1]);
ESP_LOGI("SPI", "Memory Type: 0x%02X", rx_buf[2]);
ESP_LOGI("SPI", "Capacity: 0x%02X", rx_buf);
}
2. Using DMA Transfer
// Specify DMA channel during initialization
spi_bus_initialize(SPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
// Large data transfer
#define BUF_SIZE 4096
uint8_t tx_buf[BUF_SIZE], rx_buf[BUF_SIZE];
spi_transaction_t t = {
.length = BUF_SIZE * 8,
.tx_buffer = tx_buf,
.rx_buffer = rx_buf,
};
ESP_ERROR_CHECK(spi_device_transmit(spi, &t));
3. Multi-Device Management
// Add second device
#define PIN_NUM_CS2 17
spi_device_interface_config_t devcfg2 = {
.clock_speed_hz = 1 * 1000 * 1000,
.mode = 0,
.spics_io_num = PIN_NUM_CS2,
.queue_size = 3,
};
spi_device_handle_t spi2;
ESP_ERROR_CHECK(spi_bus_add_device(SPI_HOST, &devcfg2, &spi2));