ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32智能门禁系统:人脸识别、RFID、蓝牙与密码四重认证实战

STM32智能门禁系统:人脸识别、RFID、蓝牙与密码四重认证实战

简介:这是一套面向计算机、电子信息、自动化等专业学生与嵌入式开发者的STM32智能门禁系统完整项目资料,可作为毕业设计、课程设计或大作业的参考方案,也适合初学者进阶学习。项目集成人脸识别、RFID刷卡、蓝牙App远程控制与密码锁四种开锁方式,覆盖嵌入式开发中传感器驱动、通信协议与上位机交互等核心知识点。压缩包共255个文件,约8.67MB,以C语言源文件与头文件为主体,配合Keil工程文件、编译中间产物及烧录脚本,结构完整、层次清晰,便于按模块阅读与二次开发。目前已有217人学习下载。该资源附带详细文档与全部资料,代码经过实际测试运行,功能验证通过,并获导师认可、答辩评审95分,读者可据此快速理解门禁系统的整体架构与实现思路,在此基础上修改扩展其他功能,或直接用于毕设、课设与项目立项演示。

1. 从一把钥匙到四重认证:STM32智能门禁系统到底在解决什么问题

去年帮一个做园区改造的朋友评估门禁方案,他开口就问:“能不能刷脸进门,同时保留刷卡,手机还能远程开?”我当时第一反应是——这不就是典型的STM32智能门禁系统需求吗?人脸识别负责无感通行,RFID管员工卡,蓝牙app给访客临时授权,密码锁兜底。四套认证方式跑在一颗MCU上,听起来像堆料,实际上每个模块都有明确的场景分工。这套方案适合谁?做毕设的学生、想快速搭原型的嵌入式工程师、需要低成本门禁改造的集成商。它不追求工业级可靠性,但能把人脸识别、RFID读卡、蓝牙通信、密码验证四条链路完整跑通,源码和文档齐全的情况下,两三天就能看到实物效果。核心难点不在单个模块,而在于资源调度和状态机设计——这也是后面要重点拆的部分。

2. 四重认证的硬件选型与资源分配:为什么不是堆得越多越好

2.1 主控选型:STM32F103还是F407,差别不在主频

常见做法是选STM32F103ZET6或STM32F407ZGT6。F103主频72MHz,SRAM 64KB,跑人脸识别会非常吃力;F407主频168MHz,SRAM 192KB,带FPU和DSP指令,做人脸检测和特征提取时帧率能到5-8fps,勉强可用。如果标题里强调“人脸识别”是核心功能,我一般会直接上F407,别在F103上浪费时间。Flash方面,F407ZGT6有1MB,放人脸模型和字库够用;F103ZET6只有512KB,裁剪模型后也紧张。

资源分配上,人脸识别占大头:摄像头DCMI接口+DMA,帧缓冲至少需要320x240x2字节≈150KB,F407的192KB SRAM刚好卡住。RFID用SPI接口的RC522或MFRC522模块,占用一个SPI和一根IRQ线。蓝牙用HC-05或JDY-31,走USART,波特率一般9600或115200。密码锁用矩阵键盘或触摸屏软键盘,如果配了ILI9341液晶屏,可以省掉独立键盘。这里有个热词叫“stm32使用ili9341读id是a1a1”,说明很多人用ILI9341做显示,读ID返回0xA1A1是正常现象,不用慌。

提示:如果SRAM不够,可以把人脸检测的帧缓冲降到160x120,识别距离缩短到30-50cm,但误识率会上升。

2.2 人脸识别模块:EasyAI还是ArcFace,离线方案怎么选

标题里没指定人脸识别方案,但热词里出现了“easyai人脸识别”和“arcface人脸识别”。EasyAI通常是国内一些厂商封装的离线人脸识别模块,串口输出结果,STM32只负责发指令和收结果,不跑算法。ArcFace是虹软的人脸识别SDK,需要跑在带操作系统的平台或高性能MCU上,STM32F407跑ArcFace比较吃力,一般用F429或H7系列。

我一般会分两条路:如果只是做毕设或演示,用串口人脸识别模块最省事,STM32通过UART发“识别”指令,模块返回人脸ID,STM32再控制继电器开门。如果非要自己跑算法,可以用STM32Cube.AI把轻量级人脸检测模型部署到F407上,但识别率有限,需要自己采集数据集训练。常见做法是OV2640摄像头+DCMI+DMA,图像二值化后做简单特征匹配,但这不是真正的人脸识别,只能算“人脸检测+模板比对”。

// 串口人脸识别模块通信示例(STM32 HAL库) uint8_t face_cmd[4] = {0xEF, 0x01, 0xFF, 0xFF}; // 识别指令 uint8_t face_resp[8]; HAL_UART_Transmit(&huart2, face_cmd, 4, 100); HAL_UART_Receive(&huart2, face_resp, 8, 500); if (face_resp[0] == 0xEF && face_resp[1] == 0x01) { uint16_t face_id = (face_resp[2] << 8) | face_resp[3]; if (face_id > 0 && face_id < 100) { open_door(); // 识别成功,开门 } }

