前言:为什么要做这个实验?

大家好!今天我想和大家分享一下我在华为鸿蒙班 B11 智能门禁实验中的所学所得。说实话,在做这个实验之前,我对指纹识别的印象还停留在手机解锁——按一下就开了,至于背后是怎么实现的,完全是个黑盒。

这次实验让我彻底打开了这个黑盒子。从一枚小小的光学指纹传感器,到完整的物联网门禁系统,中间涉及的知识点多得超乎想象:串口通信协议、状态机编程、实时操作系统多任务、PWM 舵机控制、光电开关检测、云平台通信……可以说,做完这个实验,你对嵌入式物联网的理解会上升一个台阶。

一、初识 AS608:一块会思考的指纹模块

AS608 是这次实验的核心主角。拿到模块的时候我还在想,这么小一块板子,居然能存 300 枚指纹?后来翻了《AS60x 指纹识别 SOC 用户手册》才明白,它根本不是一个简单的传感器——里面集成了一颗 DSP 芯片(PS1802),能自己完成图像采集、特征提取、特征比对、模板生成等全部运算,MCU 只需要通过串口发指令就行。

这就是所谓的「模块化设计」——把复杂的算法封装在模块内部,对外只暴露简单的指令接口。对于我们这些应用层开发者来说,这大大降低了使用门槛。但同时,理解它内部的工作原理也是非常重要的,不然出了问题都不知道从哪里排查。

AS608 最有意思的是它的「三缓冲区」设计:

  • ImageBuffer(图像缓冲区):存放采集到的原始指纹图像,有 72KB 那么大
  • CharBuffer1 和 CharBuffer2(特征缓冲区):各 512 字节,存放从图像中提取的指纹特征

为什么需要两个特征缓冲区呢?因为录入指纹的时候,要按两次手指,分别生成两枚特征,然后比对这两枚特征是不是来自同一根手指。如果是,就把它们合并成一个模板存起来。这个设计非常巧妙,既保证了录入的准确性,又提高了后续识别的成功率。

二、串口通信:和模块对话的艺术

和 AS608 通信靠的是 UART 串口,波特率 57600。一开始我以为串口通信就是发收数据那么简单,直到我看到了它的数据包格式——原来里面大有学问。

每个数据包都有固定的结构:包头(0xEF01)+ 设备地址(4 字节)+ 包标识(1 字节)+ 包长度(2 字节)+ 指令码/确认码(1 字节)+ 参数(N 字节)+ 校验和(2 字节)。

这个结构有几个设计亮点:

  • 固定包头:MCU 收到数据后,只要看到 0xEF01 就知道一个新包开始了,方便帧同步
  • 设备地址:支持多模块组网,通过地址区分不同的模块,默认是 0xFFFFFFFF
  • 包标识:区分指令包(0x01)、数据包(0x02)和应答包(0x07)
  • 校验和:从包标识到校验和前所有字节的和,用来检测传输过程中有没有出错

在代码里,发送指令靠 SendMessageDate_v2 函数,接收应答靠 JudgeStr 函数。前者负责把指令打包成正确格式发出去,后者负责在串口接收缓冲区里找应答包。这种「发送-等待-解析」的模式是串口通信的经典套路,几乎所有的模块通信都是这么做的。

调试这段代码的时候,我一开始用串口助手抓包,看到一串十六进制数字完全摸不着头脑。后来对照着数据包格式一个字节一个字节地分析,终于看懂了:前面两个字节是包头,接下来四个字节是地址……那种豁然开朗的感觉真的很棒!

三、录入指纹:五步法的精妙设计

对照《ATK-AS608 指纹识别模块用户手册》第 18 页的流程图,我在代码里找到了对应的 Add_FR 函数。这个函数用了一个非常经典的状态机写法,用 processnum 变量记录当前走到第几步,一共分五步:

第一步:采集第一次指纹图像,生成特征存到 CharBuffer1。指令是 PS_GetImage(0x01)+ PS_GenChar(0x02)。

第二步:采集第二次指纹图像,生成特征存到 CharBuffer2。同样的两条指令,只是 BufferID 不一样。

第三步:比对两次特征是不是同一根手指。用 PS_Match(0x03)指令,如果比对失败,说明两次按的不是同一根手指,要回到第一步重来。

第四步:合并两枚特征生成模板。用 PS_RegModel(0x05)指令,生成的模板比单个特征更完整,识别率更高。

