📄 文章总结:《示波器使用教程04:用示波器调试 SPI——全 FF、数据移位、多从机打架,波形上让真凶现行》
公众号: 嵌入式小站 | 分类: hardware | 收录日期: 2026-08-11 原链接: https://mp.weixin.qq.com/s/VEGB-DtYfAbRdt1eSVcH8w
《示波器使用教程04:用示波器调试 SPI——全 FF、数据移位、多从机打架,波形上让真凶现行》
公众号:嵌入式小站
一、开篇:SPI 调试的核心判断
SPI 比 I2C 简单:没有开漏、没有上拉、没有应答位、没有时钟拉伸。四根线各司其职,推挽输出,干净利落。
但 SPI 也有自己的麻烦:它快,几 MHz 到十几 MHz,是一路飙高速的协议。低速时无关紧要的细节——探头地线长了一点、杜邦线多绕了一个圈、CPOL 写反了——到了高速下全都变成 bug。
SPI 没有 ACK:你发出了数据,从机收没收到?你不知道。读回来全是 0xFF,你不知道是“从机没听到”、“听到但返回的就是 FF”,还是“从机根本没在驱动 MISO”。
前两篇 UART 和 I2C,示波器价值主要在“能看到你看不见的东西”。SPI 调试中,示波器价值升华了一步——它是唯一能同时看到四根线之间时序关系的工具。
本篇依赖第五篇(SPI Flash 读写)的工程基础。建议先回第五篇把 Flash 跑通,本篇专注示波器调试。
二、本篇目标
最终能力
- 能从示波器上一眼识别 SPI 的四种模式(CPOL/CPHA),并判断当前通信用的是哪一种
- 能区分“全 FF”的三种根因:CS 没拉低、时钟模式不匹配、从机根本没驱动 MISO
- 能看懂 CPHA=0 和 CPHA=1 在波形上的区别——SPI 调试中最重要的判断
- 能应对高速 SPI(≥10MHz)的新挑战:振铃、地弹、探头地线电感
跑通标准
- 四通道(或分两次双通道)捕获一次完整的 SPI 读操作:CS↓ → SCK+MOSI+MISO → CS↑
- 测出 SCK 的实际频率,验证边沿上是否有振铃
- 面对 SPI Flash 返回全 FF,按三层顺序在 30 秒内定位根因
- 能把探头地线换成弹簧接地片,观察振铃消失——理解测量方法和真实信号的差别
本篇不做什么
- 不重复 CubeMX 的 SPI 配置(05 篇已覆盖)
- 不讲 SPI 的 DMA 传输
- 不讲 Quad SPI(QSPI)多线模式
三、准备工作
| 项目 | 说明 |
|---|---|
| 上一篇工程 | 第五篇 05_i2c_spi_console 或任意能读写 SPI Flash(W25Q64)的工程 |
| 示波器 | 带宽 ≥ 100MHz(建议)。SPI 动辄 10MHz+,20MHz 带宽只能看到正弦波 |
| 示波器通道数 | 至少 2 通道,4 通道最好(同时看 CS/SCK/MOSI/MISO)。2 通道分两次测也行 |
| 探头附件 | 弹簧接地片——本篇开始,探头地线长度会影响你看到的波形 |
| SPI 从设备 | W25Q64 Flash 或任意 SPI 设备 |
四、SPI 与前面协议最大的不同:速度改变了游戏规则
- UART 115200 波特率 = 每个 bit 8.68μs
- I2C 标准模式 SCL = 100kHz = 周期 10μs
- SPI:CubeMX 默认预分频器 8,APB2 72MHz / 8 = 9MHz 的 SCK,每个 bit 宽度 = 111ns
一百纳秒级别。在这个速度下,之前两篇不需要关心的物理效应开始变得重要:
- 探头的 15cm 地线是一个不可忽略的电感,会和探头电容一起在波形上制造“振铃”(实际不存在的阻尼振荡)
- 杜邦线的寄生电容开始影响边沿斜率
- PCB 走线的反射可能引起过冲
SPI 是本系列中第一次接触“高速”信号,示波器使用方法也要升级。
五、搭建测试载体:反复读取 SPI Flash 的 JEDEC ID
用第八篇工程,在 main.c 里每隔 200ms 读取一次 W25Q64 的 JEDEC ID:
while(1)
{
uint32_t id = App_Flash_JEDEC_ID();
printf("JEDEC ID: 0x%06lX\r\n", id);
HAL_Delay(200);
}
W25Q64 的 JEDEC ID 应该是 0xEF4017。每次读 ID 都会产生一次完整 SPI 通信:
CS 拉低 → 主机发 0x9F(读 ID 命令)→ 从机返回 3 字节(0xEF、0x40、0x17)→ CS 拉高。
这是一个非常好的测试波形:SCK 上能看到时钟,MOSI 上看到命令字节,MISO 上看到从机回应,CS 上看到帧边界。
六、SPI 三层排查体系回顾
| 层 | 要回答的问题 | SPI 特有挑战 | 示波器怎么看 |
|---|---|---|---|
| 第一层:信号有无 | SCK 有时钟吗?CS 拉低了吗?MOSI 有数据吗?MISO 有数据回来吗? | SPI 有 4 根线,不能只看一根 | 至少同时看 SCK + CS,确认通信有没有发起 |
| 第二层:电压和信号质量 | 边沿干净吗?有振铃吗?MISO 电平真的到 3.3V 了吗? | SPI 速度高,振铃和地弹首次明显 | 展开到 50ns/div 看边沿质量,换弹簧接地片对比 |
| 第三层:时序和模式 | CPOL/CPHA 匹配吗?数据在哪个边沿采样?CS 的建立/保持时间够吗? | CPOL/CPHA 不匹配 = 数据移位,SPI 最常见故障 | 同时看 SCK 和 MOSI,判断数据在哪个 SCK 边沿变化 |
七、第一层:通信发起了吗——双通道锁定 SCK 和 CS
探头连接
- 四通道示波器四个全接:
- CH1 → CS (PA4)
- CH2 → SCK (PA5)
- CH3 → MISO (PA6)
- CH4 → MOSI (PA7)
- 所有地线夹 → GND
- 两通道分两次测:
- 第一次 CH1=CS + CH2=SCK(确认通信发起)
- 第二次 CH1=SCK + CH2=MOSI 或 MISO(看数据细节)
初始设置
| 设置项 | 值 | 为什么 |
|---|---|---|
| 所有通道垂直 | 1V/div,DC 耦合 | 3.3V TTL 电平 |
| 水平时基 | 先设 20μs/div | 一次 SPI 读 ID 操作约几十微秒,20μs × 10 = 200μs 窗口 |
| 触发源 | CH1(CS) | CS 下降沿标志一帧开始,是 SPI 最好的触发源 |
| 触发边沿 | 下降沿 | CS 从高拉低 = 通信开始 |
| 触发电平 | 1.5V | — |
| 触发模式 | Normal → Single | 抓一次读 ID 操作 |
你看到的是什么
- CS(CH1):从 3.3V 跳到 0V,持续几十微秒,然后跳回 3.3V。这个“低电平窗口”就是一帧 SPI 通信
- SCK(CH2):CS 变低后,出现一串密集方波脉冲。空闲时停留在 CPOL 定义的电平(CPOL=0 时低,CPOL=1 时高)
第一层判断表
| 观察 | 判断 | 下一步 |
|---|---|---|
| CS 有下降沿,SCK 有时钟脉冲 | 通信发起了,第一层通过 | 进入第二层 |
| CS 是平直线(一直高或一直低) | 代码没有操作 CS,检查 App_Flash_JEDEC_ID() 是否被调用 |
查代码 |
| CS 有下降沿,但 SCK 没有时钟 | SPI 外设没真正输出时钟,检查 CubeMX SPI 初始化、HAL_SPI_TransmitReceive 是否被调用 |
查配置 |
八、认识 SPI 四种模式:CPOL 和 CPHA
这是 SPI 调试中最核心的知识点。CPOL/CPHA 不匹配 = 从机要么收错数据,要么根本不回应。
CPOL(Clock Polarity,时钟极性)
定义 SCK 空闲时的电平:
- CPOL = 0:SCK 空闲时为低电平。活动时先上升(低→高),再下降(高→低)
- CPOL = 1:SCK 空闲时为高电平。活动时先下降(高→低),再上升(低→高)
示波器上,看 CS 拉低之前 SCK 是什么电平:高就是 CPOL=1,低就是 CPOL=0。一眼的事。
CPHA(Clock Phase,时钟相位)
定义数据在 SCK 的哪个边沿被采样:
- CPHA = 0:数据在 SCK 的第一个边沿被采样(CPOL=0 时是上升沿,CPOL=1 时是下降沿)
数据在 CS 拉低后、第一个 SCK 边沿到来前就必须准备好 - CPHA = 1:数据在 SCK 的第二个边沿被采样(CPOL=0 时是下降沿,CPOL=1 时是上升沿)
数据在第一个 SCK 边沿之后才变化
示波器上看 CPHA,核心是看 MOSI 数据变化和 SCK 边沿的相对位置:
- CPHA=0:数据在 SCK 翻转之前就变了——MOSI 在 SCK 边沿前面一点点跳变
- CPHA=1:数据在 SCK 翻转之后才变——MOSI 变化紧跟在 SCK 边沿后面
绝大多数 SPI 设备用 Mode 0(CPOL=0, CPHA=0)或 Mode 3(CPOL=1, CPHA=1)。W25Q64 支持 Mode 0 和 Mode 3。CubeMX 默认配置是 CPOL=Low, CPHA=1 Edge,对应 Mode 0。
如果 CPOL/CPHA 不匹配,会发生什么
主机配 Mode 0(CPOL=0, CPHA=0),从机期望 Mode 3(CPOL=1, CPHA=1):
- 主机 SCK 空闲是低,从机期望空闲是高
- 主机数据在 SCK 上升沿有效,从机期望在 SCK 下降沿采样
- 从机采到的每一位数据都偏移了半个时钟周期
- 命令字节 0x9F 可能被从机解读成 0x3F 或其他错误值
- 从机不回应,MISO 一直高阻 → 主机读到全 FF
全 FF 是 SPI 调试中最常见的症状,CPOL/CPHA 不匹配是它最常见的原因。
示波器验证:同时显示 SCK 和 MOSI,看第一个 SCK 边沿之前 MOSI 是否已经变了(CPHA=0),还是第一个 SCK 边沿之后才变(CPHA=1)。与 CubeMX 配置对比,确保一致。
九、第二层:边沿干净吗——高速信号的新麻烦
把水平时基展开到 50ns/div,找一个 SCK 上升沿放大看。
正常边沿(推挽输出 + 短走线)
- 上升时间 < 10ns
- 边沿有轻微过冲(比 3.3V 高一丁点,比如 3.5V),然后迅速回到 3.3V
- 没有振铃
推挽输出驱动能力强,SPI 边沿通常比 I2C 漂亮得多。
异常边沿:有振铃
- 边沿过冲后,信号没有立刻稳定,而是上下振荡几次才平息
- 振荡幅度可能到 1V pk-pk
- 频率通常在几十到几百 MHz
出现振铃,第一件事不是怀疑电路,而是怀疑测量方法有问题。
用 15cm 长地线夹的探头测 9MHz SPI SCK,地线电感(约 100~200nH)和探头输入电容(10~20pF)组成 LC 谐振回路,谐振频率正好在几十到一百 MHz——这个振铃大概率是测量假象。
验证方法:弹簧接地片
把长地线拆掉,换上弹簧接地片(探头附件里,绕在探头前端的金属弹簧片,接触点离信号不到 5mm)。重新看同一个信号:如果振铃大幅减小或消失,说明之前看到的是地线电感引入的假象,不是真实信号问题。
如果换弹簧接地片之后振铃还在,那才是真实信号完整性问题。可能原因:
- 走线太长,传输线效应明显 → 在信号线末端对地加 22~50Ω 串联终端电阻
- SCK 走线和 GND 平面没有紧耦合 → 加地线伴行
- 多个从机并联导致总线电容大 → 降低 SCK 频率
实用判断标准
- SCK 频率 < 5MHz 且走线 < 10cm → 大概率不需要担心信号完整性
- SCK 频率 > 10MHz 或走线 > 20cm → 必须用弹簧接地片测,确认信号质量
十、第三层:全 FF 的三步定位法
SPI 读回数据全是 0xFF,是 SPI 调试中最让人沮丧也最常见的场景。示波器可以在三步内定位根因。
根因 1:CS 根本没有拉低
从机靠 CS 下降沿判断“主机在跟我说话”。CS 不拉低 = 从机认为总线空闲 = MISO 保持高阻 = 上拉电阻把 MISO 拉到 3.3V = 主机每个 bit 都读到 1 = 全 FF。
示波器上:CH1(CS)是一条平直线,全程高。
| 排查项 | 具体操作 |
|---|---|
| GPIO 模式 | CubeMX 里 FLASH_CS 引脚是 GPIO_Output 吗? |
| User Label | CubeMX 里 User Label 是不是 FLASH_CS?代码里用的是 FLASH_CS_GPIO_Port 和 FLASH_CS_Pin |
| 代码 | App_Flash_JEDEC_ID() 函数里是否有拉低 CS 的操作? |
根因 2:CPOL/CPHA 不匹配
从机检测到 CS 拉低、SCK 时钟,但由于时钟极性和相位与从机期望不一致,把命令字节解读成错误值,于是不响应。MISO 依旧高阻 → 全 FF。
示波器上:CS 拉了低,SCK 有时钟,MOSI 有数据,但 MISO 始终是高电平(没被拉低过)。
| 排查项 | 具体操作 |
|---|---|
| 从机数据手册 | 查从机支持的 SPI Mode。W25Q64 = Mode 0 或 Mode 3 |
| CubeMX | CPOL = Low, CPHA = 1 Edge → Mode 0 ✓ |
| 示波器 | 同时显示 SCK 和 MOSI,确认 MOSI 数据变化在 SCK 的哪个边沿附近 |
根因 3:从机没供电或接线断了
从机根本没电,当然不会驱动 MISO。
示波器上:MISO 可能是 0V(对地短路),也可能是奇怪的中间电平(引脚浮空),也可能是 3.3V(有上拉)。和正常高电平 3.3V 的通信数据相比,全 FF 的 MISO 是没有数据结构的平直线。
| 排查项 | 具体操作 |
|---|---|
| 从机供电 | 万用表量从机 VCC-GND 电压,确认是 3.3V |
| MISO 接线 | PA6 ↔ Flash DO 是否连通?杜邦线有没有断? |
| GND 共地 | 从机和 STM32 的 GND 是同一根线吗? |
十一、高级话题:多从机共用 SPI 总线——示波器拆解总线冲突
SPI 总线上挂多个从设备时,每个从机有自己的 CS,所有从机共享 SCK/MOSI/MISO。
MISO 是共享的,这是问题根源。如果两个从机的 CS 同时被拉低(代码 bug),两个从机都会驱动 MISO:一个想输出 1,一个想输出 0 → 总线冲突。两个推挽输出对打,轻则数据错乱,重则烧坏引脚。
示波器看总线冲突:
- 同时接所有 CS 线(有多少从机就用多少通道,或分次测)
- 触发条件设为 CS 下降沿,Single 模式
- 检查有没有两个 CS 同时为低的时间窗口
- 如果发现重叠——修改代码确保任何时刻只有一个 CS 为低
通道不够看不到所有 CS 时,可以看 MISO 电压:正常时 Vmax ≈ 3.3V;如果 Vmax 只有 2.0V~2.5V,可能是两个从机在抢夺总线,电压被拉到中间值。
十二、实战:用脉冲宽度触发抓“CS 太短”的 bug
SPI Flash 有一个容易被忽视的时序要求:CS 拉低后到第一个 SCK 脉冲之间的时间(CSSETUP),以及最后一个 SCK 脉冲到 CS 拉高的时间(CSHOLD)。
如果 CS 拉高太早,最后一个 SCK 脉冲的数据还没被从机完整锁存,从机可能只收到 7 个 bit。
这种“最后一次 bit 丢了”的 bug,普通边沿触发很难抓,可能在几千次通信中才出现一次。用脉冲宽度触发来抓:
- 触发类型设为 Pulse Width(脉宽触发)
- 触发源 = CS
- 条件 = 负脉宽 < 正常脉宽的下限
- Single 模式
如果 CS 低电平持续时间因某种原因变短了(比正常值少了几个 SCK 周期),脉宽触发会把它抓住。然后展开看那一帧,确认是少了几个时钟还是 CS 提前拉高。
十三、示波器设置速查卡:SPI 调试
| 步骤 | 设置 | 目的 |
|---|---|---|
| 1 | 至少 2 通道(CH1=CS, CH2=SCK),有余力加 CH3=MOSI, CH4=MISO | CS 是最佳触发源,SCK 是时钟基准 |
| 2 | DC 耦合,1V/div | TTL 电平 |
| 3 | 水平时基 20μs/div(粗看整帧)→ 50ns/div(看边沿细节) | 两个量程切换 |
| 4 | 触发源 = CS,下降沿,Normal 模式 | 一帧抓一次 |
| 5 | 展开到 50ns/div 看 SCK 边沿 | 检查振铃、过冲、上升时间 |
| 6 | 换弹簧接地片再测一次 | 排除测量假象 |
| 7 | 同时显示 SCK + MOSI,观察数据在哪个 SCK 边沿变化 | 验证 CPOL/CPHA |
| 8 | 光标测 SCK 周期 → 反推 SCK 频率 | 和 CubeMX 配置对比 |
十四、移植到其他板子的修改点
| 改动 | 影响 | 怎么做 |
|---|---|---|
| 换 SPI 外设(SPI1→SPI2) | 引脚和句柄都变了 | CubeMX 重配,代码里改 APP_SPI_HANDLE |
| 换从机芯片 | CPOL/CPHA、最大 SCK 频率、命令格式都可能不同 | 第一件事查从机数据手册的 SPI Mode 和 Timing 章节 |
| 增加从机数量 | MISO 总线冲突风险 | 示波器确认任何时刻只有一路 CS 为低 |
| 提高 SCK 频率 | 信号完整性挑战加大 | 弹簧接地片测边沿和振铃,必要时加终端电阻 |
| 加了长排线 | 寄生电容↑、边沿变缓 | 示波器看边沿,过缓则降低 SCK 频率或增强驱动 |
十五、常见问题排查
| 现象 | 最可能的原因 | 示波器验证方法 |
|---|---|---|
| SPI 读操作返回全 FF | ① CS 没拉低 ② CPOL/CPHA 不匹配 ③ 从机没供电 | ① 看 CS 有下降沿吗 ② 双通道看 SCK 和 MOSI,确认数据变化在哪个边沿 ③ 看 MISO 全程高 = 从机没驱动 |
| JEDEC ID 有时候对有时候错 | SCK 频率太高,或边沿振铃导致从机误采样 | 展开到 50ns/div 看边沿;振铃明显则降低 SCK 频率(CubeMX 增大预分频器)再试 |
| SCK 波形不是方波而是像正弦波 | 示波器带宽不够!9MHz 信号用 20MHz 带宽示波器看,3 次谐波(27MHz)被滤掉了 | 换 ≥100MHz 带宽示波器;或把探头从 1× 切到 10×(1× 带宽通常只有 6~20MHz) |
| 数据有时移位一位 | CPHA 配反了,从机在错误边沿采样,数据位移了半拍 | 示波器双通道看 SCK 的哪个边沿和 MOSI 数据变化对齐 |
| 多从机时偶尔数据错乱 | CS 信号有重叠,两个从机同时被选中 | 多通道同时看所有 CS 线,确认没有低电平重叠窗口 |
| 振铃严重但弹簧接地片也没改善 | 真实信号完整性问题:走线太长或阻抗不连续 | 在 SCK 源端串 33Ω 电阻,或降低 SCK 频率 |
十六、本篇小结
- SPI 是推挽输出 + 高速,调试方式要升级。CS 是最好的触发源。SCK 频率高于 5MHz 时必须关注探头接地方式。
- 全 FF = 从机没有驱动 MISO。三步定位:CS 拉低了吗?→ SCK 有时钟吗?→ CPOL/CPHA 对吗?
- CPOL 看空闲电平,CPHA 看数据相对于 SCK 边沿的位置。示波器双通道同时显示 SCK 和 MOSI,CPOL/CPHA 一目了然——不需要背,只需要看。
- 探头地线电感在 MHz 级别开始造成假象。看到振铃 → 第一反应换弹簧接地片 → 还在振铃 → 才是真问题。
- SPI 没有 ACK,这是它和 I2C 最本质的区别。I2C 能在第 9 位看到从机是否应答,SPI 不能。所以 SPI 调试时 CS 和 SCK 的确认比 I2C 更加重要——你得自己确认通信有没有发生。
十七、练习
- 识别 SPI 模式:在 CubeMX 里分别把 SPI 配成 Mode 0(CPOL=Low, CPHA=1 Edge)和 Mode 3(CPOL=High, CPHA=2 Edge),两次都用示波器抓 SCK + MOSI 波形,对比 SCK 空闲电平和 MOSI 数据变化位置。做完后就能从波形上直接读出任意设备的 SPI 模式。
- 弹簧接地片对比实验:用长地线夹和弹簧接地片分别测同一个 9MHz SCK 信号,对比振铃大小,拍两张截图。做一遍,对“测量方法引入误差”的理解比看十篇文章都深刻。
- 刻意制造全 FF:把 Flash CS 引脚飞线断开(不接 PA4),运行读 ID 程序,示波器观察 MISO 全程高;把 CS 接回去,再观察 MISO 出现数据波形。体会“全 FF = 从机没理你”的波形特征。
- 降低 SCK 频率观察影响:在 CubeMX 里把 SPI 预分频器从 8 改成 256(SCK = 72MHz / 256 ≈ 281kHz),用示波器对比不同频率下的边沿质量,低频时波形漂亮得多。
- 多从机场景模拟:如果有两个 SPI 从机,用示波器同时接两个 CS 线,确认程序在切换 CS 时,两个 CS 之间有没有低电平重叠。这是多从机 SPI 系统的必做检查。
十八、下一篇预告
UART、I2C、SPI 三个通信协议的波形调试方法讲完了。下一篇不讲协议,而是进入嵌入式系统调试中最隐蔽的一类问题:电源噪声、地弹、复位异常——那些你以为和软件有关的“软故障”,示波器会告诉你其实是硬件在作怪。这可能是整个系列里最有价值的一篇。
🎯 Friday 提炼
- SPI 没有 ACK,调试时必须自己确认通信是否发生:CS 下降沿是最佳触发源,SCK 是时钟基准。
- “全 FF” = 从机没驱动 MISO,按“CS 拉低?SCK 有时钟?CPOL/CPHA 匹配?”三步定位。
- CPOL 看 SCK 空闲电平,CPHA 看数据相对 SCK 边沿的变化位置;双通道看 SCK + MOSI 即可判断模式。
- 高速 SPI 下,长地线夹会引入振铃假象;看到振铃先换弹簧接地片排除测量方法,再谈真实信号完整性问题。
- 多从机共用 MISO 时,用多通道看 CS 是否重叠,或看 MISO 是否被拉到 2.0V~2.5V 中间电平,可识别总线冲突。
一句话: 用示波器调试 SPI,核心是抓住 CS 帧边界、通过 SCK 与 MOSI 的相对位置判断 CPOL/CPHA,并用“全 FF 三步定位法”快速锁定从机没回应、模式不匹配或接线供电问题。
💡 Friday 看法
这篇文章把 SPI 调试里最玄乎的“全 FF”和“数据移位”落到波形上,确实比对着寄存器猜高效多了。我比较赞同那个观点:SPI 没有 ACK,示波器就是唯一的“反馈通道”,尤其调多从机打架时,光看代码很难想到是 CS 时序或 MISO 没释放的问题。作为平时用 AI 写代码的人,我反而觉得这类硬件调试经验是 AI 很难替代的——模型能生成逻辑,但不会帮你判断探头地线电感造成的振铃是真是假。