4. SPI 通信协议详解

SPI(Serial Peripheral Interface,串行外设接口)是一种高速、全双工、同步的串行通信总线。在 LCD TFT 驱动中,SPI 常用于传输初始化命令、配置寄存器以及低速图像数据(如 ILI9341/ST7789 等驱动 IC 的初始化序列)。本章将深入剖析 SPI 的四线模式、时钟极性与相位、主从模式以及 STM32 的配置方法。

4.1 SPI 四线模式

标准 SPI 总线使用四根信号线实现全双工通信,各信号线定义如下:

信号线 全称 方向(主→从) 功能描述
SCLK Serial Clock 主→从 由主机产生的时钟信号,同步数据传输
MOSI Master Out Slave In 主→从 主机输出数据线,从机接收
MISO Master In Slave Out 从→主 从机输出数据线,主机接收
CS/SS Chip Select / Slave Select 主→从 片选信号,低电平有效,选中特定从机
LCD 驱动中的 SPI 变体: 许多 LCD 驱动 IC(如 ILI9341)支持 3 线 SPI 模式(无 MISO),或 4 线 SPI + D/CX 模式。D/CX 线用于区分传输的是命令(Command)还是数据(Data),这是 LCD 驱动中非常关键的扩展。

4.2 时钟极性 (CPOL) 与相位 (CPHA)

SPI 协议通过配置时钟极性(CPOL)和时钟相位(CPHA)来定义数据采样与切换的时序关系,共有四种工作模式。

  • CPOL:定义 SCLK 空闲状态的电平。
    • CPOL = 0:空闲时 SCLK 为低电平。
    • CPOL = 1:空闲时 SCLK 为高电平。
  • CPHA:定义数据采样发生在 SCLK 的哪个边沿。
    • CPHA = 0:在第一个边沿(上升沿或下降沿,取决于 CPOL)采样数据。
    • CPHA = 1:在第二个边沿采样数据。
四种模式速查表:
模式 CPOL CPHA 空闲时钟电平 数据采样边沿 数据切换边沿
模式 0 0 0 低 上升沿 下降沿
模式 1 0 1 低 下降沿 上升沿
模式 2 1 0 高 下降沿 上升沿
模式 3 1 1 高 上升沿 下降沿
常见误区: 多数 LCD 驱动 IC(如 ILI9341、ST7789)默认使用 SPI 模式 0(CPOL=0, CPHA=0)。但务必查阅具体芯片的数据手册,错误配置 CPOL/CPHA 会导致通信数据完全错位。
SPI 四种模式时序对比 (CPOL & CPHA) 模式 0 (CPOL=0, CPHA=0) SCLK MOSI 采样 模式 1 (CPOL=0, CPHA=1) SCLK MOSI 采样 模式 2 (CPOL=1, CPHA=0) SCLK MOSI 采样 模式 3 (CPOL=1, CPHA=1) SCLK MOSI 采样 图例说明 模式0采样点(上升沿) 模式1采样点(下降沿) 模式2采样点(下降沿) 模式3采样点(上升沿)

4.3 SPI 主从模式

SPI 总线采用主从(Master-Slave)架构,通信始终由主机发起和控制。

  • 主机 (Master):产生 SCLK 时钟信号,控制 CS 片选,决定通信时序。通常 MCU(如 STM32)作为主机。
  • 从机 (Slave):被动接收 SCLK 和 CS 信号,在主机控制下发送或接收数据。LCD 驱动 IC 通常作为从机。

多从机连接时,每个从机独占一根 CS 线,主机通过拉低对应 CS 线来选择通信目标。MOSI 和 MISO 线可以共享。

SPI 主从多设备连接架构 主机 (Master) STM32 MCU 从机 1 LCD 驱动 IC 从机 2 Flash 存储器 从机 3 传感器 SCLK MOSI MISO CS1 CS2 CS3 💡 每个从机独占一根 CS 片选线;SCLK、MOSI、MISO 可共享。主机通过拉低对应 CS 线来选择通信目标。

4.4 STM32 SPI 配置

以 STM32F4 系列为例,使用 HAL 库配置 SPI 外设的典型步骤如下:

  1. 使能 SPI 及 GPIO 时钟:通过 RCC 开启 SPI 外设时钟和对应引脚 GPIO 时钟。
  2. 配置 GPIO 复用功能:将 SCLK、MOSI、MISO、CS 引脚设置为复用推挽输出/输入模式。
  3. 初始化 SPI 句柄:设置 SPI 模式(主/从)、数据大小(8/16位)、CPOL/CPHA、波特率预分频、MSB/LSB 优先等参数。
  4. 使能 SPI 外设:调用 HAL_SPI_Init() 完成初始化。
  5. 收发数据:使用 HAL_SPI_Transmit()、HAL_SPI_Receive() 或 HAL_SPI_TransmitReceive() 进行全双工通信。
STM32 HAL 库 SPI 配置示例代码:
// SPI 句柄定义
SPI_HandleTypeDef hspi1;

// SPI 初始化函数
void MX_SPI1_Init(void)
{
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;           // 主机模式
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;  // 全双工
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;      // 8 位数据
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;    // CPOL = 0
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;        // CPHA = 0 (模式 0)
    hspi1.Init.NSS = SPI_NSS_SOFT;                // 软件管理 CS
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 分频
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;       // MSB 先行
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
    hspi1.Init.CRCPolynomial = 10;

    if (HAL_SPI_Init(&hspi1) != HAL_OK)
    {
        Error_Handler(); // 初始化失败处理
    }
}

// GPIO 配置(以 PB3=SCLK, PB4=MISO, PB5=MOSI, PA4=CS 为例)
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (hspi->Instance == SPI1)
    {
        __HAL_RCC_SPI1_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();

        // SCLK, MOSI, MISO 复用推挽输出
        GPIO_InitStruct.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        // CS 引脚(软件控制,推挽输出)
        GPIO_InitStruct.Pin = GPIO_PIN_4;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); // CS 默认高电平
    }
}

// 发送数据示例
void SPI_WriteByte(uint8_t data)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // CS 拉低
    HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);   // CS 拉高
}
配置避坑指南:
  • 波特率:SPI 时钟频率不能超过从机支持的最大频率(LCD 驱动 IC 通常为 10~40 MHz)。
  • CS 控制:使用软件 NSS 时,务必在每次传输前后手动拉低/拉高 CS 引脚。
  • 数据大小:LCD 驱动 IC 通常使用 8 位 SPI 传输,但部分命令/参数需组合成 16 位。
  • 全双工 vs 半双工:若 LCD 只写不读(如仅发送命令和像素数据),可配置为 2 线只发模式以节省引脚。

4.5 本章小结