基于STM32F103的智能门锁系统

Posted 白面师傅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于STM32F103的智能门锁系统相关的知识,希望对你有一定的参考价值。

基于STM32F103的智能门锁系统

直接说明实现了什么效果
1 指纹解锁(基于AS608)
2 RFID解锁(基于RC522)
3 密码解锁 (基于LCD电容屏触摸控制)
4 蓝牙解锁 (基于HC-06)
5 后台服务器管理开锁信息(基于ESP8266)
6 APP集成蓝牙功能、门锁开锁信息
7 管理员密码修改开锁密码
8 管理员添加指纹删除指纹
9 实时同步网络时间(基于SIM800C)
10 监控开锁时间并手机发送短信或打电话预警开锁异常(基于SIM800C)


其他用到的模块或硬件:28BYJ-48电机、IIC通信OLED屏幕

实现的效果如视频展示
效果视频

注:这是大一利用暑假写的小demo,剪视频的时候比较乱,可以将就食用 哈哈哈


前言

此demo运用的多种模块,文中会对各个模块进行较为详细 的讲解,个人讲解有不到位的地方文末还将整理各个模块的官方或其他资料分享出来。此demo的主要难度就是各个模块之间的融合和信息上传,前后端数据整合


下面依次按照每个模块进行分析

接下来,本文会很长很长很长,挑自己需要的看即可,主要是要看整个系统的思路是怎么实现的。

一、AS608

看一眼长啥样子~