第五步:把模板存到 FLASH 指纹库。用 PS_StoreChar(0x06)指令,指定一个 ID 号保存。

整个流程环环相扣,每一步失败都会有相应的处理——比如 PS_Match 失败就回到第一步重新录入,PS_StoreChar 失败也回到第一步重试。这种容错设计在实际产品中非常重要,因为用户按手指的力度、角度、干湿程度都会影响识别效果。

我特别欣赏这个状态机的写法。以前写程序总喜欢用一大堆 if-else 嵌套,代码又长又难读。用状态机就清晰多了:一个 switch 语句,每个 case 对应一个状态,状态之间的跳转一目了然。这是我这次实验学到的一个重要编程技巧。

四、刷指纹:三秒钟的背后

相比录入的五步,刷指纹(也就是识别)就简单多了,只有三步:

  • PS_GetImage:采集图像
  • PS_GenChar(CharBuffer1):生成特征
  • PS_HighSpeedSearch:在指纹库里高速搜索匹配的指纹

别看只有三步,里面的技术含量可不低。搜索要在 300 枚指纹里找到匹配的那一枚,而且要在几百毫秒内完成,这对算法的效率要求很高。代码里用的是 PS_HighSpeedSearch(高速搜索)指令,而不是普通的 PS_Search。根据手册,高速搜索对于质量好的指纹响应更快,体验也更好。

搜索成功后,模块会返回两个重要信息:匹配到的指纹 ID(pageID)和匹配得分(mathscore)。得分越高说明相似度越高,安全等级越高的系统要求的得分阈值也越高。

有趣的是,触摸检测也有讲究。代码里的 Finger_press 函数做了 3 次采样消抖——每次间隔 100ms,连续 3 次都检测到高电平才确认手指真的按下了。这是因为传感器的输出可能会有抖动,如果不做消抖,可能会误触发。这个细节虽小,但体现了嵌入式编程的严谨性。

五、多任务:三个任务如何协同工作?

这次实验用的是 Huawei LiteOS 实时操作系统,一共创建了三个任务:control_task、view_task 和 as608_handle_task。刚接触多任务的时候我很困惑:三个任务同时运行,不会乱套吗?

后来才明白,实时操作系统的核心就是「任务调度」——操作系统按照优先级和时间片,在不同任务之间切换执行。因为切换速度非常快(毫秒级),所以从宏观上看就像是在同时运行一样。

三个任务的分工很明确:

任务

优先级

主要工作

control_task

0(最高)

和云平台通信,处理 OC 协议,管理网络状态

view_task

1

刷新 LCD 屏幕,扫描按键

as608_handle_task

0

指纹识别,控制舵机,检测光电开关

任务之间怎么共享数据呢?答案是全局变量。比如门锁状态 door_status,as608_handle_task 会修改它,view_task 会读取它来更新屏幕显示,control_task 会读取它来上报云平台。这是多任务编程中最简单也最常用的数据共享方式。

当然,全局变量也有风险——如果两个任务同时修改同一个变量,可能会出问题。这时候就需要互斥锁、信号量之类的同步机制了。不过在这个实验里,大部分变量都是「一个任务写、多个任务读」的模式,所以直接用全局变量是安全的。

六、门禁业务逻辑:从指纹到开门的完整链条

把所有模块串起来,就是一个完整的智能门禁系统了。整个业务流程是这样的:

1. 用户手指触摸 AS608 传感器,PC6 引脚检测到高电平

2. Finger_press 函数消抖后调用 press_FR 进行指纹识别

3. 识别成功(返回 0),调用 door_open_close_operation(DOOR_OPEN)

4. 舵机转动开门,同时等待 E18-D80NK 光电开关检测门是否到位

5. 门开到位后,通过 OC 协议上报云平台:门已打开

6. 5 秒后,系统自动调用 door_open_close_operation(DOOR_CLOSED) 关门

7. 门关到位后,再次上报云平台:门已关闭

这个流程看似简单,但每一步都有讲究。比如舵机转动后为什么要等光电开关?因为舵机是开环控制的——你告诉它转多少度,但它不知道自己到底转到了没有。有了光电开关做反馈,系统就能确认门是不是真的打开了,要是舵机卡了或者门被挡住了,还能报错误信息。

