STM32 SPI通信详解

张开发
2026/4/9 8:49:11 15 分钟阅读

分享文章

STM32 SPI通信详解
SPISerial Peripheral Interface串行外设接口是一种由摩托罗拉公司开发的高速、全双工、同步串行通信总线协议广泛应用于STM32微控制器与各类外设如Flash存储器、传感器、显示屏等之间的数据交换。其核心特点是采用主从架构通过时钟信号同步数据传输无需像UART那样依赖波特率约定因此速率更高通常可达几十Mbps甚至更高。一、SPI协议基础1. 物理层与信号线SPI通信至少需要4根信号线全双工模式SCK (Serial Clock)时钟信号由主设备产生用于同步数据传输。MOSI (Master Out Slave In)主设备输出、从设备输入的数据线。MISO (Master In Slave Out)主设备输入、从设备输出的数据线。NSS/CS (Slave Select/Chip Select)从设备选择信号低电平有效。每个从设备都有独立的NSS线主设备通过拉低对应NSS线来选中并启动与该从设备的通信。在多从机系统中所有从机的SCK、MOSI、MISO线并联共用而每个从机的NSS线则由主机独立控制。2. 协议层与通信时序SPI协议定义了通信的起始/停止信号、数据有效性和时钟同步规则。起始与停止信号通信以NSS信号线由高变低为开始以NSS由低变高为结束。数据有效性数据在SCK时钟的边沿进行采样和输出。MOSI和MISO数据线在SCK的每个时钟周期传输一位数据且输入输出同时进行全双工。数据传输单位可以是8位或16位且不受限制。数据顺序数据传输可采用MSB最高位先行或LSB最低位先行通信双方必须使用相同约定。3. 时钟模式CPOL与CPHA这是SPI配置的关键决定了时钟空闲状态和数据采样时刻共定义了4种工作模式模式CPOL (时钟极性)CPHA (时钟相位)SCK空闲状态数据采样时刻 (主机视角)典型应用000低电平SCK上升沿采样SD卡、W25Q Flash101低电平SCK下降沿采样部分ADC210高电平SCK下降沿采样较少见311高电平SCK上升沿采样OLED屏(SSD1306)核心要点主设备和从设备必须配置为相同的SPI模式才能正常通信。二、STM32的SPI外设STM32系列芯片集成了多个SPI外设如F103有SPI1/2F407有更多它们具有以下共同特性支持主模式和从模式操作。完全支持上述4种SPI模式。数据帧长度可配置为8位或16位。可设置MSB或LSB先行。支持全双工、半双工和单线模式。支持硬件或软件管理NSS信号。支持中断和DMA传输以减轻CPU负担。最高SCK时钟频率可达fpclk/2其中fpclk为SPI所在APB总线的时钟频率。例如STM32F103的SPI1挂载在APB2最高72MHz理论最高速率可达36Mbps。STM32 SPI架构与工作流程STM32 SPI外设的架构主要包括通讯引脚、时钟控制逻辑、数据控制逻辑和整体控制逻辑。通讯引脚需根据数据手册将特定GPIO配置为复用功能连接到SPI信号线。时钟控制通过控制寄存器CR1中的BR[2:0]位设置预分频器决定SCK频率fSCK fpclk / 预分频值。CPOL和CPHA位也在此寄存器中配置。数据控制数据通过数据寄存器DR进行读写。写入DR的数据进入发送缓冲区并通过移位寄存器从MOSI线移出从MISO线移入的数据存入接收缓冲区读取DR可获得该数据。控制与状态状态寄存器SR中的标志位指示工作状态TXE(发送缓冲区空)为1时表示可以写入新的发送数据。RXNE(接收缓冲区非空)为1时表示有数据可以读取。主模式基本通信流程控制GPIO拉低从机NSS信号起始信号。将数据写入SPI数据寄存器DR。SCK自动产生数据开始同步传输。等待TXE置1发送完成可继续发送下一数据等待RXNE置1接收完成读取DR获取接收到的数据。通信完毕拉高NSS信号停止信号。三、STM32 SPI软件配置示例以标准库为例以下是一个STM32F103作为SPI主机模式0的初始化与数据传输代码框架#include stm32f10x.h void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; SPI_InitTypeDef SPI_InitStruct; // 1. 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // 2. 配置GPIO // SCK, MOSI 配置为复用推挽输出 GPIO_InitStruct.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStruct); // MISO 配置为浮空输入 GPIO_InitStruct.GPIO_Pin GPIO_Pin_6; GPIO_InitStruct.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStruct); // NSS (这里使用软件控制配置为普通推挽输出) GPIO_InitStruct.GPIO_Pin GPIO_Pin_4; GPIO_InitStruct.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOA, GPIO_InitStruct); GPIO_SetBits(GPIOA, GPIO_Pin_4); // 初始置高不选中 // 3. 配置SPI参数 SPI_InitStruct.SPI_Direction SPI_Direction_2Lines_FullDuplex; // 全双工 SPI_InitStruct.SPI_Mode SPI_Mode_Master; // 主模式 SPI_InitStruct.SPI_DataSize SPI_DataSize_8b; // 8位数据帧 SPI_InitStruct.SPI_CPOL SPI_CPOL_Low; // 模式0 SPI_InitStruct.SPI_CPHA SPI_CPHA_1Edge; // 模式0 SPI_InitStruct.SPI_NSS SPI_NSS_Soft; // 软件管理NSS SPI_InitStruct.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_256; // 时钟分频 SPI_InitStruct.SPI_FirstBit SPI_FirstBit_MSB; // MSB先行 SPI_InitStruct.SPI_CRCPolynomial 7; // CRC多项式(可选) SPI_Init(SPI1, SPI_InitStruct); // 4. 使能SPI SPI_Cmd(SPI1, ENABLE); } // 基础轮询式单字节收发函数 uint8_t SPI1_ReadWriteByte(uint8_t TxData) { // 等待发送缓冲区为空 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); // 写入数据启动传输 SPI_I2S_SendData(SPI1, TxData); // 等待接收完成 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); // 返回接收到的数据 return SPI_I2S_ReceiveData(SPI1); } // 使用示例向从设备发送并接收数据 void CommunicateWithSlave(void) { uint8_t tx_data 0xA5; uint8_t rx_data; GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 拉低NSS选中从机 rx_data SPI1_ReadWriteByte(tx_data); // 发送0xA5并接收返回值 GPIO_SetBits(GPIOA, GPIO_Pin_4); // 拉高NSS通信结束 }四、调试与优化建议调试使用逻辑分析仪如Saleae捕获SCK、MOSI、MISO、NSS信号直观验证时序、模式、频率和数据是否正确。常见问题数据错位检查主从设备的CPHA和CPOL设置是否一致。通信失败检查NSS信号控制是否正确检查SCK频率是否超过从设备最高支持速率。MISO数据异常检查线路连接必要时在MISO线上添加合适的上拉电阻。性能优化对于大数据量传输应使用DMA或中断模式避免CPU在轮询标志位上浪费大量时间可显著降低CPU占用率并提高效率。总结在STM32上使用SPI关键在于1) 理解四种时钟模式并根据从设备手册正确配置2) 正确初始化GPIO和SPI外设3) 掌握基于状态标志位TXE/RXNE的数据收发流程。通过轮询、中断或DMA方式可以灵活高效地实现与各种SPI外设的稳定通信。

更多文章