一个简易指纹锁的制作
我们学校似乎有一个不成文的习俗:
电创每年都有人在大一的时候给宿舍改装指纹锁
刚好有闲置的51单片机,查了下常用的指纹传感器是AS608主控,采用UART协议或USB协议。
查了下AS608传感器资料,发现这货指纹对比是在传感器内处理的,不需要自己写指纹对比算法,也不需要把指纹图形回传,只需要简单的串口通信就能完成指纹识别。(PS:相关资料我会放到底部)
按照最简洁的程序逻辑,仅仅只需要四步:
- 从传感器获取图像
- 根据原始图像生成指纹特征存于缓存
- 将缓存中的特征文件搜索整个指纹库
- 开锁
查阅模块说明可得:
前三步对应的官方指令为:
PS_GetImage
PS_GenChar
PS_Search
翻译为16进制命令包为:
\xef\x01\xff\xff\xff\xff\x01\x00\x03\x01\x00\x05
\xef\x01\xff\xff\xff\xff\x01\x00\x04\x02\x01\x00\x08
\xef\x01\xff\xff\xff\xff\x01\x00\x08\x04\x01\x00\x00\x01\x2b\x00\x3a
应答包格式为:
确认码定义为:
1. 00h:表示指令执行完毕或 OK;
2. 01h:表示数据包接收错误;
3. 02h:表示传感器上没有手指;
4. 03h:表示录入指纹图像失败;
5. 04h:表示指纹图像太干、太淡而生不成特征;
6. 05h:表示指纹图像太湿、太糊而生不成特征;
7. 06h:表示指纹图像太乱而生不成特征;
8. 07h:表示指纹图像正常,但特征点太少(或面积太小)而生不成特征;
9. 08h:表示指纹不匹配;
10. 09h:表示没搜索到指纹;
11. 0ah:表示特征合并失败;
12. 0bh:表示访问指纹库时地址序号超出指纹库范围;
13. 0ch:表示从指纹库读模板出错或无效;
14. 0dh:表示上传特征失败;
15. 0eh:表示模块不能接受后续数据包;
16. 0fh:表示上传图像失败;
17. 10h:表示删除模板失败;
18. 11h:表示清空指纹库失败;
19. 12h:表示不能进入低功耗状态;
20. 13h:表示口令不正确;
21. 14h:表示系统复位失败;
22. 15H:表示缓冲区内没有有效原始图而生不成图像;
23. 16H:表示在线升级失败;
24. 17H:表示残留指纹或两次采集之间手指没有移动过;
25. 18H:表示读写 FLASH 出错;
26. 0xf0:有后续数据包的指令,正确接收后用 0xf0 应答;
27. 0xf1:有后续数据包的指令,命令包用 0xf1 应答;
28. 0xf2:表示烧写内部 FLASH 时,校验和错误;
29. 0xf3:表示烧写内部 FLASH 时,包标识错误;
30. 0xf4:表示烧写内部 FLASH 时,包长度错误;
31. 0xf5:表示烧写内部 FLASH 时,代码长度太长;
32. 0xf6:表示烧写内部 FLASH 时,烧写 FLASH 失败;
33. 0x19:未定义错误;
34. 0x1a:无效寄存器号;
35. 0x1b:寄存器设定内容错误号;
36. 0x1c:记事本页码指定错误;
37. 0x1d:端口操作失败;
38. 0x1e:自动注册(enroll)失败;
39. 0x1f:指纹库满
40. 0x20—0xefh:Reserved。
其中,39种在最简逻辑下都用不到 只需要判断是否返回成功00h
就行了。
也就是判断应答包第十位是否为00h
即可
将上面三行命令包发送给AS608并判断返回值就能完成指纹验证。
不过网上有现成的指令包代码,那我为什么要重复造轮子呢?
简单删改之后,就完成了
另外一个事情就是AS608工作原理的选择:
现在市面上就两种指纹传感器:
- 光学指纹
- 电容指纹
数码圈的都知道 电容的好用,速度快,但是湿手不能解锁
不过问题不大,因为光学的传感器实在是太大了!!!
*到这里 一切都很顺利 原本以为不到1天就能做完一个指纹锁,能打破本校最快记录来着… *
为什么是V1.6呢?因为失败了5次。。。
接下来需要硬件进行测试
线怎么接呢?
官方的模块就按照官方给的引脚定义就行了
不过不同卖家卖的AS608模块引脚定义可能不一样,具体的自己去找
别问我为什么要强调上面一句,因为我曾经就按照官方引脚定义接了个第三方的指纹模块
刚接上去的时候它很热,还有点烫手,不过现在它已经凉了
PS:指纹模块也太贵了吧!根本烧不起
用杜邦线和手头小舵机进行原理验证,很成功
随手焊好板子验证原理,接上我的小舵机,没有任何问题
但是这个舵机明显带不动门栓啊,SG90寿命又不好,那只能换上一个大舵机了。
逛了下淘宝,嗯哼?这么便宜?
够大 够黑 够暴力
不换不知道,一换踩了个大坑。
当时我已经跟舍友打包票说三天后舵机一到 指纹锁就能用来着。。。
看样子flag不能随便立
换上去之后舵机根本就不动
首先排除了舵机问题 因为我用STM32的舵机控制板是能正常工作的
因为舵机控制是50HZ的,并且我本人还没有示波器,只能成天往电创实验室跑
为啥呢?
穷!
首先怀疑供电问题
电压从5V一路调到了8.4V,还是不动
为什么用8.4V呢?因为我想着两节锂电池串联刚好8.4V,网上说了,MG996R能抗7.4V,那不就是锂电池的标准电压么?那两节锂电池充满电压应该没问题
然后示波器打了下供电,发现供电波纹很平稳
然后怀疑PWM吧,空载输出频率正常,电压正常,占空比正常…
接上小舵机 一切正常
接上大舵机 测PWM 一切正常…
见鬼了
然后回到宿舍,连上STM32的舵机控制板,咋TM不转了???
嗯,一定是舵机的问题
然后从网上下单舵机,等快递。
一天..两天…三天…四天…五天……十三天…十四天…
咋还卡在重庆分拨中心?
一看快递商 “百世快递”
算了 我还是重新买吧。。。
三天后 拿到舵机 确保万无一失的情况下 接了上去。。。
咋还不能用???
没事 坚强。。。
然后示波器往供电上打,正常
然后打PWM,,等等,不对劲啊
好嘞 问题找到了
然后怎么处理呢?
59C52的IO口可没有推挽输出,这种内部10K上拉电阻的,本来推力就不大
我就就想到两种解决方案:
- 再外接上拉电阻
- PNP三极管放大
- 换主控
1的话,不稳定 排除。
3的话,懒得换了..
那就2吧。。。常用的PNP是啥来着?S8550?查了下S8550的极限输出,可以,1.5安,够了。
然后我又又又上网买三极管去了。
为啥叫又又又?因为第一回烧指纹,第二回烧舵机,这是第三回。。。别急,后面还有第四回。。。
S8550加进去,PWM取反,一顿操作猛如虎,一看是个二百五
舵机接上去 咋MCU又TM疯狂重启了???
这回想都不用想,肯定是舵机启动瞬间拉低了单片机电流,然后重启
这不简单嘛~ DC-DC升压输出端接个二极管,二极管后面再来几个大电容,这样掉电瞬间就把电源隔离了,足够稳定。
顺便处理一下舵机待机耗电问题吧,毕竟里面也是有一个控制IC的
用什么控制呢?
- 场效应管
- 继电器
用继电器吧,因为继电器能发出声音提示指纹不符
好家伙 又踩进一个大坑。
二极管加进去之后,因为是硅二极管,所以理论上有0.5V的压降的,然后实际一测,0.7V。
算了,几伏无所谓,最后到单片机还有5.2V左右就行了。
然后一不小心又把电压拉高了,单片机又死一个。。。。
还记得刚刚说的大坑么?
51的IO没法直接控制继电器我还是知道的,然后手头还有PNP二极管,就直接用PNP推继电器了
然后发现一个问题:
TM我给51切断VCC之后 为什么继电器是常开状态?
TM没道理啊,51都没电了 51IO内部的等效三极管应该是断开了啊?
算了 不管了 换NPN。
还好NPN比较常用 能直接从电创白嫖
……
…..
…
我想去死一死
51的IO输出实在是太小了,随手在NPN基极和IO之间接上的10K限流电阻经过放大之后居然连继电器都带不动???
然后一个一个试 直到换成220欧,才能稳定导通。
一测此时继电器电压 4V…
算了 4V就4V吧…
又不是不能用…
然后电路图就成了这样(单击放大)
然后一切正常,焊接,安装,刷入…
终于完成了
待机电流,150ma…
算了 懒得管了 又不是不够用,毕竟白天都是有电源输入的,配上4000mah的锂电池,绝对够用。
本代码仅适用于11.0592MHz的12T 51单片机使用,推荐89C52,便宜
新手千万不要买成AT89C52,需要专门的下载器才能下载程序!!!
因为用了PNP三极管去推动舵机信号输入,所以这里的占空比是反向的,IO输出为高时舵机接收到的信号为低!!!
其他频率和机器周期的51单片机需要修改波特率和舵机的相关配置!
不同门锁需要修改舵机角度(占空比)的相关配置!
↓FingerPrintLock.c↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
| #include <reg52.h> #include <stdio.h> #include <string.h> #include "AS608.h" unsigned char count; sbit pwm = P1 ^ 0 ; sbit Relay = P1 ^ 1 ; sbit check = P2 ^ 0; unsigned char jd; uchar xdata Data_buff[MAX]; struct as608 As_608_data = {0}; int judge = 1; char locked = 0;
void delayms(unsigned int i) { unsigned int j, k; for (j = i; j > 0; j--) for (k = 110; k > 0; k--); }
void Time0_Init() { TH0 = (65536 - 440) / 256; TL0 = (65536 - 440) % 256; TMOD = 0x01; IE = 0x82; TR0 = 1; }
void Time0_Int() interrupt 1 { TH0 = (65536 - 440) / 256; TL0 = (65536 - 440) % 256; if (count < jd) { pwm = 0; count = (count + 1); count = count % 40; } else { pwm = 1; count = (count + 1); count = count % 40; }
}
void UART_Init(void) { SCON = 0x50; TMOD &= 0x0f; TMOD = 0x20; PCON = 0x80; TH1 = 0xFF; TL1 = 0xFF; ET1 = 0; TR1 = 1; }
void Send_Bytes(uchar *c, uchar len) { uchar i = 0; for (i = 0; i < len; i++) { SBUF = *(c + i); while (!TI); TI = 0; } }
char Receive_Bytes(uchar *c, uchar len) { uchar i = 0; uchar time = 200; for (i = 0; i < len; i++) { while (!RI && time--) { if (time <= 1) return MI_NOTAGERR; } *(c + i) = SBUF; RI = 0; } return MI_OK; }
char PS_GetImage() {
uchar *ps2 = "\xef\x01\xff\xff\xff\xff\x01\x00\x03\x01\x00\x05";
Send_Bytes(ps2, 12);
while (RI == 0);
if (Receive_Bytes(Data_buff, 12)) { return MI_ERR; }
if (Data_buff[9] == 0x00 && Data_buff[11] == 0x0a) { return MI_OK; } return MI_ERR; }
char PS_GenChar(uchar BufferID) {
uchar *ps1 = "\xef\x01\xff\xff\xff\xff\x01\x00\x04\x02\x01\x00\x08"; uchar *ps2 = "\xef\x01\xff\xff\xff\xff\x01\x00\x04\x02\x02\x00\x09"; if (BufferID == 0x01) { Send_Bytes(ps1, 13); } else { Send_Bytes(ps2, 13); }
while (RI == 0);
if (Receive_Bytes(Data_buff, 12)) { return MI_ERR; }
if (Data_buff[9] == 0x00 && Data_buff[11] == 0x0a) { return MI_OK; }
return MI_ERR; }
char PS_Search(uchar BufferID) {
uchar *ps1 = "\xef\x01\xff\xff\xff\xff\x01\x00\x08\x04\x01\x00\x00\x01\x2b\x00\x3a"; uchar *ps2 = "\xef\x01\xff\xff\xff\xff\x01\x00\x08\x04\x02\x00\x00\x01\x2b\x00\x3b";
if (BufferID == 0x01) { Send_Bytes(ps1, 17); } else { Send_Bytes(ps2, 17); }
while (RI == 0);
if (Receive_Bytes(Data_buff, 16)) { return MI_ERR; }
if (Data_buff[9] == 0x00) { return MI_OK; }
return MI_ERR; }
void lock() { Relay = 1; delayms(100); Time0_Init(); count = 0; jd = 2; delayms(1000); locked = 1; Relay = 0; }
void unlock() { Relay = 1; delayms(100); Time0_Init(); count = 0; jd = 5; delayms(1000); if (check == 1) { delayms(2500); } else { delayms(1500); } Time0_Init(); count = 0; jd = 2; delayms(1000); Relay = 0; } void main() { check = 1; lock(); unlock(); RI = 0; while (1) { UART_Init(); if (PS_GetImage() == MI_OK) { if (PS_GenChar(0x01) == MI_OK) { if (PS_Search(0x01) == MI_OK) { unlock(); } else { Relay = 1; delayms(250); Relay = 0; delayms(250); Relay = 1; delayms(250); Relay = 0; delayms(250); } } } } }
|
↓AS608.h↓
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
|
#ifndef _AS608_H_
#include "reg52.h"
#ifndef uchar #define uchar unsigned char #endif
#ifndef uint #define uint unsigned int #endif
#ifndef ulong #define ulong unsigned long #endif
#define MAX 128
#define MI_OK 0 #define MI_NOTAGERR (-1) #define MI_ERR (-2)
typedef struct as608 { uint StateRegister;
uint SensorType;
uint zw_library;
uint SecurLevel;
unsigned long addr;
uint CFG_PktSize;
unsigned long UARTs;
unsigned long Random;
};
extern struct as608 As_608_data;
void SendOneByte(unsigned char c); char Receive_Bytes(uchar *c,uchar len); void UART_Init(void); char PS_GetImage(); char PS_GenChar(uchar BufferID); char PS_Match(); char PS_Search(uchar BufferID); char PS_RegModel(); char PS_StoreChar(uchar BufferID,uint PageID); char PS_LoadChar(uchar BufferID,uint PageID); char PS_Empty(); char PS_VfyPwd(); char PS_Enroll(); char PS_Identify(); char PS_DeletChar(uint PageID,uint count); char PS_WriteReg(uchar reg,uchar cont); char PS_ReadSysPara(); char PS_Enroll(); char PS_Identify(); char PS_SetPwd(unsigned long PassWord); char PS_VfyPwd(unsigned long PassWord); char PS_GetRandomCode(); char PS_StoreChar(uchar BufferID,uint PageID);
char Record_SaveLibrary(uint ID);
#endif
|
一算时间,花了快一个月…
这尼玛不就是平均水平么???
尼玛,快递卡路上卡了15天…
当初要是用Arduino或者Stm32就没有这么多事!
算了,挑战不了最快,那就挑战最省电吧…
用32还是51?管他什么RTC,我就用51了,用外围电路就干他RTC…
Emmmm…看了下时间…快期末了啊…寒假再说吧…
PS
关于录指纹:
|建议用电脑直接录,因为有屏幕,单片机再外接一个屏幕做UI太鸡肋了。毕竟都上大学了 谁宿舍还没台电脑啊
|建议使用USB协议而不是串口协议,因为串口实在是太慢了。
|建议一个人只录一个指头 每个指头不同角度录5-10个就行了。
下一版本将于一个月内发布。
更新日志
2020-01
电路图上增加了续流二极管
2020-02-15
优化了一字节的ROM
Video
Download
AS608官方资料
文件太大了 百度一堆就不放了
程序&电路图