这段代码的逻辑是:STM32通过UART2发送固定格式的识别指令,模块返回8字节响应,前两字节是帧头,接着两字节是人脸ID。参数说明:波特率通常为115200,超时500ms,如果超时未响应,需要重发或报错。注意人脸模块的供电要独立,不要和STM32共用LDO,否则识别瞬间电流波动会导致MCU复位。

2.3 RFID与蓝牙app:RC522和HC-05的共存问题

RC522走SPI,HC-05走USART,硬件上不冲突,但软件上要注意中断优先级。RC522的中断引脚接到STM32的EXTI,蓝牙数据接收用UART空闲中断+DMA。如果两个中断同时触发,RFID刷卡响应会被蓝牙数据打断,导致刷卡失败。我一般把RFID中断优先级设得比UART高,因为刷卡是即时操作,蓝牙数据可以缓冲。

蓝牙app这边,热词里有“蓝牙app控制esp32”,但STM32方案类似。手机端用MIT App Inventor或Android Studio写一个简单app,发送“OPEN”字符串,STM32收到后驱动继电器。注意HC-05默认波特率是9600,而人脸模块常用115200,如果共用UART要分时复用或改用双UART。

// 蓝牙开门指令解析(UART空闲中断) void USART1_IRQHandler(void) { if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE)) { __HAL_UART_CLEAR_IDLEFLAG(&huart1); HAL_UART_DMAStop(&huart1); uint8_t len = BUFFER_SIZE - __HAL_DMA_GET_COUNTER(huart1.hdmarx); if (len >= 4 && strncmp((char*)rx_buf, "OPEN", 4) == 0) { open_door(); } HAL_UART_Receive_DMA(&huart1, rx_buf, BUFFER_SIZE); } }

逻辑说明:利用UART空闲中断判断一帧数据接收完成,DMA停止后计算接收长度,比较是否为“OPEN”。参数:BUFFER_SIZE设为64,DMA循环模式,空闲中断触发后重新启动DMA接收。注意字符串比较要用strncmp并指定长度,防止缓冲区溢出。

3. 从零搭建工程:CubeMX配置、外设初始化与主循环框架

3.1 CubeMX关键配置:时钟树、DMA和中断优先级

用STM32CubeMX生成工程时,时钟树要把HCLK设到最大(F407是168MHz),APB1和APB2分频要匹配外设时钟。DCMI接口需要DMA双缓冲,SPI1给RC522,USART1给蓝牙,USART2给人脸模块,USART3留作调试串口。中断优先级分组设为NVIC_PRIORITYGROUP_4,抢占优先级和子优先级按以下分配:

外设抢占优先级子优先级说明
DCMI DMA00图像采集不能丢帧
RFID EXTI10刷卡响应要快
UART1空闲20蓝牙数据可缓冲
UART2接收30人脸模块响应慢
定时器40系统时基

注意:如果用了FreeRTOS,中断优先级要符合configMAX_SYSCALL_INTERRUPT_PRIORITY限制,否则会触发硬件错误。

3.2 主循环状态机:四重认证怎么调度不打架

主循环不要用delay,用状态机轮询。我一般定义几个状态:IDLE、FACE_SCAN、RFID_WAIT、BLUETOOTH_WAIT、PASSWORD_INPUT、DOOR_OPEN。每个状态停留时间有限,比如FACE_SCAN持续2秒无结果就回到IDLE,RFID_WAIT等待中断触发。密码输入用矩阵键盘扫描,消抖用定时器。

