STM32单片机做智能车位锁桩,这个项目在嵌入式开发圈子里一直热度不低。一方面是真的能落地解决私家车位被占的痛点,另一方面硬件成本可控、技术栈覆盖全面,从电路设计到嵌入式编程再到无线通信全都能练到。这套基于RFID刷卡方案的智能车位锁桩,主控用STM32F103系列,读卡用RC522模块,配合直流电机或推杆电机实现锁臂升降,整套设备链路清晰,非常适合作为入门到进阶的实战项目。本文就从方案选型、原理图设计、程序状态机到调试踩坑,把整个项目拆开讲透。
1. 项目选型与整体方案设计思路
1.1 为什么是RFID刷卡方案
做车位锁桩,市面上成熟的方案无非这么几种:红外遥控、蓝牙APP、RFID刷卡、地感联动、以及带摄像头的车牌识别。车牌识别成本高、依赖网络和后台,明显不适合个人DIY或小范围停车位管理;蓝牙APP听起来很未来,但实际使用中会有连接慢、需要掏手机、地下车库信号不稳定等一连串问题;红外遥控虽然便宜,但编码容易串扰,指向性也强,每次都要对准锁桩操作,体验很一般。
RFID刷卡的优势恰恰是它把“识别”和“操作”完全解耦了。车主不需要对准任何东西,也不需要打开任何APP,卡片在锁桩感应区上方轻轻一晃,读卡模块把卡号上报给STM32主控,主控比对白名单后直接驱动电机升降。整个交互过程不到一秒,动作自然,老人小孩都能直接用。而且RFID卡本身是无源的,卡片成本基本可以忽略,发卡、挂失、换卡也就是在程序里改几张卡号的事。
从项目学习的角度来看,RFID方案还特意引入了SPI总线通信、卡号防碰撞处理、白名单存储这些典型嵌入式知识点,非常适合工程实践入门。
1.2 主控选型:为什么锁定STM32F103
主控我选了STM32F103C8T6。这块芯片在嵌入式领域可以说是绝对的“劳动模范”,72MHz的主频、64KB Flash、20KB SRAM,外设接口有SPI、I2C、USART、定时器、ADC,对付RC522读取和电机控制完全是杀鸡用牛刀。选它更重要的原因是生态太成熟了,标准外设库和HAL库都有大量现成例程,RC522的驱动代码网上遍地都是,遇到问题搜一下基本都有答案,这对学习调试非常有帮助。
如果换成STM32F407甚至H7系列,性能是强很多,但成本和功耗都上去了,而且大部分资源根本用不到。做产品选型讲究“够用就好”,留一点余量即可。我用F103C8T6,Flash占用大概在30%到40%左右,即便后面想加LCD显示、加语音播报、加低功耗停机模式,剩余资源也完全游刃有余。
1.3 系统框图与工作流程
整个系统的核心链路是“卡片识别—主控决策—电机执行—状态反馈”。
卡片接近时,RC522模块持续发射射频场并检测是否有卡进入感应区,读到卡后通过SPI总线把卡号传给STM32。主控将卡号与预先存储在Flash或EEPROM中的白名单进行比对,比对通过则根据当前锁桩状态判断是升锁还是降锁,驱动电机执行对应动作。执行过程中,限位开关负责检测锁臂是否到位,到位后立即停止电机,避免持续堵转烧驱动。蜂鸣器和LED指示灯同步反馈当前状态,让车主清楚知道系统是否响应成功。
这套流程看起来简单,真正写代码时把各种边界情况处理完,状态机起码要设计6到8个状态,后面会详细展开。
2. 硬件原理图拆解与关键电路设计
2.1 电源电路设计
电源是整个系统最容易出问题的环节。我之前做第一版的时候,直接用了一颗7805线性稳压给单片机和RC522供电,结果负载一上来压降明显,读卡距离缩到只有一厘米,后来改成开关电源才解决。
推荐采用两路输出方案:外部输入12V DC电源,经过降压后分成两路。一路12V直接给推杆电机或直流电机供电;另一路经过MP1584或AMS1117降压至5V,再给单片机、RC522、蜂鸣器等逻辑电路供电。STM32的IO和RC522都是3.3V逻辑,所以5V稳压之后还需要一级LDO降到3.3V,或者直接选带3.3V输出的DC-DC模块。
这里有一个常见的坑:RC522的模拟部分对电源纹波很敏感。如果供电纹波过大,读卡模块的天线场强会抖动,导致读卡距离明显变短甚至完全读不到卡。所以5V输出端要并联一个470uF电解电容和两个0.1uF、10uF的陶瓷电容,分别滤除低频纹波和高频噪声。实在讲究的话,RC522的供电可以单独加一个LC滤波,效果会更好。
供电接口要在PCB上做防反接保护,最简单的方式是串联一个SS34肖特基二极管,或者使用一颗PMOS做理想二极管防反接电路。这成本就几毛钱,但能救回一整套板子。
2.2 RFID读卡模块电路
RC522模块目前市面上有大量成熟的小板,直接买了通过排针插在主板上就行。引脚定义基本固定,需要接SDA(NSS)、SCK、MOSI、MISO、RST、3.3V、GND这七根线。
RC522的数据手册上明确写明它要求的供电电压是3.3V,绝对不能直接接5V。在实际操作中也要留意,STM32的SPI引脚是3.3V电平输出,所以单片机到RC522之间不需要额外电平转换。如果用的是5V单片机,就必须加电平转换或者使用带电平双向转换的模块,不然长期运行烧模块只是一个时间问题。
天线部分,如果使用集成的RC522小板,天线已经做在PCB上,感应距离一般在3到6厘米。如果想增加感应距离或者做大面积天线,可以在RC522的TX1、TX2引脚外接匹配电路和自制线圈天线。这个属于射频设计的范畴,需要调整匹配电容使得天线谐振频率在13.56MHz。没有频谱仪的情况下,可以通过观察读卡距离和读卡成功率来反复微调匹配电容,实操中一般从几十皮法的电容开始试。
我在实际项目中测试过,RC522模块的正常读卡距离衰减较快,卡片放到感应区中央成功率最高,边缘位置容易出现卡号读取错误或超时。所以外壳设计时,最好在刷卡区域做一个浅凹槽,引导车主把卡片放到正确位置。
2.3 电机驱动电路
车位锁桩的执行机构有两种常见选择:推杆电机和直流减速电机。推杆电机自带限位,动作慢但力矩大,噪音小;直流减速电机速度快,但必须由外部限位开关来界定行程终点。
驱动方案上,我给STM32配了一颗DRV8833双路电机驱动芯片,或者也可以用L298N模块。DRV8833的好处是导通电阻低、体积小、逻辑输入可以直接兼容3.3V单片机,价格也便宜。不过要注意它的最大持续电流标称是1.5A每通道,如果电机启动瞬间电流超过这个值,需要考虑用L298N或者把两路并联使用。
电路设计上,电机驱动芯片的INA、INB引脚接STM32的GPIO,通过PWM可以控制电机速度。对于车位锁这种开关型应用,速度其实不需要调节,直接给满PWM或者纯电平驱动即可,但保留PWM能力后可以做成启动时软启动、到位前减速停稳的效果,对机械冲击更友好。
电机两端必须反并联一个续流二极管或者TVS管,用来吸收电机换向时产生的反向电动势。这个细节如果省略,瞬间产生的尖峰电压很可能击穿驱动芯片。我第一版PCB就是因为省了这几个二极管,测试时烧了两颗DRV8833才意识到问题的严重性。
2.4 限位与车辆检测电路
限位开关我使用的是微型滚轮式微动开关,分别安装在锁臂升起到位和降落到位的机械位置。开关一端接3.3V,一端通过下拉电阻接STM32的GPIO,检测到开关闭合时引脚读到高电平。接线需要注意的是,微动开关接线处最好滴上热熔胶或者打上硅胶,防止水汽氧化导致接触不良。
车辆检测逻辑上,可以选装一个超声波模块(HC-SR04)或者地磁传感器来判断车位上是否有车。这个信息非常重要,因为如果车辆已经停入车位,锁桩却执行了升锁动作,锁臂会直接顶到汽车底盘,这是严重的功能事故。
超声波模块安装在车位后方地面,朝上发射,检测固定距离内是否有物体遮挡。不过超声波对地面反射、雨水、落叶这些因素比较敏感,如果安装在户外,建议加一个防水壳,并且程序里要做多重连续采样滤波。
2.5 声光提示电路
声光提示虽然简单,但对用户体验提升非常明显。蜂鸣器我用了有源蜂鸣器,检测到有效卡片且操作成功时长鸣一声,卡片未授权时短促鸣响两声,电机运行中再给出一种持续声音。LED指示灯用红绿双色,待机时绿色慢闪,识别成功时绿色快闪,错误时红色闪烁。
光靠声音和LED并不完全够用,成本允许的状态下还可以加一块OLED显示屏,显示当前剩余车位数量、最近一次刷卡时间、锁桩状态等信息。OLED用I2C接口,只占两个IO口,代码驱动也不复杂。
3. 程序设计核心逻辑与实现解析
3.1 软件整体架构
程序架构上,我没有用裸机前后台大循环里怼全部逻辑,而是把整个系统抽象成了一个有限状态机(FSM)。把车位锁桩的运行过程拆分成设备初始化、待机监测、卡验证、升降执行、异常处理、低功耗这几个状态,每个状态下只处理当前需要关注的输入事件,状态切换的条件非常明确。
代码结构上,建议按照模块来拆分文件。我自己比较习惯的是把硬件驱动和业务逻辑分开,工程内至少要有main.c、rfid.c、motor.c、timer.c、lowpower.c和config.h这几个文件。如果有一个文件超过三百行,基本就该琢磨按功能拆分了。
3.2 主循环与状态机设计
主循环的伪代码逻辑如下:
while (1) { switch (current_state) { case STATE_IDLE: if (rc522_check_card(&card_id)) { if (card_id在白名单中) { current_state = STATE_VERIFY_OK; } else { error_beep(2); current_state = STATE_IDLE; } } break; case STATE_VERIFY_OK: if (lock_is_up()) { current_state = STATE_LOCK_DOWN; } else { current_state = STATE_LOCK_UP; } break; case STATE_LOCK_UP: motor_forward(); if (limit_up_triggered()) { motor_stop(); current_state = STATE_IDLE; } break; case STATE_LOCK_DOWN: motor_backward(); if (limit_down_triggered()) { motor_stop(); current_state = STATE_IDLE; } break; default: current_state = STATE_IDLE; break; } }这里有一个容易忽略的细节,若验证成功后锁桩在运动途中,RC522模块还在持续读卡,车主又拿卡晃了一下,系统就会重复触发。所以状态机里要加一个“正在执行”的标志,只有回到待机状态后才重新允许刷卡操作。我最初实现时没加这个,结果调试时看到的现象是电机升到一半又降下去,折腾了好一阵才排查出来。
3.3 白名单存储与卡片管理
白名单存储的位置我选了STM32内置Flash的最后几页。STM32F103C8T6有64KB Flash,程序一般在20KB左右,剩余空间足够存几百组卡号。直接操作Flash需要注意的事情是,写之前必须先擦除整个页,而且擦除次数有寿命上限(约一万次),所以不能像变量一样随便写。
实际操作中我建议这样设计:把白名单放在一个结构体数组里,包含卡号、备注、启用状态。程序启动时将白名单从Flash读到RAM,后续比对都基于RAM副本。修改白名单时需要进入配置模式,把新卡号写入临时缓冲,再由外部指令(拨码开关或蓝牙串口)触发一次Flash擦写操作。这样既不频繁擦Flash,又能保证修改功能可用。
卡号对比的逻辑要留意大小端问题。RC522读出的卡号是4字节,不同厂家的卡高低字节顺序可能不一致。如果直接按字节硬比,有些卡永远匹配不上。解决方法是允许在配置卡片时选择“读卡后翻转字节再存储”,或者在比对时做一次大小端转换,两种方式都行,重点是逻辑一致。
3.4 电机控制与防夹保护
电机控制时,需要根据限位开关触发超时判断来保护系统。正常情况下升锁过程约2到3秒,降锁过程约3到4秒。如果程序进入电机运行状态后超过了设定的最大运行时间(比如6秒)还没触发限位,就可以判断为堵转或机械卡死,此时必须立即切断电机电源,然后鸣响蜂鸣器报警。
防夹保护方面,除了时间超时判断,还可以在电机驱动电流检测电路上做电流阈值判断。当检测到电流瞬间增大到正常运行电流的1.5倍以上,说明锁臂碰到了障碍物,立即停止电机并反向退让一小段距离,避免夹伤宠物或小孩。我自己的做法是使用了一个采样电阻加上运放放大,接到STM32的ADC输入口,程序中每20毫秒采样一次电流值。
注意,如果使用推杆电机,它内部自带限位开关,断电后锁臂会保持在当前位置,不需要刹车;但直流减速电机在断电后由于减速箱的自锁特性一般也不会滑落,如果用的是不带减速箱的普通直流电机,建议加上电磁刹车或者蜗轮蜗杆机构。
3.5 低功耗待机设计
户外车位锁桩很多是太阳能电池加锂电池的供电方案,低功耗要求非常严格。STM32在待机模式下的功耗可以做到微安级别,但前提是要把所有外设的时钟都关闭,并且把不需要的外设模块全部断电。
RC522模块是整个系统中耗电最大的外设之一,它的射频场始终在发射,待机功耗就有几十毫安。为了降低功耗,可以让一颗MOS管作为RC522的电源开关,在需要刷卡时才给RC522供电,读卡成功后立刻断电。由于模块上电到稳定需要几十毫秒,程序逻辑上要先开电源、延时100ms、再初始化RC522、然后进入读卡流程。我在实际测试中这样做的待机功耗大概只有0.3mA左右,一节2000mAh的锂电池撑半年以上没有任何压力。
进入待机模式前,GPIO的电平方向也要安排好。特别是RC522的电源开关管、电机的控制引脚,这些引脚如果没有正确配置成下拉输出低电平,MCU进入待机后引脚会悬空,不仅浪费电,还可能让LED微亮或者让蜂鸣器发出细微嘶嘶声。
3.6 定时器与延时策略
由于RC522读卡需要SPI通信时序,电机运行需要PWM控制,限位开关消抖需要定时扫描,我的做法是使用TIM4做1ms的基准时基,维护一个全局的时间戳计数器。主循环里通过比对时间戳的差值来判断超时事件,而不是用阻塞式的delay函数。
完全阻塞式的延时在状态机里是大忌,因为延时期间如果恰好有卡片刷卡,或者电机已经到位但程序还在延时里,就会错过限位信号,导致锁臂过头损坏机械结构。
4. 项目制作中的调试实录与排查技巧
4.1 读卡距离短且不稳定
我遇到过的最诡异的问题,是读卡距离时好时坏。排查了一圈,最后发现是天线附近的地平面完整性出了问题。RC522模块的感应天线正下方PCB上我走了一根12V电源线,电源线上的电流噪声耦合进天线,导致射频场被干扰。解决办法是把天线正下方的地平面铺满,关键走线绕开天线区域,读卡距离从1.5厘米恢复到了4厘米左右。
另外,RC522模块和金属外壳之间必须保持至少1厘米以上的净空。如果外壳是金属材质,需要在天线背面加贴一片铁氧体吸波材料来隔离金属对射频场的涡流损耗。这个材料淘宝上几块钱一片,效果立竿见影。
4.2 限位开关误触发导致电机反复启停
限位开关在机械接触瞬间会产生严重的抖动,如果不做软件消抖,主控可能会认为限位已经触发,立即切停电机,然后抖动结束又恢复,造成同一行程内多次启停。
消抖的做法很简单,检测到电平变化后连续采样10次,每次间隔5毫秒,如果10次结果一致才确认状态切换。如果抖动严重,硬件上再加一个100nF的RC低通滤波电容,双管齐下。
4.3 电机堵转烧毁驱动
我最初设计的电机运行超时保护是6秒,但实际测试发现电源电压偏低时,电机启动电流偏大,会触发过流保护误判。后来我把启动阶段(前500毫秒)的电流阈值放宽,运行稳定后再收紧阈值,这样既允许正常启动电流尖峰,又能保护堵转情况。
4.4 掉电导致状态丢失
如果系统运行在户外,会面临瞬断、欠压、甚至直接在电机运行途中断电的极端情况。由于电机断电后锁臂位置不变,重新上电后系统并不知道锁臂当前是在上位还是下位,这时强制初始化成某个状态是错误的。
我的做法是在Flash中专门保存一个状态标志,每次电机运行到位后更新一次。上电启动时先读取这个标志,然后让限位开关重新确认一次当前实际位置,如果标志和实际位置不一致,就按实际位置为准并重新写入标志。这个操作看似多余,但在现场能省很多不必要的故障排查。
4.5 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 读卡距离短 | 天线附近走线干扰/金属外壳影响 | 保证天线净空,铺地隔离 |
| 无法识别合法卡 | 卡号大小端顺序问题 | 检查字节序,翻转存储 |
| 电机不动作 | 驱动芯片供电不足或EN脚未拉高 | 检查12V供电逻辑,测量EN电平 |
| 电机运行抖动 | 限位开关抖动/电源纹波 | 软件消抖+RC滤波+加大电容 |
| 蜂鸣器无声 | 驱动三极管基极限流电阻过大 | 减小电阻,检查IO口复用配置 |
| 掉电后位置错乱 | Flash状态标志未保存 | 增加运行到位标志持久化 |
| 低功耗模式下功耗过高 | RC522未断电 | MOS管控制电源开断 |
4.6 户外防水与长期运行注意事项
车位锁桩只要在户外使用,防水就永远绕不开。电机和限位开关这些机械部件,建议使用带有IP67防护等级的工业级产品,电路板部分则做整板三防漆喷涂。三防漆不要涂到接插件和调试口上,否则后续想飞线调试就很痛苦。
电池供电的情况下,充电管理电路建议使用带温度保护的充电方案,防止夏天暴晒后电池温度过高时仍然大电流充电。程序里也要做低电量保护,当电池电压低于阈值时,关闭一切射频功能,只保留最简单的升锁动作,优先保住锁桩的基础功能。
5. 后续功能扩展与经验总结
这个项目做完基础版之后,可以扩展的空间其实很大。比如加一个ESP8266或ESP32模块,通过WiFi或蓝牙与手机联动,实现远程查看锁桩状态、远程开锁、访客临时授权、刷卡记录日志上传等功能。STM32与ESP8266之间通过UART通信,协议上自定义几帧简单控制指令即可。
要实现多个锁桩统一管理,可以增加一个简单的总线协议或者无线组网方案。比如用RS485总线把多个锁桩连到一台中心控制器上,中心控制器通过屏幕或触摸面板统一管理所有锁桩。每个锁桩分配一个地址,中心下发指令时根据地址判断对应操作哪个锁桩。
还有更灵活的授权方式,比如在卡验证的基础上叠加指纹识别模块,或者加人脸识别摄像头,这类方案在高端小区会更有竞争力。不过嵌入式项目的魅力在于基础功能稳定可靠后再逐步叠加,每一次增加功能都能加深对系统架构的理解。
回到这个项目本身,我体会最深的一点是,硬件和软件永远不能割裂来看。RC522读卡异常可能是布线问题导致的,电机烧驱动可能是程序时序问题导致的,这样“硬件问题软件排查,软件问题硬件背锅”的情况项目里太常见了。所以拿到任何一块板子,不要急着写功能,先把电源、时钟、复位这些系统基础条件确认清楚,再一步步点亮外设。另外,调试串口一定要在初期就留出来,后续排查问题时能省下一大半时间。
如果正准备复现这个项目,我的建议是从一块现成的STM32开发板和RC522读卡模块开始,先把卡片读取和白名单比对的逻辑跑通,再接电机驱动。硬件电路一步步搭起来,不要一次全部焊接完,每增加一个模块就做一次完整的联调。这样就算出了奇怪的问题,你也能清楚知道问题出在哪一层。