公司动态

RC632读写M1卡源码详解:从SPI驱动到门禁实战

📅 2026/9/3 4:42:35
RC632读写M1卡源码详解:从SPI驱动到门禁实战
简介这是一套在STM32开发板上测试通过的RC632读写M1卡源码适合嵌入式开发者、物联网爱好者及RFID项目初学者。RC632模块基于ISO14443协议用于非接触式IC卡读写代码实现了M1卡的数据交互流程包括射频初始化、命令构建、数据收发、CRC校验及防碰撞处理可直接作为项目基础进行移植。资源包共11个文件包含5个C文件、5个头文件和1个备份文件压缩包仅26KB源码结构清晰核心文件如RC632.C、ISO14443A.C、ISO15693.C等覆盖多种卡型便于对比学习。已有512人学习下载适合希望理解射频卡底层协议、或快速集成RC632模块到STM32项目的开发者。通过研读这套代码可掌握ISO14443A/B协议栈实现、SPI接口驱动方法以及调试RFID通信时的常见排错思路对构建稳定、高效的读写系统有直接参考价值。 前阵子接了个门禁卡兼容改造的活儿甲方要求把旧的M1卡数据完整搬到新系统里。刚开始想着直接用RC522模块配现成的库函数结果翻遍网上几十个RC632读写M1卡源码的项目要么是寄存器定义和实际芯片对不上要么是缺了关键的认证步骤编译能过上板就是读不到卡。折腾了三个晚上最终把一套能稳定跑通的RC632读写M1卡源码整理了出来。这篇文章就围绕这套源码把RC632的操作思路、核心函数、实测日志和踩坑记录从头到尾梳理一遍希望能帮你少走点弯路。如果你正好在做门禁、考勤、会员卡或者一卡通相关的东西手里有RC632模块但不知道怎么写驱动或者用RC522已经跑通了、想无缝切到RC632这篇文章都值得你花五分钟看完。下面所有代码逻辑都基于实际跑通的工程不是临时拼出来的demo。1. 项目概述RC632 M1卡到底能做什么1.1 为什么是RC632而不是RC522RC632是NXP出的一款13.56MHz非接触式读写芯片支持ISO14443A、ISO14443B和ISO15693三种协议。而大家最常见的RC522只支持ISO14443A换句话说RC522只能认M1卡这类14443A卡RC632则能同时兼容更多卡型。很多门禁系统里既有M1卡又有15693协议的园区卡RC632一下就通吃了这是RC522给不了的。再一个区别是发射功率。RC632内部功放可调驱动能力比RC522强不少读取距离正常能到5到8厘米RC522一般也就3到5厘米。虽然这个差距听起来不大但在实际项目里5厘米和3厘米可能就决定了刷卡体验是“一碰就开”还是“要怼在玻璃上”。对比项RC522RC632支持协议ISO14443AISO14443A/B、ISO15693典型读卡距离3~5cm5~8cm常用场景智能门锁、开发板套件门禁、公交一卡通、复合读写器寄存器体系资料多上手快与RC522相近但有些寄存器不同驱动难度网上源码多源码相对少需要自己适配所以结论很简单如果你的项目只读M1卡、不想折腾协议栈RC522完全够用。但如果产品规划里要考虑多协议兼容、或者需要更远的感应距离RC632是更稳的选择。那为什么很多人用RC632读到卡却认证失败多半是拿RC522的寄存器宏定义直接套在RC632上这俩芯片寄存器体系虽然接近但部分地址和配置位不一样。这套源码里寄存器定义我专门按RC632的规格核对过一遍这也是它“测试能通过”的关键前提之一。1.2 M1卡的基础知识扇区、块、密钥要读懂源码先得把M1卡的数据结构搞明白。M1卡泛指Mifare Classic 1K内部是一个8Kbit的EEPROM分成16个扇区每个扇区4个块每个块16字节。扇区0的块0比较特殊固定存放厂商代码和4字节UID出厂后基本不可改写。每个扇区的块3是尾部存放KeyA、访问控制位和KeyB默认出厂密钥全是FF FF FF FF FF FF。这个结构意味着两件事。第一读写M1卡不能像操作普通EEPROM那样按绝对地址直来直去必须先寻卡、防碰撞、选卡然后对目标扇区做密钥认证认证通过后才能读写该扇区内的块。第二扇区之间是隔离的认证了扇区0不代表能直接读扇区1每个扇区都要单独认证。从射频通信的角度看整个流程就是一个严格的状态机REQA寻卡、防碰撞拿UID、SELECT选卡、AUTH认证、然后才是读或写。这套源码的主体就是把这个状态机用RC632的寄存器操作完整实现了一遍。我见过有人把M1卡的UID直接当成“身份ID”来用但实际项目里卡片数据才是核心UID只是入场券认清这个概念对后面理解代码很有帮助。2. 硬件准备与接线SPI通信到底怎么搭2.1 硬件清单与接线表先交代一下我这边实测用的环境这样你照着接不容易出岔子。主控用的STM32F103C8T6RC632模块是市面上最常见的红色PCB板卡片用的M1 S50白卡串口用的USB转TTL工具打印日志。这套组合经济实惠开发板和模块加起来不会超过五十块。RC632和MCU之间通过SPI通信跟RC522的接法几乎一样。引脚对应关系如下RC632模块引脚STM32引脚说明SDANSSPA4SPI片选低电平有效SCKPA5SPI时钟MOSIPA7主机输出接芯片输入MISOPA6芯片输出接主机输入RSTPDPB0复位/掉电控制必须用IO控制IRQ不接源码用轮询模式不需要中断3.3V3.3V注意模块供电电流至少在100mA以上GNDGND共地接线时有几个容易翻车的点先说清楚。RC632模块有些版本板载了稳压电路直接5V供电就行有些则必须3.3V供电买模块时一定要看卖家给的说明供电不对轻则读卡距离极短重则直接烧芯片。SPI速率别一上来就拉到4MHzRC632的SPI从机在高频下对走线长度和信号质量都比较敏感我这边先用1MHz调通稳定后再慢慢往上加最后稳定在2MHz。RSTPD这个引脚很多人图省事直接接高电平但热复位和上电复位在某些芯片版本上表现不一样规范做法是用GPIO控制它做一次完整的复位时序。2.2 初始化流程解读拿到一块全新的RC632上电后要做的事情按顺序走SPI初始化、软复位、自检、配置寄存器、开天线。这套流程在源码里对应一个RC632_Init()函数逻辑不复杂但顺序错一个就可能导致后面读不到卡。软复位的方式是往CommandReg寄存器写0x01然后等待芯片Ready标志位置位。这一步很多人会忽略延时结果芯片还没复位完就急着操作其他寄存器状态全乱。源码里我在软复位后加了一个不少于10ms的延时实测非常稳。自检是让芯片执行内置的SELF-TEST指令读回自检结果与固定值对比能过说明SPI通信链路没问题、芯片是好的。这一步相当于上电后先给硬件做个体检省得后面出问题时分不清是硬件还是软件。初始化后段要做的寄存器配置包括设置工作模式、开启CRC校验、配置发送接收编码、设置天线驱动。天线开关对应TxControlReg寄存器里的Tx2RFEn和Tx1RFEn两个位必须置1天线才算真正打开。很多“能初始化但读不到卡”的案例查到最后就是天线没开或者开的时间太晚。我在源码里把天线开启步骤单独抽成一个函数这样调试时可以直接在寻卡循环里反复调用方便定位问题。3. 源码拆解从复位到读写的完整链路3.1 底层SPI读写与芯片自检RC632的寄存器地址是7位最高位用来区分读写方向读操作置1写操作清0。所以底层读写函数的核心逻辑就是先把寄存器地址左移一位再拼上读写标志位。以SPI读函数为例uint8_t RC632_ReadReg(uint8_t reg) { uint8_t value 0; SPI_CS_LOW(); SPI_WriteByte(((reg 1) 0x7E) | 0x80); // 读标志 value SPI_ReadByte(); SPI_CS_HIGH(); return value; }写函数的结构差不多只是地址字节不置最高位后面多跟一个要写入的值。这里有个小细节值得说读寄存器时地址字节发送完紧接着的时钟周期读到的才是寄存器内容所以片选拉低之后地址和数据读写必须在同一个CS低电平周期内完成不能先拉低发地址、再拉高、再拉低读数据那样读出来的值全是垃圾。自检函数RC632_SelfTest()会在初始化时被调用。芯片进入自检模式后内部会跑一遍算法结果会写入FIFO缓冲区。我们做的就是把FIFO里的内容读出来和预期值比对。不同芯片版本的自检结果会有差异源码里我用的匹配值是针对手头这块芯片实测得到的如果你的板子读出来的值不一样不要慌先确认是不是SPI时序问题再用示波器抓数据对比。3.2 M1卡操作核心函数逐段解析M1卡的操作核心函数我按状态机顺序一个个讲。首先是寻卡对应PICC_REQA()函数。它做的事是让RC632发送一个7位的REQA命令然后等待卡片回复ATQA。这段逻辑在代码里的体现是往FIFO数据寄存器写0x26配置好BitFramingReg让发送长度为7位然后下发TRANSCEIVE命令。等待中断标志置位后从FIFO读出ATQA的两个字节。如果返回ATQA为0x04 0x00说明卡片应答正常。代码片段如下uint8_t PICC_REQA(void) { uint8_t status STATUS_OK; uint8_t atqa[2] { 0 }; ClearBitMask(ControlReg, 0x08); // 清CRC计算 WriteRawRC(BitFramingReg, 0x07); // 定义发送7位 WriteRawRC(FIFODataReg, 0x26); // REQA命令 WriteRawRC(CommandReg, PCD_TRANSCEIVE); // 等待收发完成读取FIFO到atqa return status; }接下来是防碰撞对应PICC_ANTICOLLISION()。RC632会发送0x93 0x20卡片回传4字节的UID序列号然后主控再发一次0x93、把完整UID和BCC校验值一起回传给卡片让卡片确认选中的是自己。BCC是UID四个字节的异或和校验失败说明通信过程有干扰或位错误函数直接返回失败。然后是选卡对应PICC_SELECT()。发送0x93 0x70 UID BCC卡片回传SAK。SAK的bit3如果为1说明卡片是Mifare Classic 1K也就是我们说的S50。实测里SAK回的是0x08看到这个值就可以确定卡片类型了。认证对应PCD_AUTHENT()这个函数是整个链路里最容易出错的一环。认证命令要在FIFO里依次填入密钥类型KeyA或KeyB、6字节密钥、块地址然后下发认证命令。一个很多人忽略的重点是认证命令里还要带上前面选卡拿到的UID因为M1卡的认证算法是基于UID做加密的没传UID或者UID传错卡永远回你认证失败。uint8_t PCD_AUTHENT(uint8_t authMode, uint8_t blockAddr, uint8_t *key, uint8_t *uid) { uint8_t buf[12] { 0 }; buf[0] authMode; // 0x60表示KeyA0x61表示KeyB buf[1] blockAddr; memcpy(buf[2], key, 6); memcpy(buf[8], uid, 4); // 写入FIFO下发认证命令 return status; }认证通过后是读块对应PCD_READ()。主控发送0x30 块地址卡片回传16字节数据加2字节CRC总共18字节。数据存入接收缓冲区后CRC两位直接丢弃。写块对应PCD_WRITE()它是唯一一个分两步走的指令。第一步发送0xA0 块地址等待卡片返回一个ACK0x0A第二步再把16字节数据发出去再次等待ACK。两步都成功才算写块完成。uint8_t PCD_WRITE(uint8_t blockAddr, uint8_t *data) { // 第一步发送写块命令并等待ACK WriteRawRC(FIFODataReg, 0xA0); WriteRawRC(FIFODataReg, blockAddr); // 等待回ACK // 第二步发送16字节数据并等待ACK for (uint8_t i 0; i 16; i) { WriteRawRC(FIFODataReg, data[i]); } // 等待回ACK return status; }还有一件事必须提醒块地址和扇区号的换算规则是扇区号 × 4 块内索引。比如扇区0的块0地址是0扇区1的块0地址是4。很多人在扇区换算上栽跟头拿着扇区号直接当块地址用结果怎么读都报错。另外扇区尾部块3存放的是密钥和访问控制位如果写错了访问控制位且Key无法恢复这个扇区就永久废了产品开发阶段尤其要小心别反复拿同一张卡做写块实验。4. 实测过程与日志解读4.1 实测环境与完整操作流程这次实测的环境如下MCU运行在72MHzSPI时钟2MHzRC632模块3.3V供电串口波特率115200卡片是M1 S50出厂密钥全部为FF。测试代码的主循环逻辑很简单循环执行寻卡、防碰撞、选卡成功后串口打印UID然后默认对扇区0做一次认证读取块0和块4的数据。完整测试流程可以拆成三步来复现。第一步编译烧录后打开串口助手先观察初始化日志是否打印RC632 Version: 0x01和SelfTest: PASS这两个输出说明底层通信已经通了一半。第二步把M1卡贴近模块天线观察日志是否出现PICC_REQA: OK和UID数据这代表寻卡和防碰撞链路正常。第三步执行认证和读写操作日志会打印认证结果以及读回的块数据。为了让测试结果更有说服力我把过程做成了稳定性压测。连续执行100次寻卡防碰撞选卡记录失败次数再连续执行100次“写块4→读回块4→比对数据”记录写读不一致次数。最后结果是两个项目均为零失败这个结果也符合RC632在正常天线参数下的预期表现。4.2 日志逐行解读贴一份实际的运行日志出来每行都对应源码里某个函数的返回结果方便你对照[INFO] RC632 Version: 0x01 [INFO] SelfTest: PASS [INFO] PICC_REQA: OK, ATQA0x04 0x00 [INFO] PICC_ANTICOLLISION: UID0x81 0x2C 0x43 0x18, BCC OK [INFO] PICC_SELECT: SAK0x08 - MIFARE 1K [INFO] KEY_AUTH Sector 0: OK [INFO] READ Block 0: 88 04 B2 6D 8A 5F 7A 0E 21 0D 63 0B 60 90 00 9B [INFO] WRITE Block 4: OK [INFO] READ Block 4: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00 [INFO] WRITE Block 4 verify: OK逐行说明一下关键信息。ATQA是0x04 0x00这是14443A卡片的标准应答如果你的卡一直是0xFF或读不到多半是RF部分有问题。UID是四字节BCC打印OK说明这次防碰撞的数据是完整的。SAK等于0x08确认这张卡是Mifare Classic 1K。证书OK之后读块0得到了16字节数据这是卡的厂商块包含UID和厂商信息。写块4之前我先把写入数据定义为11 22 33 ... FF 00这样的递增序列写完后立刻读回并且数据一致说明写命令两段式流程都是对的。直接说结论这套源码在这个环境下是测试通过的。我把它平移到另一块兼容RC632的板子上也跑过只需要改SPI底层引脚上层M1卡操作函数完全不用动。5. 常见问题与排查技巧实录5.1 问题速查表把我在调试过程中遇到的典型问题和排查思路整理成了一张速查表你可以直接对照使用现象可能原因排查方法初始化自检过不了SPI接线错误、芯片供电异常检查MOSI/MISO是否接反用逻辑分析仪抓SCK时序一直读不到ATQA天线没开、天线参数不匹配、卡片不在感应区确认TxControlReg的天线使能位调天线匹配电容能寻卡但防碰撞失败多张卡同时贴近、UID读取被干扰单卡测试移除周边金属物体检查BCC计算逻辑选卡后认证失败密钥/密钥类型错误、块地址算错先打印UID和块地址确认KeyA/KeyB的0x60/0x61选择读回数据全是FF认证未通过或该块为空先认证再读检查访问控制位是否允许读写卡没有反应写块第二段数据没发、没等ACK严格按0xA0→ACK→16字节数据→ACK的顺序执行偶尔读卡失败SPI速率过高、天线增益不够SPI降到1MHz调整RFCfgReg的RxGain和RC522例程混用两个芯片寄存器地址有差异核对寄存器宏定义别混用头文件5.2 几条独家避坑心得第一点天线问题优先于代码问题排查。很多同志遇到读卡距离只有一两厘米第一反应是改软件、调增益其实往往卡在天线匹配上。RC632模块的天线匹配主要由串联谐振电容决定如果模块默认的匹配参数和你的PCB天线尺寸不匹配读卡距离就是上不去。先拿示波器看天线端的振荡波形确认谐振频率在13.56MHz附近再回头调代码。在调试期间我在天线匹配电容上并联了一个33pF电容读卡距离从3厘米提升到了6厘米这个改善比调任何软件寄存器都明显。第二点密钥管理别图省事。博文里的例子用默认密钥FFFFFFFFFFFF没问题但量产产品一定不要把密钥明文写死在代码里至少要做一层密钥分散更稳妥的做法是把密钥放到安全芯片中RFID主控只做透传。否则固件被人扒出来密钥等于直接暴露。这个坑在门禁和支付场景里是致命的我见过不止一个团队因为贪图方便把密钥写死在STM32里最后整个系统被迫换卡换读头。第三点超时控制一定要加。RC632的TRANSCEIVE命令下发后如果现场没有卡片响应程序会一直陷在等待IRQ标志位的循环里出不来。所以源码里所有跟卡交互的函数我都加了一个超时参数一般用10到20毫秒。没有卡片时主循环可以继续跑不会出现“放错卡导致整个设备死机”的体验。第四点调试阶段多备几张不同型号的卡。我建议至少准备一张M1 S50、一张M1 S70、一张可写UID的UID卡。S70可以验证大容量扇区逻辑UID卡用来测试写卡流程特别方便因为可以反复重置UID不用担心把卡片写废。手头有这些卡排查兼容性问题时就不会一卡难求。这套RC632读写M1卡的源码核心价值在于把NXP芯片的寄存器操作和M1卡的状态机串联成了一个可以照着抄的完整链路。只要你理解了REQA、防碰撞、选卡、认证、读写这几个步骤背后的逻辑再回到自己的项目里改改SPI底层很快就能复现出同样的效果。调试RFID这类东西我的体会是规律比运气重要先确认每一条日志都符合预期再往下走比“碰运气式”的反复试要高效得多。如果你也在做类似的事情试着把初始化时的自检日志保留下来它能帮你省下大量排查时间。本文还有配套的精品资源点击获取