恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
正点原子Mini板RC522移植实战:SPI配置与避坑指南
首页
资讯中心
/
正点原子Mini板RC522移植实战:SPI配置与避坑指南
正点原子Mini板RC522移植实战:SPI配置与避坑指南
发布时间:2026/9/2 15:13:23
简介面向正点原子Mini板上STM32 F103RCT6与RC522RFID模块的联调需求这里提供了一份可直接运行的工程代码。它适合嵌入式初学者和DIY爱好者重点演示如何利用SPI2接口驱动RC522实现RFID卡片寻卡、防碰撞、读取卡号并显示在LCD屏上。压缩包共82个文件大小1.7MB以C源码、头文件、Keil工程文件.uvprojx、编译产物.hex/.axf/.o及MAP链接映射文件为主另含readme.txt说明文档工程目录包含RFID、LCD、SYSTEM等硬件驱动模块方便对照原理图梳理初始化流程。已有1778人学习下载。通过这套程序开发者可以快速掌握STM32 SPI外设的配置方法、RC522寄存器的读写流程以及非接触式射频识别的实际应用代码注释清晰适合在此基础上扩展门禁、物流等场景也为后续移植到其他STM32型号提供了可靠起点。 做门禁、做考勤、做资产盘点很多人第一次接触RFID都是从RC522这个模块开始的。我当初是在正点原子Mini板上调这块芯片网上下的例程要么是战舰板要么是精英板的直接烧进去发现SPI引脚对不上折腾了好几天才把程序理顺。这篇文章就把我整理好的、正点原子Mini板F103RCT6上可用的RC522程序思路完整拆给你包括移植时改了什么、为什么这么改、踩过哪些坑。新手照着做能少走弯路老手也可以看看移植思路有没有可参考的地方。1. 项目整体设计与思路拆解1.1 这个程序到底要解决什么问题RC522是NXP旗下MFRC522芯片的国产兼容方案工作在13.56MHz频段支持ISO14443A协议最常见的应用就是刷M1卡S50/S70读取卡号、读写扇区数据。正点原子Mini板使用的是STM32F103RCT6这颗芯片有256KB Flash、48KB RAM、3个USART、2个SPI资源上驱动RC522绰绰有余。问题就出在“板子适配”上。正点原子官方例程里RC522的例程通常是基于战舰或精英板写的SPI引脚分配和Mini板不一样直接拿过来编译烧录大概率是读不到卡。网上很多所谓的“Mini板可用例程”又只是改了引脚宏定义底层SPI初始化还是按原来的外设写照样跑不通。所以我重新梳理了一套可靠方案核心就是三件事引脚重新映射、SPI外设重新配置、代码逻辑校验调整。1.2 正点原子Mini板的引脚特殊性先说板子本身。Mini板上的板载Flash芯片W25Q64占用了SPI1的引脚PA5-SCK、PA6-MISO、PA7-MOSI。如果你要把RC522也挂到SPI1上理论上可以通过片选区分设备但实际使用中RC522和Flash共用SPI总线时序干扰、初始化冲突都会有隐患。我最终选择使用SPI2也就是PB13SCK、PB14MISO、PB15MOSI再用一个普通GPIO做RC522的片选这样和板载Flash互不干扰代码逻辑也干净。Mini板把这几个引脚都引出来了杜邦线直接接上去就能用。注意一点正点原子Mini板的板载LED、按键占用了部分引脚不管你用SPI1还是SPI2都要避开下面这几个常用引脚否则会冲突功能引脚LED0PB5LED1PE5KEY0PE4KEY1PE3W25Q64 CSPA4SPI1通信时尤其注意用SPI2方案时PB13/14/15和这些都没有重叠可以放心用。1.3 代码移植策略为什么选择标准库现在STM32开发主流是HAL库但RC522这类老外设模块标准库的资料最多、例程最丰富、踩坑记录也最全。正点原子Mini板出厂资料里标准库和HAL库都有工程模板。我这份程序基于标准库写理由是第一标准库的SPI寄存器配置直观SPI_Cmd()、SPI_SendData()这些函数一看就懂调试时可以直接看寄存器值。第二RC522的时序要求严格标准库因为更接近底层数据手册里的时序参数对照起来更方便。第三网上大量RC522例程都是基于标准库的你用标准库去移植遇到问题搜索到的解决方案比HAL库多得多。如果你坚持用HAL库也不是不行但要把HAL_SPI_TransmitReceive()的调用方式、超时处理都调整好后面我会提到SPI配置的几个细节HAL库同样适用。2. 核心细节解析与实操要点2.1 RC522模块的工作原理速览RC522是13.56MHz非接触式读写芯片通过SPI接口和MCU通信。MCU发送命令给RC522RC522通过天线线圈和射频场跟IC卡通信再通过SPI把数据返回给MCU。整个过程可以简化成三层物理层天线线圈产生13.56MHz射频场给卡片供电并传输数据。协议层RC522内部固化了ISO14443A协议栈自动处理防碰撞、数据帧编解码。应用层MCU通过SPI寄存器控制RC522完成寻卡、防冲突、选卡、认证、读写扇区等操作。MCU这边要做的事情其实就是通过SPI读写RC522的寄存器。RC522有一堆寄存器比如CommandReg命令寄存器、FIFODataRegFIFO数据寄存器、ComIrqReg中断请求寄存器等等理解这些寄存器是调通程序的关键。2.2 硬件接线正点原子Mini板的最终连接表这是接线图我实测过得照着接就行RC522模块正点原子Mini板说明SDA片选PB12软件控制片选不能用硬件NSSSCKPB13SPI2_SCKMOSIPB15SPI2_MOSIMISOPB14SPI2_MISOIRQ不接寻卡时用轮询方式IRQ不接也可以RSTPB11复位引脚软件控制3.3V3.3V必须3.3V不能接5VGNDGND共地提示RC522绝对不能直接接5V供电板上虽然有稳压电路但很多便宜模块的稳压芯片质量一般5V输入容易烧芯片老老实实接3.3V。2.3 SPI参数配置模式、时钟、位序选什么MFRC522数据手册里明确要求SPI工作在模式0CPOL0CPHA0也就是空闲时时钟为低电平第一个边沿采样数据。这一点很多移植失败的案例都是栽在这里——默认用了模式3结果寄存器读写全是乱码。波特率方面RC522最高支持10MHz的SPI时钟但我实测在F103RCT6上把SPI2设置到18MHzPCLK136MHz2分频时通信偶尔不稳定。最终稳的方案是4分频9MHz时钟读卡速度和稳定性都很好。如果你手头的RC522模块质量一般降到8分频4.5MHz会更保险。SPI配置的另一个关键点是数据帧格式。RC522使用8位数据格式MSB先行这个和STM32的SPI默认配置一致。配置代码如下void RC522_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE); // 配置PB13/14/15为复用推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // 配置PB12为推挽输出作为RC522片选 GPIO_InitStructure.GPIO_Pin GPIO_Pin_12; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // 复位引脚PB11也是推挽输出 GPIO_InitStructure.GPIO_Pin GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_Init(GPIOB, GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_11); // RST默认拉高 GPIO_SetBits(GPIOB, GPIO_Pin_12); // CS默认拉高 SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; // 模式0 SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; // 模式0 SPI_InitStructure.SPI_NSS SPI_NSS_Soft; // 软件NSS SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_4; // 9MHz SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI2, SPI_InitStructure); SPI_Cmd(SPI2, ENABLE); }3. 实操过程与核心环节实现3.1 工程搭建在正点原子模板基础上改我用的工程模板是正点原子Mini板的标准库例程模板基于这个模板在hardware目录下新建rc522文件夹把RC522的.c和.h文件放进去然后在工程里添加源文件路径和源文件即可。新建文件时建议把RC522模块相关的代码独立成rc522.c和rc522.h不要和主函数里的业务逻辑混在一起。这样以后换板子、换项目直接复制这两个文件就能复用。头文件里把引脚宏定义集中放在一起后面换引脚只需要改一处#define RC522_CS_H() GPIO_SetBits(GPIOB, GPIO_Pin_12) #define RC522_CS_L() GPIO_ResetBits(GPIOB, GPIO_Pin_12) #define RC522_RST_H() GPIO_SetBits(GPIOB, GPIO_Pin_11) #define RC522_RST_L() GPIO_ResetBits(GPIOB, GPIO_Pin_11)3.2 RC522初始化软复位与自检的关键顺序RC522上电后要先做软复位再配置定时器、发射天线才能正常工作。初始化顺序错了会导致寻卡失败甚至读出来的卡号都是乱的。标准初始化流程是拉高RST引脚延时至少10ms。往CommandReg写入0x0F软复位命令等待10ms。往ModeReg写入0x3D配置CRC和编码方式。配置定时器TModeReg、TPrescalerReg、TReloadReg。往TxControlReg写入0x83打开天线驱动。往RFCfgReg写入0x7F设置接收器增益。发送自检命令读回值做校验确认芯片活着。第7步的自检很有用可快速判断RC522模块本身有没有坏。MFRC522的SelfTest命令是往CommandReg写入0x00PCD_IDLE然后把FIFO写入0x00再写0x10到CommandReg延时后读取FIFOLevelReg的值——正常模块返回的应该是0x0E14字节数据如果返回值不对直接换模块别浪费时间排查。3.3 核心代码解析寻卡与防冲突RC522读取IC卡卡号核心逻辑是调用PcdRequest和PcdAnticoll两个函数。我第一次看这两个函数时一头雾水后来捋清楚了寻卡就是让RC522发一个请求命令等待卡片响应防冲突就是当两张卡同时进入射频场时通过算法选中其中一张。寻卡命令代码如下char PcdRequest(unsigned char req_code, unsigned char *pTagType) { char status; unsigned int backBits; unsigned char ucComMF522Buf[MAXRLEN]; ClearBitMask(Status2Reg, 0x08); // 关闭CRC校验 WriteRawRC(BitFramingReg, 0x07); // 发送7位数据 SetBitMask(TxControlReg, 0x03); // 打开天线发射 ucComMF522Buf[0] req_code; status PCDComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, backBits); if ((status MI_OK) (backBits 0x10)) { *pTagType ucComMF522Buf[0]; return MI_OK; } return MI_ERR; }这里req_code传PICC_REQIDL0x26表示寻未休眠的卡片这个命令只能寻到第一张进入射频场的卡。如果你要做一卡一刷的门禁这个函数足够了如果要做同时识别多张卡需要改用PICC_REQALL0x52并配合防冲突算法多轮循环。PCDComMF522函数是SPI读写的核心它向RC522发送命令和数据然后读取响应。这个函数里有几处对FIFOLevelReg的轮询等待实际调试中如果卡在循环里出不来多半是SPI通信本身有问题优先查接线和SPI配置。3.4 主函数流程与串口输出主函数里我用串口1输出读卡结果方便调试。整体流程很直白int main(void) { unsigned char cardID[4]; char status; delay_init(); // 正点原子延时函数 uart_init(115200); // 串口初始化 RC522_Init(); // RC522初始化 printf(RC522 Start...\r\n); while (1) { status PcdRequest(PICC_REQIDL, cardID); if (status MI_OK) { status PcdAnticoll(cardID); // 防冲突返回卡号4字节 if (status MI_OK) { printf(Card ID: %02X%02X%02X%02X\r\n, cardID[0], cardID[1], cardID[2], cardID[3]); } PcdHalt(); // 使卡片进入HALT状态否则卡片会持续响应 } delay_ms(200); } }注意最后的PcdHalt()很多入门教程里没写这一步。如果不让卡片休眠卡片会一直响应寻卡请求会导致同一个卡号被反复输出。加一个PcdHalt()每次刷卡只输出一次卡号这才是门禁系统需要的逻辑。4. 常见问题与排查技巧实录4.1 读不到卡90%是这五个原因RC522调不通绝大多数情况不是代码逻辑问题而是下面这几个物理/配置问题现象原因解决办法完全无响应串口无输出供电不足RC522瞬间电流大换USB口或加一个100uF电容在模块电源脚有响应但卡号全是0xFF天线线圈虚焊或损坏用放大镜检查天线焊盘补锡卡片放到天线上无反应晶振27.12MHz不起振换模块晶振问题无法修复偶尔能读偶尔读不到SPI时钟太快分频系数调大到8或16降到4.5MHz以下读到的卡号不稳定天线区域贴着金属物体天线背面不要放金属悬空2cm以上先说供电问题。很多人的RC522模块直接用杜邦线从开发板3.3V取电刷卡瞬间电流可能到几十毫安甚至上百毫安电源纹波一大SPI通信就出错。我的经验是如果开发板USB供电尽量用质量好一点的USB线换一根线就好了的情况我见过好几次。再说天线问题。RC522的天线是PCB铜箔线圈模块四角有焊盘引出天线接口。有些模块出厂时天线匹配电容是0603封装的贴片质量很差震动或弯折就容易虚焊。检查时用放大镜看天线绕线有没有断裂四个天线焊盘有没有氧化。4.2 移植后程序跑飞或者卡死正点原子Mini板例程模板默认会初始化板载设备如果直接在官方例程基础上加RC522代码可能会出现GPIO冲突导致程序跑飞或者卡死在延时函数里。排查思路就三步先把主函数里除了delay_init()、uart_init()、RC522_Init()之外的所有初始化全部注释掉包括LED、按键、LCD等。跑通了再逐步加回来。检查是不是引脚冲突。Mini板的PB12/PB13/PB14/PB15如果被其他外设占用也会出问题。用正点原子给的最简模板最稳妥。如果程序卡在某个延时函数里出不来看是不是时钟配置问题。正点原子模板默认使用外部8MHz晶振SysTick正常不会出现delay卡死的情况如果你用的是别的模板先确认delay_init()里的SYSCLK参数是72。4.3 能读卡号但扇区读写失败认证是真凶很多项目不只是读卡号还要读写卡片的扇区数据。M1卡有16个扇区每个扇区有独立的密钥A和密钥B。你要读某个扇区的数据必须先通过三遍认证。我第一次读扇区时一直报认证失败后来发现是卡片的默认密钥问题。新的M1卡出厂时密钥A和密钥B都是0xFF 0xFF 0xFF 0xFF 0xFF 0xFF但市面上有些卡被写过后改了密钥。认证时要用PcdAuthState(PICC_AUTHENT1A, blockAddr, key, cardID)其中key是6字节密钥数组cardID是4字节卡号。如果认证失败先用默认密钥试试不行就换一张已知密钥的卡。另一个坑是密钥和卡号的关系。认证时需要把卡号发给卡片做校验卡号必须是防冲突时读出来的4字节顺序不能颠倒。有的代码在防冲突后把卡号存成cardID[0..3]但在认证时写成了cardID[3..0]导致每次都认证失败——这种错误肉眼看不出来建议认证前加一个串口打印确认卡号数组的实际顺序。4.4 这套代码的后续扩展方向程序跑通之后你可以往这几个方向扩展加OLED/LCD显示卡号和剩余次数替代串口输出。加蜂鸣器和LED提示刷卡成功/失败做一个完整的门禁系统逻辑。加EEPROM存储白名单卡号做成刷卡开门。把读到的卡号通过ESP8266发送到云端做联网考勤系统。我是从最简单的“串口打印卡号”开始逐步加外设、加逻辑最后做成了一台可以实际使用的简易门禁。RC522这个模块虽然老了但作为学习RFID原理的入门硬件便宜、资料全、踩坑经验多比直接上手PN532要友好得多。最后再分享一个我曾经踩过的大坑RC522模块只支持3.3V但我有次接线时顺手插到了5V引脚结果模块发烫芯片直接烧了。检查接线一定要每个引脚都确认过再上电尤其是电源脚这个习惯能帮你省下好几块模块的钱。本文还有配套的精品资源点击获取