typedef enum { IDLE, FACE_SCAN, RFID_WAIT, BT_WAIT, PWD_INPUT, DOOR_OPEN } sys_state_t; sys_state_t state = IDLE; uint32_t state_tick = 0; while (1) { switch (state) { case IDLE: if (HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_RESET) { state = PWD_INPUT; // 按键触发密码输入 } else if (face_detected()) { state = FACE_SCAN; } break; case FACE_SCAN: if (face_recognize() == SUCCESS) { state = DOOR_OPEN; } else if (HAL_GetTick() - state_tick > 2000) { state = IDLE; } break; case DOOR_OPEN: open_door(); HAL_Delay(3000); // 开门保持3秒 close_door(); state = IDLE; break; // 其他状态类似 } HAL_Delay(10); // 降低CPU占用 }

逻辑说明:状态机每10ms轮询一次,避免阻塞。参数:开门保持时间3000ms可调,人脸扫描超时2000ms。注意HAL_Delay在中断中不能用,主循环里可以。

3.3 源码组织与文档结构:怎么让“全部资料”真的能用

标题里说“源码+详细文档+全部资料”,实际拿到手往往是一堆散文件。我一般按以下结构整理:

  • Core/:CubeMX生成的初始化代码
  • Drivers/:HAL库和自定义驱动(rc522.c、face.c、bluetooth.c)
  • App/:状态机、密码管理、日志
  • Doc/:原理图PDF、PCB截图、模块接线表、常见问题
  • Tools/:蓝牙app源码、人脸模块配置工具

文档里必须有一张接线表,标明每个模块的引脚和电源。比如RC522的SDA接PA4,SCK接PA5,MOSI接PA7,MISO接PA6,RST接PB0。人脸模块TX接PA3,RX接PA2。蓝牙TX接PA10,RX接PA9。这些不写清楚,复现时必翻车。

4. 避坑与排查:RFID读卡失败、人脸误识、蓝牙断连的现场记录

4.1 RFID刷卡没反应,串口却打印正常

现象:RC522模块绿灯亮,但刷卡时STM32收不到中断,串口调试打印“RFID init ok”。原因:RC522的天线匹配电路没调好,或者SPI速率太高。解决:把SPI时钟降到1MHz以下,检查天线线圈焊接,用示波器看MISO是否有波形。另外,RC522的IRQ引脚默认是高电平,刷卡时拉低,如果配置成上升沿触发就永远等不到。

4.2 人脸识别距离短,稍微远一点就失败

现象:人脸模块在20cm内能识别,超过30cm就报错。原因:摄像头镜头焦距固定,或者补光不足。解决:换用带红外补光的模块,或者把识别距离参数调大(如果模块支持)。如果是自己跑算法,把图像分辨率从320x240降到160x120,帧率提高,但识别距离不会变远,反而更近。常见做法是加超声波测距辅助,热词里有“stm32超声波测距”,当人体靠近到50cm内再启动人脸识别,省电且提高成功率。

4.3 蓝牙app发送指令后门不开,但串口助手可以

现象:用串口助手发“OPEN”能开门,用手机app发同样的字符串没反应。原因:app发送时带了回车换行符(\r\n),STM32比较时多了两个字节。解决:在STM32端用strstr查找“OPEN”而不是strncmp,或者app端去掉换行。另外,HC-05的波特率可能被误设为38400,而STM32初始化是9600,导致乱码。

4.4 密码输入正确但继电器不吸合

现象:矩阵键盘输入密码后,串口打印“password ok”,但继电器没动作。原因:继电器驱动三极管基极电阻太大,或者继电器线圈电压不够。解决:用万用表测继电器线圈两端电压,如果是3.3V驱动5V继电器,需要加电平转换。另外,STM32的GPIO输出电流最大25mA,驱动继电器要加三极管或光耦。

4.5 系统运行几分钟后死机,看门狗复位

现象:门禁系统跑几分钟后无响应,看门狗复位后恢复。原因:DCMI DMA和UART DMA共用内存区域,或者堆栈溢出。解决:检查DMA缓冲区是否对齐,把人脸识别的帧缓冲放到CCM RAM(F407有64KB CCM),不占用主SRAM。堆栈大小在启动文件里改,默认0x400不够,改成0x800。

5. 进阶技巧:用FreeRTOS重构状态机与低功耗优化

如果你已经跑通了裸机版本,下一步可以上FreeRTOS。热词里有“freertos stm32物联网网关”,门禁系统其实就是一个微型网关。把四个认证模块拆成独立任务:FaceTask、RFIDTask、BTTask、PwdTask,用队列传递事件,互斥量保护继电器。任务优先级:FaceTask最高,因为图像处理耗时;RFIDTask次之;BTTask和PwdTask最低。这样刷卡时不会被人脸识别阻塞。

// FreeRTOS任务创建示例 xTaskCreate(FaceTask, "Face", 512, NULL, 4, NULL); xTaskCreate(RFIDTask, "RFID", 256, NULL, 3, NULL); xTaskCreate(BTTask, "BT", 256, NULL, 2, NULL); xTaskCreate(PwdTask, "Pwd", 256, NULL, 1, NULL); vTaskStartScheduler();

参数说明:FaceTask堆栈512字,因为要跑图像处理;其他任务256字够用。优先级4最高,1最低。注意FreeRTOS的堆空间在FreeRTOSConfig.h里配置,默认heap_4,总大小15KB左右,F407够用。

低功耗优化:如果门禁系统用电池供电,可以在IDLE状态调用__WFI()进入睡眠,RFID中断或按键中断唤醒。人脸识别模块平时断电,用MOS管控制电源,需要时再上电。实测待机电流可以从80mA降到15mA。

验证方法:用逻辑分析仪抓SPI和UART波形,确认时序符合模块手册。用J-Link调试时,热词里有“stm32禁用jtag”,如果PA13/PA14/PA15被占用,要在CubeMX里把调试口改成SWD,释放JTAG引脚。最后,别迷信“全部资料”,拿到源码先编译,看报错,再对照文档改引脚定义。我习惯在项目根目录放一个pinmap.md,每次改硬件先更新这个文件,省得后面自己都忘了。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进