《I2CSPICAN总线设备接入与驱动实战》

3、总线基础:SPI协议原理、模式、时钟极性与相位、全双工通信

SPI(Serial Peripheral Interface,串行外设接口)是由Motorola在20世纪80年代中期定义的一种高速、全双工、同步串行通信总线。在I2C、SPI、CAN三类总线中,SPI以极低的协议开销和极高的传输速率(通常可达10 Mbps至数十Mbps)成为嵌入式系统中连接传感器、ADC/DAC、Flash存储器、SD卡、显示控制器等外设的首选。与I2C依赖开漏极和上拉电阻不同,SPI采用推挽输出,信号边沿陡峭,时序控制更加直接。理解SPI的协议原理、四种工作模式以及时钟极性与相位的匹配,是驱动开发中确保数据采样正确、避免通信乱码的基础前提。

SPI Master (MCU/SoC) SCLK 发生器 移位寄存器 Slave 0 CS0 低电平选中 (Flash/ADC/传感器) Slave 1 CS1 低电平选中 (SD卡/显示控制器) Slave N CS_N 低电平选中 SCLK (时钟) MOSI (主→从数据) MISO (从→主数据) CS0 (片选0) CS1 (片选1) CS_N (片选N) 全双工:MOSI与MISO同时传输 CPOL=0/1 决定空闲时钟电平 CPHA=0/1 决定采样边沿

上图展示了SPI总线的基本拓扑结构。主设备(Master)通过SCLK提供时钟,通过MOSI(Master Out Slave In)发送数据,通过MISO(Master In Slave Out)接收数据,并通过独立的片选信号CS(Chip Select)选择目标从设备。SPI是典型的“一主多从”总线,所有从设备共享SCLK、MOSI和MISO,但每个从设备拥有独立的CS引脚。这种拓扑决定了SPI的硬件连接简单,但片选管理必须由软件或硬件逻辑严格保证,同一时刻只能有一个从设备被拉低CS。

3.1 SPI协议核心原理:四线制与数据交换机制

SPI协议的核心是四根信号线:SCLK(串行时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)。其中SCLK由主设备产生,决定了数据传输的速率和时序;MOSI和MISO分别承载主设备到从设备、从设备到主设备的数据流;CS为低电平有效,用于选中目标从设备。SPI没有像I2C那样的设备地址机制,完全依靠硬件片选来区分设备,因此从设备数量增加时,主控的GPIO资源消耗也随之增加。

SPI的数据交换基于移位寄存器原理。主设备和从设备内部各有一个移位寄存器,每个时钟周期,主设备通过MOSI移出1位数据,同时通过MISO移入1位数据。经过8个时钟周期(或16、32个周期,取决于字长配置),主设备和从设备完成一次字节交换。这种机制使得SPI天然支持全双工通信——发送和接收同时进行,无需像I2C那样在发送和接收之间切换方向。

信号线方向功能说明空闲状态
SCLK主→从串行时钟,由主设备产生,决定位传输速率由CPOL决定(高或低)
MOSI主→从主设备输出数据线,从设备输入高阻或最后一位电平
MISO从→主从设备输出数据线,主设备输入高阻(未选中时)
CS主→从片选信号,低电平有效,选中从设备高电平

从设备在CS为高时,MISO必须处于高阻态,以避免多个从设备同时驱动MISO造成总线冲突。这是SPI硬件设计中必须注意的关键点,尤其是在多从设备共享MISO的拓扑中。

3.2 SPI四种工作模式:CPOL与CPHA的排列组合

SPI协议定义了四种工作模式,由时钟极性(CPOL,Clock Polarity)和时钟相位(CPHA,Clock Phase)两个参数决定。CPOL决定SCLK在空闲状态时的电平:CPOL=0表示空闲时SCLK为低电平,CPOL=1表示空闲时SCLK为高电平。CPHA决定数据采样发生在SCLK的哪个边沿:CPHA=0表示在第一个边沿(即SCLK从空闲状态跳变的第一个边沿)采样数据,CPHA=1表示在第二个边沿采样数据。

四种模式的组合如下:

  • Mode 0(CPOL=0, CPHA=0):空闲时SCLK为低,数据在SCLK上升沿采样,下降沿切换。这是最常用的模式,绝大多数SPI从设备默认支持。
  • Mode 1(CPOL=0, CPHA=1):空闲时SCLK为低,数据在SCLK下降沿采样,上升沿切换。
  • Mode 2(CPOL=1, CPHA=0):空闲时SCLK为高,数据在SCLK下降沿采样,上升沿切换。
  • Mode 3(CPOL=1, CPHA=1):空闲时SCLK为高,数据在SCLK上升沿采样,下降沿切换。

驱动开发中最常见的错误之一就是主设备和从设备的模式不匹配。例如,某款MEMS加速度传感器(如ADXL345)支持Mode 0和Mode 3,但默认上电后处于Mode 0。如果主控配置为Mode 3,则通信完全失败。因此,在初始化SPI外设之前,必须仔细阅读从设备数据手册中的时序图,确认其要求的CPOL和CPHA。

判断方法:查看从设备数据手册中的时序图,重点关注两个信息:一是SCLK空闲时的电平(高或低),对应CPOL;二是数据在哪个边沿被锁存(第一个边沿还是第二个边沿),对应CPHA。如果时序图中标注了“Data setup on rising edge”或“Data sampled on falling edge”,即可直接对应到具体模式。

3.3 全双工通信机制与数据吞吐量计算

