尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于STM32与RC522的RFID考勤系统单片机程序全解析

基于STM32与RC522的RFID考勤系统单片机程序全解析 简介基于RFID考勤系统设计的单片机程序是一份完整的嵌入式实战资料面向单片机初学者、嵌入式开发人员以及物联网相关专业学生帮助理解射频识别读卡、防碰撞算法、信息处理与嵌入式硬件设计的协同实现。资源共34个文件约116KB以C源文件、头文件、Keil工程配置、hex固件、列表文件、中间目标文件等构成其中C与头文件便于阅读修改hex文件可直接烧录验证备份和工程辅助文件可用于参考与恢复。已有1220人学习下载。项目基于RC522射频模块与LCD12864液晶屏覆盖IC卡注册、注销、打卡时间记录与显示等考勤核心环节程序结构清晰对理解射频识别标签读写机制、单片机与射频模块接口设计、ALOHA等防碰撞算法的工程落地具有直接参考价值。通过研读源码与工程配置读者可快速搭建同类考勤系统并进行二次开发。 做考勤系统是很多人接触RFID的第一个完整项目这话我到现在依然觉得不夸张。不管是学生时代的课程设计、毕业设计还是公司里给前台做一套内部打卡机核心逻辑一脉相承RFID读卡器拿到卡号单片机判断合法性再结合时钟模块记录时间最后把结果打到屏幕上。这个项目门槛不高但真要做到稳定可靠、不丢卡、不误报里面的细节远比你想象的多。这篇文章我就把基于RFID考勤系统的单片机程序从方案选型、硬件搭建到代码实现、调试排障完整拆开讲一遍。项目默认用 STM32F103 RC522 这套经典组合作为主线兼容性很强看完你完全可以照着搭出一台能实际运行的考勤原型机。适合正在做课设、毕设或者想入门RFID和单片机的嵌入式开发者参考。1. 项目需求与方案选型1.1 考勤系统到底需要什么功能做项目第一步永远是先把需求钉死不然写代码一定会被自己反复推翻。一套最小可用考勤系统至少要包含这几件事卡片识别RFID读卡器能稳定读出卡片唯一编号。合法性校验判断当前卡片是否在预先录入的白名单里。打卡时间记录需要实时时钟记录“谁在什么时间打了卡”。结果反馈显示器和蜂鸣器结合告诉用户打卡成功还是失败。防重复打卡同一张卡短时间内重复刷系统要能识别并拒绝不然就成了刷存在感。掉电存储考勤记录不能一断电就清零必须存到非易失性存储里。很多刚接触的人容易上来就写RFID驱动或者先调屏幕这是顺序误区。正确做法是先列出功能清单再反推硬件选型和程序模块划分。比如你如果连“防重复打卡”这个需求都没确认写程序时可能就不会做时间窗判断后期返工成本很高。1.2 器件选型背后的取舍逻辑RFID读卡方案上我优先推荐RC522它工作在13.56MHz高频段走ISO14443A协议读卡距离大概3到6厘米非常贴近桌面打卡应用场景。市面上也有便宜的125kHz ID卡模块读卡距离远一些ID卡本身也很廉价但ID卡几乎没有加密能力属于“可轻易复制”的类型。如果是做演示和课设RC522配M1卡是最通用的组合如果是要正式商用建议直接上CPU卡或国密卡程序接口差别不大但安全性完全不是一个量级。单片机选型上STM32F103C8T6是我在这类项目里用得最多的芯片。原因很实际主频72MHz跑RC522毫无压力硬件SPI、硬件I2C都有调试工具链成熟板子还便宜。有人会问能不能用51单片机答案是能但不推荐。RC522通信本身是SPI协议51没有硬件SPI需要用普通GPIO模拟时序写起来不说多难至少多了一部分调试成本。如果手里只有51开发板注意电平问题RC522是3.3V逻辑器件别直接接5V引脚否则容易烧模块。其他外设选型我一般这样配时钟用DS1302便宜且资料多如果对精度有要求直接换DS3231温度漂移小很多存储用AT24C02或AT24C64用来存白名单和考勤记录显示用0.96寸OLEDI2C接口只要两个IO比1602液晶省脚且显示信息量更大。这套组合下来总成本在40块以内学生党完全能承受。1.3 系统整体工作流程把需求转化为程序流程图大致是这样的逻辑系统上电后初始化所有外设读取白名单和考勤记录索引然后进入待机状态。当有人把卡放到读卡区RC522触发寻卡成功程序立即停止待机循环转去执行防冲突算法拿到完整卡号。拿到卡号后先比对白名单不在名单里直接提示“陌生卡”在名单里再判断这次刷卡和上次刷卡间隔如果小于设定时间窗比如5分钟提示“已打卡”并拒绝记录如果间隔正常就读取RTC时间把“卡号 时间 结果”写入EEPROM同时更新OLED显示蜂鸣器短响一声表示成功。处理完毕程序回到待机状态等待下一次刷卡。这个流程看着简单但真正写代码时你会发现每一步都有不少细节要处理比如RC522寻卡函数的返回码判断、EEPROM磨损均衡、时钟芯片的BCD码转换等等。下面我把硬件先搭出来再逐个环节拆解代码。2. 硬件电路搭建与关键细节2.1 硬件清单动手接线之前先把物料表列清楚。我这里给的是最常用的配置所有器件都可以在常见电商平台买到。器件型号/规格作用主控板STM32F103C8T6最小系统板核心控制射频读卡模块RC52213.56MHz非接触式读卡卡片M1卡 / NTAG215存储卡号信息实时时钟DS1302产生时间戳存储芯片AT24C02 或 AT24C64保存白名单和考勤记录显示模块0.96寸OLED SSD1306显示时间、状态蜂鸣器有源蜂鸣器模块提示成功/失败电源5V适配器 AMS1117-3.3稳压为主控和RC522供电按键轻触开关 x2系统设置、卡片录入2.2 RC522与单片机的接线RC522接口是SPI模块上一共8个引脚SDA片选、SCK时钟、MOSI主出从入、MISO主入从出、IRQ中断请求、RST复位、GND、VCC。注意这个模块的VCC是3.3V千万别直接上5V。我使用的接线方式如下以STM32F103C8T6的SPI1为例RC522引脚STM32引脚SDAPA4软件片选SCKPA5MOSIPA7MISOPA6RSTPB0IRQ不接轮询方式GNDGNDVCC3.3VIRQ引脚在这里可以不接程序用轮询方式检查读卡状态对于考勤这种低频触发场景完全够用还省一个引脚。接线的核心原则是线越短越好杜邦线长度控制在20厘米以内超过了会影响SPI时钟边沿质量造成读卡不稳定。如果现场空间受限可以把SPI时钟频率降下来后面调试章节细说。2.3 供电与布局注意事项RFID读卡模块在工作时会瞬间拉高电流特别是寻卡瞬间如果电源纹波大单片机容易复位。我的做法是给系统板单独用AMS1117-3.3稳压供电RC522和主控共用一个3.3V电压域外部5V只能进开发板的电源输入端。在RC522的VCC和GND之间加一个100uF电解电容并一个104瓷片电容效果很明显至少能解决一半的“读卡过程中单片机重启”问题。天线线圈的布局也值得注意。RC522的天线区上方不要放金属物体包括金属桌面板、金属外壳会显著缩短读卡距离。单片机晶振和RC522天线保持至少3厘米距离天线附近也不要走长排线。如果发现读卡距离只有1厘米出头先怀疑天线被遮挡或者供电不足别急着换模块。3. 单片机程序设计与实现3.1 主程序状态机架构考勤程序如果全部塞在while循环里功能一多就会乱套。我习惯用状态机组织整个程序逻辑把系统划分为几个清晰状态上电初始化、待机扫描、卡片处理、结果显示。typedef enum { SYS_INIT, SYS_IDLE, CARD_DETECTED, CARD_PROCESS, SHOW_RESULT } SystemState; SystemState state SYS_INIT; while (1) { switch (state) { case SYS_INIT: System_Init(); state SYS_IDLE; break; case SYS_IDLE: if (RC522_CheckCard()) { state CARD_DETECTED; } break; case CARD_DETECTED: UID uid; if (RC522_ReadUID(uid)) { state CARD_PROCESS; } else { state SYS_IDLE; } break; case CARD_PROCESS: attendance_result_t result Attendance_Process(uid); display_show(result); buzzer_feedback(result); state SYS_IDLE; break; default: state SYS_IDLE; break; } }状态机的核心好处是每个状态职责单一排查问题非常直观。比如发现屏幕卡死直接看显示相关状态发现读卡不响应就只查IDLE和DETECTED这两个状态之间的跳转条件。对于这种小型项目状态机不会增加多少代码量但可维护性提升很明显。3.2 RC522底层驱动与SPI通信要用好RC522先要理解它和单片机之间的SPI通信。RMFRC522数据手册里寄存器很多但实际项目里你只需要掌握几个关键函数写RC522寄存器用于下发命令和配置。读RC522寄存器用于获取状态和数据。发送命令帧比如0x52寻卡、0x93防冲突。读取返回数据拿到卡序列号。SPI初始化建议把时钟频率设置在100kHz到1MHz之间。RC522数据手册说最高支持10MHz但实际用杜邦线连接时跑到几MHz就非常容易出错特别是环境有干扰时。我实测比较稳的速率是500kHz一次寻卡加防冲突的流程不到50毫秒对考勤来说完全够快。void RC522_WriteByte(uint8_t reg, uint8_t value) { CS_LOW(); SPI_ExchangeByte((reg 1) 0x7E); // 写命令地址左移一位 SPI_ExchangeByte(value); CS_HIGH(); } uint8_t RC522_ReadByte(uint8_t reg) { uint8_t val; CS_LOW(); SPI_ExchangeByte(((reg 1) 0x7E) | 0x80); // 读命令最高位置1 val SPI_ExchangeByte(0x00); CS_HIGH(); return val; }读卡的核心命令流程是寻卡 - 防冲突 - 选卡。寻卡发送PCD_TRANSCEIVE命令和0x52字节相当于在射频场里喊一声“谁在场”有卡片响应后执行防冲突循环拿到4字节的卡序列号UID最后选卡锁定这张卡。这三个步骤缺一不可尤其防冲突这一步很多人省略结果两张卡同时进场时程序就乱了。3.3 卡号识别与防重复打卡逻辑拿到卡号之后程序要做两件事合法性判断和时间窗判断。合法性判断最简单的实现是维护一张白名单数组const uint32_t valid_card_list[] { 0xA1B2C3D4, 0x12345678, 0xABCDEF01 };把读到的UID转成32位整数后遍历这个数组。这种做法在白名单数量少的情况下简单可靠但每次加卡都要重新编译烧录程序灵活性差。更好的方案是把白名单存在AT24C04里增加一个“读卡授权”模式按下设置键进入授权状态刷入新卡就把UID写入EEPROM这样管理员不需要懂单片机也能维护名单。防重复打卡逻辑用时间窗实现。系统保存每个卡号最后一次成功打卡的时间戳每次合法刷卡后取当前时间与上次时间做差值如果小于设定值就拒绝本次打卡。这个逻辑要放在“打卡成功”记录之前执行否则会出现边界问题比如5分钟内第一次打卡成功但重复刷仍提示成功。3.4 时间记录与EEPROM存储记录格式我建议固定为卡号4字节 时间戳4字节Unix时间 状态1字节。每条记录9字节。如果用AT24C648KB可以存大约900条记录对多数考勤场景够用了。EEPROM写入有两个坑要避免。第一是写入地址必须按页对齐AT24C02的页大小是8字节一次页写最多8字节跨页写会出错。第二是EEPROM寿命大约10万次擦写如果固定写同一个地址用不了多久就废了。简单做法是用一个“当前写入地址”变量每写一条记录就往后偏移写满了再回到起点覆盖最老数据实现一个简单的环形缓冲。这个指针变量也存到EEPROM尾部固定地址避免掉电复位后不知道写到哪里。RTC时间读取方面DS1302通过三个线操作数据格式是BCD码读取后需要转换成十进制再用于显示和存储。建议在系统初始化时先判断RTC是否走时正常如果发现时间异常比如年份读出来是0就在OLED上提示用户设置时间避免记录了一堆“1970年”的乌龙数据。3.5 核心代码示例下面是我在考勤项目里的主处理函数去掉了平台相关细节保留完整逻辑attendance_result_t Attendance_Process(UID uid) { // 步骤1: 查找白名单 uint8_t allow 0; for (int i 0; i white_list_count; i) { if (memcmp(uid.bytes, white_list[i].uid, 4) 0) { allow 1; break; } } if (!allow) { return RESULT_DENIED; } // 步骤2: 检查重复打卡 uint32_t now RTC_GetUnixTime(); uint32_t last EEPROM_ReadLastTime(uid.bytes); if ((now - last) CHECK_INTERVAL_SEC) { return RESULT_REPEATED; } // 步骤3: 写入考勤记录 uint8_t record[9]; memcpy(record, uid.bytes, 4); record[4] (now 24) 0xFF; record[5] (now 16) 0xFF; record[6] (now 8) 0xFF; record[7] now 0xFF; record[8] 0x01; // 状态正常 EEPROM_RingWrite(record, sizeof(record)); return RESULT_SUCCESS; }这段代码把合法性校验、时间窗判断、存储串起来了。实际项目里还需要在步骤2之前加一个“白名单不在本机”的处理分支但核心思想是一致的。3.6 数据安全与防复制设计很多人做考勤项目时会忽略一个问题普通M1卡的UID是可以被读写设备复制和改写的。如果整个考勤系统只校验UID那别人拿一个读卡器就能复制所有卡。应对措施有两个层次第一如果只是课程设计可以在PPT和报告里说明“本设计适用于演示环境正式环境需配合加密卡”。第二更进一步的方案是不把白名单判定放在单片机端的UID比对而是要求M1卡在某个特定扇区写入本系统自定义的加密数据。读卡的时候先读扇区密码验证再读取该扇区内容与系统预设值比对双因子校验。这样即使有人复制了UID也读不出扇区里的密码和数据复制出来的卡依然无效。这个思路对于面向实际部署的考勤项目非常重要建议写入你的设计文档里答辩时会是加分项。4. 常见问题与调试实录4.1 常见问题速查表这部分是我自己反复踩过、也帮别人排查过的问题汇总。现象可能原因解决办法读不到任何卡RC522供电不足、SPI速率过高降SPI到500kHz检查3.3V电源稳定性能读卡但UID随机变化接线过长、接触不良缩短杜邦线用示波器查SPI信号质量程序上电后就乱跑电源纹波大导致复位在电源引脚加100uF和104去耦电容蜂鸣器长响不止GPIO配置错误或驱动电流不足检查蜂鸣器是低电平触发还是高电平触发打卡时间全是0DS1302初始化失败或电池没电检查电池电压重新初始化RTC掉电后打卡记录丢失EEPROM写入时掉电、地址未做环形缓冲加掉电检测写入完成后再更新地址指针电脑读卡软件提示“数据连接错误”串口被占用、波特率不匹配或USB转串口芯片驱动未装检查设备管理器识别情况确认波特率与软件设置一致读卡距离只有1厘米天线被金属遮挡、天线线圈变形清理天线区域避免金属桌面直贴模块这里单独说一下“数据连接错误”的排查。很多初学者通过串口把单片机连到PC端考勤管理软件时软件提示连接错误第一时间会怀疑程序。但80%的情况是USB转串口驱动没装好、串口号选择错误或者软件里波特率与固件实际波特率不一致。建议先在电脑上用串口助手发一个测试字符串如果能收到回显再打开管理软件。这个排查顺序能少走一半弯路。4.2 实测中遇到的三个典型问题第一个问题是SPI通信乱码。我最初用杜邦线把RC522接到STM32核心板SPI速率配到2MHz结果寻卡成功率只有一半经常读到错误的CRC。折腾半天后发现是杜邦线太长加上模块VCC走了一条很细的飞线。后来把电源线加粗、SPI速率降到500kHz问题立刻消失。RC522的SPI抗干扰能力没有想象中那么强实际项目一定要留足余量。第二个问题是刷卡偶尔连触发两次。现象是刷一次卡蜂鸣器响两声记录出现两条。原因是卡片离开读卡区时射频场电离导致再次触发寻卡。解决办法是在记录写入后增加一个5秒的冷却标志冷却时间内即使读到同一张卡也不处理只更新显示。这个冷却标志和防重复打卡时间窗是两个不同概念前者是硬件防抖后者是业务逻辑。第三个问题是批量记录写入EEPROM时偶发坏数据。排查后发现是页写入跨页导致的我使用AT24C64时没注意它页大小是32字节数据一旦跨页就出错。修复方式是把记录缓冲区强制按页大小对齐并在写前检测当前页剩余空间不够就先去下一页。这个坑写EEPROM项目时非常常见建议直接用一个通用的PageWrite函数封装掉。4.3 提升稳定性的几条经验做完这套系统我有几个稳定性层面的经验分享。上电初始化阶段别急着读卡加一个500毫秒延时让RC522内部晶振稳定。很多“上电后第一次刷卡总失败”的问题都是初始化时序不够导致的。我习惯先读RC522版本号寄存器如果读出来是0x92或0x91才认为模块正常如果不是就在屏幕上打印错误码方便现场定位。程序里加一个独立看门狗特别是系统脱离开发板、用电池独立供电的场景。如果外接干扰导致程序跑飞看门狗能自动复位保证考勤机长期运行不罢工。喂狗的位置放在主循环状态机每次切换时不要在某个具体函数里喂否则某个状态卡死时看门狗形同虚设。卡片处理和显示刷新分离。OLED刷新一帧要几十毫秒如果每次读卡都在同一段代码里做完整屏重绘会导致读卡响应变慢、感应区域附近有其他卡片时误读概率变高。用缓存刷新策略只在结果变化时刷新屏幕平时显示时间时每秒钟刷新一次即可。5. 项目落地与后续扩展一套能用的考勤原型机到这里就算完成了。但如果想让这个项目真正落地而不是停留在实验台上有几个扩展方向值得花时间做。第一个方向是上位机管理软件。单片机端的EEPROM存储空间有限查询、统计报表这些操作也不适合在单片机上做。你可以加一个USB转串口把考勤记录通过串口协议上传到PC写一个Python脚本或Qt界面读取记录、生成Excel出勤表。很多管理软件提示“数据连接错误”其实就是上位机和下位机之间的协议没对齐建议先设计一个简单的帧格式比如帧头 卡号 时间 校验两边都按这个格式解析。第二个方向是联网化。在STM32F103上挂一个ESP8266或ESP32模块通过串口把考勤记录推送到HTTP接口这样就形成了一个考勤云平台。配合微信小程序管理员能直接在手机上查看谁来了谁没来。这个方向在答辩和实际商用中都很加分但项目复杂度会高一个台阶适合在基础程序跑通之后再慢慢加。第三个方向是功耗优化。如果考勤机要装到没有插座的地方可以用电池供电程序里加入待机模式平时单片机睡眠刷卡时通过RC522的中断唤醒或者定时唤醒巡卡。STM32F103进入到待机模式电流可以降到微安级配备一块2000mAh锂电池能扛很长时间。程序本身的模块化也很重要。把RFID驱动、RTC驱动、OLED显示、EEPROM存储拆成独立模块每个模块只暴露几个接口函数。这样后续换芯片平台只需要重写底层驱动上层考勤逻辑几乎不用动。我目前维护的好几个项目都是这样组织的模块之间编译互不干扰测试也能单独验证效率比大而全的单一文件高很多。最后唠叨一点实际项目里的体会。RFID考勤系统难的点从来不是RFID读卡本身而是如何把“读卡这一个动作”安全、可靠、可追溯地变成一条考勤记录。RC522寻卡流程就那几行代码真正的工程量在时间处理、存储管理、异常恢复这些容易被表面忽视的地方。如果你做完这个项目能讲清楚EEPROM为什么需要磨损均衡、状态机为什么比顺序逻辑稳定、为什么要用双因子校验而不是只认UID那这个课设或毕设的技术深度已经完全合格了。做这个项目时我最大的一个教训是硬件电路还没验证稳定就开始写业务代码结果每次读卡出错都分不清是驱动问题还是逻辑问题。正确顺序应当是先点亮OLED、再让RC522能稳定读到卡号、最后才开始写考勤流程。前两步基础打牢了后面基本就是一路顺风。希望这篇内容能帮你把弯路走直。本文还有配套的精品资源点击获取
返回列表