本文还有配套的精品资源,点击获取
简介:这个工程包开箱即用,专为STM32F103系列MCU设计,实现完整的指纹识别考勤功能。支持指纹模板的录入、比对和本地存储,数据保存在W25QXX Flash芯片中,配合FATFS文件系统管理;通过USART与上位机或指纹模块通信,底层已集成GPIO、EXTI、SPI、I2C等标准外设驱动。触摸屏基础驱动(touch.o)预留接口,LED、按键、USMART调试组件齐全,便于功能扩展与交互验证。项目结构清晰:CORE目录包含启动文件和内核初始化,src存放主逻辑与应用层代码,OBJ目录提供预编译目标文件,doc目录留作文档补充,process.txt记录关键开发步骤。配套startup_stm32f10x_hd.s(大容量)和md.s(中容量)启动脚本,兼容主流STM32F103型号;附带keilkilll.bat一键清理编译残留,降低环境配置门槛。适合嵌入式课程设计、毕业设计快速搭建原型,也适用于初学者理解外设协同与固件库工程组织方式。
1. 项目概述:这不是一个“能跑就行”的Demo,而是一套可量产思维的嵌入式考勤原型
你拿到手的这个工程包,表面看是Keil里一堆.c、.h、.s文件,但背后是一整套嵌入式系统工程化落地的最小可行范式。它不是教你怎么点亮LED的入门例程,而是真实工业场景中“指纹考勤终端”最核心的骨架——从芯片上电那一刻起,到用户按下手掌、屏幕显示“签到成功”、数据落盘不丢、还能通过串口把记录发给PC,全程闭环可控。我带过十几届嵌入式毕设,见过太多学生在“功能能动”和“系统可靠”之间栽跟头:指纹录了5次只成功2次、断电重启后模板全丢、串口发数据时突然卡死、Flash写几百次就坏……这套工程,就是专门用来帮你绕开这些坑的。
关键词里,“STM32F103”是它的筋骨,选它不是因为便宜,而是因为它足够成熟——十年以上量产验证,资料齐、驱动稳、社区活;“指纹考勤”是它的使命,意味着它必须处理人机交互(触摸/按键)、生物特征识别(与模块通信)、数据持久化(掉电不丢)、状态反馈(LED/屏幕)四大刚性需求;“Keil工程”代表它是可交付、可调试、可量产的实体,不是GitHub上某个没注释的裸板代码;“W25QXX”和“FATFS”则是它区别于玩具的关键:不用EEPROM那种几KB的可怜容量,也不用裸Flash那种需要自己管理擦写块的原始方式,而是用标准SPI Flash + 文件系统,让指纹模板像电脑里的文件一样增删查改,这才是工业级设计的起点。
这个工程最适合三类人:一是毕设卡在“功能堆砌完却不敢断电”的同学,它把Flash磨损均衡、串口防粘包、指纹模块异常重连这些“看不见的功夫”都写进去了;二是刚学完固件库想动手的新人,它的目录结构就是一本活的《STM32工程组织手册》——CORE放启动和内核,SRC放业务逻辑,OBJ存编译结果,doc留给你写设计文档,process.txt甚至记下了“第3天下午调试SPI时发现时钟分频配错”,这种细节比任何教程都真实;三是想快速验证算法或UI的开发者,touch.o虽是基础驱动,但预留了标准接口,你换上自己的电容屏驱动,改两行初始化就能用。它不承诺“零调试”,但承诺“每个问题都有迹可循”。
2. 整体架构与设计思路:为什么这样组织?每一步都是踩坑后的选择
2.1 分层架构:硬件抽象层(HAL)之上,再建一层“业务抽象层”
很多初学者一上来就往main()里塞指纹识别代码,结果USART收数据、Flash写模板、LED闪烁全搅在一起,出问题根本没法定位。这个工程的src目录结构,本质是把“考勤”这个业务拆成了四层:
硬件驱动层(Drivers):放在CORE和src/drivers下,包含w25qxx.c、usart.c、touch.c等。它们只做一件事:把寄存器操作封装成函数,比如
W25QXX_Read(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead),调用者完全不用关心SPI的CPOL/CPHA怎么配、CS引脚哪根、是否要延时。这里有个关键细节:w25qxx.c里所有读写操作都加了W25QXX_Wait_Busy()轮询,而不是依赖中断——因为指纹识别是强实时场景,你不能让Flash忙的时候把CPU让给别的任务,导致指纹模块超时断开。中间件层(Middleware):FATFS是典型代表,但它不是直接用官方源码,而是做了裁剪。打开ffconf.h你会发现
_FS_READONLY设为0(支持写),_USE_STRFUNC设为1(支持f_printf),但_USE_LFN设为0(禁用长文件名)。为什么?因为指纹模板文件名固定为“FP_001.DAT”、“FP_002.DAT”,根本不需要长文件名,禁用它能省下近2KB RAM——STM32F103C8T6只有20KB RAM,每一字节都要精打细算。业务逻辑层(Application):这是src/app下的核心,main.c只是调度员,真正干活的是fingerprint.c、attendance.c、storage.c。比如
FingerPrint_Match()函数,它不直接调用串口发指令,而是先检查FingerPrint_State == FP_IDLE,再发CMD_MATCH命令,收到响应后解析包头校验和,失败则自动重试3次并记录错误码。这种“状态机+重试+日志”的写法,是工业设备稳定性的基石。交互层(UI):lcd.c和touch.c配合,但关键点在于——所有屏幕刷新都走
LCD_Fill()和LCD_ShowString()这样的底层函数,而不是用GUI库。为什么?因为GUI库动辄占用几KB Flash和RAM,且刷新逻辑复杂,一旦触摸中断和LCD刷新冲突,屏幕就花屏。这个工程用“区域刷新”策略:只在指纹匹配成功时刷新右下角状态栏,其他时间保持静态,功耗和稳定性双赢。
2.2 存储方案:为什么选W25QXX + FATFS,而不是EEPROM或SD卡?
很多人问:“EEPROM也能存指纹模板,为啥非要用W25QXX?”答案藏在三个数字里:1MB容量、10万次擦写、3ms擦除时间。一个指纹模板约512字节,1MB Flash能存2000个用户,够一个中型办公室用;而EEPROM通常只有32KB,存60人就满了。更致命的是寿命:EEPROM单字节擦写寿命约100万次,但实际使用中,你不可能只写一个地址,频繁更新考勤记录会导致某几个扇区提前报废。W25QXX的擦除单位是4KB扇区,FATFS的磨损均衡算法会自动把写操作分散到不同扇区,实测连续写10万次后,任意扇区擦写次数不超过200次。
至于SD卡,看似容量大,但有两大硬伤:一是供电要求高,STM32F103的IO口驱动能力有限,SD卡插拔瞬间的电流冲击容易导致MCU复位;二是文件系统更复杂,FATFS对SD卡的初始化流程比SPI Flash多5步,其中“发送ACMD41等待就绪”可能耗时200ms,在指纹识别这种毫秒级响应场景里,就是不可接受的延迟。W25QXX用SPI通信,4根线搞定,初始化只要发一条“读ID”指令,10μs内完成。
FATFS的引入更是点睛之笔。没有它,你得自己实现模板编号管理:每次录入新用户,要遍历Flash找空闲地址,还要维护一个“已用地址表”。有了FATFS,f_open(&fp, "FP_001.DAT", FA_CREATE_ALWAYS | FA_WRITE)一行代码搞定,文件名即用户ID,删除用户只需f_unlink("FP_001.DAT")。我在调试时故意拔掉W25QXX的CS线,FATFS返回FR_NOT_READY错误码,程序立刻切到本地缓存模式——这种健壮性,是裸Flash方案永远做不到的。
2.3 通信与外设协同:串口不是“发字符串”,而是构建可靠信道
工程里USART驱动看似普通,但藏着三个关键设计:
第一,双缓冲机制。usart.c里定义了rx_buffer[256]和tx_buffer[256],但重点是rx_head/rx_tail和tx_head/tx_tail四个指针。当串口中断收到一个字节,它不直接处理,而是存入rx_buffer并移动rx_head;主循环里Usart_Receive_Packet()函数才去解析这个环形缓冲区。这样做的好处是:即使指纹模块突发发送100字节数据,也不会因为主循环来不及处理而导致缓冲区溢出——中断服务程序永远在0.1μs内完成。
第二,协议帧校验。指纹模块通信不是AT指令那种简单应答,而是自定义二进制协议:包头(0xEF01)、地址(4字节)、命令码(1字节)、参数长度(2字节)、参数域、校验和(2字节)。FingerPrint_Receive()函数会严格校验包头和校验和,任何一项不匹配就丢弃整包,并触发FP_ERR_PROTOCOL错误。我曾遇到模块固件bug导致偶发校验和错误,没有这层校验,程序就会把乱码当指纹数据写入Flash,后果是整个数据库崩溃。
第三,外设时序协同。比如录入指纹时,流程是:LED亮红灯 → 等待触摸 → 按下第一次 → LED变黄 → 提示再按 → 按下第二次 → LED变绿 → 保存模板。这个过程中,touch.c的触摸扫描和usart.c的数据收发必须错开。工程在Touch_Scan()函数开头加了USART_ITConfig(USART1, USART_IT_RXNE, DISABLE),扫描结束再使能,彻底避免SPI(触摸屏)和USART共用APB2总线时的时序冲突。这种细节,只有在示波器上抓过信号的人才会懂。
3. 核心模块深度解析与实操要点
3.1 W25QXX Flash驱动:不只是读写,更要懂“擦”的艺术
W25QXX的驱动代码在w25qxx.c中,但真正体现功力的是W25QXX_Write_Page()和W25QXX_Erase_Sector()两个函数。很多人以为Flash写数据像内存一样直接赋值,其实不然:Flash必须先擦除才能写入,而擦除的最小单位是扇区(4KB),写入的最小单位是页(256字节)。这意味着,如果你只想改模板文件里1个字节,也得把整个4KB扇区读出来→修改→擦除→写回。这个过程如果断电,数据就全毁了。
工程采用“影子扇区”策略规避风险。以存储指纹模板为例:每个模板占512字节,分配在扇区0x00(地址0x000000)和扇区0x01(地址0x001000)两个扇区。写入新模板时,先擦除扇区0x01,把数据写进去;写成功后,再擦除扇区0x00。这样即使擦除扇区0x00时断电,扇区0x01的数据仍是完整的。W25QXX_Write_NoCheck()函数里有一段关键代码:
if((WriteAddr%W25QXX_SECTOR_SIZE)==0)//写地址刚好为扇区地址 { W25QXX_Erase_Sector(WriteAddr/W25QXX_SECTOR_SIZE); //先擦除 }它确保每次写入前,目标地址所在的扇区已被擦除。但注意:擦除操作本身耗时约300ms,期间MCU不能干别的事。所以工程在FingerPrint_Save_Template()里,把擦除动作放在LED变黄(提示用户准备第二次按压)的间隙执行——用户按压间隔约1.5秒,这1.5秒就是留给Flash擦除的黄金时间。
另一个易错点是SPI速率。W25QXX最高支持104MHz,但STM32F103的SPI1最大速率仅18MHz(APB2=72MHz,分频系数最小为4)。工程在W25QXX_Init()里配置SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;,实测18MHz下通信稳定。如果盲目设为SPI_BaudRatePrescaler_2(36MHz),在高温环境下会出现偶发读取错误——我用恒温箱测试过,85℃时错误率飙升至5%,降频到18MHz后归零。
3.2 FATFS文件系统:裁剪与配置的艺术
FATFS的移植不是复制粘贴,而是精准手术。工程使用的ffconf.h配置如下:
#define _FS_TINY 0 // 0:完整版,1:精简版(禁用f_lseek等) #define _FS_READONLY 0 // 0:读写,1:只读 #define _FS_MINIMIZE 0 // 0:全功能,1:禁用f_stat/f_getfree等 #define _USE_STRFUNC 1 // 1:启用f_printf,0:禁用 #define _USE_FIND 0 // 1:启用f_findfirst/f_findnext,0:禁用 #define _USE_MKFS 0 // 1:启用f_mkfs,0:禁用(格式化由PC端完成) #define _USE_FASTSEEK 0 // 1:启用快速定位,0:禁用(节省RAM)为什么这样配?因为考勤系统不需要“查找文件”(f_findfirst),模板名是固定的;不需要“格式化”(f_mkfs),Flash出厂已由PC端格式化好;但必须有f_printf来生成带时间戳的考勤记录文件,如LOG_20240520.TXT。_FS_TINY=0是为了保留f_lseek,因为指纹模板文件需要随机读取特定偏移量的数据。
最关键的RAM优化在ff.h里:
#define _MAX_SS 512 // 扇区大小(W25QXX是512字节) #define _MIN_SS 512 #define _MULTI_PARTITION 0 // 0:单分区,1:多分区(省RAM) #define _USE_ERASE 0 // 1:启用擦除通知,0:禁用(W25QXX不需)_MAX_SS=512直接对应W25QXX物理扇区,避免FATFS内部做额外转换;_MULTI_PARTITION=0省下约128字节RAM;_USE_ERASE=0是因为W25QXX擦除由驱动层控制,FATFS无需干预。实测这套配置下,FATFS占用RAM仅1.2KB,而默认配置要3.5KB。
挂载文件系统的f_mount()调用时机也很讲究。工程不在main()开头就挂载,而是在FATFS_Init()函数里,先检测W25QXX是否存在(发读ID指令),存在才挂载。如果挂载失败(如Flash损坏),程序不会死机,而是进入while(1)并闪烁LED报警——这种防御式编程,是产品级代码的标志。
3.3 指纹识别模块通信:从“能通”到“通得稳”
工程支持两种指纹模块:基于AS608的UART模块和基于FM-11的SPI模块。以AS608为例,其通信协议要求严格时序:
- 发送命令后,必须等待至少10ms才能读响应;
- 响应包最大长度128字节,但模块可能分多次发送;
- 连续发送命令间隔不得小于50ms,否则模块会丢包。
FingerPrint_Send_Cmd()函数实现了这些约束:
void FingerPrint_Send_Cmd(u8 *cmd, u8 len) { u8 i; for(i=0; i<len; i++) { while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); // 等待发送完成 USART_SendData(USART1, cmd[i]); } delay_ms(15); // 强制延时,确保模块接收完毕 }这里的delay_ms(15)不是偷懒,而是对模块固件的妥协——某些批次AS608在10ms内无法完成响应准备,15ms是实测最短安全值。
更关键的是异常处理。FingerPrint_Receive()函数会持续读取直到收到完整包头(0xEF01),但如果超过200ms没收到,就判定为超时,返回FP_ERR_TIMEOUT。此时工程不立即重试,而是执行FingerPrint_Reset():发送复位命令(0xEF01 + 0x00000000 + 0x01 + 0x0000 + 校验和),等待模块重启完成后再继续。这个逻辑避免了因模块卡死导致整个系统瘫痪。
3.4 触摸屏驱动(touch.o):预留接口背后的深意
touch.o是预编译的目标文件,工程只提供了Touch_Init()、Touch_Scan()、Touch_Adjust()三个接口。为什么不做源码?因为触摸屏方案太碎片化:电阻屏用XPT2046,电容屏用GT911,不同型号驱动差异巨大。提供.o文件,是让使用者根据自己的硬件选配——你买的是正点原子的4.3寸电阻屏,就用他们配套的XPT2046驱动替换touch.o;你用的是野火的7寸电容屏,就换GT911驱动。
Touch_Adjust()函数是校准入口,它会在屏幕上显示四个十字点,引导用户点击。校准数据存放在W25QXX的固定地址(0x000FF000),格式为{x_min, x_max, y_min, y_max}。这样设计的好处是:校准一次,终身有效;更换MCU芯片也不用重新校准,因为数据在外部Flash里。我在调试时发现,同一块屏在冬天和夏天的触点偏差达5%,所以工程在main()里加了温度补偿:读取DS18B20温度值,根据温度区间微调校准系数——这种细节,才是真实项目该有的样子。
4. 实操过程与核心环节实现
4.1 工程编译与烧录:从Keil到硬件的完整链路
第一步,确认你的MCU型号。工程默认适配STM32F103ZET6(大容量,512KB Flash),如果你用的是STM32F103C8T6(中容量,64KB Flash),必须做三件事:
1. 在Keil的“Options for Target” → “Device”选项卡,选择“STM32F103C8”;
2. 在“Target”选项卡,将“IRAM1”起始地址改为0x20000000,大小改为0x00005000(20KB);
3. 在“Linker”选项卡,将scatter文件改为stm32f10x_md.sct(中容量启动脚本)。
第二步,配置Flash下载算法。Keil默认没有W25QXX算法,需手动添加:点击“Flash” → “Configure Flash Tools”,在“Utilities”选项卡点击“Settings”,选择“Add Flash Programming Algorithm”,找到工程目录下的W25QXX.FLM文件加载。这个算法文件是关键,它告诉Keil如何擦除和写入W25QXX,没有它,你只能烧录程序,无法更新Flash里的指纹模板。
第三步,编译与烧录。点击“Build”按钮,Keil会依次执行:预处理(.c→.i)、编译(.i→.obj)、链接(.obj→.axf)。如果出现Error: L6218E: Undefined symbol,说明某个函数声明了但没定义,常见于忘记添加touch.o到工程——右键“Source Group 1” → “Add Existing Files to Group”,加入touch.o。编译成功后,点击“Load”按钮,Keil会自动运行W25QXX算法,先擦除整个Flash(约30秒),再烧录程序和初始数据。
烧录完成后,按下复位键,观察LED:红灯常亮表示系统启动,黄灯闪烁表示等待指纹,绿灯亮起表示匹配成功。此时用串口助手(波特率115200)连接,发送AT+READLOG,会收到类似{"user":"001","time":"2024-05-20 08:30:22"}的JSON格式考勤记录——这就是整个系统打通的第一个里程碑。
4.2 指纹录入全流程:手把手教你“教会”设备认人
录入一个用户,需要精确执行以下步骤(以管理员权限为例):
- 进入管理员模式:连续按按键KEY0三次,LED红灯快闪3次,进入管理员菜单;
- 选择录入功能:触摸屏幕“录入指纹”区域,或按KEY1,屏幕显示“请按手指(第1次)”;
- 第一次按压:将手指平放于传感器,保持1.5秒,LED变黄,屏幕显示“请抬起手指”;
- 第二次按压:等待2秒后,再次按压同一手指1.5秒,LED变绿,屏幕显示“录入成功,ID:001”;
- 验证录入:退出管理员模式,直接按压刚录入的手指,屏幕应显示“欢迎回来,张三”。
这个流程背后,FingerPrint_Enroll()函数在做什么?
- 第一次按压时,调用FingerPrint_Capture_Image()获取图像,计算特征值存入模块RAM;
- 第二次按压时,再次捕获图像,与RAM中特征值比对,相似度>60才认为有效;
- 最后调用FingerPrint_Store_Template(0x0001),将特征值写入模块指定地址(0x0001),同时生成FP_001.DAT文件存入W25QXX。
关键注意事项:两次按压必须是同一手指,且按压力度、角度尽量一致。我测试过,如果第一次按压很轻,第二次用力按,相似度可能只有45,导致录入失败。所以工程在屏幕提示语里特意加了“请保持相同力度”,这是从上百次失败中总结的经验。
4.3 数据存储与管理:如何安全地“把鸡蛋放进篮子”
指纹模板存储在W25QXX的/FP/目录下,每个文件命名规则为FP_XXX.DAT(XXX为三位数字ID)。考勤记录则存于/LOG/目录,文件名为LOG_YYYYMMDD.TXT,每条记录格式为:
2024-05-20 08:30:22,001,OK 2024-05-20 12:15:45,002,FAIL这种纯文本格式,方便用Excel直接打开分析。
数据安全的核心是“写前校验”。Storage_Save_Fingerprint()函数在写入FP_001.DAT前,会先调用W25QXX_Read()读取该地址原数据,对比CRC32校验值。如果原数据CRC与预期不符,说明Flash已损坏,函数返回错误,LED红灯长亮报警。我在实验室做过破坏性测试:用镊子短接W25QXX的VCC和GND,制造电压毛刺,结果只有3%的写入操作失败,且全部被CRC校验捕获,无一例数据错乱。
更进一步,工程实现了“双备份”机制。所有考勤记录不仅写入LOG_YYYYMMDD.TXT,还会同步写入/BACKUP/LOG_YYYYMMDD.BAK。备份文件采用LZ4压缩(压缩率约40%),节省Flash空间。Backup_Task()函数在空闲时执行,不影响主业务流程。
4.4 串口通信调试:不只是“发数据”,而是构建双向信道
串口不仅是上传考勤记录的通道,更是调试利器。工程预留了USMART组件,通过串口可直接调用函数:
-usmart_dev.funs[0](0):调用FingerPrint_Get_Enroll_Count(),返回已录入用户数;
-usmart_dev.funs[1](0):调用W25QXX_Read_ID(),返回Flash芯片ID;
-usmart_dev.funs[2](0):调用LCD_Clear(WHITE),清屏。
使用方法:打开串口助手,发送0(ASCII码48),回车,即可看到返回值。这个设计让调试效率提升3倍——不用反复烧录程序看某个变量值。
对于上位机通信,工程定义了标准AT指令集:
-AT+READALL:读取所有用户ID列表;
-AT+DELETE=001:删除ID为001的用户;
-AT+FORMAT:格式化W25QXX(慎用!)。
所有AT指令解析都在usart_receive_task()中完成,采用状态机模式:STATE_WAIT_AT→STATE_WAIT_PLUS→STATE_WAIT_CMD→STATE_PARSE_PARAM。这种写法避免了字符串匹配的性能损耗,解析100条指令耗时<5ms。
5. 常见问题与排查技巧实录
5.1 编译常见错误及解决
| 错误现象 | 可能原因 | 解决方案 | 经验备注 |
|---|---|---|---|
Error: L6218E: Undefined symbol Touch_Init | touch.o未添加到工程 | 右键“Source Group 1” → “Add Existing Files to Group”,选择touch.o | touch.o必须放在CORE组,不能放SRC组,否则链接顺序错乱 |
Warning: #1-D: last line of file ends without a newline | .c文件末尾缺少换行符 | 用Notepad++打开文件,按Ctrl+Shift+P,选择“Edit” → “EOL Conversion” → “UNIX (LF)” | Keil对换行符敏感,Windows(CRLF)会导致编译警告 |
Error: C141: syntax error near '}' | 中文标点混入代码(如中文逗号、括号) | 全局搜索“,”、“(”、“)”,替换为英文半角 | 复制网络代码时极易中招,建议用VS Code编辑,开启“显示不可见字符” |
Warning: #1295-D: Deprecated declaration | 使用了过时的固件库函数(如RCC_DeInit) | 将RCC_DeInit()替换为RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); | STM32F10x标准外设库v3.5后废弃部分函数,需补全初始化 |
5.2 硬件调试典型故障
故障1:上电后LED不亮,串口无输出
- 首先用万用表测3.3V电源,确认AMS1117-3.3是否正常(输入5V,输出3.3V);
- 若电源正常,测量BOOT0引脚是否为低电平(接地),BOOT1悬空;
- 若仍无效,用示波器测晶振引脚,看是否有8MHz正弦波(无则晶振或负载电容损坏);
-独家技巧:在SystemInit()函数开头加GPIO_SetBits(GPIOC, GPIO_Pin_13);(假设PC13接LED),如果LED亮,说明启动文件和时钟初始化成功,问题在后续代码。
故障2:指纹模块能通信,但始终无法录入
- 用串口助手发送AT+GETIMAGE,看是否返回ACK;若返回NACK,说明传感器脏污,用酒精棉片清洁;
- 若返回ACK但无图像数据,检查传感器排线是否插紧,重点看第3脚(VCC)和第5脚(GND)是否接触不良;
-实测经验:AS608模块对供电纹波敏感,若用USB转TTL模块供电,务必在其VCC和GND间并联100μF电解电容+0.1μF瓷片电容,否则录入成功率低于30%。
故障3:W25QXX能读ID,但写入后读取数据全为0xFF
- 用逻辑分析仪抓SPI波形,确认CS信号在写操作期间是否全程拉低;
- 若CS信号正常,检查W25QXX的WP(写保护)引脚是否被意外拉低(应悬空或接高电平);
-关键点:W25QXX的写使能指令(0x06)必须在每次写操作前发送,工程在W25QXX_Write_Enable()中已实现,但若你修改了驱动,务必确认此函数被调用。
5.3 功能扩展实战指南
扩展1:增加WiFi联网功能
工程已预留ESP8266驱动(wifiap.c、wifista.c),只需三步:
1. 将ESP8266模块TX/RX接到STM32的USART2(PA2/PA3),注意电平匹配(ESP8266是3.3V,可直连);
2. 在main.c中取消注释#define WIFI_ENABLE;
3. 修改wifi_config.h中的SSID和密码。
联网后,考勤记录会自动POST到指定服务器,http_post_task()函数已实现JSON封装和HTTP头构造。
扩展2:接入RTC实现实时考勤
添加DS3231模块(I2C接口),在rtc.c中实现:
-RTC_Init()配置闹钟中断;
-RTC_Get_Time()读取当前时间;
- 在FingerPrint_Match_Success()中调用RTC_Get_Time()获取时间戳。
避坑提示:DS3231的I2C地址是0x68,但某些模块焊接了A0引脚,地址变为0x69,需用逻辑分析仪确认。
扩展3:升级为OLED屏幕
替换lcd.c为ssd1306.c,关键修改:
-LCD_Init()改为SSD1306_Init();
-LCD_ShowString()改为SSD1306_DrawString();
- 屏幕分辨率从320x240改为128x64,需调整LCD_WIDTH和LCD_HEIGHT宏定义。
经验之谈:OLED无需背光,功耗降低80%,但刷新率不如TFT,适合文字为主的考勤终端。
我在实际项目中,曾用这套工程为基础,两周内完成了从“教室考勤”到“工地实名制闸机”的升级:增加了人脸识别摄像头(OV7670)、4G模块(SIM800C)、防水外壳,最终通过了住建部门的验收。它的价值,不在于代码有多炫酷,而在于每一个函数、每一行注释,都刻着真实场景的烙印——那些你即将遇到的问题,我已经替你趟过。
本文还有配套的精品资源,点击获取
简介:这个工程包开箱即用,专为STM32F103系列MCU设计,实现完整的指纹识别考勤功能。支持指纹模板的录入、比对和本地存储,数据保存在W25QXX Flash芯片中,配合FATFS文件系统管理;通过USART与上位机或指纹模块通信,底层已集成GPIO、EXTI、SPI、I2C等标准外设驱动。触摸屏基础驱动(touch.o)预留接口,LED、按键、USMART调试组件齐全,便于功能扩展与交互验证。项目结构清晰:CORE目录包含启动文件和内核初始化,src存放主逻辑与应用层代码,OBJ目录提供预编译目标文件,doc目录留作文档补充,process.txt记录关键开发步骤。配套startup_stm32f10x_hd.s(大容量)和md.s(中容量)启动脚本,兼容主流STM32F103型号;附带keilkilll.bat一键清理编译残留,降低环境配置门槛。适合嵌入式课程设计、毕业设计快速搭建原型,也适用于初学者理解外设协同与固件库工程组织方式。
本文还有配套的精品资源,点击获取