news 2026/8/30 12:13:56

STM32MP257 SPI3从机NSS引脚claim失败排查与设备树配置

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32MP257 SPI3从机NSS引脚claim失败排查与设备树配置

这块STM32MP257F_EV1评估板本身素质不错,双核Cortex-A35加Cortex-M33,跑OpenSTLinux做工业网关绰绰有余。但当我开始验证SPI3的从机模式时,一个不到1厘米宽的NSS引脚差点让我怀疑人生:PB1在设备树里配置为SPI3从机的片选输入,结果系统起来后,内核日志直接告诉我,这个NSS引脚claim失败。这个问题非常典型,凡是把SPI配成从机、而且是硬件NSS场景的工程师,几乎都会在某个阶段撞上。这篇帖子我会把从复现、定位到解决的全过程拆开讲,包括我踩过的坑和最后真正生效的配置方式。如果你是第一次在MP2/MP13系列上做从机通信,这篇应该能帮你省下一到两个通宵。

1. 现象复盘:从错误日志判断问题边界

1.1 我这边的复现条件

先说清楚测试环境,这样下面的日志和结论才有意义。我手上这块STM32MP257F_EV1板子,软件用的是OpenSTLinux的SDK版本,内核是标准主线加ST补丁的设备树。测试目标是把SPI3配置成从机模式,外部用一个主机控制器(我手头临时用的是一块F746 Nucleo)来发起通信。SPI3的SCK、MISO、MOSI都接好了,唯独NSS这个脚,我按常规理解直接选择了PB1,想着让外部主机的CS信号直接拉低PB1来选中从机。

结果系统启动后,内核日志里SPI3的驱动确实检测到了从机模式,但紧接着就报了一串错误,核心信息就是标题里那句:SPI3 Slave mode下NSS PIN(PB1)Fails to claim。整段日志我放在下面:

[ 2.520805] spi-stm32 48003000.spi: spi3 probed as slave [ 2.521014] spi-stm32 48003000.spi: using hardware NSS, pin PB1 [ 2.521262] spi-stm32 48003000.spi: failed to claim NSS pin PB1: -16 [ 2.521443] spi-stm32 48003000.spi: spi3 initialization failed, probe error [ 2.521595] spi-stm32 48003000.spi: probe with driver spi-stm32 failed with error -16

注意,日志里的地址前缀48003000.spi在不同BSP版本里可能不一样,但核心的报错文本是一致的:SPI驱动已经确认自己工作在从机模式,也找到了NSS引脚是PB1,但在claim这个引脚时被拒绝,错误码是-16。也就是说,驱动的整体思路是对的,卡点非常具体地落在“引脚申请”这一步,整个问题边界一开始就可以缩小到GPIO资源和引脚复用上。

1.2 错误码-16到底在说什么

在Linux内核里,-16对应的errno是EBUSY,翻译成人话就是“设备或资源忙”。SPI驱动在probe过程中,会尝试把NSS引脚从GPIO子系统申请过来,这个申请动作本质上是GPIO的gpiod_get或类似接口。如果内核里已经有一个驱动提前占用了PB1,后到的SPI驱动就会收到-16。

还有一种情况,虽然不常见但确实存在:即便没有其他驱动占用,如果SPI从机的设备树节点里写了cs-gpios,Linux SPI核心会把PB1当成常规的片选GPIO去申请,而这个GPIO同时在pinctrl里又被配置成了外设复用功能(比如AF5的SPI3_NSS),两边说法不一致,GPIO子系统内部校验不过去,也会给你返回-EBUSY。

搞清楚错误码的含义之后,我并没有急着去改设备树,而是先建了一张排查清单:设备树里PB1到底怎么描述、运行时这个引脚被谁占用、SPI3外设本身有没有被安全侧或者M33侧锁住、以及物理接线是不是真的把主机的CS连到了PB1上。下面几章按这个顺序展开。

1.3 先别慌:这种错误不算罕见

