赞
踩
本文主要对项目的搭建过程及实现效果进行介绍,应用代码较少,后续有空我会单独分类贴出
在消费升级渗透在各个领域的今天,国民消费发生着巨大的变化,与每个人息息相关的家居行业也是如此。现今,越来越多的智能家居产品出现在普通老百姓的生活中,智能照明、智能窗帘、智能扫地机器人等各种智能产品都给人们的生活带来了极大的便利。智能门锁作为智能家居中重要的一环,也成为消费者家居智能化的重要选择。智能相比市面的机械门锁更加安全、更智能、更人性化而得到市场的认可,在智能门锁行业里,不少世界500强企业也在智能门锁上进行产品布局,像中国小米公司就生产智能门锁,致力打造智慧家庭,市场智能门锁能够支持五种解锁方式,支持指纹、手机、门禁卡、按键密码、钥匙解锁功能。可以说是智能家庭必不可少的成员。
注:图片来源:http://www.szjiuding.com/products/297.html
1、可通过(通过手机)指纹模块增删家庭成员的指纹信息,增删是否成功的相关信息显示在OLED屏幕上
2、在指纹匹配过程中,如果采集的指纹与指纹模块库相匹配,OLED显示匹配成功,并转动步进电机一圈(或者打开继电器)
3、可通过按键设定智能门锁密码,密码可设置为两个(密码六位),如果匹配两个中的一个成功,即可开锁,也可通过按键修改密码,所有的操作过程显示于OLED中
4、实现RFID与手机解锁(蓝牙解锁)
5、虚位密码解锁
1、0.96寸OLED模块IIC接口(四针)
2、单片机键盘 按键 矩阵 4*4 4X4 A键盘 16键
3、3路继电器模块
4、MFRC-522 RC522 RFID射频 IC卡感应模块
5、HC-06主从机蓝牙模块无线串口通讯
6、AS608指纹模块
可以实现RFID刷卡、蓝牙连接解锁、指纹解锁同步进行验证,按下中断后可进行键盘输入密码解锁,进入功能选择界面后有两种模式进行功能选择,一是通过手机蓝牙进行操控,二是使用键盘进行操控。总共有三种功能可供选择,1、录入新指纹、2、删除指纹、3、修改密码;涉及协议:SPI、IIC、FALSH。
(1)打开串口助手软件及蓝牙HC-06AT命令详解.pdf资料,配置串口软件波特率:9600(有可能不一定是9600,可能是115200或者是38400),需要修改蓝牙的名字,修改指令如下:
AT+NAMECHEN(AT+NAME:AT指令 CHEN:蓝牙名字)
`(2)安装蓝牙调试助手,设定好指令,使用手机输入蓝牙密码连接蓝牙,
打开手机上蓝牙串口助手软件,并连接蓝牙。即可手机与蓝牙通信。
注意:使用主从机6针蓝牙模块时,插入电脑USB接口前,需要长摁模块上的按键;在开发板上连接时,
系统复位后若蓝牙没反应,应重新拔插蓝牙模块引脚。
#include "usart3.h" #include "string.h" /************************************** 引脚说明 PB10 ---- USART3_TX(发送端) PB11 ---- USART3_RX(接收端) MyBaudRate 波特率 **************************************/ u8 USART3_RX_BUF[USART3_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. u16 USART3_RX_STA=0; //接收状态标记 u8 rx_flag=0; void Usart3_Init(int MyBaudRate) { //GPIO端口设置 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3时钟 //串口3对应引脚复用映射 GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3); //GPIOB10复用为USART3 GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3); //GPIOB11复用为USART3 //USART3端口配置 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10| GPIO_Pin_11; //GPIOB10与GPIOB11 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化PB10,PB11 //USART3 初始化设置 USART_InitStructure.USART_BaudRate = MyBaudRate;//波特率设置 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART3, &USART_InitStructure); //初始化串口1 USART_Cmd(USART3, ENABLE); //使能串口1 //USART_ClearFlag(USART1, USART_FLAG_TC); USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启相关中断 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;//串口3中断通道 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;//抢占优先级3 NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; //子优先级2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、 } void USART3_IRQHandler(void) //串口3中断服务程序 { u8 Res; //GPIO_ResetBits(GPIOF, GPIO_Pin_9); if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是::结尾) { Res =USART_ReceiveData(USART3);//(USART1->DR); //读取接收到的数据 if((USART3_RX_STA&0x8000)==0)//接收未完成 { if(USART3_RX_STA&0x4000)//接收到了0x0d { if(Res!=':')USART3_RX_STA=0;//接收错误,重新开始 else { USART3_RX_STA|=0x8000; //接收完成了 rx_flag = 1; //蓝牙接收标志 } } else //还没收到0X0D { if(Res==':')USART3_RX_STA|=0x4000; else { USART3_RX_BUF[USART3_RX_STA&0X3FFF]=Res ; USART3_RX_STA++; if(USART3_RX_STA>(USART3_REC_LEN-1))USART3_RX_STA=0;//接收数据错误,重新开始接收 } } } } } //检查字符串是否与接收数据匹配 u8 Usart3_cherk(char *buf) { printf("%s",USART3_RX_BUF); if(memcmp(USART3_RX_BUF,buf,6)==0) { USART3_RX_STA=0; memset(USART3_RX_BUF,0,strlen((const char *)USART3_RX_BUF)); return 0; } else return 1; }
//手机蓝牙解锁密码1 if(rx_flag==1) //蓝牙信号标志 { Error=Usart3_cherk((char*)Pwd); if(Error==0) { printf("蓝牙解锁中\r\n"); Con(); BEEP_ON; delay_ms(200); BEEP_OFF; printf("蓝牙解锁成功\r\n"); OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP2); rx_flag=0; goto MENU; } else OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//锁 Error=Usart3_cherk((char*)Pwd2); if(Error==0) { printf("蓝牙解锁中\r\n"); Con(); BEEP_ON; delay_ms(200); BEEP_OFF; printf("蓝牙解锁成功\r\n"); OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP2); //刷解锁成功图片 rx_flag=0; goto MENU; } else OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//锁 }
MF522-AN模块采用Philips MFRC522原装芯片设计读卡电路,使用方便,成本低廉,适用于设备开发、读卡器开发等高级应用的用户、需要进行射频卡终端设计/生产的用户。本模块可直接装入各种读卡器模具。模块采用电压为3.3V,通过SPI接口简单的几条线就可以直接与用户任何CPU主板相连接通信,可以保证模块稳定可靠的工作、读卡距离远;
由于购买时就配置有示例代码,可直接使用,我这里只贴出主函数中应用部分
//MFRC522测试函数 int MFRC522Test(void) { u8 i,j,status=1,card_size; u8 count; AntennaOn(); status=MFRC522_Request(0x52, card_pydebuf); //寻卡 if(status==0) //如果读到卡 { printf("rc522 ok\r\n"); status=MFRC522_Anticoll(card_numberbuf); //防撞处理 card_size=MFRC522_SelectTag(card_numberbuf); //选卡 status=MFRC522_Auth(0x60, 4, card_key0Abuf, card_numberbuf); //验卡 status=MFRC522_Write(4, card_writebuf); //写卡(写卡要小心,特别是各区的块3) status=MFRC522_Read(4, card_readbuf); //读卡 //MFRC522_Halt(); //使卡进入休眠状态 //卡类型显示 printf("卡的类型:%d %d \r\n",card_pydebuf[0],card_pydebuf[1]); //卡序列号显,最后一字节为卡的校验码 printf("卡的序列号:"); count=0; for(i=0;i<5;i++) { printf("%d ",card_numberbuf[i]); if(card_numberbuf[i]==cardid[i]) { count++; } printf("%d",count); } printf("\r\n"); if(count==5) { printf("rfid 匹配成功,解锁 \r\n"); return 0; } printf("\n"); //卡容量显示,单位为Kbits printf("\r\n卡的容量:%dKbits\r\n",card_size); //读一个块的数据显示 printf("卡数据:\n"); for(i=0;i<2;i++) //分两行显示 { for(j=0;j<9;j++) //每行显示8个 { printf("%#x ",card_readbuf[j+i*9]); } printf("\n"); } } AntennaOff(); delay_s(1); return 1; }
AS608指纹模块代码的移植和初始化实现可以根据厂家提供的资料完成,移植过程如果有空我会单独出一期进行讲解,下面展示的是我的指纹应用代码部分
//录指纹 void Add_FR(void) { u8 i,ensure ,processnum=0; unsigned char word; int key_num; u16 ID; u16 UID[3]={0}; OLED_CLS();//清屏 while(1) { key_num=Key_Value(); if(key_num==68) { OLED_CLS();//清屏 return ; } switch (processnum) { case 0: OLED_CLS();//清屏 i++; for(word=28;word<32;word++) { OLED_ShowCN(0+(word-28)*16,4,word);//请按指纹 } printf("录入指纹"); ensure=PS_GetImage(); if(ensure==0x00) { ensure=PS_GenChar(CharBuffer1);//生成特征 if(ensure==0x00) { OLED_CLS();//清屏 printf("指纹正常"); for(word=32;word<36;word++) { OLED_ShowCN(0+(word-32)*16,4,word);//指纹正常 } i=0; processnum=1;//跳到第二步 }else ShowErrMessage(ensure); }else ShowErrMessage(ensure); OLED_CLS();//清屏 break; case 1: i++; printf("请再按一次指纹"); for(word=36;word<43;word++) { OLED_ShowCN(0+(word-36)*16,4,word);//请再按一次指纹 } ensure=PS_GetImage(); if(ensure==0x00) { ensure=PS_GenChar(CharBuffer2);//生成特征 if(ensure==0x00) { OLED_CLS();//清屏 printf("指纹正常"); for(word=32;word<36;word++) { OLED_ShowCN(0+(word-32)*16,4,word);//指纹正常 } i=0; processnum=2;//跳到第三步 }else ShowErrMessage(ensure); }else ShowErrMessage(ensure); OLED_CLS();//清屏 break; case 2: printf("对比两次指纹"); for(word=43;word<49;word++) { OLED_ShowCN(0+(word-42)*16,4,word);//对比两次指纹 } ensure=PS_GetImage(); ensure=PS_Match(); if(ensure==0x00) { OLED_CLS();//清屏 printf("两次指纹相同"); for(word=49;word<55;word++) { OLED_ShowCN(0+(word-49)*16,4,word);//两次指纹相同 } processnum=3;//跳到第四步 } else { OLED_CLS();//清屏 printf("重新录入"); for(word=55;word<59;word++) { OLED_ShowCN(0+(word-55)*16,4,word);//对比设备,重新录入 } ShowErrMessage(ensure); i=0; OLED_CLS();//清屏 processnum=0;//跳回第一步 } delay_ms(1200); OLED_CLS();//清屏 break; case 3: printf("生成指纹模板"); OLED_CLS();//清屏 for(word=59;word<65;word++) { OLED_ShowCN(0+(word-59)*16,4,word);//生成指纹模板 } ensure=PS_RegModel(); if(ensure==0x00) { OLED_CLS();//清屏 printf("生成指纹模板成功"); for(word=65;word<73;word++) { OLED_ShowCN(0+(word-65)*16,4,word);//生成指纹模板成功 } processnum=4;//跳到第五步 }else {processnum=0;ShowErrMessage(ensure);} delay_ms(1200); break; case 4: OLED_CLS();//清屏 printf("请输入储存ID"); for(word=73;word<80;word++) { OLED_ShowCN(0+(word-73)*16,0,word);//请输入储存ID }; ID=GET_NUM(); printf("ID:%d",ID); sprintf(UID,"%d",ID); OLED_ShowStr(65,4,UID,2); ensure=PS_StoreChar(CharBuffer2,ID);//储存模板 if(ensure==0x00) { OLED_CLS();//清屏 printf("录指纹成功"); for(word=80;word<85;word++) { OLED_ShowCN(0+(word-80)*16,4,word);//录指纹成功 }; OLED_ShowStr(20,4,word,2); PS_ValidTempleteNum(&ValidN);//读库指纹个数 printf("指纹个数为%d",ValidN); delay_ms(1500); //OLED_Clear(); return ; }else {processnum=0;ShowErrMessage(ensure);} OLED_CLS();//清屏 break; } delay_ms(400); if(i==10)//超过10次没有按手指则退出 { OLED_CLS();//清屏 break; } } }
//刷指纹 int press_FR(void) { SearchResult seach; u8 ensure; char *str; ensure=PS_GetImage(); OLED_CLS();//清屏 //Show_Str(0,0,128,16,"正在检测指纹",16,0); if(ensure==0x00)//获取图像成功 { ensure=PS_GenChar(CharBuffer1); printf("%d\r\n",ensure); if(ensure==0x00) //生成特征成功 { ensure=PS_HighSpeedSearch(CharBuffer1,0,AS608Para.PS_max,&seach); printf("%d\r\n",ensure); if(ensure==0x00)//搜索成功 { //OLED_Clear(); printf("解锁中\r\n"); BEEP_ON; delay_ms(200); BEEP_OFF; Con(); printf("已解锁\r\n"); sprintf(str,"ID:%d 匹配分:%d",seach.pageID,seach.mathscore); printf("%d",seach.pageID); OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP2);//开锁 delay_ms(500); OLED_CLS();//清屏 return 0; } else { ShowErrMessage(ensure); delay_ms(1000); OLED_CLS();//清屏 return -1; } } else ShowErrMessage(ensure); delay_ms(2000); OLED_CLS();//清屏 } return -1; }
//删除指纹 void Del_FR(void) { u8 ensure; u16 num; int i; OLED_CLS();//清屏 for(i=85;i<91;i++) { OLED_ShowCN(0+(i-85)*16,0,i);//清空指纹库 } for(i=91;i<97;i++) { OLED_ShowCN(0+(i-91)*16,4,i);//删除单个指纹 } OLED_ShowStr(113,0,"2",2); OLED_ShowStr(113,4,"3",2); delay_ms(50); while(1) { num=Key_Value();//获取返回的数值 if(num) { switch(num) { case '1':goto MENU; //返回主页面 case '2':ensure=PS_Empty(); break; //清空指纹库 case '3':ensure=Del_Name(); break; //删除单个指纹 } break; } } printf("%d\r\n",ensure); if(ensure==0) { OLED_CLS();//清屏 for(i=105;i<111;i++) { OLED_ShowCN(22+(i-105)*16,4,i);//删除成功 } } else ShowErrMessage(ensure); PS_ValidTempleteNum(&ValidN);//读库指纹个数 delay_ms(1200); MENU: OLED_CLS();//清屏 }
移植厂家提供的核心代码文件,再利用取模软件进行取模,拷贝到codetab.h文件中的储存函数中,
进行函数调用即可实现字体在屏幕上显示。
int main(void) { unsigned char i; extern const unsigned char BMP1[]; //NVIC分组 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); Delay_Init(); Led_Init(); Usart1_Init(115200); I2C_Configuration(); OLED_Init(); //显示字符 // OLED_Fill(0x00);//全屏灭 // OLED_ShowStr(0,4,"Hello Tech",1); //测试8*16字符 // while(1); //显示汉字 // OLED_Fill(0x00);//全屏灭 // for(i=5;i<9;i++) // { // OLED_ShowCN(22+(i-5)*16,0,i);//测试显示中文 // } // while(1); //显示图片 //OLED_Fill(0x00);//全屏灭 //OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//测试BMP位图显示 //while(1); OLED_CLS();//清屏 while(1) { //OLED_Fill(0xFF);//全屏点亮 //delay_s(2); //OLED_Fill(0x00);//全屏灭 //delay_s(2); for(i=0;i<5;i++) { OLED_ShowCN(22+i*16,0,i);//测试显示中文 } //delay_s(2); OLED_ShowStr(0,3,"HelTec Automation",1);//测试6*8字符 OLED_ShowStr(0,4,"Hello Tech",2); //测试8*16字符 //delay_s(2); //OLED_CLS();//清屏 //OLED_OFF();//测试OLED休眠 //delay_s(2); //OLED_ON();//测试OLED休眠后唤醒 //OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//测试BMP位图显示 //delay_s(2); } }
最常见的矩阵键盘是4*4键盘,其实现方法是将16个按键按照4x4矩阵方式连接,如上图所示。从连接方式来看,有4根行线和4根列线。每个行线和列线的交汇处就是一个按键位。这样总共有8根线就可以实现16个按键的检测,比一个按键连接一个I/O口节省了一半的I/O端口。
我本次设计的是通过寄存器的方式实现4*4按键的输入,采用逐列扫描的方式,并编写了每次按下一个按键都要进行消抖处理,从按下到松开按键只发送一位i数据;
#include <button4_4.h> #include <delay.h> #include <stdio.h> u8 only; //一次按键只打印一次标志位 /********************************************************************* *按键用的PC6-PC9,PC11,PB6,PE5,PE6 *PC6-PC9为推挽输出 *PC11,PB6,PE5,PE6为下拉输入 *********************************************************************/ void Button_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能C端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽式输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); //使能C端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //使能B端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); //使能E端口时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //输入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉 GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化 } /********************************************************************* *函数说明: 按键扫描 *返回值 : 按键值 *参数 : void **********************************************************************/ int Key_Scan(void) { u16 keyValue; //按键值 GPIO_Write(GPIOC,(GPIOC->ODR & 0xfc3f )| 0x0040);//检索第一列 if((GPIOC->IDR & 0x0800) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOC->IDR & 0x0800) != 0x0000) { only = 1; while((GPIOC->IDR & 0x0800) != 0x0000); keyValue='1'; } } if((GPIOB->IDR & 0x0040) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOB->IDR & 0x0040) != 0x0000) { only = 1; while((GPIOB->IDR & 0x0040) != 0x0000); keyValue='4'; } } if((GPIOE->IDR & 0x0020) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOE->IDR & 0x0020) != 0x0000) { only = 1; while((GPIOE->IDR & 0x0020) != 0x0000); keyValue='7'; } } if((GPIOE->IDR & 0x0040) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOE->IDR & 0x0040) != 0x0000) { only = 1; while((GPIOE->IDR & 0x0040) != 0x0000); keyValue='*'; } } GPIO_Write(GPIOC,(GPIOC->ODR & 0xfc3f )| 0x0080);检索第二列 if((GPIOC->IDR & 0x0800) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOC->IDR & 0x0800) != 0x0000) { only = 1; while((GPIOC->IDR & 0x0800) != 0x0000); keyValue='2'; } } if((GPIOB->IDR & 0x0040) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOB->IDR & 0x0040) != 0x0000) { only = 1; while((GPIOB->IDR & 0x0040) != 0x0000); keyValue='5'; } } if((GPIOE->IDR & 0x0020) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOE->IDR & 0x0020) != 0x0000) { only = 1; while((GPIOE->IDR & 0x0020) != 0x0000); keyValue='8'; } } if((GPIOE->IDR & 0x0040) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOE->IDR & 0x0040) != 0x0000) { only = 1; while((GPIOE->IDR & 0x0040) != 0x0000); keyValue='0'; } } GPIO_Write(GPIOC,(GPIOC->ODR & 0xfc3f )| 0x0100);//检索第三列 if((GPIOC->IDR & 0x0800) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOC->IDR & 0x0800) != 0x0000) { only = 1; while((GPIOC->IDR & 0x0800) != 0x0000); keyValue='3'; } } if((GPIOB->IDR & 0x0040) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOB->IDR & 0x0040) != 0x0000) { only = 1; while((GPIOB->IDR & 0x0040) != 0x0000); keyValue='6'; } } if((GPIOE->IDR & 0x0020) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOE->IDR & 0x0020) != 0x0000) { only = 1; while((GPIOE->IDR & 0x0020) != 0x0000); keyValue='9'; } } if((GPIOE->IDR & 0x0040) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOE->IDR & 0x0040) != 0x0000) { only = 1; while((GPIOE->IDR & 0x0040) != 0x0000); keyValue='#'; } } GPIO_Write(GPIOC,(GPIOC->ODR & 0xfc3f )| 0x0200);//检索第四列 if((GPIOC->IDR & 0x0800) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOC->IDR & 0x0800) != 0x0000) { only = 1; while((GPIOC->IDR & 0x0800) != 0x0000); keyValue='A'; } } if((GPIOB->IDR & 0x0040) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOB->IDR & 0x0040) != 0x0000) { only = 1; while((GPIOB->IDR & 0x0040) != 0x0000); keyValue='B'; } } if((GPIOE->IDR & 0x0020) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOE->IDR & 0x0020) != 0x0000) { only = 1; while((GPIOE->IDR & 0x0020) != 0x0000); keyValue='C'; } } if((GPIOE->IDR & 0x0040) != 0x0000) { delay_ms(5);//延时消抖 if((GPIOE->IDR & 0x0040) != 0x0000) { only = 1; while((GPIOE->IDR & 0x0040) != 0x0000); keyValue='D'; } } return keyValue; } int Key_Value(void) { u16 keyValue; keyValue = Key_Scan(); delay_ms(50); if(only == 1) //按一次打印一次 { printf("%c",keyValue); //这里写显示的方式,把接收显示出来 only = 0; return keyValue; } }
Flash内部存储器,内存器件的一种,是一种非易失性( Non-Volatile )内存。
flash闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。任何flash器件的写入操作只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。
使用Flash储存的目的是为了实现对数据进行保护,当我们对密码、指纹、ID卡号进行修改后,系统突然掉电或者关闭,进行Flash储存后,重新启动程序时进行读取操作,密码就为重新设置的密码。达到修改密码保存的目的。
使用开发板配套的Flash例程就可以实现,调用Flash_write()函数储存即可
每次开机初始化使用Flash_Read()读取密码。
#include "stmflash.h" static uint32_t GetSector(uint32_t Address) { uint32_t sector = 0; if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)) { sector = FLASH_Sector_0; } else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)) { sector = FLASH_Sector_1; } else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2)) { sector = FLASH_Sector_2; } else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3)) { sector = FLASH_Sector_3; } else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4)) { sector = FLASH_Sector_4; } else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5)) { sector = FLASH_Sector_5; } else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6)) { sector = FLASH_Sector_6; } else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7)) { sector = FLASH_Sector_7; } return sector; } //函数声明 void Flash_write(u32 addr, u8 *write_buff, u32 len) { u32 FLASH_USER_START_ADDR, FLASH_USER_END_ADDR; u32 uwStartSector, uwEndSector, uwSectorCounter, uwAddress; /* Unlock the Flash *********************************************************/ /* Enable the flash control register access */ //FLASH解锁 FLASH_Unlock(); /* Erase the user Flash area ************************************************/ /* area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR */ /* Clear pending flags (if any) */ //清空标志位 FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR); //计算开始地址 FLASH_USER_START_ADDR = addr; //计算结束地址 FLASH_USER_END_ADDR = FLASH_USER_START_ADDR+len; /* Get the number of the start and end sectors */ //获取开始与结束的扇区 uwStartSector = GetSector(FLASH_USER_START_ADDR); uwEndSector = GetSector(FLASH_USER_END_ADDR); /* Strat the erase operation */ //开始擦写扇区赋值给uwSectorCounter uwSectorCounter = uwStartSector; //循环擦除扇区 while (uwSectorCounter <= uwEndSector) { /* Device voltage range supposed to be [2.7V to 3.6V], the operation will be done by word */ if (FLASH_EraseSector(uwSectorCounter, VoltageRange_3) != FLASH_COMPLETE) { /* Error occurred while sector erase. User can add here some code to deal with this error */ printf("erase failure\r\n"); return ; } /* jump to the next sector */ uwSectorCounter += 8; } /* Program the user Flash area word by word ********************************/ /* area defined by FLASH_USER_START_ADDR and FLASH_USER_END_ADDR */ //开始地址赋值给uwAddress uwAddress = FLASH_USER_START_ADDR; //循环写数据 while (uwAddress < FLASH_USER_END_ADDR) { //按字节写入FLASH当中 if (FLASH_ProgramByte(uwAddress, *write_buff) == FLASH_COMPLETE) { //FLASH地址加1 uwAddress = uwAddress + 1; //数据地址空间加1 write_buff++; } else { printf("erase failure\r\n"); return ; } } /* Lock the Flash to disable the flash control register access (recommended to protect the FLASH memory against possible unwanted operation) */ //FLASH加锁 FLASH_Lock(); } void Flash_Read(u32 addr, u8 *read_buff, u32 len) { u32 FLASH_USER_START_ADDR, FLASH_USER_END_ADDR, uwAddress; //计算开始地址 FLASH_USER_START_ADDR = addr; //计算结束地址 FLASH_USER_END_ADDR = FLASH_USER_START_ADDR+len; uwAddress = FLASH_USER_START_ADDR; //循环读数据 while (uwAddress < FLASH_USER_END_ADDR) { *read_buff = *(__IO uint8_t*)uwAddress; //FLASH地址加1 uwAddress = uwAddress + 1; //数据地址空间加1 read_buff++; } }
“虚位密码技术”,就是在正确的密码前面和后面加上任意位数的数字,只要中间【部分品牌是头或者尾】有连续正确的密码就可开门,这样就能有效防止密码泄漏。
只需要不改变真实密码顺序,前后或中间插入多个数字都可以实现开锁。比如正确密码如果是123456,输入时可以加入(8)(6)(7)123456(0)(2)(3),注意括号内为加入的其他数字。
//密码锁 int password(void) { int i=0,satus=0,work; u8 key_num; u16 num=0,num2=0,time3=0; u8 pwd[11]={0}; u8 hidepwd[11]=" "; OLED_CLS();//清屏 for(work=123;work<128;work++) { OLED_ShowCN(22+(work-123)*16,0,work);//请输入密码 } while(1) { key_num=Key_Value(); if(key_num) { time3=0; if(key_num>=49 && key_num<58 && i>=0 && i<10)//‘1-9’键 { pwd[i]=key_num; printf("%c",pwd[i]); i++; printf("%d",i); } if(key_num==48 && i>=0 && i<10)//‘0’键 { int m=0; printf("%c",pwd[i]); i++; printf("%d",i); } if(key_num==68) break; //‘Enter’键 if(i==1) { OLED_ShowStr(32,4,"*",2); } if(i==2) { OLED_ShowStr(32,4,"**",2); } if(i==3) { OLED_ShowStr(32,4,"***",2); } if(i==4) { OLED_ShowStr(32,4,"****",2); } if(i==5) { OLED_ShowStr(32,4,"*****",2); } if(i==6) { OLED_ShowStr(32,4,"******",2); } } } printf("%s \r\n",pwd); printf("现在第一密码是%s",Pwd); for(i=0; i<12; i++) //验证密码 //虚位密码最多输入12位 { if(pwd[i]==Pwd[num]) { printf("%c",pwd[i]); num++; } else num=0; if(num==6) break; } printf("现在第二密码是%s",Pwd2); for(i=0; i<12; i++)//验证密码 { if(pwd[i]==Pwd2[num2])num2++; else num2=0; if(num2==6) break; } if(num==6 | num2==6) { OLED_CLS();//清屏 printf("解锁中"); BEEP_ON; delay_ms(200); BEEP_OFF; Con();//电机正转 OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP2); printf("已解锁"); delay_ms(1500); OLED_CLS();//清屏 return 0; } else { OLED_CLS();//清屏 printf("密码错误"); for(work=126;work<129;work++) { OLED_ShowCN(0+(work-126)*16,4,work);//密码错误 } delay_ms(1500); OLED_CLS();//清屏 return -1; } }
以下为我未进行封装的主函数,这样可以更好的理解整个系统的实现过程
//锁屏界面 MMMD: OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1); while(1) { MFRC522_Initializtion(); Error = MFRC522Test(); //MFRC522解锁 if(Error==0) { BEEP_ON; delay_ms(200); BEEP_OFF; Con();//电机正转 OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP2);//开锁 goto MENU; } else OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//锁 //手机蓝牙解锁密码1 if(rx_flag==1) { Error=Usart3_cherk((char*)Pwd); if(Error==0) { printf("蓝牙解锁中\r\n"); Con(); BEEP_ON; delay_ms(200); BEEP_OFF; printf("蓝牙解锁成功\r\n"); OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP2); rx_flag=0; goto MENU; } else OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//锁 Error=Usart3_cherk((char*)Pwd2); if(Error==0) { printf("蓝牙解锁中\r\n"); Con(); BEEP_ON; delay_ms(200); BEEP_OFF; printf("蓝牙解锁成功\r\n"); OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP2); rx_flag=0; goto MENU; } else OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//锁 } //指纹解锁 if(PS_Sta) //检测PS_Sta状态,如果有手指按下 { Error=press_FR();//刷指纹 if(Error==0) { goto MENU; } else OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//锁 } //密码锁 if(rx_jp_flag==1) { key_num=Key_Value(); //按键扫描 if(key_num >=0) { Error=password(); if(Error==0) { rx_jp_flag=0; goto MENU; } else OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);//锁 } } } //主界面 MENU: OLED_Fill(0x00);//全屏灭 for(i=6;i<10;i++) { OLED_ShowCN(22+(i-5)*16,0,i);//功能选择 } for(i=10;i<16;i++) { OLED_ShowCN(22+(i-10)*16,2,i);//一录入指纹 } for(i=16;i<22;i++) { OLED_ShowCN(22+(i-16)*16,4,i);//二删除指纹 } for(i=22;i<28;i++) { OLED_ShowCN(22+(i-22)*16,6,i);//三修改密码 } while(1) { if(rx_flag == 1) { rx_flag =0; printf("%s",USART3_RX_BUF); buf=atoi(USART3_RX_BUF); printf("%d",buf); if(buf) { switch(buf) { case 1:Add_FR(); break;//录指 case 2:Del_FR(); break;//删指纹 case 3:SetPassworld();break;//修改密码 case 4:rx_flag = 0;memset(buf,0,strlen((const char *)buf)); memset(USART3_RX_BUF,0,strlen((const char *)USART3_RX_BUF));Rok();goto MMMD;//返回主界面 } rx_flag = 0; memset(buf,0,strlen((const char *)buf)); USART3_RX_STA=0; memset(USART3_RX_BUF,0,strlen((const char *)USART3_RX_BUF)); goto MENU; } } //printf("主界面 \r\n"); if(rx_jp_flag==1) { key_num=Key_Value(); //按键扫描 if(key_num) { switch(key_num) { case '1':Add_FR(); break;//录指 case '2':Del_FR(); break;//删指纹 case '3':SetPassworld();break;//修改密码 case '4':rx_jp_flag=0;Rok();goto MMMD; } rx_jp_flag=0; goto MENU; } } } } //whlie }//main
本次项目的难点在于如果对各个未接触过的模块进行初始化,上电进行调试,十分考验个人自主移植厂家提供代码能力以及整合网络资料的能力,实验各个独立模块功能的实现;然后就是进行模块的整合,一个一个模块的添加,一步一步解决出现的问题,不建议同时将4个模块整合到开发板上。应用逻辑代码简单容易实现;后续可以进行低功耗处理,整体代码可以使用FreeRTOS重新封装编写。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。