超市门口那道防盗门,很多人第一反应是那排灰色磁扣。但当你真正走进零售防损的圈子,会发现基于单片机的商品RFID射频安全防盗报警系统设计,正在把RFID从仓储盘点带进防盗第一线。这篇文章我打算把整个系统的需求拆解、器件选型、核心判决逻辑、硬件接线、软件实现和实测踩坑完整捋一遍,给正在做单片机课设或毕设的同学一条能直接落地的路线,也想给想给小门店做低成本防盗方案的朋友一个参考。电源、天线、报警、白名单、防抖,这些词听起来散,但组合起来就是一套完整的“防盗闭环”。我没有把它当成纯粹的读卡器演示来写,而是尽量按照真实商用的角度去还原设计思路。毕竟,会读卡只是基本功,能在嘈杂环境里分清“已结账”和“没结账”才是系统真正值钱的地方。1. 防盗的本质不是“读卡”,而是“判决”1.1 传统EAS防盗门与RFID方案的真正差异传统商超里最常见的防盗手段其实不是RFID,而是EAS电子商品防盗系统。它靠的是声磁标签或射频标签,这种标签里没有芯片,只有一个LC谐振回路,收银员在结账时用消磁板把回路破坏掉,出口天线检测到没有消磁的标签就会报警。它的优点是便宜、反应快,缺点是只能告诉你“有人带了没消磁的东西出门”,却完全不知道带出去的是什么东西。RFID方案则完全不同。每一个标签里面都有一个全球唯一的UID,容量更大的标签还有可读写存储区。出口处的读写器一旦读到标签,不仅能判断这标签是不是已结账,还能读出来这具体是哪个商品、哪个批次、库存里还剩下多少。也就是说,RFID把“防盗”和“盘点”两件事合到了一起,这比单纯防盗更进一步。但RFID也不是没有代价。标签成本比EAS标签贵,天线安装更讲究,液体和金属对信号的干扰也更明显。所以真正落地的商超往往采用“EAS做防盗、RFID做盘点”的组合方案。不过作为单片机的课设题目,把RFID作为核心来做一套完整的防盗报警演示系统,逻辑和价值都很清楚。1.2 这个系统到底要解决什么问题标题叫“商品RFID射频安全防盗报警系统”,核心问题其实是三个。第一个是“检测”。出口区域要有读写器,能够持续扫描范围内有没有RFID标签出现。第二个是“识别”。读到了标签不能盲目报警,必须先判断这个标签对应的商品是不是已经在收银台结过账。第三个是“报警”。如果检测到未结账标签进入出口范围,要触发声光报警,提醒安保人员介入。这三个环节对应到单片机系统里,分别是RFID读卡模块、EEPROM或内部Flash存储的白名单、以及继电器或蜂鸣器组成的报警执行机构。整篇文章的硬件和软件设计,都是围绕这个三角形展开的。1.3 系统整体工作流程我把系统的业务链路理成了下面几条,这样后续写代码和接电路时思路会清晰很多。前台收银环节,收银员拿正式结账后的商品靠近读写器,读取标签UID,按一下“结账确认键”,这个UID就会被写入单片机的EEPROM白名单。出口检测环节,防盗门处的单片机持续循环读卡,把读到的UID和白名单逐条对比。如果命中白名单,系统静默放行,不发出任何声音;如果没命中,系统并不会立刻报警,而是连续读到同一张卡两次之后才触发声光报警,这样可以过滤掉很多瞬时干扰。报警后的复位由专用按键完成,同时支持3秒自动停蜂鸣、只保留指示灯的联动逻辑。这条链路看起来简单,但真正做过的人知道,难点全在“细节”。后面我会逐块拆开讲。2. 器件选型:每一分钱都要花在刀刃上2.1 主控单片机:51内核还是Cortex-M3最开始选主控的时候,我在51内核单片机和Cortex-M3内核单片机之间犹豫了很久。51的优势是人人都熟,教程多、参考资料多,做课设时答辩老师看一眼就懂。但劣势也很明显:SPI往往需要软件模拟,跑RFID读卡虽然没有压力,但一旦同时要驱动屏幕、读EEPROM、处理中断,代码写起来会比较别扭。Cortex-M3内核那类芯片,硬件SPI、硬件I2C、外部中断都是现成的,读卡模块接上去用硬件外设驱动,稳定性和开发效率都高不少。缺点是部分同学对寄存器配置不熟,学习曲线稍陡。我的建议是:如果这个系统只做“读卡、判名单、报警、显示”四件事,51内核完全够用;如果你想在这个基础上加触摸屏、串口联网、数据上报这类扩展功能,直接上Cortex-M3内核的主控。配套的读卡芯片我选了13.56MHz高频方案里最常见的一款RC522模块。它支持ISO14443A协议,可以读M1卡和UID白卡的4字节ID,模块自带了天线和匹配电路,买回来焊上线就能用,非常适合这类项目起步。2.2 RFID模块:高频与超高频的取舍RFID其实分低频、高频、超高频三个档位。低频125kHz常用于动物身份识别,读距近、速度慢。高频13.56MHz就是我们熟悉的校园卡、公交卡,典型读距3到10厘米,成本低,模块便宜,适合做桌面端子机或近距离检测区。超高频860到960MHz读距可以达到3到10米,适合做真正的“门型”防盗通道,但模块、天线、标签的单价都要高一个数量级。做毕设或课设,我推荐高频13.56MHz方案。因为RC522模块几十块钱就能拿下,标签也很便宜,调试时用串口能看到完整数据,难度可控。真正的商用防盗门为什么大多不用高频RFID?就是因为高频读距太短,人走过去只有零点几秒,读写器未必能及时读到贴在商品上的标签。超高频虽然读距远,但要在单片机系统里处理复杂的天线场型、反射和多标签碰撞,工作量会大得多。所以结论是:演示级系统选高频,工程级防盗通道选超高频。这篇博文后面的实现都是基于高频RC522展开的,但它代表的判决逻辑在超高频方案里同样适用。2.3 报警输出与电源设计的连带考虑报警输出我推荐有源蜂鸣器加LED,外加一个继电器常开触点用于点亮大功率报警灯或控制门禁电磁锁。有源蜂鸣器内部自带振荡电路,只要供电就会发声,单片机只需要给高低电平控制通断,编程非常省事。LED则用来做状态指示,绿光代表已结账放行,红光代表报警。电源是整个系统里最容易翻车的地方。RC522模块要严格的3.3V,51内核单片机往往工作在5V,两个器件共用一个电源时,必须注意电平匹配。市面上常见的USB供电HUB输出5V,直接接到RC522上非常容易烧模块,哪怕它板载了一个稳压芯片,发热和纹波也让人不放心。我个人的做法是:5V输入先经过一级低压差稳压到3.3V,专门供给RC522和电平转换电路,单片机单独一路5V。两路共地,然后再相接信号线。3. 核心状态机:如何区分“已结账”和“没结账”3.1 标签状态的两种实现路线我在做这个项目时,先想清楚了一个问题:系统凭什么知道一个标签是已结账还是未结账?市面上有两条路线。第一条是白名单路线,这也是毕设里最常被采用的。结账时把商品标签UID收录到本地数据库,出口检测到标签后查询数据库,能查到的放行,查不到就报警。好处是所有RFID标签都能用,逻辑直接。坏处是数据库和出口检测要时刻保持一致,一旦结账数据没写进去,就会误报。第二条是标签写状态路线。结账时用支持读写的M1卡,把某个扇区的一个字节写成“已结账”状态;出口读写器读标签时只检查这个状态位。好处是防盗门本身不需要联网查库,标签自己携带了“合法凭证”;坏处是只能用M1卡这类可写卡,成本高一点,而且写扇区要处理密钥。国内许多商店防盗场景,在EAS时代走的其实是“消磁”这个思路,也就是写状态路线的近亲。但从单片机课设的角度看,白名单路线更容易展示完整的“录入、存储、查询、报警”闭环,也能把EEPROM应用和状态机设计讲清楚。所以我最终选了白名单路线,并在调试时用M1卡做了二次验证。3.2 白名单数据库的设计细节说到白名单,很多人的第一反应就是“一个数组”。确实,在RAM里放一个uint32_t uid[200]数组,扫描查表,几十毫秒就能出结果。但问题是单片机一断电,数组就没了。我使用的方案是外挂一颗串行EEPROM存放白名单,掉电不丢失。100个、200个条目的存储量,常规EEPROM足够。存储布局我建议做成两部分:前半段固定偏移存UID列表,后半段存有效标志位。这样清空、重建数据库都不需要全片擦写,单条记录也能独立更新。数据库启动时一次性载入RAM,运行期的查询全部在内存里完成,这样可以避免频繁操作EEPROM,延长寿命。查询算法用最简单的线性扫描就够了。200条记录,每条比较4字节,在51内核主频12MHz的条件下,一次全表扫描也就在毫秒量级,出口检测完全感觉不到延迟。如果以后白名单涨到几千条,再考虑哈希索引或按扇区切片。3.3 防止误报的三个硬性条件如果你只写下“读到卡不在白名单就报警”这种代码,出门实测会疯掉。原因很简单:顾客兜里的门禁卡、工牌里的考勤卡、甚至隔壁工位桌上的一张公交卡,都可能被读成“非法标签”。我总结出的防误报三条件是下面这三条。第一,连续读取两次相同UID才判定报警。卡在人身上晃动时,天线不一定每次都能读到,但如果连续两次都读到同一张非法卡,基本可以确认它在检测区内停留了一小段正常通过时间。第二,报警后必须人工复位或自动延迟停止。蜂鸣器一直响既扰民又容易让人麻木,我设置为报警后如果3秒内没人按复位,蜂鸣器自动停止,LED保持红色闪烁,直到人工介入。第三,检测窗口要能覆盖正常通过时间。商品经过防盗门大概1到3秒,我让系统在打开读卡循环的这段时间里持续计数,卡片离开后计数器复位,这样既不会漏报,也不会因为瞬时噪声乱叫。这三条看起来简单,但每一项都对应真实场景里的具体风险。白名单过滤、去抖确认、超时复位,缺一个都会让“防盗系统”变成“扰民系统”。4. 硬件接线与关键参数调整4.1 RC522与单片机的SPI接线和电平问题RC522模块和单片机之间走的是SPI协议,标准引脚有SCK、MOSI、MISO、NSS(模块上常标为SDA),另外还有RST和IRQ。IRQ在轮询模式下可以不接,RST接一个普通IO用于复位模块。如果主控是3.3V的Cortex-M3内核芯片,接线毫无压力,直连就好。如果是5V的51内核单片机,就必须处理电平匹配问题。我的实际接法是:单片机到RC522方向的SCK、MOSI、RST三条线各串一个1k欧姆电阻后再连模块,MISO方向因为是从3.3V模块往5V单片机送信号,5V单片机对高电平的识别阈值通常在2V左右,3.3V信号可以直接被识别,但仍建议串一个1k电阻,做限流保护。下面这张表是我常用的连接示意,具体IO端口可以按自己的板子调整。RC522引脚51内核单片机(5V)Cortex-M3主控(3.3V)SDA / NSS串1k电阻后接P1.3直接接任意GPIOSCK串1k电阻后接P1.2硬件SPI SCK引脚MOSI串1k电阻后接P1.1硬件SPI MOSI引脚MISO串1k电阻后接P1.0硬件SPI MISO引脚RST串1k电阻后接P1.4直接接GPIOIRQ不接可接外部中断输入VCC3.3V3.3VGND共地共地需要特别强调:RC522的VCC接3.3V,严禁接5V。即使模块上丝印看起来和5V兼容,长期运行也会让射频部分发热,读卡距离明显下降,严重时直接烧掉。4.2 蜂鸣器驱动电路为什么不用IO直驱蜂鸣器看起来是个小东西,但直接挂在单片机IO上很容易出问题。IO的驱动能力通常只有20毫安左右,而常见有源蜂鸣器的工作电流在30到80毫安,如果还并联了LED,IO会被瞬间拉低,整个单片机复位重跑,屏幕闪一下,报警状态丢失。正确做法是加一个NPN三极管做开关。我选的是8050这类小功率管,基极通过1k电阻接单片机IO,发射极接地,集电极接蜂鸣器负极,蜂鸣器正极接5V电源。单片机IO输出高电平时三极管导通,蜂鸣器通电发声;输出低电平时断开。整个电路一共三个元件,比用继电器驱动便宜,也没有机械抖动问题。如果是线圈式蜂鸣器,我建议在蜂鸣器两端反向并联一个二极管做续流,防止关断瞬间的反电动势击穿三极管。压电式蜂鸣器则不那么需要这个二极管。4.3 天线读距不足时的调整手段RC522模块自带的PCB天线读距通常在3到5厘米,这离“防盗门”的需求还差得远。我在实验里验证过几种改善手段。第一种是给模块换大天线板。市面上有配套的方形大天线,通过IPEX线连到RC522的射频匹配网络,读距能到10厘米以上。第二种是微调天线匹配电容。RC522模块上通常有可调匹配元件,让天线谐振在13.56MHz,我用钢针轻轻拨动电容,配合示波器观察卡响应,一点点调到了读距最大状态。第三种也是最实用的:改变天线的安装角度。卡片的线圈天线和读写器天线平行时耦合最好,我把天线板竖直固定在模拟“门框”上,让顾客身上的标签平面和门框平行,读距和成功率明显改善。如果你想要真正大于1米的读距,高频方案基本到极限了,只能上超高频。但那已经是下一档的工程挑战,这里不展开。5. 单片机端软件实现:从轮询到稳定的过程5.1 主循环框架与RFID读卡流程软件方面,我先把主循环框架搭起来,再一步步填逻辑。核心结构是:初始化、加载白名单、无限循环读卡判断。#include "main.h" #include "rc522.h" #include "eeprom.h" #define WHITELIST_MAX 200 uint32_t whitelist[WHITELIST_MAX]; uint8_t whitelist_count = 0; void system_init(void) { uart_init(9600); spi_init(); rc522_init(); eeprom_init(); alarm_init(); display_init(); load_whitelist_from_eeprom(); } int main(void) { system_init(); while (1) { // 寻卡并防冲突 if (rc522_detect_card()) { uint32_t uid = rc522_get_uid(); uint8_t allow = is_in_whitelist(uid); if (allow) { display_purchased(uid); reset_same_count(); } else { if (judge_confirm_illegal(uid)) { trigger_alarm(); } } } else { reset_same_count(); } delay_ms(50); } }这段代码最大的特点是“轮询”。所有事情都在主循环里排队做,不用RTOS,也不用复杂的定时器链表,对51内核这类资源紧张的单片机非常友好。真正的实时性要求只有报警触发,几十毫秒的延迟完全可以接受。RFID读卡的关键在于防冲突。RC522在读到一张卡后,如果紧接着又出现第二张卡,直接读UID会得到乱码。标准做法是先执行寻卡请求,再执行防冲突指令,拿到真正有效的一张卡UID。这个细节如果你漏掉,系统在两个标签同时进入检测区时就会漏报或者误报。5.2 状态机落地:结账、进门、出门三种场景主循环虽然简单,但为了防止误报,我把运行状态拆成了几个清晰的状态:空闲、读卡确认、已结账放行、非法报警。对应到代码里是一个显式的状态机,比堆一堆if else要清晰得多。状态处理的要点是,非法标签不能只读一次就报警。我在内存里维护了last_uid和same_count两个变量。第一次读到非法UID时,先记录last_uid,把计数器置1,不报警;下一次主循环再读到同一张卡时,计数器增加到2,此时才触发报警。如果中途读到的是别的卡或者没读到卡,计数器清零重来。这个过程相当于给“非法滞留”加了一个两帧确认,把偶发噪声过滤掉。下面是我在状态机里使用的判定代码片段。uint32_t last_uid = 0; uint8_t same_count = 0; uint8_t judge_confirm_illegal(uint32_t uid) { if (uid == last_uid) { same_count++; } else { last_uid = uid; same_count = 1; } if (same_count >= 2) { last_uid = 0; same_count = 0; return 1; } return 0; }前台的“结账录入”我独立成一个按键分支:刷卡-读到UID-按键确认-写入EEPROM-串口回显。这一步严格来说并不发生在出口防盗门上,而是发生在收银台,所以我用同一个单片机通过按键切换“工作模式”和“录入模式”。如果以后做工程化,收银端和出口端应拆成两台设备,通过接口同步白名单。5.3 简单但有效的防抖与超时机制报警响起来之后,不能一直响下去。我设置了两种停止方式:一是人工按复位键,立即停止并清空报警状态;二是报警3秒后蜂鸣器自动停止,LED继续闪烁,直到人工确认。这样做既保留了报警的记忆效果,又避免了无人值守时长时间噪音污染。另一个要注意的是“同卡连续确认”和“不同卡切换”之间的时序。在门禁场景里,一个人正常走出来,标签被读到的次数往往不止两次,但都是同一张卡;如果检测区域内同时有两张非法卡,状态机会先锁定其中一张,连续读两次后报警,之后再切换去确认另一张。这个机制保证了在密集人流下也不会因为多标签快速切换而漏报。白名单容量方面,EEPROM存200条4字节UID,加上有效标志,可以在运行中动态删改。查询时我做了简单的“命中即返回”,所以哪怕容量接近上限,响应速度也足够快。6. 实测结果、常见坑与改进方向6.1 三组关键测试数据我在实验室搭了一套演示环境,用普通白卡模拟未结账商品,用M1卡模拟已结账商品,把读写器天线固定在模拟门框的侧面,测试了三组关键场景。测试场景实测结果说明已结账卡进入检测区无报警,指示灯显示放行白名单命中生效未结账卡手持通过连续读到2次后约0.8秒触发报警防抖确认机制可靠未结账卡快速甩过有时漏报,成功率约70%高频读距不足,需优化天线卡上放一瓶矿泉水再读成功率下降约40%液体吸收射频能量明显白名单200条后查询单次查询耗时约15ms实时性无压力总体来看,系统在近距离、静止或缓慢移动场景下表现稳定,在快速走动场景下高频方案的读距短板就会暴露出来。这个结果完全符合预期,因为13.56MHz本来就是近场通讯技术,不该去硬刚远距离防盗。6.2 开发过程中踩过的几个坑第一个坑是RC522接5V电源。我第一次搭板时图省事直接从USB取5V给模块供电,结果读卡距离只有1厘米,手摸模块发烫,后来量电压才发现模块上的稳压芯片已经过压工作。换成3.3V供电后,读距立刻恢复正常。第二个坑是SPI速率太高。51内核没有硬件SPI,我用GPIO模拟,一开始把时钟延时调到很短,结果寻卡成功率很低,经常读到一半中断。后来我把SPI时钟压到500kHz左右,读卡稳定性立刻上去。RC522的SPI接口理论支持更高频率,但在软件模拟环境下,稳定永远比速度重要。第三个坑是蜂鸣器直接接IO。第一次响铃时,我发现LCD屏幕明显闪了一下,个别按键失灵,查了半天才意识到是IO被拉垮。改成三极管驱动后一切正常。第四个坑是混合使用不同类型的卡片。我从抽屉里翻出好几张旧卡,有些是S50卡有些是UID白卡,RC522的防冲突算法在同时出现时表现不一致。后来测试时我固定用同一品牌同一批次的卡,问题就消失了。如果你也遇到“时好时坏”的读卡问题,先排除卡片类型混杂的因素。6.3 从演示系统到真实可用还差什么这套系统做成课设绰绰有余,但要真的放到门店出口,还需要补好几块。天线阵列和功率放大是第一步,单块高频天线覆盖不了整个门洞,需要多天线分组轮询,甚至配合人体感应触发。联网上报是第二步,出口的报警记录要实时上传到后端,才能统计出哪个时段的防损压力最大。双频融合是第三步,在RFID标签之外保留EAS声磁标签,射频负责数据、声磁负责可靠防盗,二者互补。如果后续想升级,我建议从“换超高频读卡模块”和“增加Wi-Fi数据上报”两个方向入手。前者解决读距问题,后者解决数据孤岛问题。单片机的白名单机制、防抖状态机、报警联动逻辑,在升级后依然可以复用,这是这套设计架构没有被白做的原因。最后说点个人体会。这个项目做下来,我最大的感受是防盗系统真正难的不是写代码,而是让它在嘈杂环境里既不漏报也不误报。调误报那几天,我一度以为是程序逻辑有问题,最后发现是工牌上的门禁卡也被识别成了“非法标签”。增加白名单过滤之后,整个世界清净了。如果你要在实验室复现这套系统,建议先把测试用的卡片分类好,再把“连续两次确认”的机制加上,误报率会低很多。项目本身不复杂,但把边界条件想清楚,系统才能真正从“能响”变成“能用”。
超市门口那道防盗门,很多人第一反应是那排灰色磁扣。但当你真正走进零售防损的圈子,会发现基于单片机的商品RFID射频安全防盗报警系统设计,正在把RFID从仓储盘点带进防盗第一线。这篇文章我打算把整个系统的需求拆解、器件选型、核心判决逻辑、硬件接线、软件实现和实测踩坑完整捋一遍,给正在做单片机课设或毕设的同学一条能直接落地的路线,也想给想给小门店做低成本防盗方案的朋友一个参考。电源、天线、报警、白名单、防抖,这些词听起来散,但组合起来就是一套完整的“防盗闭环”。我没有把它当成纯粹的读卡器演示来写,而是尽量按照真实商用的角度去还原设计思路。毕竟,会读卡只是基本功,能在嘈杂环境里分清“已结账”和“没结账”才是系统真正值钱的地方。1. 防盗的本质不是“读卡”,而是“判决”1.1 传统EAS防盗门与RFID方案的真正差异传统商超里最常见的防盗手段其实不是RFID,而是EAS电子商品防盗系统。它靠的是声磁标签或射频标签,这种标签里没有芯片,只有一个LC谐振回路,收银员在结账时用消磁板把回路破坏掉,出口天线检测到没有消磁的标签就会报警。它的优点是便宜、反应快,缺点是只能告诉你“有人带了没消磁的东西出门”,却完全不知道带出去的是什么东西。RFID方案则完全不同。每一个标签里面都有一个全球唯一的UID,容量更大的标签还有可读写存储区。出口处的读写器一旦读到标签,不仅能判断这标签是不是已结账,还能读出来这具体是哪个商品、哪个批次、库存里还剩下多少。也就是说,RFID把“防盗”和“盘点”两件事合到了一起,这比单纯防盗更进一步。但RFID也不是没有代价。标签成本比EAS标签贵,天线安装更讲究,液体和金属对信号的干扰也更明显。所以真正落地的商超往往采用“EAS做防盗、RFID做盘点”的组合方案。不过作为单片机的课设题目,把RFID作为核心来做一套完整的防盗报警演示系统,逻辑和价值都很清楚。1.2 这个系统到底要解决什么问题标题叫“商品RFID射频安全防盗报警系统”,核心问题其实是三个。第一个是“检测”。出口区域要有读写器,能够持续扫描范围内有没有RFID标签出现。第二个是“识别”。读到了标签不能盲目报警,必须先判断这个标签对应的商品是不是已经在收银台结过账。第三个是“报警”。如果检测到未结账标签进入出口范围,要触发声光报警,提醒安保人员介入。这三个环节对应到单片机系统里,分别是RFID读卡模块、EEPROM或内部Flash存储的白名单、以及继电器或蜂鸣器组成的报警执行机构。整篇文章的硬件和软件设计,都是围绕这个三角形展开的。1.3 系统整体工作流程我把系统的业务链路理成了下面几条,这样后续写代码和接电路时思路会清晰很多。前台收银环节,收银员拿正式结账后的商品靠近读写器,读取标签UID,按一下“结账确认键”,这个UID就会被写入单片机的EEPROM白名单。出口检测环节,防盗门处的单片机持续循环读卡,把读到的UID和白名单逐条对比。如果命中白名单,系统静默放行,不发出任何声音;如果没命中,系统并不会立刻报警,而是连续读到同一张卡两次之后才触发声光报警,这样可以过滤掉很多瞬时干扰。报警后的复位由专用按键完成,同时支持3秒自动停蜂鸣、只保留指示灯的联动逻辑。这条链路看起来简单,但真正做过的人知道,难点全在“细节”。后面我会逐块拆开讲。2. 器件选型:每一分钱都要花在刀刃上2.1 主控单片机:51内核还是Cortex-M3最开始选主控的时候,我在51内核单片机和Cortex-M3内核单片机之间犹豫了很久。51的优势是人人都熟,教程多、参考资料多,做课设时答辩老师看一眼就懂。但劣势也很明显:SPI往往需要软件模拟,跑RFID读卡虽然没有压力,但一旦同时要驱动屏幕、读EEPROM、处理中断,代码写起来会比较别扭。Cortex-M3内核那类芯片,硬件SPI、硬件I2C、外部中断都是现成的,读卡模块接上去用硬件外设驱动,稳定性和开发效率都高不少。缺点是部分同学对寄存器配置不熟,学习曲线稍陡。我的建议是:如果这个系统只做“读卡、判名单、报警、显示”四件事,51内核完全够用;如果你想在这个基础上加触摸屏、串口联网、数据上报这类扩展功能,直接上Cortex-M3内核的主控。配套的读卡芯片我选了13.56MHz高频方案里最常见的一款RC522模块。它支持ISO14443A协议,可以读M1卡和UID白卡的4字节ID,模块自带了天线和匹配电路,买回来焊上线就能用,非常适合这类项目起步。2.2 RFID模块:高频与超高频的取舍RFID其实分低频、高频、超高频三个档位。低频125kHz常用于动物身份识别,读距近、速度慢。高频13.56MHz就是我们熟悉的校园卡、公交卡,典型读距3到10厘米,成本低,模块便宜,适合做桌面端子机或近距离检测区。超高频860到960MHz读距可以达到3到10米,适合做真正的“门型”防盗通道,但模块、天线、标签的单价都要高一个数量级。做毕设或课设,我推荐高频13.56MHz方案。因为RC522模块几十块钱就能拿下,标签也很便宜,调试时用串口能看到完整数据,难度可控。真正的商用防盗门为什么大多不用高频RFID?就是因为高频读距太短,人走过去只有零点几秒,读写器未必能及时读到贴在商品上的标签。超高频虽然读距远,但要在单片机系统里处理复杂的天线场型、反射和多标签碰撞,工作量会大得多。所以结论是:演示级系统选高频,工程级防盗通道选超高频。这篇博文后面的实现都是基于高频RC522展开的,但它代表的判决逻辑在超高频方案里同样适用。2.3 报警输出与电源设计的连带考虑报警输出我推荐有源蜂鸣器加LED,外加一个继电器常开触点用于点亮大功率报警灯或控制门禁电磁锁。有源蜂鸣器内部自带振荡电路,只要供电就会发声,单片机只需要给高低电平控制通断,编程非常省事。LED则用来做状态指示,绿光代表已结账放行,红光代表报警。电源是整个系统里最容易翻车的地方。RC522模块要严格的3.3V,51内核单片机往往工作在5V,两个器件共用一个电源时,必须注意电平匹配。市面上常见的USB供电HUB输出5V,直接接到RC522上非常容易烧模块,哪怕它板载了一个稳压芯片,发热和纹波也让人不放心。我个人的做法是:5V输入先经过一级低压差稳压到3.3V,专门供给RC522和电平转换电路,单片机单独一路5V。两路共地,然后再相接信号线。3. 核心状态机:如何区分“已结账”和“没结账”3.1 标签状态的两种实现路线我在做这个项目时,先想清楚了一个问题:系统凭什么知道一个标签是已结账还是未结账?市面上有两条路线。第一条是白名单路线,这也是毕设里最常被采用的。结账时把商品标签UID收录到本地数据库,出口检测到标签后查询数据库,能查到的放行,查不到就报警。好处是所有RFID标签都能用,逻辑直接。坏处是数据库和出口检测要时刻保持一致,一旦结账数据没写进去,就会误报。第二条是标签写状态路线。结账时用支持读写的M1卡,把某个扇区的一个字节写成“已结账”状态;出口读写器读标签时只检查这个状态位。好处是防盗门本身不需要联网查库,标签自己携带了“合法凭证”;坏处是只能用M1卡这类可写卡,成本高一点,而且写扇区要处理密钥。国内许多商店防盗场景,在EAS时代走的其实是“消磁”这个思路,也就是写状态路线的近亲。但从单片机课设的角度看,白名单路线更容易展示完整的“录入、存储、查询、报警”闭环,也能把EEPROM应用和状态机设计讲清楚。所以我最终选了白名单路线,并在调试时用M1卡做了二次验证。3.2 白名单数据库的设计细节说到白名单,很多人的第一反应就是“一个数组”。确实,在RAM里放一个uint32_t uid[200]数组,扫描查表,几十毫秒就能出结果。但问题是单片机一断电,数组就没了。我使用的方案是外挂一颗串行EEPROM存放白名单,掉电不丢失。100个、200个条目的存储量,常规EEPROM足够。存储布局我建议做成两部分:前半段固定偏移存UID列表,后半段存有效标志位。这样清空、重建数据库都不需要全片擦写,单条记录也能独立更新。数据库启动时一次性载入RAM,运行期的查询全部在内存里完成,这样可以避免频繁操作EEPROM,延长寿命。查询算法用最简单的线性扫描就够了。200条记录,每条比较4字节,在51内核主频12MHz的条件下,一次全表扫描也就在毫秒量级,出口检测完全感觉不到延迟。如果以后白名单涨到几千条,再考虑哈希索引或按扇区切片。3.3 防止误报的三个硬性条件如果你只写下“读到卡不在白名单就报警”这种代码,出门实测会疯掉。原因很简单:顾客兜里的门禁卡、工牌里的考勤卡、甚至隔壁工位桌上的一张公交卡,都可能被读成“非法标签”。我总结出的防误报三条件是下面这三条。第一,连续读取两次相同UID才判定报警。卡在人身上晃动时,天线不一定每次都能读到,但如果连续两次都读到同一张非法卡,基本可以确认它在检测区内停留了一小段正常通过时间。第二,报警后必须人工复位或自动延迟停止。蜂鸣器一直响既扰民又容易让人麻木,我设置为报警后如果3秒内没人按复位,蜂鸣器自动停止,LED保持红色闪烁,直到人工介入。第三,检测窗口要能覆盖正常通过时间。商品经过防盗门大概1到3秒,我让系统在打开读卡循环的这段时间里持续计数,卡片离开后计数器复位,这样既不会漏报,也不会因为瞬时噪声乱叫。这三条看起来简单,但每一项都对应真实场景里的具体风险。白名单过滤、去抖确认、超时复位,缺一个都会让“防盗系统”变成“扰民系统”。4. 硬件接线与关键参数调整4.1 RC522与单片机的SPI接线和电平问题RC522模块和单片机之间走的是SPI协议,标准引脚有SCK、MOSI、MISO、NSS(模块上常标为SDA),另外还有RST和IRQ。IRQ在轮询模式下可以不接,RST接一个普通IO用于复位模块。如果主控是3.3V的Cortex-M3内核芯片,接线毫无压力,直连就好。如果是5V的51内核单片机,就必须处理电平匹配问题。我的实际接法是:单片机到RC522方向的SCK、MOSI、RST三条线各串一个1k欧姆电阻后再连模块,MISO方向因为是从3.3V模块往5V单片机送信号,5V单片机对高电平的识别阈值通常在2V左右,3.3V信号可以直接被识别,但仍建议串一个1k电阻,做限流保护。下面这张表是我常用的连接示意,具体IO端口可以按自己的板子调整。RC522引脚51内核单片机(5V)Cortex-M3主控(3.3V)SDA / NSS串1k电阻后接P1.3直接接任意GPIOSCK串1k电阻后接P1.2硬件SPI SCK引脚MOSI串1k电阻后接P1.1硬件SPI MOSI引脚MISO串1k电阻后接P1.0硬件SPI MISO引脚RST串1k电阻后接P1.4直接接GPIOIRQ不接可接外部中断输入VCC3.3V3.3VGND共地共地需要特别强调:RC522的VCC接3.3V,严禁接5V。即使模块上丝印看起来和5V兼容,长期运行也会让射频部分发热,读卡距离明显下降,严重时直接烧掉。4.2 蜂鸣器驱动电路为什么不用IO直驱蜂鸣器看起来是个小东西,但直接挂在单片机IO上很容易出问题。IO的驱动能力通常只有20毫安左右,而常见有源蜂鸣器的工作电流在30到80毫安,如果还并联了LED,IO会被瞬间拉低,整个单片机复位重跑,屏幕闪一下,报警状态丢失。正确做法是加一个NPN三极管做开关。我选的是8050这类小功率管,基极通过1k电阻接单片机IO,发射极接地,集电极接蜂鸣器负极,蜂鸣器正极接5V电源。单片机IO输出高电平时三极管导通,蜂鸣器通电发声;输出低电平时断开。整个电路一共三个元件,比用继电器驱动便宜,也没有机械抖动问题。如果是线圈式蜂鸣器,我建议在蜂鸣器两端反向并联一个二极管做续流,防止关断瞬间的反电动势击穿三极管。压电式蜂鸣器则不那么需要这个二极管。4.3 天线读距不足时的调整手段RC522模块自带的PCB天线读距通常在3到5厘米,这离“防盗门”的需求还差得远。我在实验里验证过几种改善手段。第一种是给模块换大天线板。市面上有配套的方形大天线,通过IPEX线连到RC522的射频匹配网络,读距能到10厘米以上。第二种是微调天线匹配电容。RC522模块上通常有可调匹配元件,让天线谐振在13.56MHz,我用钢针轻轻拨动电容,配合示波器观察卡响应,一点点调到了读距最大状态。第三种也是最实用的:改变天线的安装角度。卡片的线圈天线和读写器天线平行时耦合最好,我把天线板竖直固定在模拟“门框”上,让顾客身上的标签平面和门框平行,读距和成功率明显改善。如果你想要真正大于1米的读距,高频方案基本到极限了,只能上超高频。但那已经是下一档的工程挑战,这里不展开。5. 单片机端软件实现:从轮询到稳定的过程5.1 主循环框架与RFID读卡流程软件方面,我先把主循环框架搭起来,再一步步填逻辑。核心结构是:初始化、加载白名单、无限循环读卡判断。#include "main.h" #include "rc522.h" #include "eeprom.h" #define WHITELIST_MAX 200 uint32_t whitelist[WHITELIST_MAX]; uint8_t whitelist_count = 0; void system_init(void) { uart_init(9600); spi_init(); rc522_init(); eeprom_init(); alarm_init(); display_init(); load_whitelist_from_eeprom(); } int main(void) { system_init(); while (1) { // 寻卡并防冲突 if (rc522_detect_card()) { uint32_t uid = rc522_get_uid(); uint8_t allow = is_in_whitelist(uid); if (allow) { display_purchased(uid); reset_same_count(); } else { if (judge_confirm_illegal(uid)) { trigger_alarm(); } } } else { reset_same_count(); } delay_ms(50); } }这段代码最大的特点是“轮询”。所有事情都在主循环里排队做,不用RTOS,也不用复杂的定时器链表,对51内核这类资源紧张的单片机非常友好。真正的实时性要求只有报警触发,几十毫秒的延迟完全可以接受。RFID读卡的关键在于防冲突。RC522在读到一张卡后,如果紧接着又出现第二张卡,直接读UID会得到乱码。标准做法是先执行寻卡请求,再执行防冲突指令,拿到真正有效的一张卡UID。这个细节如果你漏掉,系统在两个标签同时进入检测区时就会漏报或者误报。5.2 状态机落地:结账、进门、出门三种场景主循环虽然简单,但为了防止误报,我把运行状态拆成了几个清晰的状态:空闲、读卡确认、已结账放行、非法报警。对应到代码里是一个显式的状态机,比堆一堆if else要清晰得多。状态处理的要点是,非法标签不能只读一次就报警。我在内存里维护了last_uid和same_count两个变量。第一次读到非法UID时,先记录last_uid,把计数器置1,不报警;下一次主循环再读到同一张卡时,计数器增加到2,此时才触发报警。如果中途读到的是别的卡或者没读到卡,计数器清零重来。这个过程相当于给“非法滞留”加了一个两帧确认,把偶发噪声过滤掉。下面是我在状态机里使用的判定代码片段。uint32_t last_uid = 0; uint8_t same_count = 0; uint8_t judge_confirm_illegal(uint32_t uid) { if (uid == last_uid) { same_count++; } else { last_uid = uid; same_count = 1; } if (same_count >= 2) { last_uid = 0; same_count = 0; return 1; } return 0; }前台的“结账录入”我独立成一个按键分支:刷卡-读到UID-按键确认-写入EEPROM-串口回显。这一步严格来说并不发生在出口防盗门上,而是发生在收银台,所以我用同一个单片机通过按键切换“工作模式”和“录入模式”。如果以后做工程化,收银端和出口端应拆成两台设备,通过接口同步白名单。5.3 简单但有效的防抖与超时机制报警响起来之后,不能一直响下去。我设置了两种停止方式:一是人工按复位键,立即停止并清空报警状态;二是报警3秒后蜂鸣器自动停止,LED继续闪烁,直到人工确认。这样做既保留了报警的记忆效果,又避免了无人值守时长时间噪音污染。另一个要注意的是“同卡连续确认”和“不同卡切换”之间的时序。在门禁场景里,一个人正常走出来,标签被读到的次数往往不止两次,但都是同一张卡;如果检测区域内同时有两张非法卡,状态机会先锁定其中一张,连续读两次后报警,之后再切换去确认另一张。这个机制保证了在密集人流下也不会因为多标签快速切换而漏报。白名单容量方面,EEPROM存200条4字节UID,加上有效标志,可以在运行中动态删改。查询时我做了简单的“命中即返回”,所以哪怕容量接近上限,响应速度也足够快。6. 实测结果、常见坑与改进方向6.1 三组关键测试数据我在实验室搭了一套演示环境,用普通白卡模拟未结账商品,用M1卡模拟已结账商品,把读写器天线固定在模拟门框的侧面,测试了三组关键场景。测试场景实测结果说明已结账卡进入检测区无报警,指示灯显示放行白名单命中生效未结账卡手持通过连续读到2次后约0.8秒触发报警防抖确认机制可靠未结账卡快速甩过有时漏报,成功率约70%高频读距不足,需优化天线卡上放一瓶矿泉水再读成功率下降约40%液体吸收射频能量明显白名单200条后查询单次查询耗时约15ms实时性无压力总体来看,系统在近距离、静止或缓慢移动场景下表现稳定,在快速走动场景下高频方案的读距短板就会暴露出来。这个结果完全符合预期,因为13.56MHz本来就是近场通讯技术,不该去硬刚远距离防盗。6.2 开发过程中踩过的几个坑第一个坑是RC522接5V电源。我第一次搭板时图省事直接从USB取5V给模块供电,结果读卡距离只有1厘米,手摸模块发烫,后来量电压才发现模块上的稳压芯片已经过压工作。换成3.3V供电后,读距立刻恢复正常。第二个坑是SPI速率太高。51内核没有硬件SPI,我用GPIO模拟,一开始把时钟延时调到很短,结果寻卡成功率很低,经常读到一半中断。后来我把SPI时钟压到500kHz左右,读卡稳定性立刻上去。RC522的SPI接口理论支持更高频率,但在软件模拟环境下,稳定永远比速度重要。第三个坑是蜂鸣器直接接IO。第一次响铃时,我发现LCD屏幕明显闪了一下,个别按键失灵,查了半天才意识到是IO被拉垮。改成三极管驱动后一切正常。第四个坑是混合使用不同类型的卡片。我从抽屉里翻出好几张旧卡,有些是S50卡有些是UID白卡,RC522的防冲突算法在同时出现时表现不一致。后来测试时我固定用同一品牌同一批次的卡,问题就消失了。如果你也遇到“时好时坏”的读卡问题,先排除卡片类型混杂的因素。6.3 从演示系统到真实可用还差什么这套系统做成课设绰绰有余,但要真的放到门店出口,还需要补好几块。天线阵列和功率放大是第一步,单块高频天线覆盖不了整个门洞,需要多天线分组轮询,甚至配合人体感应触发。联网上报是第二步,出口的报警记录要实时上传到后端,才能统计出哪个时段的防损压力最大。双频融合是第三步,在RFID标签之外保留EAS声磁标签,射频负责数据、声磁负责可靠防盗,二者互补。如果后续想升级,我建议从“换超高频读卡模块”和“增加Wi-Fi数据上报”两个方向入手。前者解决读距问题,后者解决数据孤岛问题。单片机的白名单机制、防抖状态机、报警联动逻辑,在升级后依然可以复用,这是这套设计架构没有被白做的原因。最后说点个人体会。这个项目做下来,我最大的感受是防盗系统真正难的不是写代码,而是让它在嘈杂环境里既不漏报也不误报。调误报那几天,我一度以为是程序逻辑有问题,最后发现是工牌上的门禁卡也被识别成了“非法标签”。增加白名单过滤之后,整个世界清净了。如果你要在实验室复现这套系统,建议先把测试用的卡片分类好,再把“连续两次确认”的机制加上,误报率会低很多。项目本身不复杂,但把边界条件想清楚,系统才能真正从“能响”变成“能用”。