说实话,第一次看到这个报错时我也以为是什么高深的外设配置问题,甚至在SPI寄存器层面翻了好一阵子。后来回头一想,在STM32MP这种异构多核SoC上做外设验证,十个里至少六七个都卡在GPIO占用和外设所有权上。这类问题最怕的就是怀疑驱动、怀疑硬件、甚至怀疑人生,其实绝大多数时候只是设备树里同一个引脚被描述成了两种身份。

2. 从机片选背后的机制和原理

2.1 SPI从机为什么离不开NSS

很多人学SPI时重点关注SCK、MOSI、MISO,却把NSS当成一个可有可无的“使能脚”。这话在主机模式下勉强说得过去,因为主机可以自己控制CS输出;但在从机模式下,NSS就是命脉。

用一个生活化的类比:SPI主机是工地上的队长,SCK是他吹的哨子,MOSI是他下达的指令,MISO是工人的回应。而NSS这个脚,就是队长在喊“老张,这活儿归你”。从机芯片只有听到“老张”这两个字(也就是NSS被拉低)之后,才会开始听哨子声干活。如果NSS从来没有被拉低,哪怕SCK时钟跑得飞起,MOSI上的数据也和你完全没关系,整个从机外设就会一直待在空闲态,接收FIFO一个字节都不会进。

所以我在调试SPI3从机时,最先确认的就是PB1外部电平。这个在物理上极其简单,但极容易被忽略:主机端的CS如果没接对,或者CS极性配置反了,从机端的NSS永远保持高电平,从机自然“装死”。

2.2 硬件NSS和软件NSS是两条完全不同的路

STM32的SPI外设(H7及之后代的IP)在从机模式下,NSS有两种玩法。

第一种是硬件NSS,也是我们想用的方式。PB1复用为SPI3_NSS这个alternate function,外部主机通过拉低PB1来选中从机,外设硬件直接对这个引脚的电平做仲裁,再触发内部的收发状态机。这种方式实时性好、抗不过度占用CPU,适合对时序敏感的场景,同时也是多从机总线上的标准做法。

第二种是软件NSS,也叫内部从机选择。此时外部不需要真正的CS信号输入,通过设置SPI_CFG1相关的SSI位,让内部片选一直处于有效状态,SPI从机相当于永远被选中。好处是可以节省一个引脚,很多简单点对点通信就这么干;坏处是没法在一条总线上挂多个从机,因为所有从机都会同时监听这根总线。

“claim”这个词在驱动层面的意思,就是驱动去把NSS这个资源纳入自己控制。硬件NSS模式下它要拿到GPIO的控制权并配置成输入;软件NSS模式下不需要GPIO输入,只需要在控制器内部把片选信号强制置有效。两者配置方式完全不同,如果设备树里描述的NSS模式跟实际想要的不一致,报告出来的错误就会五花八门。

2.3 “claim”在驱动栈里的实际位置

我再往下挖一层。Linux SPI核心在注册一个从机设备时,会做不少准备工作:读取设备树节点里的compatible、中断号、DMA通道,以及片选描述。对从机控制器来说,如果配置要求NSS参与,控制器驱动会在自己的probe回调里通过GPIO子系统请求这个引脚。这一步失败,就是我们在日志里看到的claim失败。

在SPI驱动栈的语境中,claim一旦失败,控制器驱动可能直接放弃初始化,SPI3整个节点都不会注册成功。所以从日志看到probe error -16时,基本可以断定问题就出在GPIO请求之前或请求的瞬间,而不是SPI外设时钟、分频、极性这些更深层的配置上。这个判断帮我把排查范围缩小了一大截,后面所有动作都围绕“为什么PB1申请不下来”去展开。

3. 从设备树、GPIO占用到硬件权限逐级排查

3.1 第一件事:确认设备树里PB1的真实身份

在改任何文件之前,我先去看了运行时的设备树实际状态。在OpenSTLinux的debugfs挂载的情况下,可以通过下面几行命令快速确认引脚当前的复用和占用情况:

