SPI 通信
本教程的核心逻辑适用于所有 ESP32 开发板,但所有操作步骤均以 微雪 ESP32-S3-Zero 迷你开发板 为例进行讲解。如果您使用其他型号的开发板,请根据实际情况修改相应设置。
本节介绍 SPI 通信协议的基本概念,并演示如何使用 MicroPython 的
machine.SPI类来驱动 OLED 显示屏。
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
1.1 硬件资源
ESP32 系列芯片通常内置了多个 SPI 控制器,但它们的用途有所不同:
- SPI0 & SPI1:这两个控制器主要用于连接芯片外部的 Flash 和 PSRAM,属于系统专用资源。
- SPI2 & SPI3:这是两个通用 SPI 控制器,完全开放给用户使用。利用它们可以与显示屏、SD 卡、传感器等设备进行高速通信。
1.2 MicroPython 中的 ID 映射
MicroPython 使用 machine.SPI 类来驱动这些硬件资源。为了区分不同的控制器,MicroPython 使用 id 参数进行映射:
id=1:对应硬件 SPI2。在 ESP32 (经典款) 中常被称为 HSPI;在 ESP32-S3 中被称为 FSPI。id=2:对应硬件 SPI3。在 ESP32 (经典款) 中常被称为 VSPI。
虽然不同芯片手册中的命名(HSPI/VSPI/FSPI)可能存在差异,但在 MicroPython 代码中,仅需使用 id 参数即可区分。
1.3 硬件 SPI 引脚
ESP32 的 SPI 引脚分配非常灵活,这得益于其内部的 GPIO 矩阵 (GPIO Matrix)。SPI 的 SCK、MOSI、MISO 信号可以映射到任意支持输出/输入的 GPIO 引脚上。
不过,每个 SPI 控制器都有一组默认引脚(也称为 IO MUX 引脚)。
- 使用默认引脚:信号直接通过 IO MUX 传输,可以支持高达 80MHz 的时钟频率。
- 使用自定义引脚:信号需要经过 GPIO 矩阵路由,最大时钟频率通常限制在 40MHz 左右(依然非常快,足以满足绝大多数应用)。
以下是常见型号的默认 SPI 引脚参考:
| 芯片型号 | MicroPython ID | 硬件名称 | MOSI | MISO | SCK |
|---|---|---|---|---|---|
| ESP32 | 1 | HSPI | GPIO 13 | GPIO 12 | GPIO 14 |
| 2 | VSPI | GPIO 23 | GPIO 19 | GPIO 18 | |
| ESP32-S3 | 1 | SPI2 (FSPI) | GPIO 11 | GPIO 13 | GPIO 12 |
| 2 | SPI3 | GPIO 35 | GPIO 37 | GPIO 36 |
不同的 ESP 芯片型号,有不同的 SPI外设数量和引脚配置。请参考目标芯片的规格书获取详细信息。
也可以在 REPL 中通过以下指令来查看默认引脚:
from machine import SPI
SPI(1) # 查看 SPI2 的默认引脚
SPI(2) # 查看 SPI3 的默认引脚
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 代码
此代码示例依赖 ssd1327.py 驱动库。该库基于社区开发者 mcauser 的 micropython-ssd1327 项目。
下载链接:micropython-ssd1327-master.zip
请将该库中的 ssd1327.py 文件上传到开发板的根目录中。
from machine import Pin, SPI
import ssd1327
# SPI 引脚配置
SCK_PIN = 13
MOSI_PIN = 11
CS_PIN = 10
DC_PIN = 8
RST_PIN = 9 # 复位引脚
# 初始化硬件 SPI
# 使用 id=1 (HSPI),设置时钟频率为 10 MHz
spi = SPI(1, baudrate=10000000, sck=Pin(SCK_PIN), mosi=Pin(MOSI_PIN))
# 初始化显示器
oled = ssd1327.SSD1327_SPI(128, 128, spi, dc=Pin(DC_PIN), res=Pin(RST_PIN), cs=Pin(CS_PIN))
print("SSD1327 OLED test")
# 清屏 (填充黑色)
oled.fill(0)
# 显示文字
# framebuf.text(s, x, y, c)
# c 是颜色值,对于 4-bit 灰度,范围是 0-15。15 为最亮,0 为全黑。
oled.text("Hello,", 10, 10, 15)
oled.text("MicroPython!", 10, 25, 8)
oled.text("ESP32", 10, 40, 1) # 亮度较低的文字
# 使用 framebuf 绘制图像
# 绘制矩形框
oled.framebuf.rect(0, 0, 128, 128, 15)
# 绘制圆或椭圆
oled.framebuf.ellipse(0, 0, 128, 128, 15)
# 刷新显示
oled.show()
2.3 代码解析
-
from machine import Pin, SPI:导入 MicroPython 的Pin和SPI类。 -
SPI(1, baudrate=10000000, sck=Pin(SCK_PIN), mosi=Pin(MOSI_PIN)):初始化硬件 SPI。1:指定使用 ID 为 1 的 SPI 控制器。baudrate=10000000:设置 SPI 时钟频率为 10 MHz。sck、mosi:指定 SPI 的时钟和数据输出引脚。- 由于 OLED 显示屏通常只需要接收数据,因此本示例未配置
miso引脚。
-
ssd1327.SSD1327_SPI(...):创建 SSD1327 显示屏对象。128, 128:指定屏幕的分辨率。spi:传入已初始化的 SPI 对象。dc、res、cs:分别指定数据/命令选择引脚、复位引脚和片选引脚。
-
oled.fill(0):清空屏幕缓冲区(填充黑色)。 -
oled.text(...):在缓冲区写入文本。- 第一个参数为要显示的文本内容。
- 第二个参数为文本的 x 坐标。
- 第三个参数为文本的 y 坐标。
- 第四个参数为颜色值(亮度)。SSD1327 支持 16 级灰度(4-bit),因此可传入 0-15 之间的整数来控制亮度。15 为最亮,0 为全黑。
-
关于
framebuf模块:ssd1327驱动库基于 MicroPython 内置的framebuf(Frame Buffer,帧缓冲)模块构建。framebuf提供了一套标准的图形绘制 API,包括绘制文本、线条、矩形、圆形等基本图形。- 在代码中,
oled.framebuf.rect()和oled.framebuf.ellipse()等方法直接调用了framebuf的绘图功能。 framebuf支持多种颜色格式和绘图操作,是 MicroPython 中进行图形显示的标准工具。更多绘图方法和详细用法,请参考 MicroPython framebuf 官方文档。
-
oled.show(): 将缓冲区的数据发送到 OLED 屏幕,更新显示内容。在调用此方法之前,屏幕内容不会改变。
2.4 运行结果
代码上传并运行后,OLED 屏幕将显示以下内容:
- 第一行显示高亮度的 "Hello,"。
- 第二行显示中等亮度的 "MicroPython!"。
- 第三行显示低亮度的 "ESP32"。
- 屏幕上还会显示一个矩形框和一个内切的圆。
