树莓派5驱动RC522读卡器:从全无反应到稳定刷卡的五步排查实录

先说结果:一个 RC522 RFID 读卡器模块,接在树莓派 5 上,从”完全无反应”到稳定刷出门禁卡 UID,外加一个手机浏览器可访问的实时刷卡监控页面。整个过程跨过了五个坑——每一个坑单看都像玄学,串起来看却是一条清晰的排查链。

硬件与目标

  • 树莓派 5(Debian 13 Trixie)
  • RC522 读卡器模块(后来确认是国产兼容芯片 CI522 类)
  • 接线:SDA→Pin24(CE0),SCK→Pin23,MOSI→Pin19,MISO→Pin21,RST→Pin13(GPIO27),3.3V/GND 供电
  • 目标:读出卡片 UID,做一个远程 Web 界面实时看刷卡记录

复盘:五个坑的正确打开顺序

回过头看,这五个坑恰好构成一条自上而下的排查漏斗:库层 → 总线层 → 芯片层 → 协议层 → 应用层。如果当时按这个顺序走,一晚上就能搞定;实际排查时绕了弯路,因为每个坑的表象都指向错误的方向。

坑一:RPi.GPIO 在树莓派 5 上直接崩

现象是脚本一跑就报 Cannot determine SOC peripheral base address。这不是接线问题,是 RPi.GPIO 这个库根本没适配 Pi5 的 BCM2712 芯片。树莓派官方在 Pi4 之后主推 lgpio(基于新的 gpiochip 字符设备)。

教训:Pi5 上凡是教程里写 import RPi.GPIO 的,直接换成 lgpio,别犹豫。

坑二:spidev 能打开、能传输,但读回来全是 0xFF

这是最有迷惑性的一关。/dev/spidev10.0 存在,spi.xfer2() 不报错,寄存器却读出全 0xFF——这通常被解读为”MISO 悬空”或”模块坏了”。

破局的关键一步是用 lgpio 位模拟(bit-bang)手写 SPI 时序去读同一个寄存器:位模拟居然读到了 VersionReg。这一下把问题切成两半——接线、供电、模块全部正常,问题一定在内核 spidev 到引脚之间。

顺着这个思路查引脚复用状态:pinctrl get 8 9 10 11,四个脚全部显示 no(无功能),而不是 SPI 的 a0。对比 I2C 引脚显示 a3(正常复用),真相浮出水面:

根因是 /boot/firmware/config.txtdtparam=spi=on 被注释掉了。

更阴的是,当时系统里存在一个 /dev/spidev10.0——它是一个”幽灵设备”:设备树里 status 是 okay,但根本不对应 40-pin 排针。而正确的 /dev/spidev0.0 要等 dtparam=spi=on 生效后才会出现。取消注释、重启,引脚立刻变 a0,寄存器应声读通。

教训:Pi5 上 /dev/spidev* 存在 ≠ SPI 可用。spidev10 是陷阱,认准 spidev0,并用 pinctrl get 确认引脚真的处于 ALT 复用态。

坑三:版本号 0xB2,不是数据手册写的 0x91

VersionReg 读出来是 0xB2,而 MFRC522 数据手册说应该是 0x91 或 0x92。差一点就把好模块当坏的处理了。实际上淘宝模块大量使用国产兼容芯片(CI522、FM17522 等),返回 0xB2 是它们的典型值,指令集完全兼容。

教训:兼容芯片很常见,版本校验留白名单,别用单一值硬卡。

坑四:SPI 一切正常,卡贴上去毫无反应

这是最精彩的一关,也是耗费时间最长的一关。

排除了前面三个坑之后,REQA 寻卡指令(0x26)发出去石沉大海——86 次请求零应答,无任何错误标志。天线使能正常、寄存器写读回验证通过、FIFO 回环正常。芯片的数字部分被证明完全健康,问题只剩射频层。

转机来自一次 A/B 对照实验:前 15 秒让读卡器周围完全空开,之后把门禁卡平贴在天线上。分时段统计 IRQ 中断,发现贴卡阶段接收中断(RxIRQ)成串爆发——卡确实在耦合响应,只是解不出数据

于是做了一个决定性测试:同一时段把 REQA(0x26)和 WUPA(0x52)各发一发对比。结果 WUPA 每次都收到 ATQA=04 00 的应答,REQA 始终零应答。

根因:这张门禁卡处于 HALT 挂起状态(之前被门禁系统的读卡器挂起过),只响应 WUPA 唤醒指令。把寻卡指令换成 WUPA,UID 立刻读出:B7:06:9D:D1

教训:寻卡无反应时先试 WUPA(0x52),它对 IDLE 态和 HALT 态的卡都有效,覆盖面比 REQA(0x26)大一圈。A/B 对照实验(改变单一变量、分段统计)是射频调试的利器。

坑五:引导文档的参考实现本身有小缺陷

最后一步是工程洁癖问题。照搬的参考代码 request() 函数不清 FIFO、IRQ 判定逻辑繁琐,偶发漏检。重写为一个精简的 _transceive():固定序列清 IRQ → 清 FIFO → 装指令 → 启动收发 → 延时后读 FIFO,实测稳定。

最终架构

代码收敛成两个文件,纯 Python 标准库 + spidev + lgpio,零第三方依赖:


pi5_rc522/
├── rc522_test.py    # 命令行测试:寻卡 → 防碰撞 → 打印 UID
├── rc522_web.py     # Web 监控:http.server + 后台读卡线程 + AJAX
└── diagnostics/     # 9 个诊断脚本归档(排查链的每一步)

Web 端跑在 http://:8080:读卡器在线状态(含芯片版本)、刷卡时 UID 大字弹出、刷卡记录表(UID/时间/累计次数)、去抖动逻辑(卡贴着不动不重复计数,拿开 1 秒后再贴算新一次)。

方法论沉淀

这次调试最大的收获不是代码,是一条可复用的硬件排查链:

  1. 先分层数疑:库 → 总线 → 芯片 → 协议 → 应用,每层找一个”决定性实验”来切割问题域
  2. 位模拟是照妖镜:软件位模拟 SPI 能通而硬件 SPI 不通,问题必在内核/设备树层,与接线无关
  3. pinctrl 是听诊器:引脚显示 no 就是没复用,别被设备节点的存在骗了
  4. A/B 对照是手术刀:射频这种看不见摸不着的领域,控制变量分段统计比瞎猜快十倍
  5. 每个坑写进 git 提交:排查过程按阶段提交,git log 就是完整的问题演化史——这次全部历程封存在 v1.0 标签里

硬件调试没有玄学,只有还没被切分到的问题域。


已发布

分类

, ,

来自