AS608 指纹识别模块主要是指采用了杭州晟元芯片技术有限公司(Synochip)的 AS608 指纹识别芯片 而做成的指纹模块,模块厂商只是基于该芯片设计外围电路,集成一个可供2次开发的指纹模块;所以,只要是基于AS608芯片的指纹模块,其控制电路及控制协议几乎是一样的,只是不同厂家的性能不同而已。(其实玩嵌入式的都知道,同种类型的模块代码都大差不差啦

玩一个模块最先的必须先看各种技术指标,如上电电压,默认通讯设置,如波特率等。这一步必须要做,不然你就很有可能会了它(骚了它~)
呃…想表达的意思就是,看完指标我们才能更快的入手这个模块啊,对后续莫名其妙 的bug也能更快的排查。(其实根本没有那么多所谓莫名其妙的bug啦,肯定是你哪里的指标没看,或接错或代码逻辑错,科学都是很严谨的!!)
技术指标看完是不是就想上电啦?别急,先看看接线方式

  1. 采用8pin方式,其中有两个脚需要上电3.3V,分别是Vi和Vt(记得别上5V!!!骚了它骚了它~ )
    Vi上电是给整个模块供电,而Vt上电是给采集指纹的那块触摸感应屏幕上电,缺一不可。
  2. Tx和Rx看你用的哪个串口对应接。Rx、Tx反接记得检查。我用的是STM32F103ZET6,开发板为正点原子精英板,接的串口3,即Rx接PA3,Tx接PA2。波特率可以自己设置,默认波特率57600
  3. WAK脚接上一个可用于输入输出的ADC脚,此处接的PA6。接上此脚的原因是为了能让AS608模块收到相关的指纹命令后,做对应的输出。(所以要用输入输出捕获呀
  4. 正常接GND即可

    对于WAK脚,接不接看你要什么效果,一般脱机(PC)做项目要接上。

关于用PC端串口助手直接连接AS608进行测试的问题,文章末尾有资料包可自行查看,此处只讲实际运用

附上代码和个人理解

//与AS608握手 PS_HandShake
//参数: PS_Addr地址指针
//说明: 模块返新地址(正确地址)	
u8 PS_HandShake(u32 *PS_Addr)
{
	SendHead();    //就是一些包头
	SendAddr();		//发送模块地址
	MYUSART_SendData(0X01);  //内部调用了串口,发送一个字节,此处用于校验和
	MYUSART_SendData(0X00);
	MYUSART_SendData(0X00);	
	delay_ms(200);
	if(USART2_RX_STA&0X8000)//接收到数据
	{		
		if(//判断是不是模块返回的应答包				
					USART2_RX_BUF[0]==0XEF
				&&USART2_RX_BUF[1]==0X01
				&&USART2_RX_BUF[6]==0X07
			)
			{
				*PS_Addr=(USART2_RX_BUF[2]<<24) + (USART2_RX_BUF[3]<<16)
								+(USART2_RX_BUF[4]<<8) + (USART2_RX_BUF[5]);
				USART2_RX_STA=0;
				return 0;
			}
		USART2_RX_STA=0;					
	}
	return 1;		
}
/*
代码块主要就是看返回值,对于地址、校验这些知道它在干什么即可,学会看文档查资料很重要
*/

模块初始化代码

u32 AS608Addr = 0XFFFFFFFF; //默认

//初始化PA6为下拉输入		    
//读摸出感应状态(触摸感应时输出高电平信号)
void PS_StaGPIO_Init(void)
{   
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟
  //初始化读状态引脚GPIOA
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//输入下拉模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO	
}
// 对于官方的代码,建议不是很熟悉开发的不要改动配置

对于一个复杂模块的代码,其实不需要懂太多,看一下初始化,必要的功能函数,其他的函数只要知道它是干什么的,运用它即可。

贴上一两个重要的功能函数

//判断中断接收的数组有没有应答包
//waittime为等待中断接收数据的时间(单位1ms)
//返回值:数据包首地址
static u8 *JudgeStr(u16 waittime)
{
	char *data;
	u8 str[8];
	str[0]=0xef;str[1]=0x01;str[2]=AS608Addr>>24;
	str[3]=AS608Addr>>16;str[4]=AS608Addr>>8;
	str[5]=AS608Addr;str[6]=0x07;str[7]='\\0';
	USART2_RX_STA=0;
	while(--waittime)
	{
		delay_ms(1);
		if(USART2_RX_STA&0X8000)//接收到一次数据
		{
			USART2_RX_STA=0;
			data=strstr((const char*)USART2_RX_BUF,(const char*)str);
			if(data)
				return (u8*)data;	
		}
	}
	return 0;
}
/*
这里就是设置一些地址,接收数据包之后检查有无应答包
strstr函数常常用于一些需要指令的模块,函数的功能是判断参数str2是否包含在str1内,如果包涵就返回包涵数据的首地址,否则返回NULL。
用大白话说就是查包
查包是查应答包,包括指令的包头、模块地址、指令码。
*/
//录入图像 PS_GetImage
//功能:探测手指,探测到后录入指纹图像存于ImageBuffer。 
//模块返回确认字
u8 PS_GetImage(void)
{
  u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x01);
  temp =  0x01+0x03+0x01;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;
}

整个开发流程大概遵循以下步骤:

初始化硬件
→检查字库
→是否触摸校准(电阻屏)
→与 AS608模块通讯
→通讯成功读取模块参数
→显示模块参数
→加载虚拟键盘
→while while 循环获取触摸键值
→判断键值进入录指纹或删流程
→判断触摸感应状态
→进入刷指纹流程。

串口调试AS608模块,注意波特率,57600和115200看实际情况

验证结果

二、RFID-RC522模块

看一眼长啥样子~

该图片是读取卡

该图片是卡钥匙扣

RFID-RC522模块是众多射频模块中最常用的一种,由于模块涉及的东西还是较为复杂的,本文就简明扼要的挑选一些重要的特性进行介绍。
首先,卡本身是分扇区的,所谓分扇区,就是一片片的储存信息区域,且每个区域负责的信息不同,若需要查看请去找模块手册,我们本文的目标是教会大家怎么使用。
其次,要想对数据块进行操作,首先要看该数据块的控制位是否允许对数据块的操作,如果允许操作,再看需要验证什么密码,只有验证密码正确后才可以对该数据块执行相应操作,看到这里就会明白为什么代码里会有0XFF…的校验了。
卡的工作原理就是卡本身内置天线且绕线为线圈的形式封装在卡片内,工作的时候读卡器会向M1卡发送电磁波,卡内的电路进行处理(此处有复杂的模电数电知识)然后由电子泵将产生的电荷进行储存。随后卡片就可以向读卡器发送或接收数据,实现通信。

流程为:放卡钥匙扣 ->  读卡器天线接收到信号 -> 读卡器内置电路处理信号并存储好(此处看代码命令是怎么操作的,我们的主要目标也在这里,你也可以读取到了信号但不计入卡内部) -> 验证或其他外围操作(指的是你读到了卡钥匙扣的信息后你想干嘛当然由你制定) 

贴一些功能函数并适当讲解,完整工程文末会给出

/* 初始化函数 模块是spi的初始化 */
//RFID_RC522 GPIO SPI初始化
//RFID_NSS(CS)   PB12
//RFID_RST       PC0
//RFID_CLK       PB13
//RFID_MOSI      PB15
//RFID_MISO      PB14
extern void open_close(void);
void RFIDGPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;      //RFID_CS
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_12);//拉高RFID_CS

	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//RFID_RST
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	GPIO_SetBits(GPIOC,GPIO_Pin_0);//拉高
	
	spi2_init();    //SPI2初始化
	PcdReset();     //RC522初始化
	PcdAntennaOff();//关天线
	delay_ms(1);    
	PcdAntennaOn(); //开天线
	M500PcdConfigISOType('A');//针对ios14443A型卡进行初始化
}
//功    能:复位RC522
//返    回: 成功返回MI_OK
char PcdReset(void)
{   	
		RST_1;
		delay_us(10);
	  WriteRawRC(CommandReg,PCD_RESETPHASE);//启动命令的执行-执行复位指令
		delay_us(1);

		WriteRawRC(ModeReg,0x3d);//0x29?//0x3d//和Mifare卡通讯,CRC初始值0x6363
		WriteRawRC(TReloadRegL,30);//timer=15ms//30
		WriteRawRC(TReloadRegH,0);
		WriteRawRC(TModeReg,0x8D);//Prescaler=3390
		WriteRawRC(TPrescalerReg,0x3E);
		WriteRawRC(TxAskReg,0x40);		//forced to 100%ASK	 天线驱动
	  
		PcdAntennaOn(); 
		return MI_OK;
}
//初始化值块 查找的意思
void CZ(void)
{
  Block=4;//将1扇区的数据块0初始化为值块 初始值为0
	//寻卡、防冲突、选卡
	if(Request_Anticoll_Select(PICC_REQALL,Card_Type,Card_Buffer,Card_ID)==MI_OK)
	{
		//用修改后的密码A验证卡片
		if(PcdAuthState(PICC_AUTHENT1A,Block,Modify_Key,Card_ID)==MI_OK)
		{
			//将当前操作的块打印出来
			USART_SendData(USART1,table[Block/10]);
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			USART_SendData(USART1,table[Block%10]);
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			USART_SendData(USART1,' ');
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			usart1_sendstring("PcdAuthState_OK\\r\\n");
			//按照值块格式写入
			if(PcdWrite(Block,DefaultValue)==MI_OK)
			{
				if(PcdRead(Block,Data_Buffer)==MI_OK)
				{
					Printing(Data_Buffer,16);//将值块打印到串口
					usart1_sendstring("Init_OK\\r\\n");//修改密码成功
					usart1_sendstring("\\r\\n");
					BEEP_SET;
					delay_ms(500);
					BEEP_CLR;	
				}					
			}
		}
	}		
}
//这里的代码大概就是开发RC522功能的步骤了,其他的可以写比如钱包的加钱,减钱功能函数等
//减值(扣款)
void Decrement(void)
{
	Block=4;
	//寻卡、防冲突、选卡
	if(Request_Anticoll_Select(PICC_REQALL,Card_Type,Card_Buffer,Card_ID)==MI_OK)
	{
		//用修改后的密码A验证卡片
		if(PcdAuthState(PICC_AUTHENT1A,Block,Modify_Key,Card_ID)==MI_OK)
		{
			//将当前操作的块打印出来
			USART_SendData(USART1,table[Block/10]);
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			USART_SendData(USART1,table[Block%10]);
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			USART_SendData(USART1,' ');
			while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)!=SET);
			usart1_sendstring("PcdAuthState_OK\\r\\n");
			
			//减前读一遍钱包
			if(PcdRead(Block,Data_Buffer)==MI_OK)
			{
				Printing(Data_Buffer,16);
				
				if(Data_Buffer[0]<=0)//判断是否还能再减 
				{
					BEEP_SET;delay_ms(50);BEEP_CLR;delay_ms(50);	
					BEEP_SET;delay_ms(50);BEEP_CLR;delay_ms(50);
					BEEP_SET;delay_ms(50);BEEP_CLR;//超值报警
					return;
				}
				//减值  
				if(PcdValue(PICC_DECREMENT,Block,value_Buf)==MI_OK)
				{
					if(PcdRead(Block,Data_Buffer)==MI_OK)
					{
						Printing(Data_Buffer,16);
						usart1_sendstring("-_OK\\r\\n");
						usart1_sendstring("\\r\\n");
						BEEP_SET;
						delay_ms(50);
						BEEP_CLR;	
					}			
				}
			}
		}
	}
}

