SPI 通信
SPI(Serial Peripheral Interface,串行外设接口) 是一种高速、全双工的同步串行通信协议。SPI 广泛应用于微控制器与各种外设之间的通信,如 SD 卡、显示屏、传感器、Flash 存储器等。
SPI 具有以下特点:
- 四线通信:使用 4 根信号线进行通信(在单个从设备时可减少为 3 根)
- 主从架构:明确的主从关系,主设备控制通信时序
- 全双工:可以同时进行数据发送和接收
- 高速传输:速度通常比 I2C 和 UART 更快,可达到数十 MHz
- 同步通信:由主设备提供时钟信号,确保数据传输的可靠性
SPI 协议的四根核心信号线分别为:
- SCK(Serial Clock,时钟线):串行时钟线。由主机产生的时钟信号,用于同步数据传输。
- MOSI(Master Out Slave In,主出从入):主出从入线。主设备通过这条线向从设备发送数据。
- MISO(Master In Slave Out,主入从出):主入从出线。从设备通过这条线向主设备发送数据。
- SS/CS(Slave Select/Chip Select,从机选择/片选):片选信号线。主机用此线选择当前要通信的从机,低电平为选中。SPI 主机通过不同的 CS (片选)线选中目标从机。每增加一个 SPI 外设,就要多占用一个独立的 CS 引脚。
关于 SPI 术语更新的补充说明
你可能会在一些新的技术手册或开源项目中注意到,SPI 的术语正在发生一些变化。了解这些新术语有助于你无障碍地阅读最新资料。以下是新旧术语的对应关系:
类别 | 传统术语 | 新术语 |
---|---|---|
设备角色 | Master (主机) | Controller (控制器) |
设备角色 | Slave (从机) | Peripheral (外设) |
信号线 | MOSI | PICO (Peripheral In / Controller Out) |
信号线 | MISO | POCI (Peripheral Out / Controller In) |
1. ESP32 中的 SPI
ESP32 芯片通常内置多个 SPI 控制器(具体数量请参考目标芯片的规格书):
- SPI0 和 SPI1:用于访问 Flash 或 PSRAM。
- SPI2 和 SPI3:可供用户自由使用的通用 SPI 控制器,其引脚可灵活分配。
在 Arduino 环境中,我们通过 SPI.h
库与外设通信。不同型号的 ESP32,其 SPI 总线的访问方式如下:
- ESP32 (经典型号)
- ESP32-S3
SPI
: 默认 SPI 总线对象,对应 VSPI (硬件 SPI3)。SPIClass hspi(HSPI)
: 创建第二条总线对象,对应 HSPI (硬件 SPI2)。
在 ESP32-S3 的技术手册 中,硬件 SPI2 也被称为 FSPI。此处的 “F” 代表 “Fast”(快速),而非 “Flash”。在 Arduino 中,这个 SPI2 总线是完全可用的通用总线。
SPI
: 默认 SPI 总线对象,对应硬件 SPI2 (FSPI)。SPIClass hspi(HSPI)
: 创建第二条总线对象,对应硬件 SPI3。
调用 SPI.begin()
时,如果不指定引脚,库会使用该总线的默认引脚;若提供了自定义引脚号,则会 通过 GPIO 矩阵进行路由。
2. 示例 1:使用 SPI 控制显示屏
这个示例演示如何使用 SPI 接口驱动 OLED 显示屏。与 I2C 相比,SPI 版本的显示屏通常有更快的刷新速度。
2.1 搭建电路
需要使用的器件有:
- 微雪 1.5 寸 OLED 模块 × 1
- 面包板 × 1
- 导线
- ESP32 开发板
按照下面接线图连接电路:
Details
ESP32-S3-Zero 引脚图

ESP32 引脚 | OLED 模块 | 说明 |
---|---|---|
GPIO 13 | SCK | SPI 时钟线 |
GPIO 11 | MOSI | SPI 数据输出 |
GPIO 10 | CS | 片选信号 |
GPIO 8 | DC | 数据/命令选择 |
3.3V | VCC | 电源正极 |
GND | GND | 电源负极 |
2.2 代码
此代码示例依赖 "Adafruit_SSD1327" 库。请在 Arduino IDE 的库管理器中搜索并安装 'Adafruit SSD1327' 库。
安装方法请参考:Arduino 库管理教程。
#include <SPI.h>
#include <Adafruit_SSD1327.h>
#define OLED_SCK 13 // 时钟线
#define OLED_MOSI 11 // 数据输出线
#define OLED_CS 10 // 片选线
#define OLED_DC 8 // 数据/命令选择线
#define OLED_RESET -1 // 复位引脚(未使用)
// 创建显示器对象
Adafruit_SSD1327 display(128, 128, &SPI, OLED_DC, OLED_RESET, OLED_CS);
void setup() {
Serial.begin(9600);
// 初始化 SPI 总线
SPI.begin(OLED_SCK, -1, OLED_MOSI, OLED_CS);
Serial.println("SSD1327 OLED test");
// 初始化显示器
if (!display.begin()) {
Serial.println("Unable to initialize OLED");
while (1) yield();
}
// 显示设置
display.clearDisplay();
display.setRotation(3);
display.setTextSize(2);
display.setTextColor(SSD1327_WHITE);
// 显示文本
display.setCursor(10, 10);
display.println("Hello,");
display.setCursor(40, 30);
display.setTextColor(SSD1327_BLACK, SSD1327_WHITE);
display.println(" World!");
display.display();
display.setCursor(15, 60);
display.setTextColor(SSD1327_WHITE);
display.println("SPI TEST");
display.display();
delay(1000);
}
void loop() {
}
2.3 代码解析
-
#include <SPI.h>
: 引入 Arduino 的 SPI 通信库。 -
SPI.begin(OLED_SCK, -1, OLED_MOSI, OLED_CS)
: 初始化 SPI 总线并指定引脚。OLED_SCK
: 时钟线引脚-1
: MISO 引脚(此处未使用,因为显示屏只需要单向数据传输)OLED_MOSI
: 数据输出线引脚OLED_CS
: 片选线引脚
-
Adafruit_SSD1327 display(...)
: 创建显示器对象。128, 128
: 屏幕分辨率&SPI
: 使用默认的 SPI 对象OLED_DC
: 数据/命令选择引脚OLED_RESET
: 复位引脚OLED_CS
: 片选引脚
-
DC 引脚的作用: SPI 显示屏通常需要额外的 DC(Data/Command)引脚来区分传输的是显示数据还是控制命令。
2.4 运行结果
-
OLED 屏幕将被点亮,显示以下内容:
- 第一行是白色字体的 “Hello,”。
- 第二行是反色显示的 “ World!”(即黑色文字,白色背景)。
- 第三行是白色字体的 “SPI TEST”。
3. 示例 2:使用多个 SPI 设备
代码
在 SPI 库中,已经预先创建了 SPI
这个对象。如果要使用另一个 SPI 控制器,可以通过下面的方式定义:
#include <SPI.h>
SPIClass hspi(HSPI);
void setup() {
// 初始化 HSPI 总线,并指定其引脚
hspi.begin(HSPI_SCLK, HSPI_MISO, HSPI_MOSI, HSPI_CS);
// ...
}
void loop() {
// ...
}