SPI 协议详解

SPI 模式

SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工的同步串行通信总线,根据通信角色(主 / 从)和数据传输方向,可分为以下 8 种核心工作模式:

1. 全双工模式(Full-Duplex)

  • Full-Duplex Master(全双工主模式):

    作为 SPI 通信的主设备,可同时向从设备发送数据(MOSI 线)和接收从设备返回的数据(MISO 线),是 SPI 最常用的模式之一。例如在与 SPI 显示屏、Flash 芯片通信时,主设备发送指令 / 地址的同时,可接收设备的状态反馈,通信效率最高。

  • Full-Duplex Slave(全双工从模式):

    作为从设备,被动响应主设备的时钟(SCK),在主设备发送数据的同时,向主设备传输自身数据。典型应用如多传感器组网,传感器作为从设备,主控制器发起通信时,传感器同步回传采集数据。

2. 半双工模式(Half-Duplex)

  • Half-Duplex Master(半双工主模式):

    主设备同一时间段仅能单向传输数据(要么发、要么收),需通过硬件或软件控制数据方向。适用于无需同时收发的场景,如部分简单传感器(仅需主设备读取数据,或仅需主设备下发配置),可节省总线资源。

  • Half-Duplex Slave(半双工从模式):

    从设备仅能在主设备指定时段单向传输数据,常见于低功耗外设,减少不必要的信号交互。

3. 只读模式(Receive Only)

  • Recieve Only Master(只读主模式):

    主设备仅接收从设备数据,MOSI 线无数据输出,仅通过 SCK 提供时钟,从设备通过 MISO 线持续发送数据。适用于数据采集类场景(如高速 ADC 采样),主设备仅需读取采样结果,无需下发指令。

  • Recieve Only Slave(只读从模式):

    从设备仅向主设备发送数据,不接收主设备的任何指令 / 数据,典型如数据广播类外设(如温湿度传感器持续上报数据)。

4. 只发模式(Transmit Only)

  • Transmit Only Master(发送主模式):

    主设备仅向从设备发送数据,MISO 线无数据输入,适用于仅需下发控制指令的场景(如 LED 点阵屏控制,主设备持续发送显示数据,从设备无反馈)。

  • Transmit Only Slave(发送从模式):

    从设备仅接收主设备数据,无任何数据输出,常见于单向控制类外设(如继电器模块,仅接收主设备的通断指令)。

SPI 片选

SPI 通过片选(NSS,Slave Select)信号确定当前通信的从设备,多从设备场景下,仅被选中的从设备响应主设备的时钟和数据信号。

1. Hardware NSS Signal(硬件片选)

硬件片选由 SPI 控制器的专用 NSS 引脚实现,信号电平由硬件电路或控制器自动控制,稳定性高,减少软件开销。

  • Hardware NSS Input Signal(硬件 NSS 输入信号):

    该模式下 NSS 作为输入引脚,主设备通过检测 NSS 电平判断自身是否被选为 “从设备”(极少场景);或从设备通过 NSS 输入电平判断是否被主设备选中(主流场景)。通常高电平为 “未选中”,低电平为 “选中”,部分外设支持高电平片选,需匹配硬件手册。

  • Hardware NSS Output Signal(硬件 NSS 输出信号):

    仅主设备支持,NSS 作为输出引脚,主设备通信时自动拉低对应从设备的 NSS 电平,通信结束后拉高,实现精准的从设备选中控制。例如主设备挂载多个 SPI 从设备时,通过硬件 NSS 输出自动切换选中的从设备,无需软件干预。

2. Software NSS Signal(软件片选)

软件片选无需专用 NSS 引脚,可任意指定通用 IO 口作为片选引脚,通过软件控制引脚电平(高 / 低)实现从设备选择,灵活性高,适用于无专用 NSS 引脚或多从设备(超过硬件 NSS 数量)的场景。

  • 实现方式:初始化指定 IO 口为输出模式,通信前拉低目标从设备的片选 IO,通信完成后拉高,避免多个从设备同时响应。
  • 注意事项:软件片选需严格同步时钟(SCK)和片选电平,避免因软件执行延迟导致从设备误触发。

SPI 数据传输

SPI 数据传输的核心参数决定了数据的封装格式和解析规则,需主从设备严格一致,否则会出现数据解析错误。

