跳转至

📄 文章总结:《示波器使用教程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 字节(0xEF0x400x17)→ 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 更加重要——你得自己确认通信有没有发生。

十七、练习

  1. 识别 SPI 模式:在 CubeMX 里分别把 SPI 配成 Mode 0(CPOL=Low, CPHA=1 Edge)和 Mode 3(CPOL=High, CPHA=2 Edge),两次都用示波器抓 SCK + MOSI 波形,对比 SCK 空闲电平和 MOSI 数据变化位置。做完后就能从波形上直接读出任意设备的 SPI 模式。
  2. 弹簧接地片对比实验:用长地线夹和弹簧接地片分别测同一个 9MHz SCK 信号,对比振铃大小,拍两张截图。做一遍,对“测量方法引入误差”的理解比看十篇文章都深刻。
  3. 刻意制造全 FF:把 Flash CS 引脚飞线断开(不接 PA4),运行读 ID 程序,示波器观察 MISO 全程高;把 CS 接回去,再观察 MISO 出现数据波形。体会“全 FF = 从机没理你”的波形特征。
  4. 降低 SCK 频率观察影响:在 CubeMX 里把 SPI 预分频器从 8 改成 256(SCK = 72MHz / 256 ≈ 281kHz),用示波器对比不同频率下的边沿质量,低频时波形漂亮得多。
  5. 多从机场景模拟:如果有两个 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 很难替代的——模型能生成逻辑,但不会帮你判断探头地线电感造成的振铃是真是假。