基于STM32F103实现SPI从机接收功能完整教程
STM32F103系列微控制器集成了高性能的SPI模块,广泛应用于嵌入式通信系统中。该模块不仅具备高速数据传输能力,还支持主从模式切换、DMA传输、中断机制等多种高级功能。本章将从SPI模块的硬件结构、功能特点和寄存器布局三个方面深入解析STM32F103的SPI接口,为后续的通信配置与应用打下坚实基础。SPI(Serial Peripheral Interface)是一种高速、全双工、同步串行通
简介:SPI是一种全双工同步串行通信协议,常用于嵌入式系统中微控制器与外设之间的数据交换。本教程围绕“SPI_Rx_SPI从机接收_worldi53_primitive7s1_”项目,详细讲解如何在STM32F103微控制器上配置SPI接口为从机接收模式。内容涵盖SPI寄存器设置、引脚初始化、时钟配置、NSS管理、中断与轮询接收机制等关键技术点,并提供完整实现代码与配置说明,帮助开发者掌握SPI从机通信的开发流程。 
1. SPI通信协议基础
SPI(Serial Peripheral Interface)是一种广泛应用于嵌入式系统中的高速、全双工同步串行通信协议。它由Motorola提出,结构简单、通信速率高,常用于微控制器与传感器、存储器、显示模块等外设之间的短距离通信。
1.1 SPI的基本原理
SPI通信基于主从架构,通常由一个 主机(Master) 和一个或多个 从机(Slave) 组成。主机负责产生时钟信号(SCK),并通过以下四根信号线进行数据传输:
| 信号线 | 含义 | 方向 |
|---|---|---|
| SCK(Serial Clock) | 主机提供的同步时钟 | 输出 |
| MOSI(Master Out Slave In) | 主机发送,从机接收 | 输出 |
| MISO(Master In Slave Out) | 从机发送,主机接收 | 输入 |
| NSS(Slave Select) | 片选信号,低电平有效 | 输入/输出 |
通信过程中,主机通过拉低某个从机的NSS信号来选择通信对象,随后在SCK的驱动下,MOSI和MISO分别进行数据发送与接收,实现全双工通信。
1.2 SPI的通信模式
SPI支持四种通信模式,由两个参数决定:
- CPOL(Clock Polarity) :时钟极性,决定空闲状态下的SCK电平
- CPHA(Clock Phase) :时钟相位,决定数据采样的边沿
| 模式 | CPOL | CPHA | 数据采样时刻 |
|---|---|---|---|
| Mode 0 | 0 | 0 | 上升沿采样,下降沿切换 |
| Mode 1 | 0 | 1 | 下降沿采样,上升沿切换 |
| Mode 2 | 1 | 0 | 下降沿采样,上升沿切换 |
| Mode 3 | 1 | 1 | 上升沿采样,下降沿切换 |
主机与从机必须配置为相同的通信模式,否则会导致数据采样错误。
1.3 主从结构与数据传输过程
SPI采用 主从结构 ,主机拥有通信的主动权,负责时钟生成和设备选择(NSS),而从机只能被动响应。数据传输以 帧(Frame) 为单位,通常为8位或16位,每次传输由主机发起,从机在NSS使能后响应。
数据传输流程如下:
sequenceDiagram
主机->>从机: 拉低NSS,选中从机
主机->>从机: 发送SCK时钟
主机->>从机: 通过MOSI发送数据位
从机-->>主机: 通过MISO返回数据位
主机->>从机: 传输完成后拉高NSS
通过上述流程,主机与从机在同步时钟的控制下完成数据交换,整个过程高速且无需协议开销,非常适合实时性要求高的场景。
1.4 小结
本章介绍了SPI通信协议的基本原理、通信模式、主从结构和数据传输流程。SPI以其结构简单、高速传输和全双工特性,广泛应用于嵌入式系统的外设通信中。下一章将深入介绍STM32F103系列微控制器的SPI接口特性,为后续的寄存器配置和编程实践奠定基础。
2. STM32F103微控制器SPI接口介绍
STM32F103系列微控制器集成了高性能的SPI模块,广泛应用于嵌入式通信系统中。该模块不仅具备高速数据传输能力,还支持主从模式切换、DMA传输、中断机制等多种高级功能。本章将从SPI模块的硬件结构、功能特点和寄存器布局三个方面深入解析STM32F103的SPI接口,为后续的通信配置与应用打下坚实基础。
2.1 SPI模块概述
2.1.1 SPI模块在STM32中的作用
SPI(Serial Peripheral Interface)是一种高速、全双工、同步串行通信接口,广泛用于嵌入式系统中主控制器与外围设备之间的数据交换。在STM32F103中,SPI模块的作用主要包括:
- 连接外部设备 :如ADC、DAC、存储器、传感器、显示屏等,实现高速数据读写。
- 主从模式切换 :支持主模式和从模式,可灵活适应不同应用场景。
- 多SPI接口支持 :STM32F103通常集成多个SPI接口(如SPI1、SPI2),便于构建多设备通信系统。
- 支持DMA传输 :提高数据传输效率,减少CPU资源占用。
- 中断机制支持 :提供丰富的中断源,用于数据接收、发送完成、错误处理等事件响应。
SPI模块在STM32F103中是作为标准外设存在的,其核心功能由SPI控制器实现,通过配置相关寄存器即可完成通信初始化与数据传输控制。
2.1.2 支持的通信模式与主从切换机制
STM32F103的SPI模块支持四种通信模式,由时钟极性(CPOL)与时钟相位(CPHA)组合决定:
| 模式 | CPOL | CPHA | 说明 |
|---|---|---|---|
| 0 | 0 | 0 | 数据在上升沿采样,下降沿切换 |
| 1 | 0 | 1 | 数据在下降沿采样,上升沿切换 |
| 2 | 1 | 0 | 数据在下降沿采样,上升沿切换 |
| 3 | 1 | 1 | 数据在上升沿采样,下降沿切换 |
主从切换机制通过配置寄存器 SPI_CR1 中的 MSTR 位实现:
- 当
MSTR=1时,SPI工作在主模式; - 当
MSTR=0时,SPI工作在从模式。
此外,STM32F103还支持通过 NSS 引脚或软件控制方式选择从设备,从而实现多设备通信。
2.2 STM32F103的SPI接口特性
2.2.1 高速传输能力与多设备支持
STM32F103的SPI模块支持高达18 Mbps的通信速率(取决于系统时钟和预分频设置)。其高速特性来源于以下几点:
- 主模式下可配置SCK时钟频率 :通过设置
SPI_CR1中的BR[2:0]位,可以将主频分频为2~256倍,从而调整通信速率。 - 全双工通信 :支持MOSI和MISO同时发送和接收,提升数据吞吐量。
- 多SPI接口 :支持多个SPI外设并行工作,便于扩展外围设备。
多设备支持主要通过以下方式实现:
- NSS引脚控制 :每个从设备通过独立的NSS引脚选择,实现硬件片选。
- 软件NSS控制 :通过配置
SSI和SSM位,使用软件控制从设备的使能状态。
2.2.2 支持DMA传输与中断机制
DMA传输支持
STM32F103的SPI模块支持DMA(直接内存访问)操作,允许数据在内存与SPI外设之间自动传输,无需CPU干预。这大大提高了数据传输效率,特别适用于大数据量传输场景。
以SPI发送为例,DMA配置流程如下:
// 配置DMA通道
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR; // SPI数据寄存器地址
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)txBuffer; // 发送缓冲区地址
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; // 内存到外设
DMA_InitStruct.DMA_BufferSize = TX_BUFFER_SIZE; // 缓冲区大小
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不递增
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址递增
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 8位数据宽度
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel3, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel3, ENABLE); // 启动DMA
逻辑分析:
- DMA_PeripheralBaseAddr 设置为SPI1的DR寄存器地址,表示DMA将数据写入SPI发送寄存器。
- DMA_MemoryBaseAddr 是用户定义的发送缓冲区起始地址。
- DMA_DIR 表示传输方向为“内存到外设”。
- DMA_BufferSize 定义了DMA传输的数据项数量。
- DMA_PeripheralInc 禁用外设地址自增,因为外设地址固定为SPI的DR寄存器。
- DMA_MemoryInc 启用内存地址自增,以便DMA依次读取缓冲区数据。
- DMA_Mode 设置为Normal模式,传输完成后停止DMA。
- 最后调用 DMA_Cmd() 启动DMA传输。
中断机制支持
STM32F103的SPI模块支持多种中断源,包括:
- 接收缓冲区非空中断(RXNE) :当接收缓冲区有数据时触发。
- 发送缓冲区空中断(TXE) :当发送缓冲区为空时触发。
- 传输完成中断(CRCERR、MODF、OVR等) :用于处理错误或传输完成事件。
启用SPI接收中断的代码如下:
// 启用SPI接收中断
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);
// 配置NVIC
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = SPI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
逻辑分析:
- SPI_I2S_ITConfig() 函数用于启用SPI的中断源,此处启用RXNE中断。
- NVIC_Init() 配置NVIC中断控制器,指定中断优先级和使能状态。
- 中断服务函数需在 stm32f10x_it.c 中实现,如:
void SPI1_IRQHandler(void) {
if (SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_RXNE) != RESET) {
uint8_t data = SPI_I2S_ReceiveData(SPI1); // 读取接收数据
// 数据处理逻辑
}
}
2.3 SPI模块的硬件连接方式
2.3.1 引脚定义与连接方式(MOSI、MISO、SCK、NSS)
SPI通信使用四根信号线,分别是:
| 引脚名称 | 全称 | 功能描述 |
|---|---|---|
| MOSI | Master Out Slave In | 主设备发送数据到从设备 |
| MISO | Master In Slave Out | 从设备发送数据到主设备 |
| SCK | Serial Clock | 同步时钟信号 |
| NSS | Slave Select | 从设备使能信号,低电平有效 |
在STM32F103中,SPI接口通常映射到特定的GPIO引脚上。例如:
| SPI接口 | MOSI引脚 | MISO引脚 | SCK引脚 | NSS引脚 |
|---|---|---|---|---|
| SPI1 | PA7 | PA6 | PA5 | PA4 |
| SPI2 | PB15 | PB14 | PB13 | PB12 |
连接方式示意图如下(使用mermaid格式):
graph TD
A[STM32F103] -->|MOSI| B[SPI Device]
A -->|MISO| B
A -->|SCK| B
A -->|NSS| B
说明:
- STM32作为主设备时,通过MOSI发送数据,MISO接收数据;
- NSS引脚控制从设备的使能状态;
- SCK提供同步时钟信号,确保数据准确传输。
2.3.2 通信速率与时钟配置关系
STM32F103的SPI通信速率由主时钟(如系统时钟)分频决定,通过 SPI_CR1 寄存器的 BR[2:0] 位设置分频系数,如下表所示:
| BR[2:0] | 分频系数 | 通信速率(假设系统时钟为72MHz) |
|---|---|---|
| 000 | 2 | 36 MHz |
| 001 | 4 | 18 MHz |
| 010 | 8 | 9 MHz |
| 011 | 16 | 4.5 MHz |
| 100 | 32 | 2.25 MHz |
| 101 | 64 | 1.125 MHz |
| 110 | 128 | 562.5 kHz |
| 111 | 256 | 281.25 kHz |
例如,设置SPI1的通信速率为18 MHz的代码如下:
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // 72/4=18MHz
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStruct.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStruct);
参数说明:
- SPI_BaudRatePrescaler_4 表示对主频进行4分频;
- SPI_Mode_Master 设置为主模式;
- SPI_CPOL 和 SPI_CPHA 配置通信模式;
- SPI_DataSize 设置数据位长度为8位;
- SPI_Direction 设置为全双工模式;
- SPI_FirstBit 设置MSB先发;
- 最后调用 SPI_Init() 完成初始化。
小结
本章详细介绍了STM32F103微控制器中SPI模块的硬件结构、功能特性及引脚配置方法。通过分析主从模式、DMA传输、中断机制和通信速率设置等内容,读者可以全面掌握SPI模块的使用方式,为后续的SPI通信编程与应用开发打下坚实基础。
3. SPI寄存器配置详解(SPI_CR1、SPI_CR2、SPI_I2SCFGR)
STM32F103系列微控制器的SPI模块功能强大,但其灵活性和复杂性也体现在多个关键寄存器的配置上。本章将深入分析SPI_CR1、SPI_CR2和SPI_I2SCFGR寄存器,逐一讲解其位字段的功能,帮助读者掌握从底层配置SPI模块的方法,为后续主从模式通信、中断控制和DMA传输等高级功能打下坚实基础。
3.1 控制寄存器SPI_CR1
SPI_CR1是STM32F103 SPI模块的核心控制寄存器之一,用于配置SPI的基本通信模式、主从模式选择、数据帧长度、传输顺序、时钟极性和相位等关键参数。
3.1.1 位描述与主从模式设置
SPI_CR1寄存器是一个16位寄存器,其位字段如下所示:
| 位号 | 名称 | 描述 |
|---|---|---|
| 15:13 | Reserved | 保留位 |
| 12 | BIDIMODE | 双线模式选择(0=单线模式,1=双线模式) |
| 11 | BIDIOE | 双线输出使能(仅在BIDIMODE=1时有效) |
| 10 | CRCEN | CRC使能 |
| 9 | CRCNEXT | CRC传输控制 |
| 8 | DFF | 数据帧格式(0=8位,1=16位) |
| 7 | RXONLY | 接收只模式选择(0=全双工,1=只接收) |
| 6 | SSM | 软件NSS管理(0=硬件管理,1=软件管理) |
| 5 | SSI | 内部NSS电平(用于主模式下使能SPI) |
| 4 | LSBFIRST | 数据传输顺序(0=MSB先发,1=LSB先发) |
| 3 | SPE | SPI使能(0=禁用,1=使能) |
| 2:1 | BR[2:0] | 波特率控制(000到111对应不同分频) |
| 0 | MSTR | 主/从模式选择(0=从模式,1=主模式) |
主从模式配置示例
以下代码展示了如何配置SPI1为主模式:
SPI1->CR1 |= SPI_CR1_MSTR; // 设置为主模式
SPI1->CR1 |= SPI_CR1_SPE; // 使能SPI模块
逐行分析:
-SPI1->CR1 |= SPI_CR1_MSTR;:通过位或操作设置MSTR位,将SPI1配置为主设备。
-SPI1->CR1 |= SPI_CR1_SPE;:使能SPI模块,开始通信。参数说明:
-SPI_CR1_MSTR是宏定义,值为(1 << 2),对应位2。
-SPI_CR1_SPE是宏定义,值为(1 << 6),对应位6。
主从模式切换机制
在主模式下,SPI模块将控制SCK时钟信号,并启动通信。从模式下,SPI模块将等待外部主设备发送时钟信号。切换主从模式只需修改MSTR位即可。
3.1.2 数据帧长度与传输顺序配置
数据帧长度配置
SPI_CR1寄存器的DFF位(Data Frame Format)用于设置数据帧长度:
DFF = 0:8位数据帧DFF = 1:16位数据帧
示例代码:
SPI1->CR1 &= ~SPI_CR1_DFF; // 设置为8位数据帧
逐行分析:
-SPI1->CR1 &= ~SPI_CR1_DFF;:通过位与非操作清除DFF位,设置为8位模式。
- 如果需要设置为16位,则使用SPI1->CR1 |= SPI_CR1_DFF;
传输顺序配置
LSBFIRST位用于控制数据传输顺序:
LSBFIRST = 0:MSB(最高位)先发LSBFIRST = 1:LSB(最低位)先发
示例代码:
SPI1->CR1 |= SPI_CR1_LSBFIRST; // 设置为LSB先发
逐行分析:
-SPI1->CR1 |= SPI_CR1_LSBFIRST;:设置LSBFIRST位,启用LSB优先传输。
3.2 控制寄存器SPI_CR2
SPI_CR2寄存器用于控制SPI模块的中断、DMA请求和帧结束信号等高级功能。
3.2.1 中断使能位的设置与使用
SPI_CR2的中断使能位包括:
RXNEIE:接收缓冲区非空中断使能TXEIE:发送缓冲区空中断使能ERRIE:错误中断使能
中断使能示例
SPI1->CR2 |= SPI_CR2_RXNEIE; // 使能接收缓冲区非空中断
逐行分析:
-SPI1->CR2 |= SPI_CR2_RXNEIE;:启用接收缓冲区非空中断,用于中断方式接收数据。
中断处理流程图(Mermaid)
graph TD
A[SPI接收到数据] --> B{RXNE标志置位?}
B -- 是 --> C[触发RXNE中断]
C --> D[读取DR寄存器]
D --> E[清除中断标志]
E --> F[处理接收数据]
流程说明:
- 当SPI接收到数据时,RXNE标志被置位。
- 若已使能RXNEIE中断,将触发中断服务函数。
- 在中断处理中读取DR寄存器并清除标志,完成数据处理。
3.2.2 DMA请求控制与帧结束信号
DMA请求配置
SPI_CR2中的 TXDMAEN 和 RXDMAEN 位分别用于使能发送和接收DMA请求。
示例代码:
SPI1->CR2 |= SPI_CR2_RXDMAEN; // 使能SPI接收DMA请求
逐行分析:
-SPI1->CR2 |= SPI_CR2_RXDMAEN;:启用接收DMA请求,使能DMA通道传输数据。
帧结束信号(FRF)配置
虽然STM32F103的SPI模块默认为标准SPI模式,但FRF位可用于配置为TI同步模式(在SPI_I2SCFGR中也有涉及)。这部分将在下一节详细说明。
3.3 SPI_I2SCFGR寄存器简介
SPI_I2SCFGR寄存器用于配置I2S模式下的SPI通信。虽然I2S主要用于音频传输,但其与SPI在物理层上具有高度兼容性,因此了解该寄存器有助于理解SPI模块的多功能性。
3.3.1 I2S模式下的配置要点
SPI_I2SCFGR寄存器的位字段如下:
| 位号 | 名称 | 描述 |
|---|---|---|
| 11:10 | I2SSTD[1:0] | I2S标准选择(Philips、MSB对齐等) |
| 9 | PCMSYNC | PCM帧同步方式(仅PCM模式有效) |
| 8 | I2SCFG[1:0] | I2S配置(主/从模式,发送/接收) |
| 7 | I2SE | I2S使能 |
| 6 | CKPOL | 时钟极性 |
| 4 | DATLEN[1:0] | 数据长度(16、24、32位) |
| 3 | CHLEN | 通道长度(0=16位,1=32位) |
I2S主模式配置示例
SPI1->I2SCFGR |= SPI_I2SCFGR_I2SE; // 使能I2S模式
SPI1->I2SCFGR |= SPI_I2SCFGR_I2SCFG_0; // 设置为主发送模式
逐行分析:
-SPI1->I2SCFGR |= SPI_I2SCFGR_I2SE;:启用I2S功能。
-SPI1->I2SCFGR |= SPI_I2SCFGR_I2SCFG_0;:设置为主发送模式。
3.3.2 与标准SPI模式的兼容性分析
尽管I2S和SPI在物理接口上相似,但它们在时序和功能上存在差异:
| 特性 | 标准SPI | I2S |
|---|---|---|
| 时钟极性 | 可配置 | 通常固定 |
| 数据宽度 | 8/16位 | 16/24/32位 |
| 主从模式 | 支持 | 支持 |
| 传输方向 | 全双工 | 半双工(发送或接收) |
| 适用场景 | 通用外设通信 | 音频设备通信 |
兼容性说明:
- SPI模块可以在I2S模式下工作,但需要外部音频设备支持。
- 从软件角度看,配置I2S与配置SPI的流程类似,但需关注不同的寄存器和时序要求。
SPI与I2S模式切换流程图(Mermaid)
graph LR
A[初始化SPI模块] --> B{是否启用I2S模式?}
B -- 是 --> C[配置SPI_I2SCFGR寄存器]
B -- 否 --> D[配置SPI_CR1/CR2寄存器]
C --> E[设置I2S标准与数据格式]
D --> F[设置SPI通信参数]
流程说明:
- 初始化SPI模块时,判断是否启用I2S模式。
- 若启用I2S,则配置SPI_I2SCFGR寄存器。
- 否则,配置SPI_CR1和SPI_CR2寄存器。
本章通过对SPI_CR1、SPI_CR2和SPI_I2SCFGR三个核心寄存器的详细分析,帮助读者理解STM32F103 SPI模块的底层配置机制。掌握这些寄存器的使用,是实现高效、灵活SPI通信的基础,也为后续章节中中断、DMA、主从模式等高级功能的实现提供了技术支撑。
4. SPI从机模式配置流程
从机模式是SPI通信中一个非常关键的应用场景,尤其是在多设备通信系统中,它作为数据响应端扮演着重要角色。STM32F103系列微控制器支持SPI从机模式,并提供了丰富的寄存器配置选项,使得开发者可以根据具体需求灵活配置。本章将深入剖析从机模式的配置流程,包括引脚初始化、寄存器设置和通信参数调整,确保SPI通信的稳定性和高效性。
4.1 SPI从机模式的基本要求
在SPI通信中,从机模式的配置必须满足特定的硬件与软件条件,确保其能够正确响应主机的通信请求。其中,NSS信号的处理尤为关键,而SCK、MISO和MOSI信号的响应机制也决定了通信的可靠性。
4.1.1 NSS信号的作用与配置方式
NSS(Slave Select)信号是SPI通信中用于选择从机的关键信号。在从机模式下,NSS引脚必须被配置为输入模式,用于检测主机是否选中该设备。
- NSS信号功能 :
- 当NSS为低电平时,表示该从机被选中,SPI通信可以开始。
-
若NSS为高电平,则从机忽略所有SCK信号,不参与通信。
-
配置方式 :
- 在STM32F103中,NSS引脚可以是硬件自动管理或软件控制。
- 若使用硬件NSS,则需将SPI_CR1寄存器的
SSM位设为0,NSS引脚作为输入。 - 若使用软件NSS,则设置
SSM=1,并通过SSI位控制是否被选中。
SPI1->CR1 &= ~SPI_CR1_SSM; // 硬件NSS模式
SPI1->CR1 |= SPI_CR1_SSI; // 若使用软件NSS,可设置SSI=1表示被选中
- 代码逻辑说明 :
- 第一行关闭软件管理NSS功能,启用硬件模式。
- 第二行设置SSI位为1,表示当前从机处于“被选中”状态。
4.1.2 SCK、MISO、MOSI信号的响应机制
在从机模式中,SCK由主机驱动,从机必须在SCK上升沿或下降沿进行数据采样。MISO为从机输出数据,MOSI为从机接收数据。
- SCK响应机制 :
- 需要根据主机的时钟极性(CPOL)和相位(CPHA)配置从机的对应参数。
-
CPOL决定空闲时SCK的电平,CPHA决定数据在第几个边沿被采样。
-
MISO与MOSI配置 :
- MISO引脚必须配置为复用推挽输出(Alternate Function Push-Pull)。
- MOSI引脚配置为输入模式,用于接收主机发送的数据。
GPIO_InitTypeDef GPIO_InitStruct;
// 配置SCK为输入(从机模式)
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置MISO为复用推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 配置MOSI为输入
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- 逻辑分析 :
GPIO_MODE_INPUT:将SCK和MOSI引脚设为输入,响应主机的时钟和数据。GPIO_MODE_AF_PP:MISO引脚为复用推挽输出,确保数据输出稳定。Alternate字段设置为GPIO_AF5_SPI1,表示该引脚连接至SPI1模块。
4.2 配置流程详解
配置SPI从机模式需要按照一定的顺序进行,包括初始化SPI模块、设置寄存器参数以及检测模块状态,以确保通信流程的正确启动。
4.2.1 SPI模块的初始化顺序
SPI模块的初始化通常包括以下几个步骤:
- 使能SPI外设时钟 ;
- 配置GPIO引脚功能 ;
- 设置SPI工作模式为从机 ;
- 配置通信参数(如数据长度、时钟极性) ;
- 使能SPI模块 。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
SPI_InitTypeDef SPI_InitStruct;
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_Mode = SPI_Mode_Slave;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Hard_Input;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStruct.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
- 参数说明 :
SPI_Mode_Slave:设置为从机模式。SPI_DataSize_8b:使用8位数据帧。SPI_CPOL_Low:SCK空闲时为低电平。SPI_CPHA_1Edge:在第一个边沿采样数据。SPI_NSS_Hard_Input:使用硬件NSS输入。SPI_BaudRatePrescaler_16:从机模式下该参数无效,但需设置。
4.2.2 寄存器配置与状态检测
在SPI从机模式下,需要关注SPI_SR(状态寄存器)以判断通信状态,同时通过SPI_DR(数据寄存器)进行数据读写。
| 寄存器 | 功能描述 |
|---|---|
| SPI_CR1 | 控制寄存器1,设置模式、数据长度等 |
| SPI_SR | 状态寄存器,包含忙标志、接收缓冲区非空标志等 |
| SPI_DR | 数据寄存器,用于读写数据 |
// 检测是否接收到数据
if (SPI1->SR & SPI_SR_RXNE) {
uint8_t data = SPI1->DR; // 读取接收到的数据
// 数据处理逻辑
}
- 状态检测说明 :
RXNE标志位表示接收缓冲区非空,可以读取数据。- 读取SPI_DR寄存器会自动清除该标志。
- 若未及时读取可能导致溢出错误。
4.3 从机模式下的通信稳定性优化
在实际应用中,SPI从机通信可能因时钟同步、数据采样等问题导致通信失败。因此,必须采取相应的优化策略,提高通信的稳定性和可靠性。
4.3.1 时钟同步问题与解决方案
由于SCK由主机提供,从机无法控制其频率和稳定性,可能导致时钟抖动或同步失败。
- 解决方案 :
- 使用硬件流控(如NSS信号)确保选通同步;
- 设置适当的采样边沿(CPHA)以匹配主机;
- 增加软件检测机制,如超时判断。
graph TD
A[主机发送SCK] --> B{从机检测NSS低电平}
B -- 是 --> C[开始接收数据]
B -- 否 --> D[忽略通信]
C --> E{检测到SCK边沿}
E -- 是 --> F[读取SPI_DR数据]
E -- 否 --> G[等待或超时退出]
4.3.2 数据采样与传输时序调整
在高速通信中,若从机未能及时响应SCK信号,可能导致数据采样错误。
- 优化建议 :
- 设置合适的CPOL和CPHA参数,确保采样时序匹配;
- 在中断或DMA模式下处理数据接收,提高响应速度;
- 增加硬件滤波电路,减少信号干扰。
| 参数 | 推荐值 | 说明 |
|---|---|---|
| CPOL | 0(低电平空闲) | 主机通常使用该设置 |
| CPHA | 1(第二个边沿采样) | 保证数据稳定后采样 |
| 数据位宽 | 8位 | 最常用配置 |
- 时序匹配示意图 (mermaid):
sequenceDiagram
主机->>从机: NSS=0
主机->>从机: SCK上升沿
从机->>主机: MISO输出数据
主机->>从机: MOSI发送数据
从机->>从机: 采样MOSI数据
- 时序说明 :
- NSS为低时通信开始;
- SCK上升沿时,从机采样MOSI数据;
- SCK下降沿时,从机输出MISO数据;
- 保证数据稳定后采样,避免误读。
综上所述,SPI从机模式的配置是一个系统性工程,涉及引脚配置、寄存器设置以及通信时序优化。通过合理配置SPI模块和引脚,结合状态检测与优化策略,可以实现稳定、高效的SPI通信。下一章将介绍如何通过轮询方式实现SPI从机的数据接收,进一步提升通信的可操作性与实用性。
5. 轮询方式实现SPI数据接收
轮询方式是一种基础但稳定的SPI数据接收方式,适用于资源有限或对实时性要求不高的应用场景。本章将通过代码实例,讲解如何使用轮询方式实现SPI从机的数据接收,并分析其优缺点。
5.1 轮询方式的工作原理
轮询方式(Polling Method)是指主程序通过不断地查询特定寄存器的状态位,来判断是否满足某一操作条件。在SPI通信中,轮询方式常用于数据接收、发送等操作,尤其在资源受限的嵌入式系统中,具有实现简单、稳定性高的特点。
5.1.1 状态寄存器的查询机制
STM32F103的SPI模块通过状态寄存器(SPI_SR)来反映当前SPI模块的运行状态。SPI_SR寄存器的关键位如下:
| 位 | 名称 | 含义 |
|---|---|---|
| 0 | RXNE |
接收缓冲区非空(Ready to Read) |
| 1 | TXE |
发送缓冲区为空(Ready to Write) |
| 2 | CHSIDE |
仅用于I2S模式 |
| 3 | UDR |
下溢标志 |
| 4 | CRCERR |
CRC校验错误标志 |
| 5 | MODF |
模式错误标志 |
| 6 | OVR |
接收溢出标志 |
| 7 | BSY |
总线忙标志 |
在轮询方式下,我们主要关注的是 RXNE 位。当SPI从机接收到一个完整的字节数据后,该位会被硬件置位,表示可以读取接收缓冲区中的数据。
5.1.2 数据接收流程与代码实现
下面以STM32F103为例,展示使用轮询方式实现SPI从机接收数据的基本流程:
1. 初始化SPI模块为从机模式
void SPI_Slave_Init(void) {
SPI_InitTypeDef SPI_InitStruct;
// SPI1配置为从机模式
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 全双工
SPI_InitStruct.SPI_Mode = SPI_Mode_Slave; // 从机模式
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // 8位数据帧
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // 时钟空闲为低
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // 第一个边沿采样
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);
SPI_Cmd(SPI1, ENABLE); // 启用SPI1
}
代码逻辑分析:
- SPI_Direction :设置为全双工模式,允许同时收发数据。
- SPI_Mode :配置为从机模式,由外部主机控制时钟。
- SPI_DataSize :选择8位数据帧长度。
- CPOL与CPHA :根据外部主机的SPI模式进行配置,常见为CPOL=0,CPHA=0(Mode 0)。
- NSS :采用软件管理,避免硬件冲突。
- BaudRatePrescaler :在从机模式下无效,可任意设置。
- FirstBit :MSB先发,常见格式。
2. 轮询方式读取SPI数据
uint8_t SPI_ReceiveByte(void) {
// 等待接收缓冲区非空
while (!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
// 读取接收寄存器中的数据
return SPI_I2S_ReceiveData(SPI1);
}
代码逻辑分析:
- while (!SPI_I2S_GetFlagStatus(…)) :不断轮询SPI_SR寄存器的
RXNE位,直到有数据可读。 - SPI_I2S_ReceiveData(SPI1) :读取SPI接收寄存器(SPI_DR),清除
RXNE标志。
3. 主循环中接收数据
int main(void) {
SPI_Slave_Init();
while (1) {
uint8_t received_data = SPI_ReceiveByte(); // 轮询接收一个字节
// 对接收到的数据进行处理(例如回传、显示等)
SPI_SendByte(received_data); // 发送回主机
}
}
注意:
SPI_SendByte()函数实现方式与接收类似,此处不再赘述。
5.2 接收缓冲区管理与数据处理
虽然轮询方式简单可靠,但在实际应用中,如果数据量较大或处理复杂,可能会造成缓冲区溢出、数据丢失等问题。因此,合理的接收缓冲区管理是确保通信稳定的关键。
5.2.1 数据缓存策略与缓冲区溢出防范
在实际开发中,建议使用 环形缓冲区(Ring Buffer) 来存储接收到的数据,以提高效率和避免数据丢失。
环形缓冲区结构定义:
#define BUFFER_SIZE 128
typedef struct {
uint8_t buffer[BUFFER_SIZE];
uint16_t head;
uint16_t tail;
} RingBuffer;
RingBuffer rx_buffer;
环形缓冲区写入函数:
void buffer_write(RingBuffer *rb, uint8_t data) {
uint16_t next = (rb->head + 1) % BUFFER_SIZE;
if (next != rb->tail) { // 缓冲区未满
rb->buffer[rb->head] = data;
rb->head = next;
}
}
环形缓冲区读取函数:
uint8_t buffer_read(RingBuffer *rb) {
if (rb->tail == rb->head) return 0; // 缓冲区为空
uint8_t data = rb->buffer[rb->tail];
rb->tail = (rb->tail + 1) % BUFFER_SIZE;
return data;
}
使用示例:
while (1) {
if (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)) {
uint8_t data = SPI_I2S_ReceiveData(SPI1);
buffer_write(&rx_buffer, data); // 写入环形缓冲区
}
// 处理缓冲区中的数据
if (rx_buffer.tail != rx_buffer.head) {
uint8_t data = buffer_read(&rx_buffer);
process_data(data); // 用户自定义处理函数
}
}
5.2.2 接收完成后的数据解析与反馈机制
在实际应用中,SPI通信往往需要接收一组数据包(如命令+参数+校验),因此需要对接收的数据进行 协议解析 和 反馈机制设计 。
示例协议结构:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 命令 | 1 | 命令码 |
| 参数 | n | 可变长数据 |
| 校验 | 2 | CRC16校验码 |
数据解析流程(mermaid流程图):
graph TD
A[开始接收数据] --> B{缓冲区有数据?}
B -- 是 --> C[读取第一个字节作为命令]
C --> D[根据命令解析参数长度]
D --> E[读取参数]
E --> F[读取2字节CRC校验码]
F --> G{校验是否通过?}
G -- 是 --> H[执行对应命令]
G -- 否 --> I[返回错误码]
H --> J[发送响应数据]
I --> J
5.3 轮询方式的性能分析
轮询方式虽然实现简单、稳定性高,但在性能方面也存在一些明显的缺点。在实际项目中,需根据具体应用场景选择是否使用轮询方式。
5.3.1 优点:稳定性高、实现简单
优点分析:
- 无需中断机制 :适合资源受限的MCU平台。
- 无上下文切换开销 :避免中断带来的中断服务程序跳转与上下文保存开销。
- 易于调试 :程序流程清晰,便于调试与维护。
5.3.2 缺点:CPU占用率高、响应延迟大
缺点分析:
- 占用CPU资源 :持续查询状态位会消耗大量CPU周期,影响其他任务的执行。
- 响应延迟大 :若主循环中有其他耗时任务,可能导致接收数据延迟甚至丢失。
- 不适用于高速通信 :在高波特率下,轮询方式无法及时响应数据变化。
性能对比表:
| 特性 | 轮询方式 | 中断方式 |
|---|---|---|
| 实现复杂度 | 简单 | 较复杂 |
| CPU占用率 | 高 | 低 |
| 实时性 | 差 | 好 |
| 是否适合高速通信 | 否 | 是 |
| 调试难易度 | 简单 | 中等 |
| 是否依赖中断配置 | 否 | 是 |
总结与建议
轮询方式适用于低速通信、实时性要求不高、资源受限的嵌入式系统。其优点在于实现简单、稳定性高,特别适合初学者或教学项目使用。然而,在需要高效、实时处理的场景中,应考虑使用中断方式或DMA方式来提升通信效率。
后续章节将进一步介绍中断方式实现SPI数据接收,帮助读者构建更完整的SPI通信能力体系。
6. 中断方式实现SPI数据接收
中断方式是提高SPI通信效率的关键手段,尤其适用于需要实时响应的场景。本章将深入讲解如何配置SPI中断,实现从机端的数据接收,并通过项目实例展示中断服务程序的设计与实现技巧。
6.1 中断机制与SPI通信
6.1.1 中断源与中断优先级配置
在STM32F103中,SPI通信模块支持多个中断源,包括:
- 接收缓冲区非空(RXNE) :当SPI接收寄存器中有数据时触发;
- 发送缓冲区空(TXE) :当SPI发送寄存器为空时触发;
- CRC错误(CRCERR) :CRC校验失败时触发;
- 模式错误(MODF) :当发生主从模式冲突时触发;
- 溢出错误(OVR) :接收缓冲区未及时读取导致溢出。
在NVIC(嵌套向量中断控制器)中,可以设置每个中断的抢占优先级和子优先级。通常,SPI接收中断应设置较高的优先级,以确保实时响应。
6.1.2 SPI接收中断的触发条件与处理流程
当SPI从机接收到一个完整的数据字节后,硬件会将该数据存入接收缓冲区,并设置RXNE标志位。若该中断被使能,则会触发SPI全局中断,进而调用中断服务函数。
中断处理流程如下:
- 判断中断标志位(如RXNE)是否置位;
- 读取SPI_DR寄存器以清除标志;
- 将接收到的数据存储至缓冲区;
- 若接收完成,触发用户回调函数进行数据处理;
- 退出中断服务函数。
6.2 基于STM32的中断配置步骤
6.2.1 NVIC中断控制器的设置
在STM32中,SPI1的中断通道为 SPI1_IRQn 。我们可以通过如下方式配置NVIC:
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = SPI1_IRQn; // 选择SPI1中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_Init(&NVIC_InitStruct);
6.2.2 SPI中断使能与中断服务函数编写
首先,我们需要在SPI控制寄存器中使能RXNE中断:
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE); // 使能SPI接收中断
然后编写中断服务函数。STM32标准外设库会自动调用 void SPI1_IRQHandler(void) 函数,我们需要在该函数中处理中断事件:
void SPI1_IRQHandler(void) {
if (SPI_I2S_GetITStatus(SPI1, SPI_I2S_IT_RXNE) != RESET) {
// 读取接收数据并清除中断标志
uint8_t data = SPI_I2S_ReceiveData(SPI1);
// 将数据缓存到数组或队列中
rx_buffer[rx_index++] = data;
// 检查是否接收完成
if (rx_index >= BUFFER_SIZE) {
rx_complete_flag = 1;
rx_index = 0;
}
}
}
6.3 实战案例:SPI_Rx项目源码解析
6.3.1 项目结构与功能模块划分
本项目采用模块化设计,主要分为以下几个模块:
| 模块 | 功能描述 |
|---|---|
main.c |
程序入口,初始化外设并进入主循环 |
spi.c |
SPI模块初始化与中断处理 |
stm32f10x_it.c |
中断服务函数实现 |
buffer.c |
接收缓冲区管理与数据解析 |
led.c |
LED状态指示 |
6.3.2 中断服务函数与数据处理流程
在 stm32f10x_it.c 中,我们重写了SPI1的中断处理函数,如前所述。在数据接收完成后,设置 rx_complete_flag 标志,主程序中通过轮询该标志进行后续处理:
if (rx_complete_flag) {
process_received_data(rx_buffer, BUFFER_SIZE); // 处理接收到的数据
rx_complete_flag = 0;
}
其中 process_received_data() 函数负责解析数据、校验数据完整性并执行用户逻辑。
6.3.3 性能测试与优化建议
| 测试项 | 测试结果 | 说明 |
|---|---|---|
| 接收速率 | 最高可达18Mbps | 依赖SPI时钟配置 |
| CPU占用率 | 显著低于轮询方式 | 仅在有数据时响应中断 |
| 数据丢失率 | 接近于0 | 采用双缓冲可进一步优化 |
优化建议:
- 使用DMA进行SPI数据接收,进一步降低CPU负担;
- 在中断中仅做数据缓存,处理逻辑交由主循环或任务调度;
- 增加环形缓冲区结构,提高数据处理效率;
- 对接收到的数据进行CRC校验,提高通信可靠性。
下一章将深入探讨DMA方式在SPI通信中的应用,进一步提升系统性能与资源利用率。
简介:SPI是一种全双工同步串行通信协议,常用于嵌入式系统中微控制器与外设之间的数据交换。本教程围绕“SPI_Rx_SPI从机接收_worldi53_primitive7s1_”项目,详细讲解如何在STM32F103微控制器上配置SPI接口为从机接收模式。内容涵盖SPI寄存器设置、引脚初始化、时钟配置、NSS管理、中断与轮询接收机制等关键技术点,并提供完整实现代码与配置说明,帮助开发者掌握SPI从机通信的开发流程。
更多推荐




所有评论(0)