SPI的全双工特性意味着主设备在发送一个字节的同时,也能接收到从设备返回的一个字节。这在许多场景中非常有用:例如,读取Flash存储器的状态寄存器时,主设备发送命令字节(0x05),同时从设备在MISO上返回状态值;读取ADC转换结果时,主设备发送空字节(0x00),从设备在MISO上返回采样数据。全双工机制减少了通信往返次数,提高了总线利用率。

数据吞吐量的计算公式为:吞吐量 = SCLK频率 / 8(以字节为单位)。例如,SCLK为10 MHz时,理论最大吞吐量为1.25 MB/s。但实际吞吐量受限于从设备的响应时间、片选切换开销以及软件处理延迟。在驱动设计中,如果追求高吞吐量,应尽量减少片选切换次数,采用连续读取模式(如Flash的Fast Read命令),并利用DMA(直接内存访问)传输数据,避免CPU逐字节搬运。

// 示例:SPI Mode 0 下读取ADC数据(伪代码)
uint16_t spi_read_adc(void) {
    uint8_t tx_data = 0x00;  // 发送空字节以产生时钟
    uint8_t rx_high, rx_low;
    // 拉低CS,选中ADC
    CS_LOW();
    // 读取高字节(全双工:发送0x00,接收高字节)
    rx_high = spi_transfer_byte(tx_data);
    // 读取低字节
    rx_low = spi_transfer_byte(tx_data);
    // 拉高CS,释放总线
    CS_HIGH();
    return (rx_high << 8) | rx_low;
}

上述代码展示了全双工通信的典型用法:主设备发送空字节,从设备在MISO上返回数据。需要注意的是,从设备在CS拉低后可能需要一定的准备时间(t_CS_Setup),在读取数据前应确保从设备已准备好输出。

3.4 实战案例:配置STM32 SPI驱动外部Flash(W25Q64)

以STM32F4系列MCU驱动W25Q64 SPI Flash为例,演示SPI模式配置和全双工通信的完整流程。W25Q64支持Mode 0和Mode 3,本案例选择Mode 0(CPOL=0, CPHA=0),SCLK频率设为10 MHz。

背景与约束:系统需要存储设备配置参数和日志数据,Flash容量为8 MB,要求写入速度不低于500 KB/s。MCU的SPI1外设挂载在APB2总线上,APB2时钟为84 MHz,SPI1最大分频系数为256,最小为2。要获得10 MHz的SCLK,需要设置分频系数为8(84/8=10.5 MHz,接近目标)。

初始现象:在未正确配置SPI模式时,读取Flash的JEDEC ID(命令0x9F)返回0xFF 0xFF 0xFF,说明通信失败。通过逻辑分析仪抓取波形,发现SCLK空闲电平为高(CPOL=1),而W25Q64在Mode 0下要求空闲电平为低,导致数据采样错误。

分析判断过程:检查STM32的SPI配置代码,发现初始化时设置了SPI_InitStructure.SPI_CPOL = SPI_CPOL_High,而W25Q64数据手册明确要求Mode 0或Mode 3。由于代码中CPHA设置为SPI_CPHA_1Edge(即CPHA=0),因此实际配置为Mode 2(CPOL=1, CPHA=0),与W25Q64不匹配。

实际操作步骤:

  1. 修改SPI初始化参数:将SPI_CPOL改为SPI_CPOL_Low,SPI_CPHA保持SPI_CPHA_1Edge,即Mode 0。
  2. 设置SPI时钟分频:SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8,得到SCLK约10.5 MHz。
  3. 配置SPI数据帧格式为8位,MSB先行。
  4. 重新编译烧录,读取JEDEC ID,返回0xEF 0x40 0x17,与W25Q64的预期ID一致。

结果对比:修正前读取ID失败,修正后读取成功。进一步测试写入和读取速度,实测写入速度约520 KB/s,读取速度约1.1 MB/s,满足系统要求。

风险与代价:如果SCLK频率过高(超过Flash的最大支持频率),可能导致数据采样不稳定。W25Q64支持最高133 MHz的时钟,但实际布线较长时,应适当降低频率以保证信号完整性。

经验提醒:在SPI驱动开发中,务必使用逻辑分析仪或示波器抓取SCLK、MOSI、MISO和CS的波形,确认空闲电平、采样边沿和数据对齐是否符合预期。不要仅凭代码配置判断模式正确性,因为硬件连接和从设备内部逻辑可能引入额外延迟。

3.5 常见误区与总结复盘

SPI驱动开发中常见的误区包括:

  • 模式不匹配:主设备和从设备的CPOL/CPHA不一致,导致数据采样错误。解决方法是查阅数据手册,并在初始化代码中明确设置。
  • 片选信号时序不当:CS拉低后立即开始时钟,未给从设备留出准备时间;或CS拉高过早,导致最后一个字节传输不完整。应参考数据手册中的t_CS_Setup和t_CS_Hold时间。
  • MISO多从设备冲突:多个从设备共享MISO时,未选中设备的MISO必须为高阻态。如果从设备不支持三态输出,需要增加外部缓冲器或使用GPIO模拟片选。
  • 忽略从设备的字节序:部分从设备采用LSB先行(如某些音频编解码器),而MCU默认MSB先行,导致数据错位。
  • DMA与中断竞争:使用DMA传输时,未正确处理传输完成中断,导致数据覆盖或丢失。

总结复盘:SPI协议虽然简单,但工程实现中涉及模式匹配、时序约束、信号完整性和软件架构等多个层面。驱动开发者应养成“先看时序图、再写代码、最后用仪器验证”的习惯。在调试阶段,建议使用低速时钟(如1 MHz)排除信号完整性问题,待通信稳定后再逐步提高频率。