SPI(Serial Peripheral Interface)作为嵌入式系统中最常用的通信协议之一,其设计哲学体现了Motorola工程师对高效硬件通信的深刻理解。我第一次接触SPI是在调试一块STM32F103开发板与NOR Flash的通信时,当时被其简洁而高效的特性所吸引。
SPI本质上是一个同步串行数据链路标准,采用主从架构设计。与I²C等协议不同,SPI最显著的特点是它不依赖复杂的协议栈,而是通过四根基础信号线实现高速数据传输。这种设计使得SPI在10MHz以上的时钟频率下仍能稳定工作,远高于I²C的400kHz标准模式。
实际工程中,我曾用SPI接口在72MHz主频的STM32上实现了36MHz的通信速率,传输1MB数据仅需0.23秒,这是UART或I²C难以企及的。
SPI的拓扑结构支持一主多从模式,但实现方式灵活多样。最常见的是独立片选方案,每个从设备需要独占一个GPIO作为片选信号。在引脚资源紧张时,也可以采用菊花链连接,但会牺牲部分性能。这种灵活性使得SPI既能满足高速存储设备的需求,也能适应简单的传感器读取场景。
SPI的四根基础信号线各司其职:
MOSI(Master Out Slave In):主设备数据输出线。在STM32的硬件SPI实现中,MOSI通常映射到特定的GPIO复用功能引脚。例如在STM32F4系列中,SPI1的MOSI固定为PA7引脚,这种硬件设计确保了信号完整性。
MISO(Master In Slave Out):从设备数据输出线。一个容易忽视的细节是,当SPI配置为主模式时,即使不使用MISO接收数据,也必须将该引脚配置为浮空输入模式,否则可能导致通信异常。
SCLK(Serial Clock):时钟信号线。其极性和相位可通过CPOL和CPHA参数配置,形成四种工作模式。我在调试BME280气压传感器时发现,该芯片要求SPI模式3(CPOL=1,CPHA=1),配置错误会导致数据读取全为0xFF。
CS/SS(Chip Select/Slave Select):片选信号线。硬件设计时需要注意,CS信号应通过10kΩ电阻上拉到VCC,避免上电期间从设备误响应。对于高速SPI设备(如ADXL345加速度计),CS走线应尽量短以减少寄生电容。
SPI接口的电气特性直接影响通信稳定性:
以下是一个典型的SPI接口电路设计参数:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| 上拉电阻 | 4.7k-10kΩ | CS信号线必需 |
| 走线宽度 | 0.2-0.3mm | 保持阻抗连续 |
| 线间距 | 2倍线宽 | 减少串扰 |
| 最大长度 | 15cm@10MHz | 与频率成反比 |
SPI的核心是移位寄存器机制。主从设备各有一个8位移位寄存器,通过MOSI和MISO连接成环形结构。当时钟边沿到来时,两个寄存器同时左移一位,实现数据交换。这个过程类似于两个人面对面传递一摞盘子,每次只移动最上面的那个。
在STM32的SPI外设中,数据传输通过三个寄存器协同工作:
SPI的四种工作模式组合源于CPOL和CPHA的不同设置:
| 模式 | CPOL | CPHA | 典型应用设备 |
|---|---|---|---|
| 模式0 | 0 | 0 | 多数SPI Flash(如W25Q64) |
| 模式1 | 0 | 1 | TI ADS8320 ADC |
| 模式2 | 1 | 0 | Maxim MAX31855热电偶转换器 |
| 模式3 | 1 | 1 | Bosch BMP280气压传感器 |
实际调试中发现,模式选择错误是最常见的SPI通信故障。一个快速判断方法是:用逻辑分析仪捕获CS下降沿后的第一个SCLK边沿,数据采样应该发生在相反的边沿。
STM32的SPI外设提供丰富的配置选项,以下是关键寄存器位的功能说明:
| 寄存器 | 关键位 | 功能 | 推荐配置 |
|---|---|---|---|
| SPI_CR1 | SPE | SPI使能 | 1 |
| BR[2:0] | 波特率分频 | 001(FPCLK/4) | |
| MSTR | 主模式选择 | 1 | |
| CPOL/CPHA | 时钟模式 | 根据从设备 | |
| SPI_CR2 | SSOE | 硬件NSS输出 | 0(软件控制) |
| FRF | 帧格式 | 0(Motorola格式) |
对于高速数据传输(如LCD刷新),使用DMA可以大幅提升效率。以下是SPI1与DMA1通道3配合的配置示例:
c复制// SPI1 TX DMA配置(内存到外设)
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tx_buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = buffer_size;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_Init(DMA1_Channel3, &DMA_InitStructure);
// 使能SPI TX DMA
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
DMA_Cmd(DMA1_Channel3, ENABLE);
以华邦W25Q128为例,完整的擦除-编程流程包含以下关键步骤:
c复制// 安全写函数实现
void SPI_Flash_Write_Safe(uint32_t addr, uint8_t *data, uint16_t len) {
uint16_t page_remain;
while(len > 0) {
page_remain = 256 - (addr % 256); // 计算当前页剩余空间
uint16_t write_len = MIN(len, page_remain);
FLASH_WriteEnable(); // 写使能
FLASH_PageProgram(addr, data, write_len); // 页编程
FLASH_WaitForWriteEnd(); // 等待完成
addr += write_len;
data += write_len;
len -= write_len;
}
}
在复杂系统中管理多个SPI从设备时,推荐采用以下架构:
硬件设计:
软件架构:
c复制// 多设备管理示例
typedef struct {
GPIO_TypeDef* cs_port;
uint16_t cs_pin;
SPI_InitTypeDef spi_config;
} SPIDevice;
SPIDevice devices[] = {
{GPIOB, GPIO_Pin_12, {SPI_Direction_2Lines_FullDuplex, SPI_Mode_Master,...}}, // Flash
{GPIOC, GPIO_Pin_7, {SPI_Direction_1Line_Rx, SPI_Mode_Master,...}} // ADC
};
void SPI_SelectDevice(uint8_t dev_id) {
// 先取消所有片选
for(int i=0; i<sizeof(devices); i++) {
GPIO_SetBits(devices[i].cs_port, devices[i].cs_pin);
}
// 选中指定设备
GPIO_ResetBits(devices[dev_id].cs_port, devices[dev_id].cs_pin);
// 重新配置SPI参数
SPI_Init(SPI1, &devices[dev_id].spi_config);
}
通过实测发现,SPI实际吞吐量受以下因素影响:
软件开销:轮询标志位的延迟
时钟分频:不当的BR设置
code复制f_SCLK = f_PCLK / (2×(BR[2:0]+1))
数据宽度:8位 vs 16位传输
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| 数据全为0xFF | 从设备未响应 | 检查CS信号、供电电压 |
| 数据错位 | CPOL/CPHA配置错误 | 用逻辑分析仪捕获时序 |
| 偶发错误 | 信号干扰 | 缩短走线,添加终端电阻 |
| DMA传输停止 | 缓冲区溢出 | 检查DMA_CNDTR寄存器 |
| 从设备发热 | 总线冲突 | 检查MISO是否配置为输入 |
当SPI不适用时,可考虑以下替代方案:
| 方案 | 速率 | 引脚数 | 适用场景 |
|---|---|---|---|
| I²C | ≤3.4MHz | 2 | 多设备、低速传感器 |
| UART | ≤12Mbps | 2 | 设备间通信 |
| QSPI | ≤80MHz | 6 | 高速Flash |
| LVDS | ≥100Mbps | 2 | 长距离传输 |
在最近的一个物联网项目中,我们同时使用了SPI(连接LoRa模块)、I²C(连接环境传感器)和UART(连接GPS模块),每种协议都发挥了其独特优势。这种混合架构设计既满足了性能需求,又优化了硬件资源分配。