STM32MP257 SPI从机NSS引脚claim失败排查与修复 1. 问题现象与影响1.1 表面现象Slave 永远等不到 NSS 被拉低最近在 STM32MP257F_EV1 上调试一个外接 FPGA 的通信链路FPGA 作为 SPI Master板子上的 SPI3 作为 Slave。SPI3 的 NSS 引脚按照原理图走的是 PB1于是我把 PB1 配成 SPI3_NSS中断里等 FPGA 把 NSS 拉低后开始收发。结果一跑起来FPGA 那边明明已经正常输出时钟和数据SPI3 这边却毫无反应。逻辑分析仪挂在 PB1 上波形很清楚主机确实把 NSS 拉低了时钟也来了但从机完全没有进入接收状态。更让人抓狂的是读寄存器看到的 NSS 电平状态始终是 1好像 PB1 根本没连到 SPI3 内部。这种问题最难受的地方在于硬件看着没问题软件代码也翻了无数遍最后折腾一圈才发现问题根本不在 SPI 外设本身而是 PB1 这个引脚在系统初始化阶段就没有成功被 SPI3 驱动“claim”下来。1.2 这类问题的影响范围不光是 STM32MP257F_EV1 这一块板子所有跑 Linux 的 STM32MP 系列芯片都会遇到类似情况。只要涉及引脚复用、pinctrl、设备树就存在“引脚被谁抢走”的问题。尤其是这种从机模式NSS 引脚必须老老实实进 SPI 控制器一旦被其他设备或者 GPIO 子系统占用从机功能就会静默失效而且不会有特别明显的报错。影响范围不仅仅是 NSS 这一个脚。SCK、MISO、MOSI 如果被别的节点占走现象会更早爆发。但 NSS 这个脚比较特殊它有时候会以 GPIO 方式复用导致问题更隐蔽。所以我建议所有做 STM32MP2 系列外设开发的工程师都看一遍这篇文章里的排查思路省得到时候被“假硬件故障”坑一整天。2. SPI 从机和 NSS 的基本功2.1 从机是怎么被“选中”的SPI 通信里Master 和 Slave 之间的关系很像开会Master 是主持人SCK 是会议室里的时钟铃NSS 就是会议邀请。只有收到邀请的人NSS 拉低才有资格在总线上发言。从机的内部逻辑很简单NSS 有效电平到来之前它内部的所有收发状态机都处于 idleSCK 上无论来多少时钟都不会理会。NSS 有效的那一瞬间从机被激活开始按 SCK 边沿采样 MOSI 或输出 MISO 数据。如果你的 NSS 根本没被正确映射到 SPI 外设那即使外部电平变化从机内部也根本看不到。所以“NSS 无法 claim”这句话翻译成人话就是SPI 控制器并没有获得对 PB1 这个引脚的访问权。它既不能读取 NSS 的高低压也不能控制 NSS 的去抖和边沿检测。2.2 硬件 NSS 和软件 NSS 的区别STM32 的 SPI 从机模式支持两种 NSS 管理方式。第一种是硬件 NSS也就是 NSS 信号由外部输入到专用引脚SPI 外设直接读取这个引脚的电平。好处是响应快不需要软件参与特别适合一主一从、对时序敏感的场景。坏处就是引脚必须被正确复用而且外部信号质量要过关。第二种是软件 NSS通过配置 SPI_CFG2 寄存器里的 SSOE、NSSP 等位让从机不依赖外部引脚而是由软件在合适的时机“假装”收到选中信号。这种方式省掉一个引脚但时序上会有一定延迟而且主从双方必须约定好通信窗口。我在调试的时候优先用的是硬件 NSS因为 FPGA 作为主机时 NSS 波形非常干净延迟完全可控。如果硬件 NSS 在引脚申请上出了岔子切换到软件 NSS 也只能算“能跑”并不能真正解决根因。2.3 NSS claim 究竟 claim 了什么在 Linux 系统里一个引脚从启动到能被某个外设使用要走完“pin control”这条路。NSS 引脚也不例外。claim 这个动作实际上是 Linux pinctrl 子系统在帮 SPI 驱动向 pinmux 模块“登记入住”。一旦 claim 失败SPI3 驱动仍然会加载但 Pin 的 ownership 不在它手里。外设寄存器和物理引脚之间处于“断线”状态。这种失败往往不会导致 SPI3 设备节点不出现只是功能完全不正常。简单类比你订了酒店房间但门禁卡没有发到你手里你既进不了房间也看不到房间状态。表面看一切正常实际上资源根本不可用。3. STM32MP257F_EV1 板卡上的 SPI3/PB1 地盘3.1 从原理图到 pinmux 的必经之路在 STM32MP257F_EV1 上SPI3 和 PB1 的关系并不像单片机那么容易看明白。第一件事永远是查原理图确认 PB1 在板级上到底连到哪里。我在这块板子上踩过很多次坑发现不少 EV1 板上的某些引脚会同时引到多个外设比如一边接扩展座一边接按键或者指示灯。如果你只盯着芯片手册看 PB1 的 SPI3_NSS 复用编号而不去确认板级上有没有其他器件占用它后面一定会出问题。确认完原理图后需要做第二件事看芯片数据手册里 PB1 的 Alternate Function 表格。STM32MP257F 的引脚复用非常多同一个 PB1 可以复用为 GPIO、SPI3_NSS、某个定时器通道等选错 AF 值会导致信号完全乱掉。这个 AF 值不能靠猜必须查手册。3.2 pinctrl 的 claim 机制STM32MP 系列跑 Linux引脚管理统一走 pinctrl 子系统。设备树中每个外设节点通过pinctrl-0指定需要的引脚组内核启动时由 pinctrl 驱动把这些引脚从“空闲状态”分配给对应设备。关键点来了pinctrl 的分配是排他性的。同一个引脚同一时刻只能被一个设备 claim。SPI3 节点想用 PB1它得首先通过 pinctrl 框架将自己声明为 PB1 的 owner。如果同一个 PB1 已经被其他设备的 pin controller 服务节点声明了SPI3 这边的请求就会失败。而这个失败通常只是在内核 log 里默默出现一行用户空间程序完全无感。SPI3 驱动可能继续注册成功但 NSS 引脚实际上停留在“别人家”的状态永远不会把外部电平送回 SPI 外设。3.3 PB1 最容易踩的冲突PB1 这块板子上的一个高频冲突来源是 LED 或者按键。很多开发板为了快速演示喜欢把默认状态设置为 GPIO 控制 LED 或者读取按键而 ST 官方设备树里经常会出现gpio-leds、gpio-keys这类节点。如果那棵设备树里给某个按键或 LED 用了gpios gpiof 5 ...但你的系统镜像里又启用了另一个节点用gpiob 1 ...两者正好指到同一个引脚位置claim 失败就来了。另外还有一种冲突是 SPI3 节点自己写了多个 pinctrl state。比如 default 里写了一个“master 模式”的引脚组sleep 里却要切成“slave 模式”的引脚组两边都引用了 PB1。一旦 state 切换顺序没有做好也会出现“当前 state 没有获取到 PB1”的尴尬结果。4. 排查过程实录4.1 从 dmesg 里挖第一手线索遇到这个问题后第一步不是改代码而是打开串口终端抓启动日志。我当时执行了dmesg | grep -i -E spi3|pinctrl|PB1|gpio日志里出现了类似这样的信息pinctrl core: request pin 123 for 5000f000.spi failed pinctrl core: could not request pin 123 on device 50002000.pinctrl这里的5000f000.spi就是 SPI3 控制器123是芯片内部的引脚编号具体数值取决于实际 BSP不一定和 GPIO 号一致。这条日志直接告诉我SPI3 想申请 PB1 时失败了。如果日志里找不到还可以看pinmux相关的完整输出dmesg | grep -i pinmux多数情况下失败原因会连带指出当前占到 PB1 的是哪个节点。如果日志没有写明那就需要去 pinctrl 的 debugfs 里手动查。4.2 进入 pinctrl debugfs 确认占用者挂载 debugfs 后可以查看系统中所有 pin 的状态mount -t debugfs none /sys/kernel/debug cat /sys/kernel/debug/pinctrl/50002000.pinctrl/pins输出里每一行都会标注该 pin 当前被哪个设备占用。重点关注 PB1 这一行如果 owner 显示的是gpio-leds或者gpio-keys那问题就实锤了。还可以看 pinmux 的映射状态cat /sys/kernel/debug/pinctrl/50002000.pinctrl/pinmux-pins这会显示该引脚当前处于哪个 function 下、被哪个设备使用。如果 owner 不是spi3说明 NSS 引脚根本没有归属到 SPI3。4.3 用 GPIO 调试确认电平通路在进一步改设备树之前我习惯先用 GPIO 子系统确认物理引脚没有坏。把 PB1 临时在设备树里解开所有冲突然后拉成普通 GPIO 输入读取电平echo 97 /sys/class/gpio/export cat /sys/class/gpio/gpio97/value这里的编号只是示例实际值要根据板卡的 GPIO 控制器地址和引脚号换算。注意这一步只是为了验证外部电路能不能把电平送到引脚不能直接验证 SPI 功能。如果电平能正常读到 0 和 1说明板级电路没问题。4.4 用逻辑分析仪看物理波形软件层面确认之后还要从物理线上排除问题。把逻辑分析仪探头夹到 PB1 上让 FPGA 主机运行一次拉低 NSS 的操作。如果逻辑分析仪能看到明显的下降沿说明外部硬件没问题问题还是在芯片内部的 pin mux / pinctrl 上。如果看不到下降沿就要查连接线、焊接、或者跳线帽。我当时看到的现象是逻辑分析仪有下降沿但 SPI3 寄存器里的 NSS 状态位纹丝不动。结合 dmesg 里的失败日志基本可以断定是 pinctrl 层的 claim 失败。5. 根因分析与修复方案5.1 根因一PB1 被其他设备占用在我这单问题里最终定位到设备树中一个gpio-keys节点它把 PB1 配置成了按键输入用于检测一个物理按键。这个节点在默认设备树里是开启的SPI3 再去申请同一根引脚时pinctrl 直接返回-EBUSY。解决方式很简单把gpio-keys节点里与 PB1 相关的配置删除或者直接在根节点里把该按键子节点status disabled。修改前先确认这个按键确实没有实际用途。如果按键没接任何外部电路禁用它是完全安全的。修改后重新编译设备树烧写启动再看 dmesg 就没有 claim 失败日志了。5.2 根因二AF 配置成了主模式而不是输入模式还有一种常见情况是设备树里用了错误的 pinctrl 配置比如把 NSS 引脚配置成了 SPI3 主模式下的输出引脚而不是从机模式下的输入引脚。从机模式下NSS 是输入信号。pinctrl 节点里必须把它配置成输入模式并且使能内部上拉/下拉或者保持浮空取决于具体外部电路。错误示例spi3 { pinctrl-0 spi3_slave_nss_output; };正确示例spi3 { pinctrl-0 spi3_slave_nss_input; };“output”和“input”的区别看似很小但会导致 NSS 引脚被驱动到固定电平外部主机的拉低动作根本不起作用。5.3 根因三NSS 电气连接悬空在从机模式下如果外部 Master 没有连接或者 NSS 线路上没有上下拉NSS 引脚会悬空。SPI 外设内部虽然有施密特触发器但悬空状态下输入很容易受到干扰表现为 NSS 状态随机翻转或者始终无法稳定 claim。这种情况下需要检查原理图。通常从机 NSS 在空闲时应保证为高电平建议在外部加一个 10kΩ 上拉电阻到 3.3V。这样主机没有驱动时NSS 稳定在高电平从机不会被误选。不过这个根因在“Fails to claim”里不是最常见的原因做排障时还是要先看 dmesg再查电气。5.4 修复后的设备树写法以 Linux 下把 SPI3 配成从机模式为例设备树里至少需要以下几个元素spi3 { pinctrl-names default, sleep; pinctrl-0 spi3_slave_nss_input; pinctrl-1 spi3_slave_nss_sleep; spi-slave; slave0 { compatible st,stm32-spi-slave-dummy; reg 0; spi-max-frequency 10000000; }; };spi-slave;这行是关键它告诉 SPI 控制器驱动这个外设不是作为 Master 使用而是作为 Slave 使用。如果没有这一行驱动会尝试以 Master 方式初始化NSS 引脚的状态反馈逻辑完全不同。slave0子节点可以理解成一个“虚拟从设备设备”用来让用户空间通过 spidev 或特定协议访问从机数据。具体 compatible 取决于你的 BSP 里有没有自带 slave 测试驱动。5.5 软件 NSS 方案作为兜底如果 pinctrl 冲突实在来不及解决比如硬件上必须保留某个功能在 PB1 上可以临时切换到软件 NSS。软件 NSS 模式下NSS 引脚不参与 SPI3 的选中逻辑。从机在使能接收之前由软件直接触发内部选中。这个方案适合测试验证但不太适合有严格时序要求的真实通信。在软件 NSS 下设备树里可以省略 NSS 的 pinctrl直接把管脚留空或者只配置 SCK/MISO/MOSI 三个脚。同时需要在驱动或应用层里做好“什么时候开始通信”的约定。我个人的建议是软件 NSS 只作为临时兜底不要把这种方案带到产品里。因为 SPI 从机本身依赖外部时序软件介入选中逻辑会引入不确定延迟调试起来非常痛苦。6. 验证与踩坑经验6.1 验证从机是否真正被“选中”修好设备树后不能只看 dmesg 没报错就收工。要让 FPGA 主机实际发起一次通信同时用示波器或逻辑分析仪抓 PB1、SCK、MISO、MOSI 的波形。关键判据是PB1 拉低后SPI3 状态寄存器中 NSS 标志位应该立刻跟着变化。如果能在示波器上看到 SCK 边沿和 MISO 输出紧跟 NSS 拉低说明从机真的被选中了。另外可以尝试在从机端设置一个超长数据包故意让主机连续读写看看是否有数据被正确接收和发送。先用短数据包等链路稳定后再加大包长。6.2 值得记住的排查命令整理几个我觉得在 STM32MP2 系列上排查引脚问题特别有用的命令# 查看 SPI 设备是否注册成功 ls /dev/spidev* # 查看 SPI 控制器状态 cat /sys/bus/platform/devices/*spi*/of_node/name # 查看 pinctrl 状态 cat /sys/kernel/debug/pinctrl/*/pinmux-pins # 查看 pin 的 owner cat /sys/kernel/debug/pinctrl/*/pins # 实时抓 pin 状态 cat /sys/kernel/debug/gpio当你遇到“外设加载了但功能不对”的情况优先用 debugfs 查 owner不要一开始就怀疑寄存器配置。6.3 几条个人心得在高性能 MPU 上做 SPI 从机第一原则是先确认引脚所有权再谈外设配置。第二原则是别迷信自动生成代码。CubeMX 生成的是底层初始化但在 Linux 环境下设备树里的 pinctrl 才是真正的“全局仲裁者”。两边必须保持一致否则就会出现“初始化里配了 AF但设备树根本没分配给 SPI3”的冲突。第三原则是善用示波器和逻辑分析仪。这种问题靠肉眼是看不出来的SPI 从机只有在 NSS 被正确拉低后才会响应一切判断都要建立在真实的物理波形上而不是只看软件寄存器。这次调完 SPI3 Slave 模式后我把设备树里所有与 PB1 相关的节点都重新过了一遍顺便把其他外设的引脚占用表也梳理了一遍。踩过的坑记录下来下次遇到类似问题能省一大半时间。