首先先附上原理图,如下图所示。为了方便制版,于是画了原理图进行PCB打板。
PCB的三维图如下图所示。看起来还是很不错的。由于指纹模块AS608的供电电压是3.3v,大家可以买AMS1117模块进行降压。我没有试过AS608接5V。大家可以试一下,如果可以用那还省事一点。
首先肯定是要录入指纹的,录入指纹的指令与详解如下所示,我就不一 一赘述了。我直接讲解我写这个代码的思路叭。首先可以肯定的是串口初始化,串口初始化完成之后,我们可以将包头、芯片地址之类的生成一个不可改变的数组,通过调用数组去发送,会让代码看起来更容易理解一点。
我的程序里并没有用到,所以导致录入指纹的时候会出现同一个手指录了两个不同指纹。我想应该是没有比对的原因。
搜索指纹,由于AS608有内部flash,所以可以存好多枚指纹,于是大家在扫描指纹开锁的时候,可以采用搜索指纹的方式,看看是否内部存有指纹进行开锁。
另外在附录一下24C02的代码,大家可以借鉴学习一下。
/*************************************************
D罗电子设计qq:1679960378 qq:672939453
***************************************************/
#include
#include
#include "24C02.h"
//#include"mytype.h"
#include <24c02.h>
void nop()
{
_nop_();
_nop_();
}
/24C02读写驱动程序
void delay1(unsigned int m)
{
unsigned int n;
for(n=0;n<m;n++);
}
void x24c02_init() //24C02初始化
{
wp=0;
scl=1;
nop();
sda=1;
nop();
}
void start() //启动I2C总线
{
sda=1;
nop();
scl=1;
nop();
sda=0;
nop();
scl=0;
nop();
}
void stop() //停止I2C总线
{
sda=0;
nop();
scl=1;
nop();
sda=1;
nop();
}
void writebyte(unsigned char j) //写一个字节
{
unsigned char i,temp;
temp=j;
for (i=0;i<8;i++)
{
temp=temp<<1;
scl=0;
nop();
sda=CY; //temp左移时,移出的值放入了CY中
nop();
scl=1; //待sda线上的数据稳定后,将scl拉高
nop();
}
scl=0;
nop();
sda=1;
nop();
}
unsigned char readbyte() //读一个字节
{
unsigned char i,j,k=0;
scl=0; nop(); sda=1;
for (i=0;i<8;i++)
{
nop(); scl=1; nop();
if(sda==1)
j=1;
else
j=0;
k=(k<<1)|j;
scl=0;
}
nop();
return(k);
}
void clock() //I2C总线时钟
{
unsigned char i=0;
scl=1;
nop();
while((sda==1)&&(i<255))
i++;
scl=0;
nop();
}
从24c02的地址address中读取一个字节数据/
unsigned char read24c02(unsigned char address)
{
unsigned char i;
start();
writebyte(0xa0);
clock();
writebyte(address);
clock();
start();
writebyte(0xa1);
clock();
i=readbyte();
stop();
delay1(100);
return(i);
}
//向24c02的address地址中写入一字节数据info/
void write24c02(unsigned char address,unsigned char info)
{
start();
writebyte(0xa0);
clock();
writebyte(address);
clock();
writebyte(info);
clock();
stop();
delay1(5000); //这个延时一定要足够长,否则会出错。因为24c02在从sda上取得数据后,还需要一定时间的烧录过程。
}
/*************************************************
D罗电子设计
***************************************************/
#include
#include
#include"12864.h"
/**************************************************************
这里采用串行方式控制,因为不要接数据传输的脚,为了省实物焊接过程。
**************************************************************/
unsigned char code AC_TABLE[]={
//坐标编码
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
};
/****************************************************************
发送一个字节
*****************************************************************/
void SendByte(unsigned char Dbyte)
{
unsigned char i;
for(i=0;i<8;i++)
{
SCK = 0;
Dbyte=Dbyte<<1;
SID = CY;
SCK = 1;
SCK = 0;
}
}
/**********************************************************
接收一个字节
***********************************************************/
unsigned char ReceiveByte(void)
{
unsigned char i,temp1,temp2;
temp1=temp2=0;
for(i=0;i<8;i++)
{
temp1=temp1<<1;
SCK = 0;
SCK = 1;
SCK = 0;
if(SID) temp1++;
}
for(i=0;i<8;i++)
{
temp2=temp2<<1;
SCK = 0;
SCK = 1;
SCK = 0;
if(SID) temp2++;
}
return ((0xf0&temp1)+(0x0f&temp2));
}
/****************************************************************
检查忙状态
******************************************************************/
void CheckBusy( void )
{
do SendByte(0xfc); //11111,RW(1),RS(0),0
while(0x80&ReceiveByte());
}
/******************************************************************
写一个字节的指令
*******************************************************************/
void WriteCommand( unsigned char Cbyte )
{
CS = 1;
CheckBusy();
SendByte(0xf8);
SendByte(0xf0&Cbyte);
SendByte(0xf0&Cbyte<<4);
CS = 0;
}
/*************************************************************
写一个字节的数据
**************************************************************/
void WriteData( unsigned char Dbyte )
{
CS = 1;
CheckBusy();
SendByte(0xfa); //11111,RW(0),RS(1),0
SendByte(0xf0&Dbyte);
SendByte(0xf0&Dbyte<<4);
CS = 0;
}
/******************************************************************
lcd初始化函数
*******************************************************************/
void LcmInit( void )
{
WriteCommand(0x30);
WriteCommand(0x03);
WriteCommand(0x0c);
WriteCommand(0x01);
WriteCommand(0x06);
}
/*******************************************************************************************************
设定光标函数
********************************************************************************************************/
void Location_xy_12864(unsigned char x,unsigned char y)
{
switch(x)
{
case 0:
x=0x80;break;
case 1:
x=0x90;break;
case 2:
x=0x88;break;
case 3:
x=0x98;break;
default:
x=0x80;
}
y=y&0x07;
WriteCommand(0x30);
WriteCommand(y+x);
WriteCommand(y+x);
}
/***********************************************************************************
清除文本
************************************************************************************/
void LcmClearTXT( void )
{
unsigned char i;
WriteCommand(0x30);
WriteCommand(0x80);
for(i=0;i<64;i++)
WriteData(0x20);
Location_xy_12864(0,0);
}
/***********************************************************************
显示字符串
***********************************************************************/
void PutStr(unsigned char row,unsigned char col,unsigned char *puts)
{
WriteCommand(0x30);
WriteCommand(AC_TABLE[8*row+col]);
while(*puts != '\0')
{
if(col==8)
{
col=0;
row++;
}
if(row==4) row=0;
WriteCommand(AC_TABLE[8*row+col]);
WriteData(*puts);
puts++;
if(*puts != '\0')
{
WriteData(*puts);
puts++;
col++;
}
}
}
unsigned char Keycan(void)
{
unsigned char row,col,i;
P1=0xf0;
if((P1&0xf0)!=0xf0)
{
delay(50);
delay(50);
if((P1&0xf0)!=0xf0)
{
row=P1^0xf0; //确定行线
i=0;
P1=a[i]; //精确定位
while(i<4)
{
if((P1&0xf0)!=0xf0)
{
col=~(P1&0xff); //确定列线
break; //已定位后提前退出
}
else
{
i++;
P1=a[i];
}
}
}
else
{
return 0;
}
while((P1&0xf0)!=0xf0);
return (row|col); //行线与列线组合后返回
}
else
return 0; //无键按下时返回0
}
void KeyDeal(unsigned char Key)
{
//unsigned char n;
if(Key!=0)
{
switch(Key)
{
case 0x11: K=1; break;
case 0x21: K=2; break;
case 0x41: K=3; break;
case 0x81: break;
case 0x12: K=4; break;
case 0x22: K=5; break;
case 0x42: K=6; break;
case 0x82: break;
case 0x14: K=7; break;
case 0x24: K=8; break;
case 0x44: K=9; break;
case 0x84: break;
case 0x18: break;
case 0x28: K=0; break;
case 0x48: K=11; break;
case 0x88: K=34;break;
default: break;
}
}
}
附上代码。
//不可改变与可以改变的数组如下所示
unsigned char code a[]={
0xFE,0xFD,0xFB,0xF7}; //按键扫描数组
unsigned char code FPM10A_Pack_Head[6] = {
0xEF,0x01,0xFF,0xFF,0xFF,0xFF}; //协议包头
unsigned char code FPM10A_Get_Img[6] = {
0x01,0x00,0x03,0x01,0x00,0x05}; //获得指纹图像
unsigned char code FPM10A_Img_To_Buffer1[7]={
0x01,0x00,0x04,0x02,0x01,0x00,0x08}; //将图像放入到BUFFER1 PageId+0x07
unsigned char code FPM10A_Img_To_Buffer2[7]={
0x01,0x00,0x04,0x02,0x02,0x00,0x09}; //将图像放入到BUFFER2 PageId+0x07
unsigned char code FPM10A_Reg_Model[6]={
0x01,0x00,0x03,0x05,0x00,0x09}; //将BUFFER1跟BUFFER2合成特征模版
unsigned char code FPM10A_Search[11]={
0x01,0x00,0x08,0x04,0x01,0x00,0x00,0x03,0xE7,0x00,0xF8}; //搜索指纹搜索范围0 - 999,使用BUFFER1中的特征码搜索 //起始页+页数+校验和
unsigned char code FPM10A_Delete_All_Model[6]={
0x01,0x00,0x03,0x0d,0x00,0x11};//删除指纹模块里所有的模版
volatile unsigned char FPM10A_Delete_Finger[10]={
0x01,0x00,0x07,0x0c,0x00,0x00,0x00,0x01,0x00,0x15};//删除指纹模块里的模版
volatile unsigned char FPM10A_Save_Finger[9]={
0x01,0x00,0x06,0x06,0x02,0x00,0x0B,0x00,0x19};//将BUFFER1中的特征码存放到指定的位置
串口初始化代码如下:
void UART_Init()
{
SCON= 0x50; //串口方式1 //REN=1; 允许接收
PCON=0x00; //SMOD=0
TMOD= 0x20; //定时器1定时方式2
TH1= 0xFD; //11.0592MHz 模块默认波特率为9600bps
TL1= 0xFD;
TR1= 1; //启动定时器
EA=1;
}
/***********************************************************************************************
串口发送函数
D罗电子设计qq:1679960378 qq:672939453
***********************************************************************************************/
void Uart_Send_Byte(unsigned char c)//UART Send a byte
{
SBUF = c;
while(!TI);
TI = 0;
}
unsigned char Uart_Receive_Byte()//UART Receive a byteg
{
unsigned char dat;
while(!RI);
RI = 0;
dat = SBUF;
return (dat);
}
大家还是不要看后面的注释哈,有的是最开始就注释了的,但是后面改了忘了修改注释。
在添加指纹代码之前,现将各种包头、与接收的数据写成函数
void FPM10A_Cmd_Send_Pack_Head(void)
{
int i;
for(i=0;i<6;i++) //包头
{
Uart_Send_Byte(FPM10A_Pack_Head[i]);
}
}
//接收反馈数据缓冲
void FPM10A_Receive_Data(unsigned char ucLength)
{
unsigned char i;
for (i=0;i<ucLength;i++)
FPM10A_RECEICE_BUFFER[i] = Uart_Receive_Byte();
}
//FINGERPRINT_获得指纹图像命令
void FPM10A_Cmd_Get_Img(void)
{
unsigned char i;
FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头
for(i=0;i<6;i++) //发送命令
{
Uart_Send_Byte(FPM10A_Get_Img[i]);
}
}
//将图像转换成特征码存放在Buffer1中
void FINGERPRINT_Cmd_Img_To_Buffer1(void)
{
unsigned char i;
FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头
for(i=0;i<7;i++) //发送命令 将图像转换成 特征码 存放在 CHAR_buffer1
{
Uart_Send_Byte(FPM10A_Img_To_Buffer1[i]);
}
}
//将图像转换成特征码存放在Buffer2中
void FINGERPRINT_Cmd_Img_To_Buffer2(void)
{
unsigned char i;
for(i=0;i<6;i++) //发送包头
{
Uart_Send_Byte(FPM10A_Pack_Head[i]);
}
for(i=0;i<7;i++) //发送命令 将图像转换成 特征码 存放在 CHAR_buffer2
{
Uart_Send_Byte(FPM10A_Img_To_Buffer2[i]);
}
}
//合成特征模板
void FPM10A_Cmd_Reg_Model(void)
{
unsigned char i;
FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头
for(i=0;i<6;i++)
{
Uart_Send_Byte(FPM10A_Reg_Model[i]);
}
}
再就是添加指纹函数了,添加指纹函数有如下几步
发送包头,发送添加指纹的指令这个函数 FPM10A_Cmd_Get_Img(void);在前文添加指纹的指令中我们发现如果返回来接收到的数据是0则接收到了指纹,如果没有则一直扫描指纹,直到接收到。在录入了指纹之后应该先与指纹库比对一下,如果指纹库中有这个指纹则不再存入。如果指纹库中没有这个指纹则再次按下指纹,于是将指纹存下来,此时指纹ID加1,24C02中的区域中也要存下ID值,以便掉电存储。
/***********************************************************************************************
添加指纹函数合集
D罗电子设计qq:1679960378 qq:672939453
***********************************************************************************************/
//保存指纹
void FPM10A_Cmd_Save_Finger( unsigned int storeID )
{
unsigned long temp = 0;
unsigned char i;
FPM10A_Save_Finger[5] =(storeID&0xFF00)>>8;
FPM10A_Save_Finger[6] = (storeID&0x00FF);
for(i=0;i<7;i++) //计算校验和
{
temp = temp + FPM10A_Save_Finger[i];
}
FPM10A_Save_Finger[7]=(temp & 0x00FF00) >> 8; //存放校验数据
FPM10A_Save_Finger[8]= temp & 0x0000FF;
FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头
for(i=0;i<9;i++)
Uart_Send_Byte(FPM10A_Save_Finger[i]); //发送命令 将图像转换成 特征码 存放在 CHAR_buffer2
}
//添加指纹
void FPM10A_Add_Fingerprint()
{
uchar IDa1,IDa2,IDa3;
LcmClearTXT();
PutStr(1,2,"请按手指");
delayms(2000);
FPM10A_Cmd_Get_Img(); //获得指纹图像
FPM10A_Receive_Data(12);
while(FPM10A_RECEICE_BUFFER[9]!=0)
{
FPM10A_Cmd_Get_Img(); //获得指纹图像
FPM10A_Receive_Data(12);
}
if(FPM10A_RECEICE_BUFFER[9]==0)
{
FINGERPRINT_Cmd_Img_To_Buffer1();
FPM10A_Receive_Data(12);
FPM10A_Cmd_Search_Finger();
FPM10A_Receive_Data(16);
if(FPM10A_RECEICE_BUFFER[9] == 0) //搜索是否已经存储
{
PageID = FPM10A_RECEICE_BUFFER[10]*256 + FPM10A_RECEICE_BUFFER[11];
IDa1=PageID/100;
IDa2=PageID/10%10;
IDa3=PageID%10;
LcmClearTXT();
PutStr(0,1,"该指纹已存储");
PutStr(1,1,"编号为:");
WriteCommand(0x8D);
WriteData(0x30+IDa1);
WriteData(0x30+IDa2);
WriteData(0x30+IDa3);
PutStr(3,0," 按任意键继续");
while(Keycan()==0);
}
if(FPM10A_RECEICE_BUFFER[9] == 9)
{
LcmClearTXT();
PutStr(1,1,"请再次按指纹");
FPM10A_Cmd_Get_Img(); //获得指纹图像
FPM10A_Receive_Data(12);
if(FPM10A_RECEICE_BUFFER[9] == 0)
{
FINGERPRINT_Cmd_Img_To_Buffer2();
FPM10A_Receive_Data(12);
FPM10A_Cmd_Reg_Model();
FPM10A_Receive_Data(12);
IDa1=PageID/100;
IDa2=PageID/10%10;
IDa3=PageID%10;
LcmClearTXT();
PutStr(1,1,"指纹采集成功");
PutStr(2,1,"编号为:");
WriteCommand(0x8D);
WriteData(0x30+IDa1);
WriteData(0x30+IDa2);
WriteData(0x30+IDa3);
delay(1000);
write24c02(116,PageID+1);
FPM10A_Cmd_Save_Finger(PageID++);
FPM10A_Receive_Data(12);
}
}
PutStr(3,0," 按任意键继续");
while(Keycan()==0);
}
LcmClearTXT();
}
由于篇幅关系,只介绍录入指纹的代码,但是其余代码都大同小异,只要弄清楚指令就可以了。
另外将剩下的的代码都附录如下,以便大家学习借鉴。
/***********************************************************************************************
管理员与开锁合集
D罗电子设计——邓工
***********************************************************************************************/
void shuazhiwen()
{
LcmClearTXT();
PutStr(1,1,"请按手指开锁");
FPM10A_Find_Fingerprint();
}
void gaimima() //修改用户密码
{
uchar i,j=0,mima1[6],mima2[6];
uchar k,temp;
LcmClearTXT();
PutStr(1,1,"请输入新密码");
for(i=0;i<6;i++)
{
mima1[i]=0;
}
Key=Keycan();
while(Key!=queren)
{
Key=Keycan();
KeyDeal(Key);
delay(30);
if(Key==0)
{
K=10;
}
if((K>=0)&&(K<=9))
{
mima1[j]=K;
if(j<6)
{
WriteCommand(0x89+j); //指定第三行显示位置
//WriteData(0x0f);
WriteData(K+0x30);
}
++j;
if(j==7)
j=6;
} //显示LCD12864并行显示
if(K==34) //按了删除键
{
if(j==0)
{
WriteCommand(0x89); //指定第三行显示位置
WriteData(0x20);
}
else
{
--j;
WriteCommand(0x89+j); //指定第三行显示位置
WriteData(0x20);
}
}
}
LcmClearTXT();
j=0;
PutStr(1,0,"请再次输入新密码");
for(i=0;i<6;i++)
{
mima2[i]=0;
}
Key=Keycan();
while(Key!=queren)
{
Key=Keycan();
KeyDeal(Key);
delay(30);
if(Key==0)
{
K=10;
}
if((K>=0)&&(K<=9))
{
mima2[j]=K;
if(j<6)
{
WriteCommand(0x89+j); //指定第三行显示位置
//WriteData(0x0f);
WriteData(K+0x30);
}
++j;
if(j==7)
j=6;
} //显示LCD12864并行显示
if(K==34) //按了删除键
{
if(j==0)
{
WriteCommand(0x89); //指定第三行显示位置
WriteData(0x20);
}
else
{
--j;
WriteCommand(0x89+j); //指定第三行显示位置
WriteData(0x20);
}
}
}
LcmClearTXT();
if((mima1[0]==mima2[0])&&(mima1[1]==mima2[1])&&(mima1[2]==mima2[2])&&(mima1[3]==mima2[3])&&(mima1[4]==mima2[4])&&(mima1[5]==mima2[5]))
{
for(i=0;i<6;i++)
mimaID[i]=mima1[i];
for(i=0;i<6;i++) //密码限制在6位以内
{
UserPassword[i]=mima1[i]+0x30;
}
temp=(Member-1)*100+10;
delayms(5);
for(k=0;k<6;k++)
{
write24c02(temp,UserPassword[k]);
delayms(10);
temp++;
}
//
PutStr(0,1,"密码修改成功");
PutStr(3,0," 按任意键继续");
Member=0; //方便下次修改密码
while(Keycan()==0);
}
else
{
PutStr(0,0," 密码修改失败 ");
PutStr(1,0,"两次输入的密码不");
PutStr(2,0,"一致,请重新操作");
PutStr(3,0," 按任意键继续");
while(Keycan()==0);
}
LcmClearTXT();
}
void zhu()
{
LcmClearTXT();
PutStr(1,2,"门已打开");
jidianqi=0;
delay(2500);
jidianqi=1;
PutStr(3,0," 按任意键继续");
while(Keycan()==0);
}
void guanliyuan() //管理员
{
uchar i,j=0,x=1;uchar Right_flag;
LcmClearTXT();
PutStr(1,1,"请输入密码:");
for(i=0;i<6;i++)
{
mima[i]=0;
}
Key=Keycan();
while(Key!=queren)
{
Key=Keycan();
KeyDeal(Key);
delay(30);
if(Key==0)
{
K=10;
}
if((K>=0)&&(K<=9))
{
mima[j]=K;
if(j<6)
{
WriteCommand(0x89+j); //指定第三行显示位置
WriteData(0x0f);
}
++j;
if(j==7)
{
j=6;
}
} //显示LCD12864并行显示
if(K==34) //按了删除键
{
if(j==0)
{
WriteCommand(0x89); //指定第三行显示位置
WriteData(0x20);
}
else
{
--j;
WriteCommand(0x89+j); //指定第三行显示位置
WriteData(0x20);
}
}
}
LcmClearTXT();
for(i=0;i<6;i++)
{
UserPassword[i]=mima[i]+0x30;
}
if(j==6)
{
Right_flag=PassWord_Chack();
}
if (Right_flag==1)
{
Right_flag=0;
Key=Keycan();
while(Key!=tuichu)
{
PutStr(0,0,"按键1 : 增加指纹");
PutStr(1,0,"按键2 : 删去指纹");
PutStr(2,0,"按键3 : 清空所有");
PutStr(3,0,"按键4 : 修改密码");
Key=Keycan();
KeyDeal(Key);
switch(K)
{
case 1:
FPM10A_Add_Fingerprint(); K=8; //仅录一次,如果不设置K=8(可以比4大就行),会一直录指纹
break;
case 2:
FPM10A_Delete_Fingerprint();
break;
case 3:
FPM10A_Delete_All_Fingerprint();
break;
case 4:
gaimima();
break;
default:
break;
}
}
}
else
{
alarm=0;
PutStr(1,2,"密码错误");
PutStr(2,0," 请重新操作!");
PutStr(3,0," 按任意键继续");
delay(2500);
alarm=1;
while(Keycan()==0);
}
Key=0;
LcmClearTXT();
}
//功能介绍
void MEMU()
{
PutStr(0,2,"欢迎使用");
PutStr(1,1,"D罗电子设计");
PutStr(2,1,"qq1679960378");
PutStr(3,1,"qq:672939453");
Key=Keycan();
if(k2==1) //指纹刷机
{
LcmClearTXT();
shuazhiwen();
LcmClearTXT();
}
if(Key==0x81) //管理员操作
{
LcmClearTXT();
guanliyuan();
LcmClearTXT();
}
}
void main()
{
UART_Init(); //串口初始化
x24c02_init(); //24C02初始化
LcmInit(); //LCD12864初始化
LcmClearTXT(); //LCD12864清屏
// write24c02(110,0x30);
// write24c02(111,0x30);//24c02的第110到115地址单元作为密码存储区
// write24c02(112,0x30);
// write24c02(113,0x30);
// write24c02(114,0x30);
// write24c02(115,0x30);
// write24c02(116,0);
PageID=read24c02(116);
while(1)
{
MEMU();
delay(100);
}
}
图中所示的3.2是5v降压到3.3的显示屏,大家降压可以前文提到的AMS1117模块。
1、串口初始化有问题,如果想知道初始化是否正确,用USB TO TTL串口下载器进行调试一下,如果不对,大家再多查一下串口的资料。
2、另外,最后最重要的是,AS608模块是有波特率的,一开始你不可能一下就能把波特率设置对。你可以自己用单片机串口初始化设置波特率。运用指纹上位机调试指纹模块波特率,与单片机一致就可以了。
指纹上位机去我的资源里下载,免费哈。
指纹模块波特率设置就是与下载器普通的连接就行了。RXD----TXD,TXD-----RXD。