整个工程下来,最重要的是怎么读取到你的当前卡号问题,代码中有个数组尾Card_ID,里面存储的就是读取到的卡号。读卡的操作也可以用电脑串口助手调试。如图所示(这不是卖广告,测试自带的)

三、LCD模块以及触摸功能讲解

因为LCD模块的代码多且复杂,故这里也就将它分开讲解并以实用为主。
首先因为LCD的型号也是多且杂的,先放上本文讲解的版本

它的功能用大白话来说就是,显示+输入。分辨率为240*320,此分辨率也就是能加载一些较为模糊的图片和弄好的UI布局,我了解过的一个是emwin图形界面,可以设计很多的“美丽”布局,见仁见智的美丽。
LCD屏幕另一个重要的用途就是用作触摸输入了。市面上又会分电阻屏和电容屏的区别,TFT只是一种液晶屏幕的材料,电容屏是靠电流热感应工作,只能用手指操作, 电阻屏靠压力工作,任何东西压在上面都有反应·所以用指甲或手写笔都可以。图中的是电阻屏
上代码先看看是怎么驱动的

#include "lcd.h"				//导入头文件
LCD_Init();					//LCD初始化
tp_dev.init();			//初始化触摸屏 

以上三个就是其他花里胡哨的功能实现的前提下必须要写的。看起来低级,但往往有人功能写得花里胡哨却忘记初始化一个模块或者把它注释了之类的,那么就导致了不能使用LCD的功能。
其他形如一些指定的标准库函数就直接拿来用就好了

