跳到主要内容

SPI 通信

重要提示:关于开发板的兼容性

本教程的核心逻辑适用于所有 ESP32 开发板,但所有操作步骤均以 微雪 ESP32-S3-Zero 迷你开发板 为例进行讲解。如果您使用其他型号的开发板,请根据实际情况修改相应设置。

本节介绍 SPI 通信协议的基本概念,并演示如何使用 MicroPython 的 machine.SPI 类来驱动 OLED 显示屏。

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

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硬件名称MOSIMISOSCK
ESP321HSPIGPIO 13GPIO 12GPIO 14
2VSPIGPIO 23GPIO 19GPIO 18
ESP32-S31SPI2 (FSPI)GPIO 11GPIO 13GPIO 12
2SPI3GPIO 35GPIO 37GPIO 36
注意

不同的 ESP 芯片型号,有不同的 SPI外设数量和引脚配置。请参考目标芯片的规格书获取详细信息。

也可以在 REPL 中通过以下指令来查看默认引脚:

from machine import SPI
SPI(1)  # 查看 SPI2 的默认引脚
SPI(2)  # 查看 SPI3 的默认引脚

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

提示

此代码示例依赖 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 代码解析

  1. from machine import Pin, SPI:导入 MicroPython 的 PinSPI 类。

  2. SPI(1, baudrate=10000000, sck=Pin(SCK_PIN), mosi=Pin(MOSI_PIN)):初始化硬件 SPI。

    • 1:指定使用 ID 为 1 的 SPI 控制器。
    • baudrate=10000000:设置 SPI 时钟频率为 10 MHz。
    • sckmosi:指定 SPI 的时钟和数据输出引脚。
    • 由于 OLED 显示屏通常只需要接收数据,因此本示例未配置 miso 引脚。
  3. ssd1327.SSD1327_SPI(...):创建 SSD1327 显示屏对象。

    • 128, 128:指定屏幕的分辨率。
    • spi:传入已初始化的 SPI 对象。
    • dcrescs:分别指定数据/命令选择引脚、复位引脚和片选引脚。
  4. oled.fill(0):清空屏幕缓冲区(填充黑色)。

  5. oled.text(...):在缓冲区写入文本。

    • 第一个参数为要显示的文本内容。
    • 第二个参数为文本的 x 坐标。
    • 第三个参数为文本的 y 坐标。
    • 第四个参数为颜色值(亮度)。SSD1327 支持 16 级灰度(4-bit),因此可传入 0-15 之间的整数来控制亮度。15 为最亮,0 为全黑。
  6. 关于 framebuf 模块:

    • ssd1327 驱动库基于 MicroPython 内置的 framebuf(Frame Buffer,帧缓冲)模块构建。framebuf 提供了一套标准的图形绘制 API,包括绘制文本、线条、矩形、圆形等基本图形。
    • 在代码中,oled.framebuf.rect()oled.framebuf.ellipse() 等方法直接调用了 framebuf 的绘图功能。
    • framebuf 支持多种颜色格式和绘图操作,是 MicroPython 中进行图形显示的标准工具。更多绘图方法和详细用法,请参考 MicroPython framebuf 官方文档
  7. oled.show(): 将缓冲区的数据发送到 OLED 屏幕,更新显示内容。在调用此方法之前,屏幕内容不会改变。

2.4 运行结果

代码上传并运行后,OLED 屏幕将显示以下内容:

  • 第一行显示高亮度的 "Hello,"。
  • 第二行显示中等亮度的 "MicroPython!"。
  • 第三行显示低亮度的 "ESP32"。
  • 屏幕上还会显示一个矩形框和一个内切的圆。
示例运行结果

3. 相关链接