mount -t debugfs none /sys/kernel/debug cat /sys/kernel/debug/pinctrl/*/pinmux-pins | grep -i pb1 cat /sys/kernel/debug/gpio | grep -i pb1

pinmux-pins会告诉你PB1当前被复用成哪个功能,是GPIO还是某个外设的AF;gpio文件则会列出当前所有被声明过的GPIO请求者。如果PB1出现在某个不相关的设备名下,那它就是在你之前已被占用。

我还用了libgpiod的工具补了一刀:

gpioinfo gpiob

gpioinfo会直接列出BANK0到BANK8所有引脚状态,并且标注哪些被内核占用。看到PB1旁边如果跟着一个陌生的label,例如led或者usb,那基本就是它了。

3.2 典型反面教材:把从机NSS写成了cs-gpios

在查看设备树源文件时,我第一版写的是下面这种样子:

&spi3 { status = "okay"; pinctrl-names = "default"; pinctrl-0 = <&spi3_slave_pins>; cs-gpios = <&gpiob 1 GPIO_ACTIVE_LOW>; };

这里就埋下了一个非常隐蔽的雷:cs-gpios这个属性在Linux SPI框架里,是给SPI主机控制器用的,它是一个输出信号,由主机主动拉高拉低去控制外部从设备。可我这边恰恰是要把SPI3配置成从机,让外部主机来控制我的NSS,方向上就反了。当驱动尝试把这个GPIO当成输出来claim时,GPIO子系统发现这个引脚在pinctrl里被配置成了SPI3_NSS的复用功能,方向和模式都对不上,于是返回-EBUSY。

正确的做法是不把PB1当作常规GPIO输出,而是让PB1复用成SPI3_NSS的外设功能,在pinctrl里声明,并且不要在SPI3节点中使用cs-gpios。下面是我后来调整过的pinmux片段:

spi3_slave_pins: spi3-slave-pins { pins1 { pinmux = <STM32MP_PINMUX('B', 1, AF5)>, <STM32MP_PINMUX('B', 4, AF5)>, <STM32MP_PINMUX('B', 5, AF5)>; bias-disable; }; };

注意这里的AF编号在不同芯片上可能不一样,以实际数据手册为准,我这边是AF5。PB1走SPI3_NSS,PB4和PB5走SPI3_SCK和SPI3_MOSI,具体引脚分配要对照EV1板原理图。

3.3 第二步:查GPIO是否被其他模块占用

有时候你的设备树写得完全正确,但PB1仍然claim失败,这时候就要怀疑板子上别的驱动把你的引脚抢走了。嵌入式开发板上最常用的几个GPIO占用大户包括板载LED、用户按键、SD卡检测脚、模组复位脚。以EV1这种评估板来说,默认设备树里已经把很多GPIO分配给了板载外设,PB1不一定是“空闲可用的”。

我在调试时把debugfs的gpio列表翻了个底朝天,很快就看到PB1旁边挂着一个板载功能的名字。这种时候通常是两种处理方案:第一种是换一个确认真空的引脚来做NSS,改设备树和接线;第二种是直接在板级DTS里把占用PB1的那个节点status改成disabled,把引脚让出来。

从工程角度,如果SPI3的引脚位置已经根据EV1的Arduino接口或者排针固定好了,那我建议优先用第二种方案,把冲突节点关掉。一旦关了冲突源,重新编译dtb,重启后再看gpio信息,PB1就是干净状态,claim自然就通过了。

3.4 第三步:确认SPI3和GPIOB没有被安全侧或M33侧锁住

STM32MP系列和普通MCU很大的不同在于,同一个外设可能在系统启动早期就被分配给了安全世界(TrustZone)或Cortex-M33内核。如果SPI3外设被资源配置给了安全侧,那么Linux(跑在A35的非安全世界)访问这个外设时,会在总线层面直接失败,表现出来也不一定是清晰的中文错误,可能是访问超时、寄存器写入无效、甚至probe defer。

检查方法很简单,看板级设备树里的&etzpc节点配置。OpenSTLinux的设备树里往往有一段类似下面这样的宏定义区域:

&etzpc { st,decprot = < DECPROT(STM32MP1_ETZPC_SPI3_ID, DECPROT_NS_RW) DECPROT(STM32MP1_ETZPC_GPIOB_ID, DECPROT_NS_RW) >; };

如果SPI3的ID被配置成了DECPROT_S_RW或者DECPROT_MCU,那Linux是没有权限碰它的。这时候需要在U-Boot阶段改环境变量,或者修改设备树里etzpc的配置,把外设权限放开,重新烧录启动。

我这次遇到的情况,一开始也怀疑过是不是M33固件把SPI3占了,但在U-Boot启动日志里翻了半天,发现SPI3和GPIOB都还是非安全侧资源,所以暂时排除了这条路径。如果你发现自己排查下来GPIO、设备树都没有明显问题,那一定不要漏掉这一步。

3.5 第四步:物理层别想当然

软件排查完之后,我回到硬件上又验证了一遍。EV1板子的排针很多,但PB1未必直接引到排针上,它可能和板载外设连在一起。我要确保外部主机的CS信号确实物理上连通到了PB1。

我用万用表做了通断测试,然后在主机侧写了一个最简单的GPIO翻转程序,反复拉低PB1,从机端用示波器看PB1波形。如果波形能看到一个干净的拉低,硬件通路就通了。如果波形始终是高电平,甚至根本看不到翻转,那就要回去查主机端的CS配置和接线。

一个很容易踩的坑是主机端GPIO的推挽/开漏配置。推挽输出能正常拉低到地,但如果主机端CS配置成了开漏,又忘了外接上拉电阻或下拉电阻,电平可能会飘忽不定,从机端的NSS读取也会跟着出错。

4. 最终修改方案与完整验证流程

4.1 删除cs-gpios,用硬件NSS的正规配置

我经过完整排查后,最终把设备树改成了下面这套结构,整套配置也通过了验证。

&spi3 { status = "okay"; compatible = "st,stm32mp13-spi-slave"; pinctrl-names = "default"; pinctrl-0 = <&spi3_slave_pins>; };

注意里面没有任何cs-gpios属性,也没有多余的GPIO请求。PB1纯粹通过pinctrl的引脚复用配置来承担SPI3_NSS功能。如果你用的是比较新的OpenSTLinux版本,compatible字段名可能有一点差异,最稳妥的方式是把st,stm32mp13-spi-slave换成你实际BSP里源码中spi-stm32驱动声明的从机兼容字符串。这一点必须和内核源码对齐,不能靠猜。

如果你确实需要一个外部来的CS信号,同时还想让这个CS在GPIO层面可见、可以读取状态,你可以考虑用普通GPIO输入的方式,绕开SPI硬件NSS逻辑,但那是另一种玩法了,和这次的问题不是一回事。

4.2 一个临时兜底方案:软件NSS对照实验

在最终定位到根因之前,我做了一个非常有效的对照实验:临时把SPI3从机切到软件NSS方式。软件NSS模式下不需要外部片选,也不需要PB1做硬件输入,SPI3从机永远处于被选中状态。具体做法是在设备树中去掉PB1的复用配置,并在SPI控制器内部通过寄存器把内部选择信号置为有效。

切到软件NSS之后,SPI3的probe就再也没有报过claim失败的错误,dmesg显示spi3正常注册成了从机控制器,主机端发过来的数据也能在从机端收到。这个结论一下子就把问题锁定在了“硬件NSS相关描述”上,而不再怀疑SPI外设本身配置有问题。

软件NSS只能作为验证手段,实际项目中如果要从机可靠地和多个主机设备在总线上共存,还是得用硬件NSS。因为软件NSS意味着所有从机都在听总线,一旦总线上有两个从机用了同一个协议头,冲突就是灾难性的。

4.3 重新编译设备树并部署到板子

OpenSTLinux SDK环境下,重新编译单板设备树不需要构建整个内核,通常只需要在Linux源码目录下执行:

make stm32mp257f-ev1.dtb

如果你用的是独立SDK环境,也可以通过dtc手动把dts编译成dtb,但需要注意头文件的包含路径,不然很多宏定义找不到。编译好之后把新的dtb文件拷贝到开发板的/boot分区,覆盖同名文件前先做备份:

cp stm32mp257f-ev1.dtb /boot/stm32mp257f-ev1.dtb.bak cp stm32mp257f-ev1.dtb /boot/ sync reboot

重启之后,我第一时间就去看了dmesg和gpio状态。这次日志干净利落,SPI3正常以从机模式初始化,PB1也如愿出现在spi-stm32驱动的名下。

4.4 从机模式的验证手段

确认初始化只是第一步,通信是否能真正工作还要实测。我在从机端没有直接使用现成的spidev工具,因为spidev更多面向主机模式。这里分享一个有效的验证思路。

先确保主机端能正常发送一个自定义的帧,比如5个字节0x5A。从机端我先在设备树里注册了一个简单的自定义从机设备驱动,或者直接用内核提供的spi-slave测试接口,把接收到的数据打印出来。如果从机端能在主机每次拉低CS之后收到对应字节,就证明NSS工作了,收发链路也通了。

如果不想写驱动,还有一个更快的验证方式:用逻辑分析仪同时抓SCK、MOSI和PB1三根线。只要看到主机拉低PB1、然后SCK上出现时钟、同时MOSI上有数据,就说明从机NSS被正确claim,硬件通路完全正常。

我在验证时还用了一个很笨但有效的土办法:把从机的MISO引脚通过一根杜邦线直接连到逻辑分析仪,主机发送一帧数据,观察从机MISO上有没有正确的响应数据。这样能从物理层确认从机确实在NSS选中后参与了通信。

5. 问题速查表与这几天的经验笔记

5.1 一张表总结常见claim失败原因

错误码可能原因处理方式
-16 EBUSY引脚被其他驱动占用查debugfs/gpioinfo,释放冲突GPIO
-16 EBUSY同时用了cs-gpios和AF复用删除cs-gpios,走pinctrl复用
-6 ENXIO设备树引用了不存在的GPIO控制器检查&gpiob节点是否使能
-22 EINVAL引脚号、极性、偏移配置错误核对数据手册和原理图
probe defer依赖的时钟或pinctrl还没就绪查看完整启动日志,确认依赖链

这张表虽然没有穷尽所有内核版本的情况,但对于“NSS claim失败”这个具体报文,覆盖了绝大部分实际问题。遇到类似的报错,直接套这张表排一遍,效率会高很多。

还有一个值得记录的细节:内核日志里的probe失败不一定是永久失败。如果你看到的是probe defer-EPROBE_DEFER,那说明驱动只是暂时没等到资源,后面还有其他驱动释放资源时可能再次触发probe。这种情况下不要急着判断失败,先确认整个启动过程是否完整。

5.2 我这次最终定位到的根因

我这边最终锁定的原因是:默认板级设备树中已经把PB1定义成了一个板载状态指示用途,并且占用了这个GPIO;而我的SPI3从机配置里又通过cs-gpios重复请求了同一个引脚。GPIO子系统拒绝了一个引脚同时被两个驱动以不同方向申请的情况,于是SPI驱动在claim PB1时收到了-EBUSY。

把设备树改为PB1走SPI3_NSS的复用功能,删掉cs-gpios,并在pinctrl里明确声明PB1为SPI3_NSS之后,问题彻底消失。整个过程让我再次确认了一个道理:在异构多核SoC上,外设功能的实现往往不是靠多写代码,而是靠把系统资源描述的“所有权”理清楚。GPIO资源尤其如此,它不像时钟可以有共享计数器,同一个引脚同一时刻只能属于一个方向、一个使用者。

5.3 如果你也想做类似调试,我给几条建议

第一,不要在probe失败的第一时间就去翻SPI外设寄存器。先看GPIO资源,因为GPIO层面的错误往往在日志最前面就写得很清楚。第二,设备树里不要同时写pinctrl复用和cs-gpios,这两者是互斥的描述方式,同时出现会把驱动带偏。第三,准备一个逻辑分析仪,在SPI从机调试中的价值远超大部分仿真工具,它能把真实信号给你看,省去大量猜谜时间。

我实际使用中发现,真正花在“改设备树、编译、重启”上的时间只占三成,剩下七成都耗在了“为什么这个引脚不能申请”的排查上。如果一开始就按GPIO占用、权限、硬件通路的顺序来,可能半天就能定位到问题,不至于像我这样折腾到半夜。希望这篇复盘能让你少走这一圈弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/30 12:07:57

React面试八股文:组件化、Hooks与渲染机制核心解析

1. 组件化思维&#xff1a;面试官第一题就在考察你的 React 功底 做过面试官的朋友都知道&#xff0c;开场第一个问题往往不是让你背概念&#xff0c;而是随便指一个项目里的组件问&#xff1a;“这个组件为什么这么写&#xff1f;换成类组件行不行&#xff1f;如果父组件重新渲…

作者头像 李华
网站建设 2026/8/30 11:58:11

企业文件管理进阶:自动化任务与版本同步实战

企业文件管理进阶&#xff1a;自动化任务与版本同步实战 在工程开发团队里&#xff0c;文件管理往往是最容易被忽视却又最让人头疼的环节。代码包、配置文件、需求文档、设计稿——每次版本更新&#xff0c;手动整理、命名、归档&#xff0c;重复劳动占用了大量有效开发时间。本…

作者头像 李华
网站建设 2026/8/30 11:54:47

百度2016研发工程师笔试题复盘:覆盖算法、OS与C++核心考点

已经过去这么多年&#xff0c;百度2016年的研发工程师笔试题&#xff08;一&#xff09;依然在不少技术社群里被反复翻出来。很多人在牛客网、CSDN或者GitHub的面经仓库里刷过这套题&#xff0c;它看起来只是“一套选择题为主、夹杂少量编程题的笔试卷”&#xff0c;但真正刷完…

作者头像 李华
网站建设 2026/8/30 11:54:09

STM32CubeIDE工程转VS Code:启动文件.s缺失导致链接失败的排查与修复

如果你哪天真把 STM32CubeIDE 工程迁到 Visual Studio Code 里&#xff0c;编译走到链接阶段突然报一堆莫名其妙错&#xff0c;先别急着怀疑工具链版本、怀疑优化选项&#xff0c;先回头看看那个不起眼的 .s 文件还在不在。我最近用 ST 官方的 STM32CubeIDE for Visual Studi…

作者头像 李华
网站建设 2026/8/30 11:54:02

TAMX 本地虚拟宠物应用:从桌面部署到状态管理与存档恢复

如果你最近逛 Hacker News 时刷到“Show HN: TAMX – Personal Tamagotchi”&#xff0c;大概率会产生一个直觉&#xff1a;这就是一个放在桌面上的虚拟宠物&#xff0c;用来陪伴、提醒、打发碎片时间。从项目名看&#xff0c;TAMX 是 Tamagotchi 的变体拼写&#xff0c;它的核…

作者头像 李华
网站建设 2026/8/30 11:52:48

零基础学Python的正确路径:从基础语法到爬虫数据分析实战

如果你已经收藏了十几个G的 Python 教程&#xff0c;却依然在“变量、循环、函数”里原地打转&#xff0c;那这篇文章就是写给你的。很多人学 Python 失败的真正原因&#xff0c;不是不够努力&#xff0c;也不是智商不够&#xff0c;而是学习路径太乱。今天刷两集基础语法&…

作者头像 李华