STM32开发之SPI通信详解:从协议基础到寄存器配置与编程实战

发布时间:2026/10/12 2:23:27
STM32开发之SPI通信详解:从协议基础到寄存器配置与编程实战 1. SPI概述SPISerial Peripheral Interface串行外设接口是一种高速、全双工、同步的通信总线由摩托罗拉公司于20世纪80年代提出。SPI总线广泛应用于嵌入式系统中用于连接微控制器与各种外设如传感器、存储器、显示屏、ADC/DAC等。SPI总线具有以下显著特点全双工通信数据可同时发送和接收通信效率高。高速传输SPI时钟频率可达数十MHz远高于I2C等低速总线。简单灵活协议简单无复杂寻址机制硬件实现成本低。主从结构由一个主机控制多个从机通过片选信号CS选择通信对象。在STM32系列微控制器中SPI外设被广泛集成支持多种工作模式和DMA传输为开发者提供了高效、可靠的数据通信方案。2. SPI协议2.1 物理层与信号线SPI总线通常由四根信号线组成信号线名称功能说明SCLK串行时钟由主机产生用于同步数据传输MOSI主机输出/从机输入主机向从机发送数据MISO主机输入/从机输出从机向主机发送数据CS/SS片选信号低电平有效用于选中目标从机当主机需要与某个从机通信时将该从机的CS引脚拉低即可建立通信链路。多个从机可共享SCLK、MOSI和MISO线但每个从机需要独立的CS引脚。2.2 通信原理SPI通信基于移位寄存器原理。主机和从机各有一个8位或更多位的移位寄存器通过时钟信号逐位交换数据。具体过程如下主机拉低目标从机的CS引脚启动通信。主机在SCLK引脚上产生时钟脉冲。在每个时钟边沿主机通过MOSI线发送一位数据同时从机通过MISO线返回一位数据。经过8个时钟周期后主机和从机的移位寄存器完成一次数据交换。通信结束后主机拉高CS引脚释放从机。这种机制使得SPI在发送数据的同时也能接收数据实现了真正的全双工通信。2.3 四种工作模式SPI协议定义了四种工作模式由时钟极性CPOL和时钟相位CPHA两个参数决定模式CPOLCPHA数据采样边沿模式000上升沿采样模式101下降沿采样模式210下降沿采样模式311上升沿采样CPOL时钟极性决定空闲状态下SCLK的电平。CPOL0时空闲为低电平CPOL1时空闲为高电平。CPHA时钟相位决定数据在哪个时钟边沿被采样。CPHA0时在第一个边沿采样CPHA1时在第二个边沿采样。主机和从机必须配置为相同的工作模式才能正常通信。在实际开发中模式0CPOL0CPHA0最为常用。2.4 数据帧格式SPI支持多种数据帧长度常见的有8位、16位和32位。STM32的SPI外设可通过寄存器配置数据帧格式。数据发送时高位在前MSB First或低位在前LSB First均可配置默认通常为MSB First。此外SPI还支持多字节连续传输。在连续传输模式下主机保持CS引脚为低电平持续发送多个数据帧直到所有数据发送完毕后再释放CS适用于Flash存储器等需要连续读写的外设。2.5 多从机连接方式当系统中存在多个SPI从机时常见的连接方式有两种独立片选方式每个从机使用独立的CS引脚主机通过控制不同的CS引脚来选择通信对象。这是最常用的方式。菊花链方式所有从机串联在一起数据依次经过每个从机传递。该方式可减少CS引脚数量但延迟较高适用于特定场景。3. STM32 SPI外设STM32系列微控制器内置了多个SPI外设如SPI1、SPI2、SPI3等支持主机模式和从机模式并具备以下特性可编程时钟频率通过预分频器配置SCLK频率最高可达外设时钟的1/2。DMA支持可与DMA控制器配合实现数据的高速、无CPU干预传输。中断支持提供发送完成、接收完成、错误检测等多种中断。硬件CS管理支持硬件自动控制CS引脚简化软件设计。FIFO缓冲部分型号支持收发FIFO提高数据传输效率。在后续章节中我们将进一步介绍STM32 SPI的寄存器配置、初始化流程以及实际应用示例。4. STM32 SPI寄存器配置STM32的SPI外设通过一组寄存器进行配置和控制。下面以STM32F4系列为例介绍SPI常用的核心寄存器及其配置方法。STM32F4的SPI外设与F1系列相比在寄存器位定义和功能上有所差异例如F4的SPI_CR1中DFF位为Bit11且新增了FRXTH、TXDMAEN、RXDMAEN等位配置时需特别注意。4.1 常用寄存器一览寄存器名称功能说明SPI_CR1控制寄存器1配置主从模式、波特率、CPOL/CPHA、数据帧格式等SPI_CR2控制寄存器2配置DMA、中断使能、SS输出、帧格式等SPI_SR状态寄存器反映发送/接收状态、忙标志、错误标志等SPI_DR数据寄存器发送和接收数据的缓冲寄存器SPI_I2SCFGRI2S配置寄存器配置SPI为I2S模式部分型号支持4.2 SPI_CR1控制寄存器1STM32F4的SPI_CR1是SPI配置的核心寄存器主要位定义如下位名称功能说明Bit15BIDIMODE双向数据模式使能Bit14BIDIOE双向模式下的输出使能Bit13CRCEN硬件CRC使能Bit11DFF数据帧格式0为8位1为16位Bit10RXONLY只接收模式使能Bit9SSM软件从机管理使能Bit8SSI内部从机选择Bit7LSBFIRST帧格式0为MSB First1为LSB FirstBit6SPESPI使能位Bit5-3BR[2:0]波特率预分频控制Bit2MSTR主从模式选择1为主机0为从机Bit1CPOL时钟极性Bit0CPHA时钟相位与F1系列相比F4的SPI_CR1将DFF位调整到Bit11并将BR位调整为Bit5-3同时新增了RXONLY位Bit10用于只接收模式。配置时需注意这些位位置的差异。4.3 SPI_CR2控制寄存器2STM32F4的SPI_CR2在F1基础上增加了帧格式和FIFO相关配置位主要位定义如下位名称功能说明Bit14TXDMAEN发送缓冲区DMA使能Bit13RXDMAEN接收缓冲区DMA使能Bit12FRXTHFIFO接收阈值0为8位1为16位Bit11DS[3:0]数据大小配置配合SPI_CR1的DFF使用Bit7TXEIE发送缓冲区空中断使能Bit6RXNEIE接收缓冲区非空中断使能Bit5ERRIE错误中断使能Bit2SSOESS输出使能Bit1NSSPNSS脉冲模式F4新增4.4 寄存器配置示例下面以STM32F4系列标准外设库STM32F4xx Standard Peripheral Library为例演示SPI1主机模式的寄存器配置流程void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; // 1. 使能SPI1和GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 2. 配置SCLK、MOSI、MISO引脚为复用功能 GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, GPIO_InitStructure); // 3. 连接SPI1的引脚到AF5复用功能 GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); // 4. 配置SPI1为主机模式模式08位数据帧 SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_16; SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI1, SPI_InitStructure); // 5. 使能SPI1 SPI_Cmd(SPI1, ENABLE); }与F1系列不同F4的GPIO配置需要先使能AHB1总线时钟并将引脚设置为复用功能GPIO_Mode_AF再通过GPIO_PinAFConfig函数将引脚连接到SPI1的复用功能编号AF5。若直接操作寄存器核心步骤等价于设置SPI_CR1的MSTR、BR、CPOL、CPHA、DFF等位最后置位SPE使能SPI。4.5 数据收发与状态检测SPI数据收发通过SPI_DR数据寄存器完成。发送数据时将数据写入SPI_DR接收数据时从SPI_DR读取。收发过程中需要检测SPI_SR状态寄存器中的相关标志位TXE发送缓冲空发送缓冲区为空可写入下一个数据。RXNE接收缓冲非空接收缓冲区有数据可读。BSY忙标志SPI正在通信发送数据前需等待其清零。一个典型的单字节收发流程如下uint8_t SPI1_ReadWriteByte(uint8_t data) { // 等待发送缓冲区为空 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); // 写入待发送数据 SPI_I2S_SendData(SPI1, data); // 等待接收缓冲区非空 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); // 读取接收数据 return SPI_I2S_ReceiveData(SPI1); }通过上述寄存器配置和收发流程即可完成STM32F4 SPI外设的基础通信功能。在实际项目中还可结合DMA和中断机制进一步提升传输效率。5. SPI编程完整流程前面章节介绍了SPI协议和寄存器配置本节将给出一个完整的SPI编程流程涵盖初始化、数据收发和实际应用。整个流程以STM32F4系列标准外设库为例从硬件初始化到数据通信逐步展开。5.1 编程流程总览一个完整的SPI编程流程通常包含以下几个步骤使能时钟使能SPI外设时钟和对应GPIO端口的时钟。配置GPIO引脚将SCLK、MOSI、MISO引脚配置为复用功能并连接到SPI外设。配置SPI外设通过SPI_InitTypeDef结构体设置工作模式、数据帧格式、波特率等参数。使能SPI外设调用SPI_Cmd函数使能SPI。数据收发通过SPI_DR寄存器或库函数进行数据读写。状态检测根据SPI_SR状态寄存器判断发送/接收是否完成。5.2 结构体详解SPI_InitTypeDef是标准外设库中用于配置SPI外设的核心结构体其定义如下typedef struct { uint16_t SPI_Direction; // 数据传输方向 uint16_t SPI_Mode; // 主从模式 uint16_t SPI_DataSize; // 数据帧大小 uint16_t SPI_CPOL; // 时钟极性 uint16_t SPI_CPHA; // 时钟相位 uint16_t SPI_NSS; // 从机选择管理方式 uint16_t SPI_BaudRatePrescaler; // 波特率预分频 uint16_t SPI_FirstBit; // 数据位传输顺序 uint16_t SPI_CRCPolynomial; // CRC多项式可选 } SPI_InitTypeDef;该结构体各成员的含义如下成员可选值功能说明SPI_DirectionSPI_Direction_2Lines_FullDuplex双线全双工模式数据可同时发送和接收SPI_DirectionSPI_Direction_2Lines_RxOnly双线只接收模式仅通过MISO接收数据SPI_DirectionSPI_Direction_1Line_Tx单线发送模式MOSI和MISO复用同一根线SPI_DirectionSPI_Direction_1Line_Rx单线接收模式SPI_ModeSPI_Mode_Master主机模式由本设备产生SCLK时钟SPI_ModeSPI_Mode_Slave从机模式接收外部主机产生的时钟SPI_DataSizeSPI_DataSize_8b8位数据帧最常用SPI_DataSizeSPI_DataSize_16b16位数据帧SPI_CPOLSPI_CPOL_Low空闲时SCLK为低电平对应模式0和模式1SPI_CPOLSPI_CPOL_High空闲时SCLK为高电平对应模式2和模式3SPI_CPHASPI_CPHA_1Edge在第一个时钟边沿采样数据SPI_CPHASPI_CPHA_2Edge在第二个时钟边沿采样数据SPI_NSSSPI_NSS_Soft软件管理从机选择NSS引脚由软件控制SPI_NSSSPI_NSS_Hard硬件管理从机选择NSS引脚由硬件自动控制SPI_BaudRatePrescalerSPI_BaudRatePrescaler_2 至 _256波特率预分频系数决定SCLK频率SPI_FirstBitSPI_FirstBit_MSB高位在前传输SPI_FirstBitSPI_FirstBit_LSB低位在前传输其中SPI_BaudRatePrescaler的取值决定了SCLK的实际频率计算公式为SCLK频率 SPI外设时钟频率 / 预分频系数。例如当SPI1挂载在APB2总线上时钟为84MHz预分频系数为16时SCLK频率为84MHz / 16 5.25MHz。GPIO_InitTypeDef用于配置GPIO引脚的工作模式在SPI编程中用于设置SCLK、MOSI、MISO引脚的复用功能。其定义如下typedef struct { uint32_t GPIO_Pin; // 要配置的引脚编号 GPIOMode_TypeDef GPIO_Mode; // 引脚工作模式 GPIOSpeed_TypeDef GPIO_Speed; // 引脚输出速度 GPIOOType_TypeDef GPIO_OType; // 输出类型推挽或开漏 GPIOPuPd_TypeDef GPIO_PuPd; // 上下拉配置 } GPIO_InitTypeDef;各成员说明如下GPIO_Pin指定要配置的引脚如GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7表示同时配置PA5、PA6、PA7三个引脚。GPIO_Mode设置为GPIO_Mode_AF复用功能将引脚连接到SPI外设。这是F4系列与F1系列的重要区别F1系列使用GPIO_Mode_AF_PP复用推挽输出。GPIO_Speed设置引脚输出速度常用GPIO_Speed_50MHz。速度越高信号边沿越陡峭但功耗和EMI也越大。GPIO_OType输出类型SPI通信通常使用GPIO_OType_PP推挽输出可同时驱动高电平和低电平。GPIO_PuPd上下拉配置SPI引脚通常设置为GPIO_PuPd_NOPULL无上下拉因为SPI信号由主机和从机共同驱动。5.3 完整编程示例下面给出一个完整的SPI1主机模式编程示例包含初始化、数据收发和实际应用。该示例以STM32F4系列标准外设库为基础演示如何通过SPI与外部Flash存储器通信。#include stm32f4xx.h #include stm32f4xx_spi.h #include stm32f4xx_gpio.h #include stm32f4xx_rcc.h // SPI1引脚定义PA5SCLK, PA6MISO, PA7MOSI #define SPI1_SCLK_PIN GPIO_Pin_5 #define SPI1_MISO_PIN GPIO_Pin_6 #define SPI1_MOSI_PIN GPIO_Pin_7 #define SPI1_CS_PIN GPIO_Pin_4 // 片选引脚使用PA4 // 1. GPIO初始化配置SPI1引脚和片选引脚 void SPI1_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIOA时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); // 配置SCLK、MOSI、MISO为复用功能 GPIO_InitStructure.GPIO_Pin SPI1_SCLK_PIN | SPI1_MISO_PIN | SPI1_MOSI_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, GPIO_InitStructure); // 连接SPI1引脚到AF5复用功能 GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); // 配置片选引脚为普通推挽输出 GPIO_InitStructure.GPIO_Pin SPI1_CS_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, GPIO_InitStructure); // 片选默认拉高表示未选中从机 GPIO_SetBits(GPIOA, SPI1_CS_PIN); } // 2. SPI1初始化配置为主机模式模式08位数据帧 void SPI1_Init(void) { SPI_InitTypeDef SPI_InitStructure; // 使能SPI1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // 配置SPI1参数 SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_16; SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI1, SPI_InitStructure); // 使能SPI1 SPI_Cmd(SPI1, ENABLE); } // 3. 单字节收发函数 uint8_t SPI1_ReadWriteByte(uint8_t data) { // 等待发送缓冲区为空 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); // 写入待发送数据 SPI_I2S_SendData(SPI1, data); // 等待接收缓冲区非空 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); // 读取接收数据 return SPI_I2S_ReceiveData(SPI1); } // 4. 片选控制函数 void SPI1_CS_Low(void) { GPIO_ResetBits(GPIOA, SPI1_CS_PIN); } void SPI1_CS_High(void) { GPIO_SetBits(GPIOA, SPI1_CS_PIN); } // 5. 主函数演示完整的SPI通信流程 int main(void) { uint8_t tx_data 0xA5; // 待发送数据 uint8_t rx_data 0; // 接收数据 // 初始化GPIO和SPI SPI1_GPIO_Init(); SPI1_Init(); // 拉低片选选中从机 SPI1_CS_Low(); // 发送数据并接收从机返回的数据 rx_data SPI1_ReadWriteByte(tx_data); // 拉高片选释放从机 SPI1_CS_High(); // 此处可根据rx_data进行后续处理 while (1) { // 主循环 } }上述代码展示了SPI编程的完整流程首先初始化GPIO引脚将SCLK、MOSI、MISO配置为复用功能并连接到SPI1外设然后通过SPI_InitTypeDef结构体配置SPI1为主机模式、模式0、8位数据帧最后通过片选控制函数选中从机调用SPI1_ReadWriteByte完成数据收发。5.4 编程注意事项时钟使能顺序必须先使能GPIO时钟和SPI时钟再进行引脚和外设配置否则寄存器写入无效。引脚复用配置F4系列必须通过GPIO_PinAFConfig函数将引脚连接到SPI外设的复用功能编号AF5否则引脚无法工作。片选控制使用软件NSS时片选引脚需要手动控制。在通信开始前拉低CS通信结束后拉高CS。波特率选择SCLK频率不宜过高需根据从机支持的最大时钟频率确定。若从机不支持高速通信应适当增大预分频系数。模式匹配主机和从机必须配置为相同的CPOL和CPHA否则数据采样时序不一致导致通信失败。数据帧格式主机和从机的数据帧长度8位或16位必须一致且传输顺序MSB First或LSB First也要匹配。6. 总结本文围绕STM32的SPI通信展开从协议基础到实际编程进行了系统梳理。下面回顾全文的核心要点6.1 核心要点回顾SPI协议基础SPI是一种高速、全双工、同步的串行通信总线由SCLK、MOSI、MISO和CS四根信号线组成通过移位寄存器实现数据交换。四种工作模式由CPOL和CPHA两个参数组合决定主机和从机必须配置为相同模式才能正常通信实际开发中模式0最为常用。寄存器配置以STM32F4为例通过SPI_CR1配置主从模式、波特率、CPOL/CPHA和数据帧格式通过SPI_CR2配置DMA和中断通过SPI_SR检测收发状态。编程流程完整的SPI编程包含时钟使能、GPIO复用配置、SPI外设初始化、数据收发和状态检测五个核心步骤。结构体配置SPI_InitTypeDef和GPIO_InitTypeDef是标准外设库中配置SPI和GPIO的核心结构体掌握其成员含义是编写SPI程序的基础。6.2 常见问题与调试建议在实际开发中SPI通信失败通常由以下几类原因引起调试时可优先排查模式不匹配主机和从机的CPOL、CPHA配置不一致导致数据采样时序错乱。建议先用逻辑分析仪观察SCLK和MOSI波形。时钟频率过高SCLK频率超过从机支持的最大时钟频率导致数据采样不稳定。可适当增大预分频系数降低波特率。引脚复用配置错误F4系列必须通过GPIO_PinAFConfig将引脚连接到正确的复用功能编号AF5否则引脚无法工作。片选控制不当使用软件NSS时通信前必须拉低CS引脚通信结束后拉高否则从机无法正确识别通信起始。数据帧格式不一致主机和从机的数据帧长度8位或16位和传输顺序MSB First或LSB First必须一致。6.3 进阶学习方向掌握本文的基础内容后可以从以下几个方向继续深入学习DMA传输结合DMA控制器实现数据的高速、无CPU干预传输适用于大数据量通信场景。中断机制利用TXEIE、RXNEIE等中断使能位实现基于中断的异步收发提高CPU利用率。多从机管理掌握独立片选和菊花链两种多从机连接方式合理设计片选控制逻辑。实际外设驱动通过驱动Flash存储器、SD卡、LCD显示屏等常见SPI外设加深对SPI通信的理解。HAL库开发在标准外设库基础上进一步学习STM32CubeMX和HAL库的SPI配置方式提升开发效率。SPI作为嵌入式系统中应用最广泛的通信接口之一其原理清晰、实现简单掌握好本文所讲的基础知识将为后续开发各类SPI外设驱动打下坚实基础。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询