1. Frame Format(协议格式)

SPI 主流协议格式为Motorola(摩托罗拉)格式,也是几乎所有 SPI 外设默认支持的格式;少数场景会兼容 TI 格式(同步串行通信的另一种格式),但非 SPI 标准,需特殊配置。

  • Motorola 格式特征:数据以帧为单位,时钟(SCK)同步,片选(NSS)拉低后开始传输,每帧数据的位宽由 Data Size 定义,高位 / 低位先行由 First Bit 定义。

2. Data Size(数据位宽)

定义单次传输的数据位数,需主从设备匹配,否则会出现数据截断或错位。

  • 8bits(八位传输):

    最常用的位宽,适用于绝大多数外设(如 Flash、传感器、显示屏),单帧传输 1 字节数据,解析简单,兼容性强。

  • 16bits(16 位传输):

    适用于高精度数据传输场景(如 ADC/DAC、电机驱动),单帧传输 2 字节数据,可减少传输次数,提升效率。

    • 从模式注意事项:单片机作为从设备时,需提前确认对接设备的传输位宽(8/16bits),若主设备发送 16bits 数据,从设备配置为 8bits 会导致仅接收低 8 位,高 8 位丢失;反之则会接收无效数据。

3. First Bit(数据位序)

定义数据帧中第一位传输的是高位还是低位,需主从设备严格一致。

  • MSB First(高位先行):

    数据的最高位(Most Significant Bit)先传输,是 SPI 默认且最主流的配置,几乎所有显示屏、Flash、传感器均采用此模式。例如传输字节 0x81(二进制 10000001),先传输最高位 “1”,最后传输最低位 “1”。

  • LSB First(低位先行):

    数据的最低位(Least Significant Bit)先传输,仅少数特殊外设(如部分工业传感器)使用,需手动配置控制器匹配。

SPI 时钟配置

SPI 的时钟(SCK)是同步通信的核心,时钟参数决定了通信速度和数据采样时机,需根据外设的最大支持速度和传输稳定性配置。

1. Prescaler(分频系数)

SPI 控制器的时钟源通常为单片机的系统时钟(如 72MHz、168MHz),通过分频系数降低时钟频率,得到 SPI 的实际波特率。

  • 常见分频系数:2、4、8、16、32、64、128 等,分频系数越小,SCK 频率越高,通信速度越快;反之则速度越慢。
  • 配置原则:需低于外设支持的最大 SCK 频率(如多数 SPI Flash 支持最高 108MHz,显示屏多支持≤50MHz),同时考虑传输距离(远距离传输需降低频率,避免信号衰减)。

2. Baud Rate(波特率)

SPI 的波特率即 SCK 的频率,计算公式为:波特率 = 系统时钟频率 / 分频系数

  • 示例:系统时钟 72MHz,分频系数 8,则波特率 = 72/8=9MHz,即每秒传输 9M 位数据。
  • 注意:全双工模式下,波特率同时决定 MOSI 和 MISO 的传输速度;单向模式(只读 / 只发)仅决定对应数据线的速度。

3. Clock Polarity (CPOL)(时钟极性)

定义 SPI 时钟线(SCK)在空闲状态(无数据传输时)的电平。

  • CPOL=0(LOW):空闲时 SCK 为低电平,数据传输时 SCK 在高 / 低电平间切换。
  • CPOL=1(HIGH):空闲时 SCK 为高电平,数据传输时 SCK 在低 / 高电平间切换。

4. Clock Phase (CPHA)(时钟相位)

定义数据采样的时机,即在 SCK 的第几个边沿(上升沿 / 下降沿)采集数据。

  • CPHA=0(1 Edge):第一个边沿采样

    • 若 CPOL=0(空闲低):SCK 从低到高的上升沿(第一个边沿)采样数据,下降沿发送数据;
    • 若 CPOL=1(空闲高):SCK 从高到低的下降沿(第一个边沿)采样数据,上升沿发送数据。
  • CPHA=1(2 Edge):第二个边沿采样

    • 若 CPOL=0(空闲低):SCK 从高到低的下降沿(第二个边沿)采样数据,上升沿发送数据;
    • 若 CPOL=1(空闲高):SCK 从低到高的上升沿(第二个边沿)采样数据,下降沿发送数据。