还有那个 5 秒自动关门的设计,用的是 HAL_GetTick() 记录开门时间,然后在主循环里不断判断时间差。这种「非阻塞延时」的写法比直接用 LOS_TaskDelay 好多了——在等待的 5 秒里,系统还能响应其他事情,比如按键操作、云平台指令等等。

七、踩过的那些坑

做实验怎么可能不踩坑?下面分享几个我印象深刻的:

1:串口波特率不对

一开始 AS608 完全没反应,查了半天发现 LPUART1 的波特率配成了 115200,而 AS608 默认是 57600。改过来之后立马就握手成功了。这个故事告诉我们:模块不通的时候,先查硬件连接,再查通信参数——波特率、数据位、停止位、校验位,一个都不能错。

2:手指太干/太湿都不行

录入指纹的时候,有时候 PS_GenChar 会返回 0x04(指纹图像太干)或者 0x05(指纹图像太湿)。后来掌握了技巧:按手指的时候力度适中,停留时间稍微长一点,让传感器充分采集图像。而且录入时两次按压的角度最好略有不同,这样生成的模板更全面,后续识别率也更高。

3:状态机的超时退出

Add_FR 函数里有个 i++ 的计数,如果 10 次都没检测到手指就自动退出。一开始我以为 10 次很多,后来发现每次循环有 800ms 的延时,10 次就是 8 秒,其实还挺合理的——总不能用户走了还一直在那等吧?这个设计既给了用户足够的操作时间,又不会无限等待下去。

4:光电开关的触发逻辑搞反了

E18-D80NK 光电开关,检测到物体时输出低电平,没检测到时输出高电平。一开始我搞反了,导致开门关门的判断条件全反了。后来对着 datasheet 仔细看了一遍才搞明白。这种细节问题,看手册永远是最靠谱的。

八、收获与感悟

做完这个实验,我最大的收获不是学会了怎么用 AS608 模块,而是理解了一个完整的物联网嵌入式系统是怎么搭建起来的。

从硬件层面来说,这是一次「多外设协同」的实战:指纹模块、舵机、光电开关、WiFi 模块、LCD、LED、按键——七个外设各司其职,又相互配合。以前学外设都是一个一个单独学的,这次终于把它们串起来了。

从软件层面来说,我对状态机编程、多任务编程、串口通信协议都有了更深的理解。特别是状态机这种思想,不光可以用在指纹录入上,很多需要分步执行的场景都能用——比如通信协议的解析、用户界面的交互流程等等。

从系统层面来说,我理解了「端-边-云」协同的物联网架构。设备端做感知和执行,云平台做管理和控制,两者通过网络协议通信。智能门禁不只是一个门锁,它是整个智能家居系统的一部分,可以和其他设备联动——比如开门后自动开灯、自动撤防安防系统等等。

九、未来可以怎么玩?

实验做完了,但创意是无限的。我想到了几个可以继续折腾的方向:

方向 1:多模态认证

单一的指纹识别还是不够安全。如果加上 RFID 刷卡或者密码键盘,实现「指纹+密码」双重认证,安全性就会大大提高。就像手机的指纹+面部解锁,多一道防线总是好的。

方向 2:指纹考勤

给系统加上 RTC 实时时钟,每次开门都记录是谁、什么时间开的门,然后上传到云平台生成考勤报表。这样一个简单的指纹考勤系统就做出来了,小公司或者实验室用完全没问题。

方向 3:低功耗改造

现在的系统一直跑着,功耗不低。如果做成电池供电的智能门锁,就得好好优化功耗了。STM32 的 STOP 模式 + AS608 的触摸中断唤醒,应该能把功耗降下来不少。

方向 4:临时授权

通过云平台下发临时指纹,有效期 24 小时,到期自动删除。这个功能在酒店、民宿、快递上门等场景太实用了——不用给实体钥匙,也不用担心钥匙被复制。

结语

回头看,一个看似简单的智能门禁,背后竟然有这么多学问。从最底层的光学传感器和 DSP 算法,到中间的串口通信协议和状态机编程,再到上层的多任务调度和云平台通信,每一层都有值得深入研究的东西。

我想,这就是嵌入式物联网的魅力吧——你永远在和硬件、软件、算法、网络打交道,每解决一个问题,就多掌握一分对系统的掌控力。而这种「从零到一把东西做出来」的成就感,真的会上瘾。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