十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

51单片机驱动MFRC522 RFID读卡器:Proteus仿真与软件SPI实现详解

51单片机驱动MFRC522 RFID读卡器:Proteus仿真与软件SPI实现详解 1. 项目缘起为什么选择51单片机做RFID读卡器仿真最近在整理一些老项目的资料翻到了几年前带学生做的一个基于51单片机的RFID读卡器仿真设计。当时市面上STM32已经挺火了但很多电子专业的同学入门还是从51开始课程设计、毕业设计也常常指定用这个平台。这个项目虽然“老”但麻雀虽小五脏俱全从硬件原理图、单片机程序到Proteus仿真调试完整走了一遍产品开发流程对于理解嵌入式系统开发、串口通信、RFID协议乃至仿真验证都很有价值。今天就把这个项目的核心设计思路、关键代码、仿真搭建过程以及我踩过的几个坑系统地梳理出来。这个项目的核心目标很明确在Proteus仿真环境中用一块51单片机比如经典的AT89C51去驱动一个RFID读卡模块通常是基于RC522或FM1702这类13.56MHz的芯片实现读取Mifare 1K卡片UID唯一标识符并显示出来的功能。听起来简单但里面涉及到单片机IO口模拟SPI通信、RFID模块的寄存器配置、防冲突算法、数据显示等多个环节。对于初学者来说能独立调通这个系统对单片机编程、硬件接口和调试能力的提升是实实在在的。2. 核心硬件架构与元器件选型解析一套完整的读卡器系统硬件是基础。在仿真设计中我们虽然不用焊接实物但原理图必须正确元器件模型必须匹配否则仿真根本无法进行。下面拆解一下这个系统的硬件构成。2.1 主控芯片AT89C51的坚守与考量为什么是51单片机在ARM Cortex-M内核大行其道的今天这个问题很有代表性。对于这个仿真项目选择AT89C51有几个现实原因教学普及性51架构是国内大多数高校单片机课程的起点其指令集、寄存器结构、编程模式特别是Keil C51对学生而言最为熟悉。基于此进行拓展学习曲线最平缓。仿真资源丰富Proteus元件库中对51系列单片机的支持最为完善和稳定模型成熟仿真时不易出现一些兼容性问题。资源足够RFID读卡操作对主频和内存要求不高。AT89C51的12MHz时钟、4KB Flash、128字节RAM足以应对RC522的驱动、UID存储和简单的串口或LCD显示任务。成本与焦点项目重点在于理解RFID读卡流程和系统集成而非追求性能。用最经典的芯片可以让大家把注意力集中在核心逻辑上而不是纠结于复杂的新架构。当然它的局限性也很明显没有硬件SPI需要用IO口模拟处理速度慢资源紧张。但在仿真和学习阶段这些“缺点”恰恰成了深入理解底层通信协议的好机会。2.2 RFID读卡模块RC522 vs FM170213.56MHz的RFID读卡芯片常见的有NXP的RC522和复旦微电子的FM1702以及其兼容型号如MFRC522。它们在功能上高度相似都支持ISO/IEC 14443 A类协议都能读写Mifare系列卡片。在Proteus仿真中我们通常使用“MFRC522”这个元件模型。这里有一个关键点Proteus自带的MFRC522模型是一个“理想化”的模型。它简化了许多底层射频细节但提供了标准的SPI接口和寄存器映射足以让我们仿真核心的寻卡、防冲突、选卡、读UID流程。这意味着我们为实物RC522或FM1702编写的驱动程序经过少量适配主要是引脚定义通常可以直接用于这个仿真模型。选型建议对于仿真和入门学习无需纠结直接使用Proteus库中的MFRC522即可。它的引脚定义和通信时序是标准的。如果未来要做实物RC522的资料和开源库更多FM1702则更具成本优势两者驱动代码大同小异。2.3 外围电路与仿真元件清单一个最小系统除了主控和读卡芯片还需要一些必要的外围电路和显示设备。以下是构建该仿真项目的核心元器件清单及其在Proteus中的名称元器件类别Proteus中名称/型号关键参数/作用备注主控MCUAT89C5112MHz晶振 程序存储器选择内部ROM也可用AT89S52 注意仿真时无需外部复位电路 Proteus已内置RFID读卡芯片MFRC52213.56MHz RFID Reader在Proteus的“RFID”类别或直接搜索RFID标签TAG-PASSIVEMifare 1K 卡片模型仿真时 将其放置在读卡器天线线圈附近即可显示设备LM016L (16x2 LCD)显示读取到的卡片UID需连接至单片机一组IO口如P0口加上拉电阻串口通信COMPIM串口组件 连接虚拟终端用于调试输出UID信息 需设置波特率如9600上拉电阻RES阻值10k用于P0口驱动LCD 以及某些需要确定电平的IO口电源POWER / GROUND5V, GND为所有芯片供电晶振CRYSTAL12MHz为AT89C51提供时钟电容CAP22pF x2与晶振构成振荡电路注意Proteus仿真时单片机的复位电路RST引脚接高电平电容和电阻通常可以省略因为仿真模型上电即处于复位完成状态。但为了原理图的规范性和为实物设计做准备画上也无妨。另外MFRC522的天线部分在仿真中被极大简化我们只需要连接其天线引脚通常为TX1, TX2, RX到一个简单的LC谐振电路模型或直接连接一个“RFID天线”符号即可Proteus会处理射频耦合的仿真。3. 通信桥梁单片机如何与MFRC522“对话”AT89C51没有硬件SPI而MFRC522默认通过SPI接口与主机通信。因此我们必须用软件来模拟SPI的时序。这是整个驱动层最核心的部分任何一个时序的偏差都可能导致通信失败。3.1 软件模拟SPI的时序实现SPI是一种同步、全双工的串行通信协议主要涉及四根线SCK (Serial Clock)时钟线由主机单片机产生。MOSI (Master Out Slave In)主机输出从机输入。MISO (Master In Slave Out)主机输入从机输出。SS/CS (Slave Select/Chip Select)片选线低电平有效。我们需要用51单片机的四个普通IO口来模拟这四根线。假设定义如下根据你的原理图连接调整sbit MF522_SCK P1^0; // 时钟线 sbit MF522_MOSI P1^1; // 主机输出数据线 sbit MF522_MISO P1^2; // 主机输入数据线 sbit MF522_SS P1^3; // 片选线 sbit MF522_RST P1^4; // 复位线非SPI必需但MFRC522需要SPI的通信以字节为单位在时钟SCK的驱动下高位(MSB)先出。下面是一个典型的字节写入单片机发送数据给MFRC522函数void Write_MFRC522_Byte(unsigned char addr, unsigned char data) { unsigned char i; unsigned char addr_temp, data_temp; MF522_SS 0; // 拉低片选开始通信 // 发送地址字节写模式最高位为0 addr_temp (addr 1) 0x7E; // 左移一位最低位补0写并确保格式 for (i0; i8; i) { MF522_SCK 0; // 时钟拉低准备数据 if (addr_temp 0x80) { MF522_MOSI 1; } else { MF522_MOSI 0; } MF522_SCK 1; // 时钟上升沿MFRC522采样数据 addr_temp 1; } // 发送数据字节 data_temp data; for (i0; i8; i) { MF522_SCK 0; if (data_temp 0x80) { MF522_MOSI 1; } else { MF522_MOSI 0; } MF522_SCK 1; data_temp 1; } MF522_SCK 0; MF522_SS 1; // 拉高片选结束通信 }对应的字节读取函数稍微复杂一点因为需要在发送地址字节读模式最高位为1后切换IO口方向来读取MISO线上的数据unsigned char Read_MFRC522_Byte(unsigned char addr) { unsigned char i; unsigned char addr_temp, data_temp 0; MF522_SS 0; // 发送地址字节读模式最高位为1 addr_temp ((addr 1) 0x7E) | 0x80; // 左移一位最低位置1读 for (i0; i8; i) { MF522_SCK 0; if (addr_temp 0x80) { MF522_MOSI 1; } else { MF522_MOSI 0; } MF522_SCK 1; addr_temp 1; } // 切换为读取模式注意MISO引脚配置51单片机IO口为准双向可直接读 MF522_MOSI 1; // 释放MOSI线或将其设置为高阻输入状态如果IO口可配置 // 读取数据字节 for (i0; i8; i) { MF522_SCK 0; MF522_SCK 1; // 在时钟上升沿后读取数据更稳定 data_temp 1; if (MF522_MISO) { data_temp | 0x01; } } MF522_SCK 0; MF522_SS 1; return data_temp; }实操心得软件模拟SPI的难点在于时序的精确性。MFRC522的SPI模式0CPOL0 CPHA0是最常用的即时钟空闲时为低电平在上升沿采样数据。上述代码严格遵循了这一时序。在仿真中由于是理想模型对时序抖动不敏感。但在实物调试时如果读写出错第一个要怀疑的就是模拟SPI的时序尤其是SCK高低电平的保持时间。可以用示波器或逻辑分析仪抓取SCK、MOSI、MISO的波形与MFRC522数据手册的时序图对比。3.2 MFRC522的初始化流程与关键寄存器通信链路打通后下一步就是配置MFRC522让它进入正常工作状态。初始化流程是一系列标准操作硬复位拉低RST引脚一段时间再拉高。这是最彻底的复位方式。软复位向Command寄存器0x01写入0x0F进行软件复位。关闭天线初始化阶段先关闭射频发射设置TxControlReg寄存器。定时器设置配置定时器用于控制通信超时。写入密钥如果需要认证后读块数据需要向密钥缓冲区写入默认密钥如6个0xFF。配置寄存器ModeReg(0x11): 设置发送和接收模式。TxModeReg(0x12),RxModeReg(0x13): 设置发送和接收的调制参数。TReloadRegL/H(0x2C, 0x2D): 设置定时器重载值。TModeReg(0x2A),TPrescalerReg(0x2B): 设置定时器模式和预分频。TxASKReg(0x15): 设置100% ASK调制某些模式需要。开启天线设置TxControlReg寄存器开启天线驱动。这里列举几个关键寄存器的配置示例// 关闭天线 Write_MFRC522_Byte(TxControlReg, 0x00); // 设置定时器TMode0x8DTAuto1, 在发送结束后自动重载 TPrescaler0x3E Write_MFRC522_Byte(TModeReg, 0x8D); Write_MFRC522_Byte(TPrescalerReg, 0x3E); // 设置定时器重载值决定超时时间 Write_MFRC522_Byte(TReloadRegL, 30); Write_MFRC522_Byte(TReloadRegH, 0); // 设置TxModeReg和RxModeReg使用CRC协处理器帧格式符合ISO14443A Write_MFRC522_Byte(TxModeReg, 0x00); Write_MFRC522_Byte(RxModeReg, 0x00); // 设置ModWidthReg调制宽度影响通信速率和稳定性常用0x26 Write_MFRC522_Byte(ModWidthReg, 0x26); // 开启天线 Write_MFRC522_Byte(TxControlReg, 0x83); // 引脚TX1和TX2驱动天线初始化完成后MFRC522就处于“寻卡”就绪状态了。4. 核心流程拆解从寻卡到获取UID这是整个程序最核心的逻辑链每一步的返回值都必须正确处理。流程可以概括为寻卡 - 防冲突 - 选卡 - 认证可选- 读数据这里我们只读UID。4.1 寻卡PICC_REQIDL与防冲突Anticoll单片机需要主动去“寻找”进入射频场的卡片。这是通过向MFRC522发送寻卡指令实现的。unsigned char PcdRequest(unsigned char req_code, unsigned char *pTagType) { unsigned char status; unsigned int unLen; unsigned char ucComMF522Buf[MAXRLEN]; ClearBitMask(Status2Reg, 0x08); // 清除MFCrypto1On位 Write_MFRC522_Byte(BitFramingReg, 0x07); // 设置帧格式 SetBitMask(TxControlReg, 0x03); // 确保天线开启 ucComMF522Buf[0] req_code; // 寻卡指令PICC_REQIDL (0x26) 寻天线区内未进入休眠的卡 // 或 PICC_REQALL (0x52) 寻天线区内全部卡 status PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, unLen, pTagType[0], 2); if ((status MI_OK) (unLen 0x10)) { // 成功且返回16位卡类型 *pTagType ucComMF522Buf[0]; *(pTagType1) ucComMF522Buf[1]; } else { status MI_ERR; } return status; }如果寻卡成功说明有卡片在附近。但可能有多张卡这时就需要“防冲突”流程来获取其中一张卡的唯一标识符UID。unsigned char PcdAnticoll(unsigned char *pSnr) { unsigned char status; unsigned char i, snr_check 0; unsigned int unLen; unsigned char ucComMF522Buf[MAXRLEN]; ClearBitMask(Status2Reg, 0x08); Write_MFRC522_Byte(BitFramingReg, 0x00); // 清除位计数寄存器 ClearBitMask(CollReg, 0x80); // 清除ValuesAfterColl位 ucComMF522Buf[0] PICC_ANTICOLL1; // 防冲突指令 0x93 ucComMF522Buf[1] 0x20; // 防冲突层1 NVB字节数0x20 status PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, unLen, pSnr, 4); // pSnr用于接收UID if (status MI_OK) { // 验证接收到的UID4字节的校验和第5字节 for (i0; i4; i) { snr_check ^ pSnr[i]; } if (snr_check ! ucComMF522Buf[4]) { // 校验和不匹配 status MI_ERR; } } return status; }防冲突成功后pSnr数组里就存储了这张卡的4字节UID对于Mifare 1K。4.2 选卡PICC_SELECT与休眠PICC_HALT获取UID后需要“选择”这张卡以便后续进行读写操作。unsigned char PcdSelect(unsigned char *pSnr) { unsigned char status; unsigned char i; unsigned int unLen; unsigned char ucComMF522Buf[MAXRLEN]; ucComMF522Buf[0] PICC_SELECT1; // 选卡指令 0x93 ucComMF522Buf[1] 0x70; // NVB0x70 for (i0; i5; i) { // 发送UID和BCC校验字节 ucComMF522Buf[i2] *(pSnri); } // 计算CRC16校验码附加在命令后 CalulateCRC(ucComMF522Buf, 7, ucComMF522Buf[7]); status PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, unLen, NULL, 0); if ((status MI_OK) (unLen 0x18)) { // 选卡成功返回0x183字节的SAK status MI_OK; } else { status MI_ERR; } return status; }操作完成后应该让卡片进入“休眠”状态避免干扰后续对其他卡片的操作。void PcdHalt(void) { unsigned char status; unsigned int unLen; unsigned char ucComMF522Buf[MAXRLEN]; ucComMF522Buf[0] PICC_HALT; // 休眠指令 0x50 ucComMF522Buf[1] 0x00; CalulateCRC(ucComMF522Buf, 2, ucComMF522Buf[2]); status PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, unLen, NULL, 0); }4.3 主程序逻辑循环将上述步骤组合起来就构成了主程序的核心循环void main() { unsigned char status; unsigned char str[MAX_LEN]; unsigned char snr[4]; // 存储卡片UID unsigned char TagType[2]; // 存储卡片类型 Init_MFRC522(); // 初始化MFRC522 LCD_Init(); // 初始化LCD UART_Init(); // 初始化串口用于调试 LCD_ShowString(0, 0, RFID Reader Ready); UART_SendString(System Start\r\n); while(1) { // 1. 寻卡 status PcdRequest(PICC_REQIDL, TagType); if (status ! MI_OK) { // 没找到卡 可以延时后继续寻找 delay_ms(100); continue; } UART_SendString(Card Detected.\r\n); // 2. 防冲突获取UID status PcdAnticoll(snr); if (status ! MI_OK) { UART_SendString(Anticoll Error.\r\n); PcdHalt(); // 发生错误让卡片休眠 continue; } // 3. 选卡 status PcdSelect(snr); if (status ! MI_OK) { UART_SendString(Select Card Error.\r\n); PcdHalt(); continue; } // 4. 成功显示UID sprintf(str, UID:%02X%02X%02X%02X, snr[0], snr[1], snr[2], snr[3]); LCD_ShowString(0, 1, str); // 在LCD第二行显示 UART_SendString(UID: ); UART_SendHexArray(snr, 4); // 自定义函数发送16进制数组 UART_SendString(\r\n); // 5. 让卡片休眠准备下一次读取 PcdHalt(); delay_ms(1000); // 读卡成功后等待一秒避免连续读同一张卡 LCD_ClearLine(1); // 清空LCD第二行等待下一次读取 } }这个循环清晰地展示了从检测到卡片到获取并显示UID的完整流程。每一个status的判断都至关重要它决定了程序是继续执行还是进行错误处理。5. Proteus仿真搭建与调试实战有了原理图和代码下一步就是在Proteus中把它们连接起来进行虚拟调试。这是检验设计是否正确、代码逻辑是否通顺的关键一步。5.1 原理图绘制与关键连接新建工程打开Proteus ISIS新建一个设计文件。放置元件从库中按第二节的清单找到并放置所有元器件。核心连线单片机与MFRC522将AT89C51的P1.0-P1.4分别连接到MFRC522的SCK、MOSI、MISO、SDA即SS、RST引脚。注意Proteus中MFRC522模型的引脚名称可能略有不同如NSS对应SS需查看元件属性确认。MFRC522天线将其TX1、TX2引脚连接到一个简单的LC电路例如一个几微亨的电感和一个几十皮法的电容串联或者直接连接Proteus提供的“ANTENNA”符号。RX引脚通常通过一个分压电阻网络连接到天线回路用于接收信号仿真中可以简化处理。LCD连接将LM016L的D0-D7数据线连接到单片机的P0口记得在P0口加上8个10k的上拉电阻排。RS、RW、E三个控制引脚连接到单片机的其他IO口如P2.0、P2.1、P2.2。串口连接放置一个“COMPIM”元件将其RXD连接到单片机的TXDP3.1TXD连接到单片机的RXDP3.0。再放置一个“VIRTUAL TERMINAL”虚拟终端将其RXD连接到COMPIM的TXD用于显示串口发送的数据。在虚拟终端属性中设置好波特率如9600。电源与地为所有芯片的VCC和GND引脚接上5V和地。5.2 程序编译与加载编写代码在Keil uVision中新建一个C51工程将上述所有驱动函数和主程序编写完整。编译生成HEX文件确保编译无误在Output选项中勾选“Create HEX File”重新编译后会在工程目录下生成.hex文件。加载到Proteus在Proteus中双击AT89C51元件在弹出的属性窗口中在“Program File”一栏选择刚才生成的.hex文件。在“Clock Frequency”一栏填入“12MHz”。5.3 仿真运行与问题排查点击Proteus左下角的运行按钮开始仿真。此时LCD应该显示初始信息“RFID Reader Ready”。虚拟终端窗口也会打开。关键操作用鼠标左键点击并拖动“TAG-PASSIVE”元件将其移动到MFRC522天线线圈的附近大约重叠或紧贴。这时你应该能看到LCD屏幕的第二行显示出类似“UID: 3A 7B 91 2C”的字符串具体数值取决于仿真卡片的随机UID。虚拟终端中打印出“Card Detected.”和“UID: 3A 7B 91 2C”等信息。如果仿真没有成功可以按照以下步骤排查现象可能原因排查方法LCD无显示1. LCD初始化失败2. 数据/控制线连接错误3. 上拉电阻未加针对P0口4. 程序未成功加载HEX1. 检查LCD初始化代码时序延时是否足够。2. 对照原理图检查RS、RW、E引脚连接是否正确。3. 确认P0口接了上拉电阻排。4. 双击单片机确认HEX文件路径正确并重新加载。虚拟终端无输出1. 串口初始化波特率设置错误2. COMPIM与单片机TX/RX交叉连接错误3. 虚拟终端波特率设置不匹配1. 检查代码中定时器1的配置计算波特率是否为9600。2. 确认COMPIM的RXD接单片机TXDTXD接单片机RXD。3. 右键虚拟终端检查其波特率属性是否与代码设置一致。始终检测不到卡1. MFRC522初始化失败2. 软件SPI时序错误3. 天线未有效连接或卡片未放置到位4. 寻卡指令或防冲突指令错误1. 在代码中初始化后尝试读取MFRC522的版本寄存器0x37正常应返回0x92。可在虚拟终端打印此值验证。2. 这是最常见的问题。仔细核对Write_MFRC522_Byte和Read_MFRC522_Byte函数确保SCK、MOSI、MISO的时序与SPI模式0完全一致。可以单步仿真观察IO口电平变化。3. 确保TAG元件与读卡器天线线圈图形有重叠。4. 核对发送的指令码0x26, 0x93等是否正确。能寻卡但防冲突失败1. 防冲突算法实现有误2. 接收数据校验出错3. 多张卡冲突仿真中少见1. 重点检查PcdAnticoll函数特别是发送的字节数和接收处理逻辑。2. 检查CRC校验计算函数CalulateCRC是否正确。可以在成功接收到数据后先不校验直接打印出来看看是否正确。调试技巧善用Proteus的调试功能。可以在Keil中设置断点然后使用Proteus的“Remote Debug Monitor”功能与Keil联合调试。更简单的方法是在代码中关键位置如初始化完成、寻卡成功、防冲突成功通过串口发送不同的调试信息如“Init OK”、“Request OK”、“Anticoll OK”这样在虚拟终端就能清晰地看到程序执行到哪一步失败了。6. 从仿真到实物的关键跨越与进阶思考仿真成功只意味着逻辑和代码框架基本正确。要做出一个能稳定工作的实物还需要考虑很多仿真中忽略掉的实际问题。6.1 硬件设计注意事项电源与滤波MFRC522对电源噪声比较敏感。实物电路中必须在芯片的VCC引脚附近通常是0.1uF和10uF电容并联放置去耦电容以滤除高频噪声。天线匹配电路的电容电感精度要求较高最好使用NPO/C0G材质的高精度电容和绕线电感。天线设计天线的形状通常是矩形线圈、匝数、线径直接影响读卡距离和稳定性。可以参考MFRC522数据手册或AN1445应用笔记中的推荐设计。天线回路的总电感值需要与匹配电容谐振在13.56MHz。PCB布局射频部分天线及匹配电路应尽量远离数字电路单片机、晶振等。天线下面和周围最好做铺铜隔离。信号线尤其是MISO走线尽量短。6.2 软件优化与稳定性提升增加超时与重试机制实物环境中电磁干扰、卡片移动都可能导致单次通信失败。在PcdComMF522底层收发函数中必须加入超时判断如果在一定时间内未收到响应则返回超时错误。上层函数如PcdRequest在收到错误后不应立即放弃而应进行有限次数的重试。抗干扰处理在寻卡循环中加入适当的延时如100ms避免过于频繁地发射射频信号这既能降低功耗也能让系统更稳定。可以定期如每寻卡10次重新初始化一次MFRC522以清除可能出现的异常状态。功能扩展获取UID只是最基本的功能。可以在此基础上实现读写数据块通过PcdAuthState进行密钥认证A密钥或B密钥然后使用PcdRead和PcdWrite函数对Mifare 1K卡的16个扇区进行读写。注意每个扇区的块3是控制块存放密钥和访问条件切勿误操作导致卡片锁死。多卡识别与处理通过循环执行防冲突和选卡流程可以处理多张卡片。需要设计一个卡片列表来管理已识别的UID。与上位机通信通过串口将读取到的UID或块数据发送给PC配合上位机软件如用C#、Python编写实现更复杂的管理功能如门禁日志、考勤记录等。6.3 项目价值的再思考完成这个仿真项目其意义远不止于“让LCD显示一串数字”。它是一次完整的、微缩版的嵌入式产品开发演练硬件层面你理解了单片机最小系统、SPI通信接口、射频前端电路的基本构成。软件层面你掌握了在资源受限的MCU上用C语言实现设备驱动、协议解析、状态机控制的能力。系统层面你体验了从需求分析、方案选型、代码编写、仿真验证到问题排查的全流程。工具层面你熟悉了Keil和Proteus这两个在电子领域极其重要的开发与仿真工具。即使未来你使用更强大的STM32、ESP32或者直接调用现成的RC522库这段用51单片机“裸撸”驱动的经历会让你对RFID技术底层原理的理解更加深刻在遇到复杂问题时你的调试思路和解决能力也会更强。这个项目就像一个基石掌握了它再去学习更高级的框架和工具会感到更加得心应手。
返回列表