POINT_COLOR=BLUE; //字体颜色为蓝色
LCD_Clear(WHITE); //清屏
Show_Str_Mid(0,40,“LCD模块正常!”,16,240); //形如这样子的显示,前提是检测好字库

抽取一段实现触摸的代码进行说明

//得到触摸屏的输入
//x,y:键盘坐标
//返回值:(1~15,对应按键表)
u8 AS608_get_keynum(u16 x,u16 y)
{
	u16 i,j;
	static u8 key_x=0;//0,没有任何按键按下
	u8 key=0;
	tp_dev.scan(0);		//设置扫描模式 		 
	if(tp_dev.sta&TP_PRES_DOWN)//触摸屏被按下
	{	
		for(i=0;i<5;i++)		//以下的一大段就是计算被点击的物理地址得到键值
		{
			for(j=0;j<3;j++)
			{
			 	if(tp_dev.x[0]<(x+j*80+80)&&tp_dev.x[0]>(x+j*80)&&tp_dev.y[0]<(y+i*30+30)&&tp_dev.y[0]>(y+i*30)) //就是根据LCD屏幕的大小计算  
				{	
					key=i*3+j+1;	 
					break;	 		   
				}
			}
			if(key)
			{	   
				if(key_x==key)key=0;
				else 
				{
					AS608_key_staset(x,y,key_x-1,0);
					key_x=key;
					AS608_key_staset(x,y,key_x-1,1);
				}
				break;
			}
	  }  
	}else if(key_x) 
	{
		AS608_key_staset(x,y,key_x-1,0);
		key_x=0;
	} 
	return key; //返回的key是对应LCD显示键盘上的数字
}
//触摸按键扫描
//tp:0,屏幕坐标;1,物理坐标(校准等特殊场合用)
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 TP_Scan(u8 tp)
{			   
	if(PEN==0)//有按键按下
	{
		if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//读取物理坐标
		else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//读取屏幕坐标
		{
	 		tp_dev.x[0]=tp_dev.xfac*tp_dev.x[0]+tp_dev.xoff;//将结果转换为屏幕坐标
			tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yof

以上是关于基于STM32F103的智能门锁系统的主要内容,如果未能解决你的问题,请参考以下文章

基于STM32F103设计的智能门锁(支持多种开锁解锁方式)

基于STM32F103——SIM900A发送短信+串口打印

基于STM32F103——SIM900A发送短信+串口打印

基于STM32F103的智能门禁系统

基于STM32F103的智能门禁系统

基于STM32F103c8t6的智能垃圾桶项目