1. SPI3 没信号送出的现场形态:先搞清楚问题到底长什么样
先交代一下背景。最近在调一块板子,主控是某款 Cortex-A 系列处理器,板载一个 SPI3 接口外接到一颗 NOR Flash 和一片 LCD 驱动芯片。同事反馈说接口“完全没信号”,我拎着示波器过去一看,CLK、MOSI、CS 三个引脚全部平得像心电图停跳,一根毛刺都看不到。
这里要插一句:“SPI3 接口没有信号送出”这个描述,在实际工程里至少能拆成好几种完全不同的现象,排查方向完全不一样。
- 现象 A:CLK、MOSI、CS 全部无波形,像没初始化过一样。
- 现象 B:CLK 有时钟输出,但 MOSI 上什么都没有,CS 也不动。
- 现象 C:CLK、MOSI 都有,但 CS 一直是高电平,从机根本没被选中。
- 现象 D:主控这边的信号都有,但到了连接器/排线那一端就没了,中间链路断了。
- 现象 E:所有信号都有,但电平幅度不对,比如 1.8V 的主控接 3.3V 的外设,中间电平转换没工作。
我遇到的属于现象 A,但下面的排查思路对 B、C、D、E 同样适用。先说一个最容易犯的认知错误:很多人一听到“SPI3”就默认它一定存在,但 SPI3 这个编号的含义在不同平台上有两种常见解释。
一种解释是 SoC 内部的第 3 个 SPI 控制器,比如 i.MX6 的 ECSPI3、STM32H7 的 SPI3、树莓派 BCM2711 的 SPI3 控制器;另一种解释是 SPI 工作在三线制模式,即只有 CLK、MOSI(双向)和 CS,没有独立的 MISO。这两种情况下的排查手法差别很大。我先按“第 3 个 SPI 控制器”来展开,因为这是绝大多数嵌入式项目里遇到的情况,三线制模式会在后面单独说。
在动手之前,先把问题域收窄。SPI 是一个同步串行接口,主控要发出完整的传输事务,必须同时满足:时钟线有翻转、数据线有正确的电平变化、片选线有有效的拉低动作。这三根线只要有一根不对,从设备就不会响应,而工程师往往只看到“没信号”这个笼统现象。所以第一步不是去怀疑某个寄存器,而是用示波器把四根线(CLK、MOSI、MISO、CS)全部挂上,看看到底哪根没有、哪根有、哪根波形不对。
如果四根线全部平直,那问题几乎可以肯定出在主控侧:要么外设时钟没使能,要么引脚复用没配,要么控制器根本没被初始化。如果 CLK 有而其他没有,往往是传输事务没有真正启动,比如片选极性配置错误导致 CS 一直处于无效电平,或者发送缓冲区为空。如果主控侧波形完整但下游没有,那就是链路中间的问题,比如电平转换方向控制脚没拉对、连接器虚焊、排线断了。
我习惯用一个比喻来给刚入行的同事解释:SPI 就像一场有主持人的电话会议,CLK 是主持人打节拍的拍子,CS 是“现在开始点名”的信号,MOSI 是主持人说的话,MISO 是参会者的回答。拍子没响、没点名、没人说话,任何一个环节断了,会议都开不起来。排查 SPI3 没信号,本质上就是沿着这条“会议链路”逐段检查,看是主持人的问题、接线的问题,还是话筒的问题。
2. 从引脚到时钟树:SPI3 信号链路到底包含哪些环节
确定现象之后,下一步是把 SPI3 从“软件寄存器”到“物理引脚”的完整链路画在脑子里。很多人排查时喜欢一上来就翻代码,但我建议先打开原理图,从主控芯片的引脚一路看到对外连接器,把每个环节都过一遍。大多数 SPI3 无声问题,根源不是 SPI 控制器本身,而是链路中间的某个“隐形开关”没打开。
2.1 引脚复用:最高频的翻车点
现代 SoC 的引脚基本都是多功能引脚,同一个物理引脚可能同时挂着 UART、I2C、PWM、GPIO、SPI 等多组功能。芯片上电默认状态往往是 GPIO 模式,而且经常是带上拉或下拉的 GPIO。如果软件里没有把该引脚切换成 SPI3 的复用功能,那么即使 SPI 控制器已经在跑,时钟也翻转了,信号也到了引脚内部,但引脚仍然以 GPIO 模式输出,表现为“寄存器看起来在工作,示波器上看不到波形”。
这里有个特别容易踩的坑:复用功能寄存器写入的值必须在具体芯片手册里查,不能想当然。不同芯片对相同外设的 ALT 编号可能完全不同。比如某颗芯片上 SPI3_MOSI 的复用功能是 ALT2,另一颗同系列的芯片却可能变成 ALT5。我见过不止一次,有同事拿着上一颗芯片的设备树直接改型号就上,结果 SPI3 引脚全部处于 GPIO 模式,死活没波形。
检查方法很简单:读芯片手册的“Pin Multiplexing”章节,找到 SPI3 相关引脚,确认当前代码里写的 mux 值对应的是 SPI3 功能。如果用的是 Linux 下的设备树,重点检查 pinctrl 节点里的pinctrl-0和pinctrl-names是否真的被 SPI3 驱动引用到了。
2.2 SPI 控制器时钟树:不开时钟,寄存器全是空气
引脚复用配对了,接下来要看时钟树。SPI 控制器本身是一个数字外设,它需要两路时钟:一路是总线接口时钟(用来访问寄存器),另一路是外设功能时钟(用来产生 SPI 的 SCK 信号)。在很多 SoC 上,这两路时钟是独立的,门控位也可能分开。
外设功能时钟没打开,是“SPI3 完全没有信号”的第二大常见原因。这时候寄存器能读写,控制器状态寄存器也显示 enabled,但 SCK 引脚就是没有任何输出,因为波特率发生器没有时钟源,产生的 SCK 频率是 0。
排查手段是在驱动里读取时钟状态寄存器,确认 SPI3 的CKEN或类似门控位已经置 1。如果是 Linux 环境,检查设备树里 SPI3 节点的clocks属性和assigned-clock-rates,确保时钟源频率设置正确。用clk_summary或者/sys/kernel/debug/clk/clk_summary可以快速确认 SPI3 的时钟树是否 enable、频率是多少。
我遇到的这颗主控,SPI3 的时钟源来自一个 PLL 分频,PLL 默认状态是关闭的,必须由 bootloader 或内核初始化时打开。这就导致很多人只改了 SPI 驱动配置,没动时钟树,结果 SPI3 寄存器访问正常,但没有 SCK 输出。检查时钟树时,我习惯先看/sys/kernel/debug/clk/clk_summary里 SPI3 相关节点的prepare_count和enable_count,如果都是 0,那基本可以断定就是时钟门控的问题。
2.3 SPI 模式参数:不是“有配置”就一定能通
设好引脚和时钟之后,还要检查 SPI 模式参数。SPI 有四种工作模式,由 CPOL(时钟极性)和 CPHA(时钟相位)两个参数组合而成。CPOL 决定空闲时 SCK 是高电平还是低电平,CPHA 决定数据是在第一个边沿还是第二个边沿采样。主从双方必须匹配,否则从机可能完全不响应,甚至数据全错。
如果从机是 NOR Flash,大多数器件支持 Mode 0 或 Mode 3。如果是从机是 LCD 驱动芯片,不同型号支持的时序可能完全不同。如果 SPI3 接口接了多个从机,一定要确认每个从机事务都用了正确的 mode,而不是用一个全局配置通吃所有从机。在 Linux 下,每个从设备节点都可以单独配置spi-max-frequency和模式位,别偷懒。
另一个参数是数据位宽。STM32 的 SPI 外设默认是 8 位,但有的 SoC 支持 4 位、16 位、32 位甚至可编程位宽。如果驱动里配置了 16 位,而总线上的从机只支持 8 位,波形看起来是有的,但数据完全对不上。这次“没信号”的现场里,如果示波器上能看到 CLK 和数据线都有动作,但数据内容不对,先从位宽和模式检查起。
3. 硬件侧排查实操:示波器该夹哪里,量什么波形
软件配置检查完,如果还是没信号,就该上硬件手段了。很多工程师在硬件排查时有个坏毛病:拿起万用表乱点一通,点不出问题就一脸茫然。硬件排查必须按信号路径逐段推进,每推进一步都要能回答“这个节点上的信号应该长什么样”。
3.1 上电静态测量:先确认供电和电平域
第一步是万用表量电压。量三个东西:
- SPI3 相关引脚所在的电源域电压是否正常。很多 SoC 有多个 VDDIO 域,SPI3 可能在 1.8V 域,也可能在 3.3V 域,电压不对,引脚电平自然不对。
- 引脚对地阻抗是否正常。SPI3 引脚如果对地短路,信号会被直接拉低,示波器上就是平的。量阻抗时最好在断电状态下测,避免误判。
- 外部上拉/下拉电阻是否焊接正确。SPI 的 CS 引脚在系统里通常有上拉电阻,保证空闲时处于高电平。如果这个上拉电阻没焊,或者焊到了错误的位置,CS 可能一直悬空,状态不定。
静态测量能排除大概三成问题,剩下的要看动态波形。
3.2 动态波形测量:探头的挂法有讲究
我习惯用四通道示波器同时挂 CLK、MOSI、MISO、CS,四根线一起看。如果没有四通道,至少也要保证 CLK 和 CS 同时看,因为CS 的有效拉低是判断一个 SPI 事务是否启动的最直观标志。
探头的接地夹要尽量靠近被测点,最好用弹簧地针,不要用长地线夹子。SPI 的时钟频率通常在几 MHz 到几十 MHz,长地线会引入寄生电感,导致波形上出现过冲和振铃,影响判断。我见过有人拿着 60MHz 带宽的示波器去量 50MHz 的 SPI 时钟,量出来的波形严重失真,还以为是信号质量问题。
触发方式建议用 CS 下降沿触发。CS 平时是高电平,事务开始时拉低,用这个沿触发能稳定抓到整个事务的波形。如果 CS 一直没有低电平出现,说明事务根本没有启动;如果 CS 有低电平而 CLK 没有翻转,说明控制器认为自己在传输,但波特率时钟或分频配置有问题;如果 CS、CLK 都有而 MOSI 没有,说明发送数据寄存器是空的,或者 DMA 没有正确触发。
3.3 回环测试:一句话区分“没发出”和“没收到”
在排查 SPI3 没信号的现场,最快的一个定位手段是回环测试。直接把 MOSI 和 MISO 短接,或者在软件里把 SPI 配置成 loopback 模式,然后发一串已知数据,看能不能收回来。
- 如果回环能收到,说明 SPI 控制器的发送和接收路径都正常,问题在外部的从机设备、PCB 走线或连接器。
- 如果回环收不到,说明控制器本身可能就没正常工作,回到软件配置和时钟树检查。
Loopback 有两种实现方式:一种是 SoC 自带的硬件 loopback 模式,在 SPI 控制器的配置寄存器里打开,信号在芯片内部直接回环,不经过引脚;另一种是外部物理回环,用导线或者 PCB 测试点把 MOSI 和 MISO 短接。外部回环比内部回环更能反映问题,因为它把引脚复用、PCB 走线、连接器全链路都覆盖了。
我做外部回环时,习惯在连接器的从机端做短接,而不是在主控芯片引脚附近做。这样如果回环成功,说明从主控引脚到连接器这一段链路都是通的,问题可以进一步缩小到从机端。
3.4 电平转换芯片的方向控制脚:一个特别隐蔽的坑
如果主控和从机的电压域不同,中间需要加电平转换芯片,比如 TXS0108、SN74LVC4245 之类。这类芯片的方向控制脚(DIR、OE)如果没接对,信号根本过不去。
常见的坑是:OE 脚应该拉低使能,结果被悬空了;DIR 脚方向接反,导致数据从从机流向主机而不是从主机流向从机;或者方向控制脚接到了某个 GPIO,而该 GPIO 在软件里没有初始化。表现为 CPU 这边的 SPI3 引脚有完整波形,但电平转换芯片输出端什么都量不到。
这种问题用示波器一夹就能发现,但前提是你知道要在电平转换芯片的输入端和输出端同时挂探头对比。很多人在主控引脚上看到信号正常,就以为整条链路都正常,跳过了中间环节的测量,这是排查效率低下的主要原因之一。
4. 软件配置里最容易埋雷的几个位置
如果说硬件排查是“看得见摸得着”的部分,软件配置就是“藏在代码里”的部分。而且软件配置的错误往往比硬件错误更隐蔽,因为它不会在产品上留下任何物理痕迹,只会在运行时表现为功能失效。
4.1 设备树或初始化结构体:每个字段都得较真
在 Linux 环境下,SPI3 没信号最常见的原因是设备树节点没有正确匹配到驱动。检查的顺序很重要,我列一下我常用的核对清单:
| 检查项 | 典型错误 | 排查方式 |
|---|---|---|
| compatible 字符串 | 与驱动不匹配,导致 probe 不执行 | dmesg里看是否有 spi3 相关 probe 信息 |
| reg 属性 | 控制器基地址写错,访问到无关寄存器 | 对照芯片手册确认基地址 |
| interrupts | 中断号配置错误,传输没完成时无法触发回调 | cat /proc/interrupts确认中断是否产生 |
| pinctrl-0 | 引脚复用配置未被引用,引脚处于 GPIO 模式 | 读/sys/kernel/debug/pinctrl确认引脚状态 |
| spi-max-frequency | 频率设太高,从机跟不上导致无响应 | 先降到 1MHz 以下尝试 |
| mode 位 | CPOL/CPHA 与从机不匹配 | 对照从机数据手册确认 |
很多人看到设备树里 SPI3 节点存在,就默认驱动已经正常工作。但dmesg里如果出现spi3 supply spi not found或者failed to get clock,说明还有依赖资源没就绪。我排查时有个习惯:先看dmesg | grep -i spi,再看/dev/spidev3.0是否存在,最后直接跑spidev_test工具发数据。
4.2 时钟使能与 GPIO 复用:两个最隐蔽的失败点
前面硬件部分已经提过时钟,但软件侧还要再强调一次:很多时候不是芯片不支持,而是驱动里的时钟框架没有正确使能 SPI3 的时钟。在设备树里配置了clocks属性不代表时钟已经打开,驱动必须在probe函数里调用clk_prepare_enable(),或者依赖运行时 PM 框架自动开时钟。如果驱动本身的pm_runtime_enable没调用,或者时钟的enable计数不对,SPI3 就会处于“时钟未使能”的状态。
GPIO 复用也一样。设备树里 pinctrl 节点存在,不代表它生效。如果 SPI3 驱动和某个 GPIO 驱动同时请求了同一个物理引脚,后加载的驱动可能把复用配置覆盖掉。我在项目中遇到过一次:某个按键驱动把 SPI3 的 CS 引脚当成了 GPIO 输入,加载顺序刚好晚于 SPI 驱动,结果 CS 引脚被切成了 GPIO 模式,SPI3 的 CS 永远无法拉低。排查这类问题,需要查看/sys/kernel/debug/pinctrl/下各个引脚当前的 mux 状态,确认 SPI3 引脚没有被别的驱动抢占。
4.3 DMA 与中断配置:传输启动了但数据是空的
SPI 控制器支持 DMA 模式时,数据搬运依赖 DMA 通道。如果 DMA 通道配置失败,或者 DMA 请求信号没有被正确映射到 SPI3 的事件输出,那么发送时寄存器里可能只写入了第一个字节,后面的数据全部丢失。现象表现为:CLK 有时钟,MOSI 上只有零星几个脉冲,然后就没有然后了。
检查手段是看 DMA 引擎的状态。Linux 下可以查看/sys/kernel/debug/dmaengine/summary,确认 SPI3 的 DMA 通道是否注册成功。如果驱动里使用的是dma_request_chan,却在dmesg里看到failed to get dma channel,那就要检查设备树里dmas和dma-names属性是否配了正确的 DMA 请求 ID。
中断配置问题也类似。SPI3 在没有 DMA 的情况下,依赖 TX FIFO 为空和 RX FIFO 非空这两类中断。如果中断号配错,或者中断处理函数里没有正确清除标志位,传输会卡死在某个状态。这时候示波器上能看到 CLK 只翻转了几下就停了,这是因为发送端在等 TX FIFO 有空位,而中断没有触发,驱动认为 FIFO 还是满的。
4.4 从机没响应导致的“假性无输出”
还有一种容易被误判为“SPI3 没信号”的情况:主控确实发出了完整的事务,CLK、MOSI、CS 波形全部正常,但 MISO 上什么都没有,整条读取的数据全是 0xFF。从软件角度往回查,会看到 SPI 驱动超时,返回-ETIMEDOUT。这其实是从机设备没有正常工作,而不是主控没有发送信号。
遇到这种情况,先查从机供电、复位引脚、使能引脚是否正常。很多从机芯片都有硬件复位脚或使能脚,如果这些引脚被 GPIO 控制,但 GPIO 初始化顺序不对,从机可能一直处于复位状态或者掉电状态。我遇到过一颗 LCD 驱动芯片,它的 TE(撕裂效应)引脚被复用成了 GPIO 且被拉高,导致从机认为数据输入被暂停,SPI 传输的数据全部被丢弃。这种问题看起来像是“SPI3 没信号”,实际上链路全通,只是从机不干活。
5. 一次真实案例的完整复现:从现象到根因的排查路径
讲了这么多理论,拿一个实际案例串一下整个排查思路。这个案例来自我最近调试的板子,过程比较典型,希望能帮你建立一条可复用的排查路径。
5.1 现象确认:量到的波形
同事反馈 SPI3 外接的 NOR Flash 读不出来,flash_erase命令直接报错。我用示波器挂上四根线,触发方式设成 CS 下降沿,结果 CS 上的下降沿都看不到,四根线全部平直。当时第一反应是软件根本没初始化 SPI3。
5.2 排查过程:从 dmesg 到时钟树
先看dmesg | grep -i spi3,输出显示spi3 spi3.0: setup mode 0, 8 bits, 10000000 Hz max,说明驱动 probe 成功,也正确配置了模式。再看/sys/kernel/debug/clk/clk_summary | grep spi3,发现 spi3 的时钟节点enable_count是 1,看起来也正常。
这时候陷入了僵局:软件配置看起来正常,引脚复用也查过没有冲突,但就是没有波形。于是回头再看了一遍时钟树,发现clk_summary里有一个父时钟节点的prepare_count是 0。这个父节点正好是 SPI3 的源时钟,虽然是直连的分频节点,但父时钟没 prepare,子时钟 enable 了也没用。查驱动代码,发现有一个时钟是通过devm_clk_get_optional()获取的,驱动认为它是可选的,获取失败也不报错,但这个时钟恰恰是外设功能时钟的父时钟。
解决办法是在设备树的 SPI3 节点里补上缺失的时钟引用,同时在驱动里把devm_clk_get_optional()改成必需的devm_clk_get(),获取失败直接返回-EPROBE_DEFER。改完重新编译烧录,示波器上立刻出现了完整波形。
5.3 复盘要点:为什么一开始没发现
回头看,这个问题的隐蔽性在于:clk_enable_count已经置 1,但父级时钟没有 prepare,导致时钟根本没有真正到达 SPI3 外设。如果你只检查 SPI3 节点本身的时钟状态,永远发现不了问题。必须沿着时钟树往上追,确认每一个父节点都处于可工作状态。
排查这种问题,我的经验是:在clk_summary里找到 SPI3 时钟节点后,一路往根节点方向看,任何prepare_count为 0 的中间节点都有可能是问题所在。很多时候 SoC 内部的时钟树比你想的要复杂,多一个 mux、多一个 divider,就多一个开关。
5.4 另一个案例:外部回环定位链路断点
另一个案例是和 SPI3 无关但思路相同。某板子的 SPI3 连接器测不到信号,但主控引脚上波形正常。我在连接器的从机端把 MOSI 和 MISO 短接,主机发送 0xAA,结果收不到任何数据,说明从主控引脚到连接器这一段链路是断的。然后拿万用表量连接器到主控引脚的走线导通性,发现某个过孔虚焊,补焊后回环测试通过。整个过程用了不到十分钟,比盲改代码高效得多。
6. 几个容易忽略但非常实用的补充细节
前面算是一条主线的排查思路,但实际项目里还有几个零散但很实用的细节值得单独拿出来说,省得你在现场重复踩坑。
6.1 三线制 SPI3 模式的特殊性
开头提到过,有些语境下 SPI3 指的是三线制模式。三线制 SPI 只有 SCK、CS、DATA 三根线,数据线是双向的,主控发送时需要把方向切到输出,接收时需要切到输入。这种模式下,如果驱动里的 GPIO 方向切换时机不对,就会出现“发送正常、接收全零”或者反过来“发送时数据线上的波形是乱的”。
三线制 SPI 的排查重点是数据线的方向切换时序。用示波器量数据线,发送阶段应该有主控驱动的电平变化,接收阶段应该变成高阻或从机驱动。如果在接收阶段数据线上还是主控的高电平或者低电平,说明方向没有切换成功,数据冲突了。这类问题在 STM32 上开启三线制模式时尤其常见,因为它依赖硬件自动切换方向,一旦引脚复用配置成普通推挽输出,方向切换就失效了。
6.2 CS 片选的“假正常”状态
CS 是 SPI 信号里最容易被忽略又最容易出问题的一根线。很多人看到 CS 在示波器上是高电平,就认为它正常——但 SPI 空闲时 CS 本来就是高电平。要确认 CS 是否正常,必须在触发事务时看它有没有拉低。
CS 的常见问题有三个:一是极性配置反了,设备树里配成spi-cs-high,导致 CS 在传输时是高电平,从机永远处于未选中状态;二是 CS 被某个驱动当成了普通 GPIO 控制,导致 SPI 控制器无法驱动它;三是 CS 线上下拉电阻没接,在 EMI 干扰下电平抖动,从机误动作。
排查 CS 最直接的办法是在/sys/kernel/debug/pinctrl里查看该引脚的当前状态,如果是 GPIO 模式而设备树里没配置 CS 为 GPIO 控制,那就说明 pinctrl 配置有问题。
6.3 时序余量的判断:不要被“有波形”骗了
有时候示波器上波形完整,但系统就是不工作。这时候要关注信号的时序参数,包括建立时间、保持时间、上升沿/下降沿时间。SPI 主控输出的信号经过 PCB 走线、电平转换、连接器之后,到达从机引脚时可能已经变差。如果从机要求的建立时间余量不足,传输就会偶发失败。
判断方法是看数据线的跳变沿和 CLK 采样沿之间的关系。在 Mode 0 下,数据在 CLK 上升沿被采样,数据线的跳变应该发生在 CLK 下降沿附近,给采样留出完整的建立时间。如果数据线的跳变沿几乎和采样沿重合,说明时序余量已经很紧张,需要考虑降低 SPI 时钟频率、优化 PCB 走线长度、或者换用压摆率更高的电平转换芯片。
6.4 逻辑分析仪还是示波器?
排查 SPI3 信号,工具选择上有讲究。示波器适合看模拟特性,比如电平、边沿、振铃;逻辑分析仪适合看时序关系和数据内容,尤其是需要解码一串很长的数据时。
如果只是确认“有没有信号”,示波器就够;如果要确认“数据内容对不对”,逻辑分析仪更方便。实际操作中,我通常是示波器先挂上确认物理层正常,再用逻辑分析仪抓一段完整的事务解码验证。两者配合,排查效率最高。
7. 最后再分享一点个人体会
SPI3 接口没有信号送出,这类问题的排查过程,说到底就是“沿着信号链路走一遍”的过程。软件配置查完查硬件,硬件查完再回头看软件,每一步都要有明确证据,而不是靠猜。我在实际工作中见过太多人一上来就反复改寄存器、改设备树,改完一测还是不行,又改回去,一个下午就这么耗掉了。
我更推荐的做法是:先花十分钟把示波器挂好,量清每一根线的实际状态。波形是最好的证据,它不会骗人。CLK 有没有、CS 有没有拉低、MOSI 有没有数据,这三个信息一出来,问题范围立刻缩到很小。剩下的就是对症下药:没有时钟查时钟树,没有片选查复用配置,有波形但数据错查时序和模式参数。
排查过程中养成记录的习惯也很有价值。每次改动什么参数、波形有什么变化、最终根因是什么,记下来之后,下次遇到类似问题可以快速对照。我自己就维护了一份“SPI 接口排查笔记”,里面记录了每个平台 SPI3 的引脚复用编号、时钟树路径、常见坑点。遇到新项目时直接翻出来对照,比重新踩一遍坑高效得多。
希望这篇经验分享能帮你少走一些弯路。如果你在排查 SPI3 时遇到的情况和上面说的都不一样,也欢迎交流,毕竟嵌入式世界里的“没信号”千奇百怪,多一个案例就多一分经验。