时钟模式组合(CPOL+CPHA)

CPOL 和 CPHA 的组合决定了 SPI 的 4 种核心时钟模式(Mode 0~3),是 SPI 配置的关键:

  • Mode 0:CPOL=0,CPHA=0(空闲低,上升沿采样)→ 最主流模式;
  • Mode 1:CPOL=0,CPHA=1(空闲低,下降沿采样);
  • Mode 2:CPOL=1,CPHA=0(空闲高,下降沿采样);
  • Mode 3:CPOL=1,CPHA=1(空闲高,上升沿采样)。

SPI CRC

CRC(Cyclic Redundancy Check,循环冗余校验)是 SPI 的可选数据校验机制,用于检测传输过程中的数据错误,提升通信可靠性。

1. CRC Polynomial(CRC 多项式)

SPI 控制器通过预设的 CRC 多项式生成校验码,主设备发送数据时附加 CRC 码,从设备接收后重新计算 CRC 并与接收的 CRC 码对比,判断数据是否出错。

  • 常用多项式:SPI 默认采用 CRC-8(多项式为 x⁸+x⁷+x⁶+x⁴+x²+1,对应十六进制 0x107),部分控制器支持 CRC-16(如 0x8005)。
  • 配置方式:需主从设备配置相同的 CRC 多项式,否则校验结果不匹配;若外设不支持 CRC,需关闭 SPI 的 CRC 功能,避免数据附加冗余位导致解析错误。
  • 应用场景:适用于高可靠性要求的场景(如工业控制、医疗设备),普通消费类外设(如显示屏)通常关闭 CRC 以提升传输效率。

2. CRC 校验流程

  1. 主设备初始化 CRC 计算器,写入待传输数据,生成 CRC 码;
  2. 主设备发送数据帧(含有效数据 + CRC 码);
  3. 从设备接收数据后,用相同多项式计算 CRC,对比接收到的 CRC 码;
  4. 若一致,确认数据正确;若不一致,判定传输错误,可触发重传或报错。

SPI 额外注意事项

  1. 总线拓扑:SPI 支持一主多从的星形拓扑,需注意片选信号的隔离和匹配,避免信号串扰;
  2. 信号电平:SPI 通常为 3.3V 电平,部分外设支持 5V,需通过电平转换芯片匹配,避免引脚烧毁;
  3. 传输同步:主设备的 SCK 时钟需与数据传输严格同步,高频传输时需考虑 PCB 走线长度,减少信号延迟;
  4. 中断 / DMA:高速 SPI 传输建议使用 DMA(直接存储器访问),减少 CPU 占用;低速传输可使用中断或轮询方式。

片选和数据/命令控制

涉及到数据传输。后面再补充

1. GRAM 写操作模式

LCD 驱动 IC 具有 GRAM(图形显存)写模式。当主机发送写显存命令(通常为 0x2C)后,控制器进入该模式,后续接收的所有数据均被写入 GRAM,并直接反映在屏幕上。

重要行为

  • 在 GRAM 写模式下,如果 DC(数据/命令)引脚被意外拉低(即发送了命令),控制器会立即退出 GRAM 模式,转而执行命令解析。
  • 因此,连续写入像素数据期间必须保持 DC 为高电平(数据模式)。

2. 命令分类

根据是否需要附带数据,LCD 命令可分为三类:

