STM32多模态智能门禁系统:密码、刷卡、蓝牙、人脸四合一实战拆解

STM32多模态智能门禁系统:密码、刷卡、蓝牙、人脸四合一实战拆解 简介本资源是一套基于STM32平台实现的多功能智能门禁系统完整源码工程面向计算机、电子信息、自动化等专业的本科生课程设计、毕业设计及单片机进阶学习者解决传统门禁功能单一、扩展性差的问题集成人脸识别、RFID卡识别、蓝牙手机App控制与数字密码锁四大核心模块。压缩包共254个文件含49个头文件.h定义硬件接口与功能模块46个C源文件.c实现算法逻辑与外设驱动以及编译中间文件.o/.d/.crf、Keil工程配置.uvprojx/.uvoptx、可执行镜像.hex/.axf等结构完整开箱即用总大小8.66MB。已有355人下载学习资源包含标准STM32F10x固件库驱动如stm32f10x_rcc.c、stm32f10x_i2c.c等并附带keilkill.bat等实用工具脚本便于快速清理与重建工程适合希望深入理解嵌入式多模态身份认证系统架构与协同控制机制的学习者参考与二次开发。 做智能门禁系统这个项目前后断断续续花了大概一个半月。最开始的需求很简单实验室入口的门禁想升级原来只有一张IC卡人来人往经常丢卡管理员补卡补到崩溃。聊了几轮之后需求就变成了要四种方式开锁密码、刷卡、蓝牙、人脸最好还能看记录。我评估了一下手头的模块直接定了STM32为主控然后把整个系统搭了起来。这篇文章就把整个项目的技术拆解、硬件选型、软件实现和踩坑记录整理出来给正在做类似项目的朋友做个参考。这套系统做下来核心价值其实不在某一个模块而在多模态融合这件事上。单独玩人脸识别、单独刷RC522、单独做蓝牙APP网上教程一大把但真正把它们集成到一块板子上、用一套逻辑管理起来让四种方式稳定共处这才是整个项目最花精力的地方。适合正在做嵌入式课程设计、毕业设计或者想把手头的门禁方案升级成多模态系统的工程师参考。1. 项目整体架构四种门禁方式的设计取舍1.1 业务逻辑与四种开门方式的优先级先明确一个核心问题四种开门方式之间的关系是什么是或的关系谁都能开还是分层管理不同人有不同权限我最终采用的是并行验证、统一执行的策略——四种方式各自独立识别任何一种验证通过系统就走同一套开锁逻辑驱动舵机/继电器。但在具体实现上每种的业务逻辑有差异密码锁最基础的兜底方案。即使人脸识别挂掉、蓝牙连不上、卡丢了只要记住密码就能进。我把密码设计成管理员密码用户密码两级管理员密码可以修改所有用户密码和卡片白名单用户密码只能开门。RFID刷卡最稳定的方案。RC522读取卡片UID和存储在Flash里的白名单比对。白名单支持管理员卡注册、注销。实测下来识别速度在几百毫秒级别比人脸快得多适合日常高频进出场景。蓝牙APP最灵活的方案。手机连上HC-05通过自定义协议发指令。APP端走的是按钮状态反馈模式点击开门按钮STM32校验帧结构后执行开锁并回传状态。这个方案很适合远程临时授权比如人不在实验室让同事帮忙进去拿东西。人脸识别科技感最强的方案。但也是四者里最娇气的受光线、角度影响最大。所以我把它的定位设定为辅助验证而不是唯一验证——比如门禁场景要求高安全时可以设置成人脸密码双因子验证否则单独人脸通过就开门。四种方式的执行顺序上我做了个简单的互斥锁设计任意方式验证成功后系统进入开锁状态期间其他方式的验证请求会被挂起直到门锁复位我设定的是5秒后自动上锁。这避免了多个验证同时通过导致舵机反复动作的问题。1.2 为什么选STM32而不是树莓派、ESP32或K210单片方案这是个很实际的选择题。我当时在几个候选方案里纠结过一轮树莓派方案性能最强Python生态写OpenCV人脸识别几乎是现成的但短板很明显——体积大、成本高、需要Linux系统开机要几十秒做门禁这种上电几秒内就要能用的场景不太合适。而且树莓派的GPIO电平是3.3V虽然和多数模块兼容但整体方案的嵌入式属性偏弱学生做毕设容易被质疑没有嵌入式含量。ESP32方案集成了WiFi和蓝牙价格比同性能的STM32还便宜理论上完全能跑这个项目。但问题是ESP32的人脸识别生态不如K210成熟大部分得跑ESP-DL框架调试成本高。另外很多学校的嵌入式课程主线是STM32用ESP32在技术对接度上会弱一些。STM32 K210协处理器方案这是我最终采用的方案。STM32F103C8T6负责整个系统的调度——扫描矩阵键盘、通过SPI读RC522、通过USART和HC-05通信、通过另一个USART接收K210的人脸识别结果、输出PWM控制舵机。K210只干一件事摄像头采集图像跑人脸检测和识别模型把结果通过串口发给STM32。各司其职非常清晰。这里给新手一个建议不要试图让STM32F103去跑人脸识别算法。F103的主频是72MHzRAM只有20KB跑卷积神经网络根本不可能。人脸识别必须挂在带硬件加速器或独立算力的模组上K210、OpenMV、树莓派Zero都行STM32只负责接收结果和执行动作。1.3 方案选型的三个关键决策点第一个决策点是主控选型。我选的是STM32F103C8T6也就是大家常说的蓝板理由是它足够便宜模块价格在十元级别外设接口丰富多路USART、SPI、I2C、ADC资料生态极好——不管是标准库还是HAL库网上的例程一堆踩坑时很救命。如果你的项目需要同时跑人脸识别屏幕显示更多传感器可以升级到F407或F429但F103对这个门禁场景是够用的。第二个决策点是人脸识别模组。我对比过三种方案OpenMV贵但上手简单MicroPython开发、树莓派Zero做上位机性能强但要配Linux启动慢、K210性价比最高KPU硬件加速支持本地跑yolo和face detection模型。最后选了K210搭配OV2640摄像头用MaixPy开发。K210模组几十块钱识别速度能跑到每秒几帧对门禁场景足够了。第三个决策点是RFID模块选型。市面上最常见的RC522是SPI接口也有I2C和UART版本。我选了SPI版本因为STM32的硬件SPI速度比模拟I2C快读卡延迟更小。要注意RC522是3.3V电平和STM32的IO电平匹配不需要额外转换电路。2. 硬件选型与电路连接要点2.1 硬件模块清单与选型理由整个项目用到的模块清单如下模块型号/规格接口供电作用主控STM32F103C8T6-3.3V系统调度与逻辑控制人脸识别K210 OV2640摄像头UART5V人脸检测、特征比对RFIDMFRC522SPI3.3V读取卡片UID蓝牙HC-05UART5V板载3.3V LDO手机APP通信密码输入4x4矩阵键盘GPIO3.3V密码输入显示OLED SSD1306128x64I2C3.3V状态显示开锁执行SG90舵机PWM5V控制锁舌每个模块的选型我都是经过实测对比的。比如蓝牙模块HC-05和HC-06都试过HC-06只能做从机适合手机主动连开发板的场景但如果以后想升级成开发板主动连手机比如远程升级固件HC-06就不行了。所以直接上HC-05它支持主从一体AT指令可以配置角色通用性更强。再比如矩阵键盘4x4的比1x4薄膜按键好用的多——1x4只能输数字连确认和删除都要额外做按键组合。4x4直接可以把*当确认键、#当删除键操作逻辑简单很多。2.2 接线表与电平匹配细节接线这块是软件调试之前最需要仔细核对的部分。我把各个模块和STM32的连接整理成了一张表照着接线基本不会出错STM32引脚连接的模块引脚说明PA0-PA7矩阵键盘的8根线4行4列行接PA0-PA3列接PA4-PA7PA8SG90舵机信号线TIM1_CH1PWM输出PA5RC522 SCKSPI1_SCKPA6RC522 MISOSPI1_MISOPA7RC522 MOSISPI1_MOSIPA4RC522 SDACSSPI1_NSS软件片选PA2HC-05 TXDUSART2_RXPA3HC-05 RXDUSART2_TXPB6OLED SCLI2C1_SCLPB7OLED SDAI2C1_SDAPB10K210 TXDUSART3_RXPB11K210 RXDUSART3_TX这里有两个接线时必须注意的细节电平匹配问题。HC-05模块的供电是5V但它的RXD引脚如果直接接3.3V的STM32 TXD是没问题的5V模块的RXD通常兼容3.3V逻辑可反过来就不一定——STM32的RXD承受不了5V。稳妥做法是给模块供电5V但确保信号线都是3.3V逻辑。HC-05的设计一般是兼容的但别的蓝牙模块不好说保险起见可以用万用表量一下RXD在空闲状态的电平。舵机供电单独走。SG90舵机转动时峰值电流可能到几百毫安如果和STM32共用一路5V供电会拉低电压导致MCU复位。我试过直接从一个USB口取电舵机一打角度OLED闪屏、蓝牙掉线全是电源问题。最终给舵机单独一路5V供电用独立USB口或一个AMS1117-5.0的降压模块地和STM32共地问题立刻消失。2.3 供电设计与抗干扰处理整板供电用的是最常见的方案USB 5V输入 → AMS1117-3.3稳压 → STM32及各3.3V模块。如果你想让系统做到12V/24V门禁供电可以在入口加一个MP1584或LM2596降压模块先降到5V再接LDO降到3.3V。但要注意降压模块的纹波可能会比较大如果OLED显示不稳定或蓝牙误码率升高要考虑在5V和3.3V之间加个几十微法的钽电容滤波。人脸识别部分我单独说一句K210模组对供电质量比较敏感建议在它的5V供电入口并联一个100uF电解电容和0.1uF陶瓷电容实测能明显减少人脸识别偶发死机的问题。这个细节在大多数教程里不会提但实际做下来非常关键。3. 软件实现从底层驱动到APP联调软件部分是这个项目的大头也是大多数朋友卡住最多的地方。我按照功能模块拆开讲最后再讲它们是怎么被调度到一块的。3.1 密码锁模块矩阵键盘扫描与防爆破设计密码锁的实现核心是矩阵键盘的扫描。4x4矩阵键盘的原理是8个IO口组成4行4列通过逐行拉低、逐列读取的方式判断按键。我写了一个常用的扫描函数思路是先把所有行引脚设为输出低电平所有列引脚设为输入上拉然后逐行将某一行设为低电平读取列的电平变化有按键按下时对应列会被拉低。uint8_t MatrixKey_Scan(void) { uint8_t row, col, key 0xFF; // 所有行输出低电平列输入上拉 for (row 0; row 4; row) { // 将当前行设为低电平其他行设为高电平 Set_Row_Low(row); for (col 0; col 4; col) { if (GPIO_ReadPin(Column_Pin[col]) RESET) { delay_ms(10); // 消抖 if (GPIO_ReadPin(Column_Pin[col]) RESET) { while (GPIO_ReadPin(Column_Pin[col]) RESET); // 等待松开 key keymap[row][col]; break; } } } Set_Row_High(row); if (key ! 0xFF) break; } return key; }密码存储方面我坚持不把明文密码写死在代码里。做法是把密码哈希后存到STM32内部Flash的特定扇区每次输入密码后做同样的哈希运算再比对。F103没有硬件加密单元我就用了简单的CRC32或MD5的软件实现虽然谈不上绝对安全但比明文强得多。管理员密码可以通过连续按住*键3秒进入管理模式来修改修改过程要求先输入旧密码再输入新密码防止误操作。防爆破是密码锁必须做的功能连续输错5次密码系统锁定60秒。锁定期间无论输入什么都不会校验OLED上显示Locked 60s倒计时。这个逻辑看着简单但真能挡住绝大多数暴力试密码的尝试。3.2 RFID模块RC522读卡与卡片白名单管理RFID模块的软件核心是RC522的SPI通信和ISO14443A协议的卡片防碰撞流程。RC522读卡的底层流程大概是复位RC522 → 设置天线 → 发送寻卡指令 → 收到卡片响应 → 读取卡片UID → 校验。大多数教程都会用现成的RC522库我不建议自己从零写底层协议直接用现成驱动库效率最高。但要注意两个细节// 读取卡片UID的核心伪代码 uint8_t RF_ReadCardUID(uint8_t *uid_buf) { uint8_t status; uint8_t tag_type[2]; status PcdRequest(PICC_REQALL, tag_type); // 寻卡 if (status ! MI_OK) return 0; status PcdAnticoll(uid_buf); // 防碰撞获取UID if (status ! MI_OK) return 0; PcdHalt(); // 休眠卡片 return 1; }卡片白名单管理是我额外加的Flash里存一个最多20个UID的数组管理员卡默认UID为0xXXXXXXXX的那张在系统启动5秒内刷卡可以进入注册模式之后刷的每一张新卡都会自动加入白名单。这个设计避免了每次改卡都要重新烧录固件的尴尬实际使用中非常方便。还有一个坑是RC522的天线线圈。如果你用的是那种带线圈的分体天线板连接线长度超过10cm就可能出现读卡距离急剧缩短的情况。我遇到过这个问题排查一晚上最后把连接线缩短到5cm内读卡距离立刻恢复到3cm以上。模块自带PCB天线的版本就没这个问题建议新手直接买自带天线的模块。3.3 蓝牙模块HC-05配置与应用层协议先讲HC-05的配置。新买的HC-05一般默认是从模式波特率9600或38400。我把它配置成了从模式、38400波特率、配对码1234。用USB转TTL在电脑上通过AT指令配置ATORGL # 恢复默认设置 ATUART38400,0,0 # 设置波特率384008N1 ATROLE0 # 从模式 ATPSWD1234 # 设置配对码 ATNAMESmartDoor # 设备名配置完成后STM32通过USART2和HC-05通信。通信协议我自定义了一套简单帧结构帧头(0xAA) 指令码 数据长度 数据 校验和。门禁场景只需要少数几条指令指令码方向含义0x01APP→STM32请求开门0x02STM32→APP开门成功回传开锁时间戳0x03STM32→APP密码错误0x04APP→STM32查询门锁状态0x05STM32→APP门锁状态反馈0锁定1解锁APP端我用的是经典的Android蓝牙串口开发方案——BluetoothAdapterBluetoothSocketUUID走标准的SPP服务UUID00001101-0000-1000-8000-00805F9B34FB。如果你不想从零写原生APP可以用MIT App Inventor或E4A这类图形化平台拖一个按钮出来逻辑就是点击按钮→建立蓝牙连接→发送0xAA 0x01 0x00 0x00 0xAB。实际测试下来图形化平台的APP稳定性确实不如原生但演示和毕设完全够用。有个细节必须提醒APP重连时一定要主动断开旧的Socket。Android的蓝牙连接很轴你上次连接没有正常close下次连接大概率报错。我在APP里每次连接前都强制做一次bluetoothSocket.close()实测可以解决90%的连不上问题。3.4 人脸识别模块K210方案与代码衔接人脸识别是整个项目里软件最重的一块但好消息是大部分算力工作不在STM32上跑。我的方案是K210跑MaixPy固件内置的人脸识别使用的是KPU硬件加速器模型是face_detect检测face_recognize识别。K210端的代码逻辑大概是import sensor, image, lcd, time from Maix import KPU import ustruct # 初始化摄像头、LCD、KPU加载人脸检测模型 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.run(1) kpu KPU() kpu.load_kmodel(/sd/face_detect.kmodel) kpu2 KPU() kpu2.load_kmodel(/sd/face_recognize.kmodel) features [] # 已注册的人脸特征 while True: img sensor.snapshot() # 检测人脸框 faces kpu.run_with_output(img) # 对每个检测到的人脸计算特征和features比对 # 相似度超过阈值通过UART发送 C 给STM32 # 相似度不足发送 F 给STM32实际操作中先让K210巡游采集5张人脸特征存成数组然后循环识别。K210通过UART发给STM32的数据很简单——一个字节就够了O代表识别通过X代表不通过。STM32那边用USART3接收中断收到O就执行开锁。这里有个关键调参点K210输出的相似度阈值。默认0.6左右时有时候两个人长得比较像会误判调到0.8以上又会出现识别不出来的情况。我实测下来在光线均匀的室内环境0.72是一个比较靠谱的阈值。如果你的门禁装在有背光的门口建议代码里加一个简单的光照补偿——先统计画面亮度均值低于阈值就提示光线不足别硬识别。3.5 主程序状态机设计最后是整个系统怎么转起来的。我用了最简单的状态机模型把门禁系统分成四个状态STATE_IDLE待机、STATE_CHECK验证中、STATE_OPEN开锁中、STATE_LOCKED锁定中。主循环里不断轮询四个输入源键盘、RC522、蓝牙、K210一旦任一输入源触发了验证请求就进入STATE_CHECK对应分支处理验证成功跳到STATE_OPEN这期间其他输入源请求会被丢弃。while (1) { switch (door_state) { case STATE_IDLE: // 轮询四种输入源 if (Key_Scan() ! NO_KEY) door_state STATE_CHECK_PWD; if (RFID_Check()) door_state STATE_CHECK_RFID; if (BLE_ReceiveFrame()) door_state STATE_CHECK_BLE; if (Face_ReceiveFlag()) door_state STATE_CHECK_FACE; break; case STATE_CHECK_PWD: // 密码校验逻辑通过则STATE_OPEN失败则回IDLE break; // ... 其他状态类似 case STATE_OPEN: Servo_Open(); delay_ms(5000); Servo_Close(); door_state STATE_IDLE; break; } }这个状态机看着简单但它解决了多模态系统最大的资源竞争问题。有人同时刷卡又按密码系统只处理最先触发的那个不会出现舵机乱转的情况。如果你后续要加语音播报、加远程日志上报也是往这个状态机里加分支结构不会乱。4. 联调踩坑记那些我替你踩过的坑4.1 高频问题速查表实际调试过程中我遇到的问题远不止上面提到的那些。挑几个典型的整理成表格方便你按图索骥现象根本原因解决方案HC-05连不上手机模块默认波特率和手机APP不一致或模块处于AT模式确认模块退出AT模式上电前不要按住按键用38400重试RC522读卡距离只有1cm天线线圈信号衰减或供电不足检查SPI线是否过长20cm天线区域远离金属和杜邦线人脸识别经常识别失败光线变化大阈值固定加光照补偿阈值动态调整或换更高清的OV5640摄像头舵机转动时MCU复位舵机电流拉低电源电压舵机独立供电共地电源入口加大电容OLED间歇性花屏I2C线太长、上拉电阻没接缩短I2C线板载OLED模块需确认是否有上拉电阻蓝牙指令时好时坏帧校验不严出现粘包严格校验帧头和校验和接收用环形缓冲区K210上电后一直红屏供电电流不足导致反复重启换2A以上的5V适配器T型口供电时别用劣质数据线4.2 三个让我印象最深的排障过程第一个坑是蓝牙粘包问题。APP发送的字节流如果连续帧间隔太短STM32端可能一次中断收到两帧导致第二帧校验失败。排查方式是在串口调试助手里连续发100帧数据看丢包率最后用环形缓冲区接收、主循环里统一解析解决。这个经验对以后做任何串口通信项目都适用。第二个坑是K210的人脸误检。一开始把检测阈值设得很低结果K210把实验室门口挂的消防面具当成人门自己开了。后来在代码里加了连续3帧检测到人脸且相似度达标才触发的防抖逻辑误报率才降下来。这个教训比任何教程都深刻——做门禁误开门比打不开门更吓人。第三个坑是舵机机械限位。SG90舵机的转动范围只有0~180度而我用的锁舌需要转90度左右。刚开始没设置PWM占空比的上下限结果舵机一上电就狂转到物理极限位置咔咔响差点把齿轮打坏。后来在软件里限制了PWM脉宽范围0.5ms~2.5ms对应0~180度同时舵机摇臂上加了物理限位块双保险。4.3 联调顺序的建议先通再全最后给一个联调顺序的实战建议。很多人习惯把所有模块都焊好、全部初始化一次性上电调试结果出问题完全不知道从哪排查。我的做法是分层联调先通再全先单独调密码锁矩阵键盘OLED显示舵机。把整个输入到执行的闭环打通。再加RFID验证刷卡能开锁白名单注册、注销功能正常。再加蓝牙在密码锁和RFID都正常的前提下加上蓝牙指令控制验证UART通信和协议解析。最后加人脸识别因为K210的调试需要串口监视器如果一上来就一起跑K210串口输出的调试信息和蓝牙的USART容易混淆。这样每加一个模块系统仍然处在基本可用的状态出了问题可以精准定位到新加模块上。写在最后的一点体会这个项目做完我总结出一个做多模态嵌入式系统的核心心得硬件选型决定上限软件架构决定下限。如果一开始拍脑袋把OpenCV跑在树莓派上、把RFID和蓝牙全堆在一块电源上后面可能有一半时间都在跟硬件打架。而把人脸识别放K210、控制逻辑放STM32这个架构想清楚之后每个模块的调试都是独立完成的系统集成反而变得很顺。另外一个小技巧分享给你在每个模块正常工作的状态下把串口调试输出加上模块单词的错误码比如[RFID] Read failed、[BLE] Checksum error调试时一眼就能看出问题出在哪个环节。这个小习惯在毕业设计答辩时也很加分。这个项目后续还能往几个方向扩展加一个ESP8266模块做云端日志上报密码验证记录和刷卡记录推送到钉钉/企业微信增加TFT-LCD触摸屏菜单把系统参数设置界面化或者把K210替换成更强的边缘AI模组提升复杂光线下的识别鲁棒性。如果你正好在做类似的项目欢迎多交流共同把这个方案磨得更完善。本文还有配套的精品资源点击获取