STM32‑SPI 外设简介(F103)逐点解析
STM32F103C8T6:SPI1、SPI2 两个硬件 SPI 外设
- SPI1 挂载在 APB2 总线,最大时钟 72MHz
- SPI2 挂载在 APB1 总线,最大时钟 36MHz
- 硬件收发电路,减轻 CPU 负担硬件自动生成 SCK 时钟、移位收发比特;不需要 CPU 循环翻转 IO(对比软件模拟 SPI)。CPU 只读写
SPI->DR寄存器即可。 - 可配置 8 位 /16 位数据帧、高位先行 / 低位先行
- W25Q64 Flash:8 位帧,高位先行 MSB
- 由 CR1 寄存器
DFF位控制 8/16bit;LSBFIRST控制比特顺序。
- 主机时钟频率:\(f_{PCLK}/(2,4,8,16,32,64,128,256)\)
只有主机模式有效!从机 SCK 由外部输入,分频设置无效。 SPI1:PCLK2=72MHz;SPI2:PCLK1=36MHz。 例:SPI1 分频 128 → SCK = 72M /128 = 562.5kHz。
- 支持多主机模型、主机 / 从机操作
- MSTR 位切换主机 / 从机。
- 多主机模式:多个 STM32 都可当主机,硬件自带冲突检测;实际工程极少用,大多单主机。
- 通信模式:全双工、半双工、单工
- 全双工:MOSI、MISO 两根线同时收发(最常用)
- 半双工 BIDI 模式:只用一根 IO 线,分时收发
- 单工:只接收不发送。
- 支持 DMA可以 DMA 搬运数据,数据收发不需要 CPU 干预,大数据传输效率高,CR2 寄存器 TXDMAEN、RXDMAEN 使能。
- 硬件外设复用:SPI / I2S同一个硬件外设,既可以做 SPI,也可以切换成 I2S 音频协议,二选一,不能同时用。
考试 / 实验高频考点总结
- SPI1 在 APB2 (72M),SPI2 在 APB1 (36M),最大 SCK 时钟不一样。
- 波特率分频只对主机生效,从机不受 BR 分频位影响。
- W25Q64 配置:8bit,MSB 高位先行,SPI 模式 0(CPOL=0,CPHA=0)。
- 硬件 SPI 对比软件 SPI:软件 SPI 全部 GPIO 手动翻转;硬件 SPI 外设接管 SCK/MOSI/MISO。
- 同一个外设可切换 SPI/I2S,二者互斥。
容易混淆
TDR、RDR 对外共用同一个地址
SPI->DR,写 = 填发送缓存,读 = 拿接收缓存。
标志位 SR 寄存器
坑:很多人只判断 TXE 就认为发送完成,此时移位寄存器还在输出,数据没发完。完整结束要看 BSY=0。
3. 控制寄存器 CR1 / CR2
SPI_CR1(主配置)
SPI_CR2:中断、DMA 使能
波特率发生器:对 APB 总线时钟分频,生成 SCK 串行时钟,只有主机有效,从机 SCK 由外部输入。
4. 主机发送 1 字节完整流程(写 DR 发送)
SPI 主机发送一个字节,必然会收到一个字节。哪怕从机什么都不输出,也会读到一个 dummy 哑字节。
5. 高频易错考点
6. 和软件模拟 SPI 对比
SPI
I2C
完整对比表格
表格
| 对比项 | USART (异步串口) | SPI | I2C |
|---|---|---|---|
| 通信类型 | 异步通信,无时钟线,约定波特率 | 同步通信,主机输出 SCK 时钟 | 同步通信,主机输出 SCL 时钟 |
| 主从关系 | 无主从,对等通信,两端各自生成时钟 | 严格主‑从;主机输出 SCK,从机被动跟随 | 严格主‑从;主机输出 SCL;靠 7/10 位地址寻址从机 |
| 移位硬件 | 发送移位、接收移位,两套独立硬件 | 收发共用同一个移位寄存器 | 硬件内部集成移位逻辑,不直接暴露给 CPU;自动生成起始、停止、ACK 应答 |
| 收发行为 | 可只发不收、只收不发,收发互不绑定 | 发送必然伴随接收;即使不需要接收,也要读 DR 清标志,否则 OVR 溢出 | SDA 双向,一根线分时收发;主机发地址,从机应答;多设备靠地址区分 |
| 信号线 | TX、RX、GND;最少 2 根,交叉对接 | MOSI、MISO、SCK、NSS;软件 NSS 最少 3 根 | SDA、SCL + GND,仅 2 根线;必须上拉电阻 |
| 帧格式 | 8/9 位数据;带起始位、停止位、校验位 | 8/16 位;高位 / 低位可选;无起始停止位 | 起始信号、从机地址 + 读写位、应答 ACK、数据字节、停止信号 |
| 通信速率 | 较低,常用 9600‑115200;最高数 Mbps | 很高,SPI1 最高 72MHz,SPI2 最高 36MHz | 标准 100kHz;快速 400kHz;高速模式最高几 MHz |
| 多设备扩展 | 原生不支持多设备,需要 RS485 等外部芯片 | NSS 片选线,一主机挂多个从机 | 依靠 I2C 地址,一条总线最多挂载数十个从机 |
| 电气特性 | 推挽输出 | 推挽输出 | 开漏输出,必须外接上拉电阻 |
| 典型标志位 | TXE (发送寄存器空)、TC (发送完成)、RXNE | TXE (发送缓冲区空)、BSY (移位忙‑传输完成看 BSY)、RXNE | 无 DR 移位标志;靠 EV 事件标志(EV5/EV6/EV7 等) |
| 典型外设 | GPS、蓝牙模块 | W25Q Flash、OLED 显示屏 | AT24C02 EEPROM、OLED、传感器 |
考试 / 做题高频坑点总结
简单记忆
硬件 SPI vs 软件模拟 SPI
重点坑点(你之前记录)
软件 NSS 模式下,拉高 PA4(SS 片选),硬件 SPI 不会自动把 MOSI (PA7) 置高电平。 MOSI 电平由 SPI 外设内部状态决定,SS 引脚不控制 MOSI 引脚电平。 如果需要空闲时 MOSI 为高,代码要手动处理,不要指望拉高 NSS 自动完成。
寄存器对应
配置完之后两步必做
W25Q 关键点记忆
W25Q64:SPI 模式 0,8bit,MSB 高位先行,软件 NSS。
易错点
- SPI全双工一定同时收发;写一个字节,必然收到一个哑字节,就算不需要接收,也要读 DR 清除 RXNE 标志,否则 OVR 溢出错误。
串口是低位线行,发送的是0X55的数据,它读数据是从右往左,SPI和I2C大多都是高位先行
STM32 SPI 内部框图解析(F1 系列)
这是 STM32 硬件 SPI 外设内部结构图,全双工,收发同时发生,重点把数据流、寄存器、引脚梳理清楚。
1. 外部 4 个引脚
表格
引脚 作用 MOSI 主机输出,从机输入 MISO 主机输入,从机输出 SCK 串行时钟,主机输出时钟,从机接收时钟 NSS 片选,硬件片选;也可以软件 SSM 位,用普通 GPIO 模拟片选 左上角那个二选一开关:主模式用 MOSI 输出、MISO 输入;从模式两个引脚功能互换。
2. 数据流向(核心:发送缓冲区、移位寄存器、接收缓冲区)
CPU写DR寄存器 → 发送缓冲区TDR → 移位寄存器 → MOSI引脚输出 同时: MISO引脚输入 → 移位寄存器 → 接收缓冲区RDR → CPU读DR寄存器⚠️ SPI全双工强制同步:只要 SCK 输出 N 个时钟脉冲,就一定同时发 N 位、收 N 位。不存在单纯 “只发不收”。
- 发送缓冲区 TDR:CPU 写
SPI->DR就写入 TDR; - 移位寄存器 Shift Reg:真正一位一位往外挪比特的硬件;LSBFIRST 控制:低位先行还是高位先行;
- 接收缓冲区 RDR:外部进来比特全部移入,CPU 读
SPI->DR读取 RDR; - TXE:发送缓冲区空。发送缓冲区已经空,可以写新数据进去。⚠️注意:TXE 只代表缓冲区空,移位寄存器可能还在往外发,通信没结束!
- RXNE:接收缓冲区非空,收到数据,可以读 DR。
- BSY:忙标志!移位寄存器正在工作,整个通信还没完成。BSY=0 代表完整收发完成。
SPE:SPI 使能开关,硬件 SPI 总开关。MSTR:1 = 主机模式;0 = 从机模式。CPOL、CPHA:SPI 四种模式,时钟极性、相位。BR[2:0]:波特率分频,APB 时钟分频生成 SCK 时钟。LSBFIRST:0 高位先行;1 低位先行。W25Q64 Flash 是高位先行。SSM:软件从机管理;SSM=1打开软件 NSS,NSS 引脚可以用作普通 GPIO。- TXEIE:TXE 中断;RXNEIE:RXNE 中断;
- TXDMAEN / RXDMAEN:发送、接收 DMA 使能。
- 等待
TXE == 1:发送缓冲区为空; SPI->DR = data;把数据写入发送缓冲区 TDR;- 硬件自动把 TDR 搬运到移位寄存器;波特率发生器输出 SCK 时钟;移位寄存器逐位从 MOSI 输出;同时 MISO 逐位采样外部数据。
- 8 个时钟脉冲结束:移位寄存器收到外部字节,搬运进接收缓冲区,置
RXNE=1。 - 如果要确认整个帧彻底发完:等待
BSY == 0。 - 读
SPI->DR把收到的数据读走(哪怕你不需要接收,也要读清 RXNE 标志)。 - TXE 仅仅代表发送缓冲区空,移位寄存器还在发送,BSY 才代表硬件是否正在传输。
- 只写 DR 不读 DR,RXNE 标志一直置 1,溢出错误 OVR。
- W25Q 系列 Flash:CPOL=0,CPHA=0,高位先行。
- SSM 软件片选:打开之后 NSS 引脚不再被 SPI 硬件接管,完全交给 GPIO 代码控制高低电平。
- 全双工模式 MOSI/MISO 同时工作;半双工 BIDI 模式只用单根线收发。
- 硬件 SPI:移位寄存器、SCK 时钟硬件自动生成;CPU 只读写 DR 寄存器;
- 软件模拟 SPI:全部 IO 口电平由 CPU 循环翻转,没有移位寄存器硬件。
发送缓存区,向一位寄存器发送数据,一位一位从MOSI转出去,发出去的同时从MISO接收数据,等移位寄存器接收完整了,再发到接收缓存区里至标志位为1,然后再读取接收缓存区的数据
USART / SPI / I2C 完整对比
硬件框图核心特点
USART
- 发送:CPU 写 DR → TDR 发送寄存器 →发送移位寄存器→ TX
- 接收:RX →接收移位寄存器→ RDR 接收寄存器 → CPU 读 DR
- 收发两套移位硬件完全独立;可以只发不收、只收不发。
- 发送:CPU 写 DR → 发送缓冲区 →共用移位寄存器→ MOSI
- 接收:MISO →共用移位寄存器→ 接收缓冲区 → CPU 读 DR
- 单移位寄存器共用;发 1bit 必然收 1bit,不能只发不收。
- SDA:双向数据线(开漏输出);SCL:时钟线,主机产生 SCL 时钟
- 没有硬件移位寄存器暴露给 CPU;硬件自动产生起始、停止、应答信号;靠地址寻址从机。
- SDA 是双向,同一根线既发又收;开漏,必须外部上拉电阻。
- USART:发送完成看TC;TXE 只是寄存器空,不代表全部比特移出。收发硬件相互独立,发送不会强制接收。
- SPI:传输完成看BSY,不要看 TXE;发必收,不读 DR 会 OVR 溢出;推挽输出,不需要上拉电阻。
- I2C:只有两根线,开漏,必须上拉电阻;靠地址区分从机,不需要片选引脚;硬件自动处理起始、停止、ACK 应答。
- USART:异步、两根线、对等,适合远距离。
- SPI:高速、三四根线、共用移位寄存器,发就必收,适合板上高速外设。
- I2C:两根开漏线、靠地址找设备,速度慢,适合多个传感器、EEPROM。补充: SPI:每个从机需要单独 NSS 片选引脚;I2C 不需要片选,靠地址。 I2C 所有设备 SDA/SCL 并联在同一总线上。
非连续传输时,它在传完字节后会先等RXNE也置为一再读下一个连续的是当它TXE=1后直接向发送寄存器存入下一个
硬件spi的接线情况
SPI1(软件 NSS 模式,SSM=1)引脚配置梳理
硬件 SPI1:PA4‑NSS,PA5‑SCK,PA6‑MISO,PA7‑MOSI,软件片选模式
引脚模式
- PA4 SS(NSS):普通推挽输出
GPIO_Mode_Out_PP - 不归 SPI 硬件管理,完全软件手动拉低 / 拉高做片选
- 对应寄存器
CR1‑SSM=1(软件从设备管理开启) - PA5 SCK、PA7 MOSI:复用推挽输出
GPIO_Mode_AF_PP - 交给 SPI 外设硬件接管,硬件自动输出 SCK 时钟、MOSI 串行比特。
- PA6 MISO:上拉输入
GPIO_Mode_IPU - 外部从机的数据送到 MISO 引脚,SPI 硬件内部自动采样读取电平。
- 软件模拟 SPI:4 根引脚全部配置普通 GPIO,所有时序、高低电平全部由 CPU 代码手动翻转。CPU 负担大。
- 硬件 SPI(软件 NSS):SCK/MOSI/MISO 交给外设复用模式;只有 NSS (PA4) 保留普通 GPIO,软件手动控制。时钟、移位、采样全部硬件自动完成。
CR1‑SSM = 1:软件 NSS 模式;NSS 引脚变成普通 GPIO,硬件不再接管 NSS 功能。CR1‑MSTR = 1:主机模式。CR1‑SPE =1:SPI 使能打开。
结构体的初始化SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; // 主机模式 MSTR=1 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;// 双线全双工 MOSI+MISO SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8位数据帧 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; // **高位先行MSB**,W25Q要求 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; // PCLK2/128;SPI1时钟72M/128 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // CPOL=0:空闲时SCK低电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // CPHA=0:**第一个时钟边沿采样数据 → SPI模式0** SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // ✅软件NSS模式,SSM=1;NSS引脚交给普通GPIO软件控制 SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC多项式,不用CRC时该值无效,随便写 SPI_Init(SPI1, &SPI_InitStructure);每个成员含义
- SPI_Mode_Master:主机模式,内部波特率发生器工作,输出 SCK 时钟。
- SPI_Direction_2Lines_FullDuplex:双线全双工,MOSI、MISO 都启用;最常用。
- SPI_DataSize_8b:每次传输 8bit 字节;可选 16b。W25Q Flash 固定 8bit。
- SPI_FirstBit_MSB:高位先发送;W25Q 必须 MSB;LSB 为低位先行。
- SPI_BaudRatePrescaler_128:波特率分频,仅主机生效。SPI1 挂载 APB2 (72MHz),72M/128。可选:2,4,8,16,32,64,128,256。
- CPOL + CPHA→ 决定 SPI4 种模式
CPOL_Low(0):SCK 空闲为低CPHA_1Edge(0):第一个边沿采样→SPI 模式 0(Mode0),W25Q64、大部分 SPI Flash 使用。
- SPI_NSS_Soft:软件 NSS 模式,对应寄存器
SSM=1。NSS (PA4) 不再由 SPI 硬件接管,由你代码手动置 0 / 置 1 做片选。如果写
SPI_NSS_Hard就是硬件 NSS 模式,硬件自动驱动 NSS 引脚,工程很少用。 - SPI_CRCPolynomial:CRC 校验多项式,不用 CRC 功能该字段不起作用。
SPI_Cmd(SPI1,ENABLE);使能 SPI 外设(SPE 位置 1)- 初始化 GPIO:
- PA4 (NSS):普通推挽输出
- PA5 (SCK)、PA7 (MOSI):复用推挽输出 AF_PP
- PA6 (MISO):上拉输入 IPU
SPI_NSS_Soft只是把 NSS 交给软件,不会自动操作 PA4 引脚电平,需要你自己写GPIO_SetBits / ResetBits。- CRC 那个参数不用管,即使填 7 也不会开启 CRC,需要另外寄存器开启 CRCEN。
- 波特率分频只对主机生效,从机此配置无效
- 等待数据txe是1,写入数据,等待rxne是1,读取数据。
不需要手动清除标志位