类型说明
无数据操作命令仅发送命令字节,无后续数据。例如:软件复位、进入睡眠模式等。
有数据操作命令发送命令后,必须发送固定数量的数据字节。例如:设置对比度、伽马校正。
不定长数据操作发送命令后,可连续发送任意长度的数据,直到被新命令打断。典型例子:写显存命令(0x2C

对于写显存操作,通常需要先通过 0x2A0x2B 命令设置窗口(地址范围),包括起始坐标 (x, y) 和结束坐标 (x1, y1)。之后发送的像素数据将按以下规则自动填充:

  • 第一个数据写入 (x, y)
  • 随后每个数据依次写入 (x+1, y)(x+2, y),… 直到该行末尾(x1)。
  • 到达行末后,列地址自动加 1(y+1),行地址回到起始列 x,继续写入。
  • 重复上述过程,直到最后一个数据写入 (x1, y1)。此后,控制器忽略任何额外发送的数据,直至收到新的命令或重新设置窗口。

:地址递增方向(先 X 后 Y 或先 Y 后 X)可通过特定寄存器配置,用于实现横屏/竖屏刷新方向。


3. SPI 通信方式:4 线制与 3 线制

3.1 4 线制 SPI(4W)

  • 信号线:SCK、SDI/MOSI、CS、DC(数据/命令区分线)。
  • 主机通过 DC 电平告知当前传输的是命令(DC=0)还是数据(DC=1)。

8 位模式

  • 每个传输周期为 8 个时钟周期。CS 可保持低电平以连续发送多个字节。

16 位模式

  • 若 LCD 控制器仅支持 8 位接收,则 16 位传输会被自动拆分为两个连续的 8 位传输。
  • 关键限制:发送命令时(如 0x2A0x2B0x2C),必须使用 8 位传输。若强行使用 16 位发送一个 8 位命令(例如高 8 位为命令,低 8 位填充 0),控制器会将低 8 位误判为数据,导致错误。 因此,16 位模式通常仅用于批量传输像素数据(颜色值),以提高效率。

3.2 3 线制 SPI(3W)

  • 无专用 DC 线。命令/数据的区分信息嵌入在数据流中:每个 9 位传输周期内,第 1 位为 DC 标志(1=数据,0=命令),后续 8 位为有效数据。

8 位模式的软件模拟

主机需用 GPIO 精确产生 9 个时钟周期:

// 假设已定义 DC_flag(0或1),data_byte 为要发送的8位数据

// 1. 发送 DC 标志位
HAL_GPIO_WritePin(SPI_SDIO_GPIO_Port, SPI_SDIO_Pin, DC_flag);
HAL_GPIO_WritePin(SPI_SCK_GPIO_Port, SPI_SCK_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(SPI_SCK_GPIO_Port, SPI_SCK_Pin, GPIO_PIN_RESET);

// 2. 发送 8 位数据(MSB 优先)
for (int i = 7; i >= 0; i--) {
    uint8_t bit = (data_byte >> i) & 0x01;
    HAL_GPIO_WritePin(SPI_SDIO_GPIO_Port, SPI_SDIO_Pin, bit);
    HAL_GPIO_WritePin(SPI_SCK_GPIO_Port, SPI_SCK_Pin, GPIO_PIN_SET);
    HAL_GPIO_WritePin(SPI_SCK_GPIO_Port, SPI_SCK_Pin, GPIO_PIN_RESET);
}

16 位模式的处理技巧

在 3 线制下,若想利用硬件 SPI 的 16 位传输能力,需解决“每 9 位为一个周期”与硬件“每 16 位连续时钟”之间的矛盾。常用两种方法:

方法原理实现要点
方法一:屏幕自动截取LCD 控制器的移位寄存器仅保留前 9 位,丢弃后续时钟产生的额外位。发送完整的 16 位数据,其中高 9 位有效(1 位 DC + 8 位数据),低 7 位任意填充(如 0)。发送完毕后拉高 CS,控制器在 CS 上升沿处理已接收的前 9 位,忽略剩余位。
方法二:CS 提前终止在发送完前 9 位后立即拉高 CS,强制终止本次传输,后 7 位不发送。该方法要求硬件能精确控制 CS 的拉高时机,但多数 SPI 外设难以在传输中途改变 CS 状态,故实际较少使用。

推荐方法一:构造 16 位数据 (DC_flag << 8) | data_byte,然后左移 7 位(使有效位对齐到高 9 位),低 7 位补 0。调用 HAL_SPI_Transmit 发送该 16 位数据,并在传输完成后拉高 CS。屏幕控制器将正确解析前 9 位。


4. 关键注意事项

  • GRAM 写模式下,务必保持 DC 为高电平,否则会意外退出数据模式。
  • 对于 4 线制 SPI,发送 8 位命令时禁止使用 16 位传输
  • 对于 3 线制 SPI 的 16 位传输,必须确保 LCD 控制器能够忽略多余的时钟位(多数主流控制器支持),否则需退回到 8 位模拟方式。
  • 窗口地址设置后不会自动清空,除非重新发送 0x2A / 0x2B 命令覆盖。