跳到主要内容

SPI 通信

SPI(Serial Peripheral Interface,串行外设接口) 是一种高速、全双工的同步串行通信协议。SPI 广泛应用于微控制器与各种外设之间的通信,如 SD 卡、显示屏、传感器、Flash 存储器等。

SPI 具有以下特点:

  • 四线通信:使用 4 根信号线进行通信(在单个从设备时可减少为 3 根)
  • 主从架构:明确的主从关系,主设备控制通信时序
  • 全双工:可以同时进行数据发送和接收
  • 高速传输:速度通常比 I2C 和 UART 更快,可达到数十 MHz
  • 同步通信:由主设备提供时钟信号,确保数据传输的可靠性

SPI 协议的四根核心信号线分别为:

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 (外设)
信号线MOSIPICO (Peripheral In / Controller Out)
信号线MISOPOCI (Peripheral Out / Controller In)

1. ESP32 中的 SPI

ESP32 芯片通常内置多个 SPI 控制器(具体数量请参考目标芯片的规格书):

  • SPI0 和 SPI1:用于访问 Flash 或 PSRAM。
  • SPI2 和 SPI3:可供用户自由使用的通用 SPI 控制器,其引脚可灵活分配。

在 Arduino 环境中,我们通过 SPI.h 库与外设通信。不同型号的 ESP32,其 SPI 总线的访问方式如下:

常见误区

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 搭建电路

需要使用的器件有:

按照下面接线图连接电路:

Details
ESP32-S3-Zero 引脚图

ESP32-S3-Zero-Pinout

接线图
ESP32 引脚OLED 模块说明
GPIO 13SCKSPI 时钟线
GPIO 11MOSISPI 数据输出
GPIO 10CS片选信号
GPIO 8DC数据/命令选择
3.3VVCC电源正极
GNDGND电源负极

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 代码解析

  1. #include <SPI.h>: 引入 Arduino 的 SPI 通信库。

  2. SPI.begin(OLED_SCK, -1, OLED_MOSI, OLED_CS): 初始化 SPI 总线并指定引脚。

    • OLED_SCK: 时钟线引脚
    • -1: MISO 引脚(此处未使用,因为显示屏只需要单向数据传输)
    • OLED_MOSI: 数据输出线引脚
    • OLED_CS: 片选线引脚
  3. Adafruit_SSD1327 display(...): 创建显示器对象。

    • 128, 128: 屏幕分辨率
    • &SPI: 使用默认的 SPI 对象
    • OLED_DC: 数据/命令选择引脚
    • OLED_RESET: 复位引脚
    • OLED_CS: 片选引脚
  4. DC 引脚的作用: SPI 显示屏通常需要额外的 DC(Data/Command)引脚来区分传输的是显示数据还是控制命令。

2.4 运行结果

  1. 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() {
// ...
}

4. 相关链接