基于stm32 c8t6的智能门锁超详细源码解析

本项目基于stm32c8t6单片机,拥有四种解锁方式:蓝牙解锁,指纹解锁,IC卡解锁,密码解锁。

通过舵机旋转模拟开关锁,指纹解锁和IC卡解锁多次失败之后,蜂鸣器发出警报声,其次无法再用指纹或者IC卡解锁,

技术栈 

  包括        STM32C8T6 USART SPI  指纹模块 MFRC522 蓝牙

key是按键模块,这个项目用不到,OLED是显示屏模块,主要作用是显示文字和图案。MySPI是进行SPI输出的,RC522是指纹模块,MatrixKey是4*4矩阵键盘模块,Serial是串口1通信,为蓝牙解锁提供服务。PWM是输出PWM波形的模块用于控制舵机,Servo是舵机模块,as608是指纹模块,usart2是串口2通信,为指纹解锁提供服务的,Horn是蜂鸣器模块。

main代码解析

引入头文件

main文件中函数声明,主要是在里面写了指纹模块的函数

变量解析

31行是用于串口1接收的一个中间变量

32是密码锁的初始密码,所以你一开始就可以用这个密码(123456D)

33是管理员密码,主要作用是,这个密码和密码锁初始密码都可以作为蓝牙解锁的密码,34password_Input[]是密码锁解锁时的一个中间变量,demo[]是将输入密码变成*的数组

35行中KeyNum是4*4矩阵键盘输入的数据,key_error是密码输入错误的次数,i和password_Input[]搭配使用,输入密码代表的密码位数,y是矩阵键盘输入数值的一个中间变量。

36-38是存储IC卡,这里并没有存储到内部FLASH去,无法做到断电不丢失指纹模块同理没有存储到内部FLASH中

39行的作用是将键盘输入类型的转化为字符类型所保存的变量

/*

Num是一个界面确定的变量  Num=9代表关锁界面   Num=1代表开锁界面  Num=0是输入密码界面  Num=10是关于界面 Num=3是更改密码界面
Num=4是添加IC卡界面 Num=5是删除卡的界面 Num=6 添加指纹 Num=7删除指纹 Num=8恢复出厂设置
ID_i是卡号数量,i<2,也就是最多存储两张卡
ID_Start是寻卡 1开始寻卡 0停止寻卡
ID_key代表存储到第几个IC卡号中,(0-5)最多存储6张IC卡
*/
uint8_t Num = 9,Num_Key,ID_i,ID_Start,ID_State,ID_key;   


uint8_t demo_1,del_ID,Return,repeat;



uint8_t horn = 0;	                                                            	//蜂鸣器 0代表关闭 1代表打开


uint8_t card = 0;		                                                           //卡号是否正确标志位置,这里再修改一下,这里card的作用是是(0)否(1)能进入锁屏界面和关于界面,初始化为0,如果ID卡解锁或者指纹解锁失败一次后就置1 ,在锁屏界面进按D键进入输入密码界面后就置0.

初始化解析 

74行的波特率设置为你的指纹模块比特率,刚买来的话一般不用动就是57600

上面主要进行密码锁各个模块的初始化

while循环解析

while(1)
	{			



/*
	    **********************************************


		
                   指纹	解锁	
		
		
		************************************************
*/		
		if(PS_Sta && Num == 9 && (as608_SUO == 0))		//如果检测到有手指按下(GPIOA1为高电平)   as608_SUO为是(0)否(1)允许指纹解锁标志位
		{
			press_FR_demo = press_FR();                 //press_FR_demo为press_FR(识别指纹的函数)的返回值,成功则返回1
			if(press_FR_demo)	                       //如果解锁成功
			{  
				KeyNum = 1;		                       //进入操作界面
				Num = 1;		                       //解锁标志
				as608_demo = 0;	//
			}
		}
		
	/*
	    **********************************************


		
                   指纹	录入   	as608_add下面来的
		
		
		************************************************
*/	
		
		
		if(as608_add == 1)	                            //开始录入指纹
		{
			if(as608_ID_Date[as608_ID] == 1)	        //指纹已存在的情况
			{
				Horn_ON();
				OLED_ClearArea(0,16,16*7,16);
				OLED_ShowChinese(32,16,"指纹已存在");
				OLED_ShowNum(16*7,0,as608_ID_i,1,OLED_8X16);//显示当前录入指纹数
				OLED_Update();
				as608_add = 0;                              //停止录入指纹                   
				Delay_ms(1500);
				Horn_OFF();
			}
			else                                                      //指纹不存在的情况
			{
				as608_ttt = as608_add_fingerprint(as608_ID);          //后面是录指纹的函数,返回值为0则说明录入成功
				if(as608_ttt == 0)	                                  //录入成功
				{
					Horn_ON();                                              

					as608_ID_i++;		                  //卡号数量++
					as608_ID_Date[as608_ID] = 1;		  //录入标志
					OLED_ClearArea(0,16,16*7,16);
					OLED_ShowChinese(32,16,"录入成功");
					OLED_ShowNum(16*7,0,as608_ID_i,1,OLED_8X16);//显示当前录入指纹数
					OLED_Update();
					as608_add = 0;		//停止录入指纹
					Delay_ms(500);
					Horn_OFF();
				}
				else
				{
					Horn_ON();
					OLED_ClearArea(0,16,16*7,16);
					OLED_ShowChinese(32,16,"录入失败");
					OLED_Update();
					Delay_ms(1500);
					Horn_OFF();
				}
			}

		}

		if(horn == 1)                             //horn是蜂鸣器,蜂鸣器打开 1000ms之后,horn=0,蜂鸣器关闭,
		{
			Horn_ON();
			Delay_ms(1000);
			Horn_OFF();
			horn = 0;
		}
/*
	    **********************************************


		
                   ID卡	解锁	
		
		
		************************************************
*/
		if(ID_i && (Num == 9)&&IC_ST)		        //有卡号且在关锁的状态且IC_ST表示可以用卡解锁
		{
			IC_IDGet(ID);		                                    //读卡
			if(strcmp(ID,"00000000") != 0)       	//成功读到卡号(ID不等于00000000) strcmp函数相等返回0 ,不像等返回1 
			{
				for(j = 0;j < 7;j++)
				{
					if(strcmp(ID_data[j],ID) == 0)      
					{
						Num = 1;		               //开锁
						KeyNum = 1;
					}
				}
				
				if(Num == 9)		//如果读到的卡号不正确情况下     Num=9代表锁着前面没有将Num置1,所以这里还是9代表卡号不正确
				{
					card_error++;           //卡错误次数+1
					Horn_ON();              //蜂鸣器打开
					card = 1;	             //卡号是否正确标志位置1 
					OLED_Clear();
					OLED_ShowChinese(0,0,"解锁失败!");
					OLED_ShowNum(16*7,0,card_error,2,OLED_8X16);    //显示失败次数
					OLED_ShowChinese(0,16,"卡号不存在!");
					OLED_ShowChinese(16*4,48,"密码解锁");
					OLED_Update();
					if(card_error > 4)		//如果输入的卡号错误5次
					{
						OLED_ShowChinese(0,16,"警告警告!!!");
						OLED_Update();
						card_error = 0;
						IC_ST = 0;	         //无法用卡解锁
					}
					Delay_ms(1500);
					Horn_OFF();
				}

			}

		}
/*
	    **********************************************


		
                   ID卡 卡号录入,由下面函数跳转而来
		
		
		************************************************
*/
		if(ID_Start == 1 && repeat == 0)		                 //开始读取录卡卡号
		{
			IC_IDGet(ID);		                                //读卡
			if(strcmp(ID,"00000000") != 0)		                //读卡成功,也就是读的ID卡号不是00000000
			{
				if(ID_i > 1)		                            //阈值,最多输入两张卡
				{
					Horn_ON();
					OLED_Clear();
					OLED_ShowChinese(0,0,"录入失败!");
					OLED_ShowChinese(0,16,"卡号已达阈值!");
					OLED_ShowChinese(54,48,"返回");
					OLED_Update();
					Delay_ms(1500);
					Horn_OFF();
				}
				else                                          //卡号没有超过上限,可以录入ID卡
				{
					Horn_ON();                                 //蜂鸣器打开
					for(j = 0;j < 7;j++)
					{
						if(strcmp(ID_data[j],ID) == 0)
						{
							repeat = 1;		//判断是否重复录取
						}
					}
					if(repeat == 0)
					{
						OLED_ClearArea(0,16,16*8,16);
						strcpy(ID_Data,ID);			           //获取ID值
						OLED_ShowString(0,16,"ID:",OLED_8X16);
						OLED_Update();
						ID_Start = 0;	                       //停止寻卡
					}
					else
					{
						OLED_ClearArea(0,16,16*8,16);
						OLED_ShowChinese(16,16,"卡号已存在!");
						OLED_Update();
					}
					Delay_ms(500);
					Horn_OFF();
				}
			}

		}

		
	}
}

上面主要是主循环中的代码,主要由四部分组成,分别为指纹解锁/录入,IC卡解锁/录入。

操作界面代码解析 

uint32_t count;
uint8_t Num_demo,z;
void TIM1_UP_IRQHandler(void)		//每1毫秒进一次中断
{
	
	if (TIM_GetITStatus(TIM1, TIM_IT_Update) == SET)
	{
		count++;
		if(count > 20)
		{
/********************************************************************************



			矩阵按键识别部分,Key_Num代表经过键盘处理之后的指令
			
			1            2         3           A
			4            5         6           B (向下键)
			7            8         9           C (向上键)
			>(返回)    =(0)    <           D (确认键)
			             
			
**********************************************************************************/
			count = 0;
			
			//GPIO_SetBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11);
			//GPIO_ResetBits(GPIOB, GPIO_Pin_0);
			GPIO_SetBits(GPIOA, GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);     //拉高4,5,6,7行
			GPIO_ResetBits(GPIOA, GPIO_Pin_4);                                     //拉低第4行
			y = Key_LoopY(1);                          	                            //检测按键是否按下
			if(y)		                                                           //判断第一行是否按下
			{
				if(y == 4)y = 17;	                                         //A
				KeyNum = y;                                                  //1-3,这里因为那个函数返回第(1.2.3.4)列被按下,所以这里不用计算直接返回出去
			}

			//GPIO_SetBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11);
			//GPIO_ResetBits(GPIOB, GPIO_Pin_1);
			GPIO_SetBits(GPIOA, GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
			GPIO_ResetBits(GPIOA, GPIO_Pin_5);
			y = Key_LoopY(2);	                                     //检测按键是否按下
			if(y)		                                             //判断第二行是否按下
			{
				if(y == 4)y = 18-3;	//B
				KeyNum = y + 3;                                       //4-6,返回的是(1.2.3.4),因为第二列第(1.2.3.4)对应的是(5.6.7.B),所以需要进行一下处理,将其+3  
			}
			
			//GPIO_SetBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11);
			//GPIO_ResetBits(GPIOB, GPIO_Pin_10);
			GPIO_SetBits(GPIOA, GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
			GPIO_ResetBits(GPIOA, GPIO_Pin_6);
			y = Key_LoopY(3);	//检测按键是否按下
			if(y)
			{
				if(y == 4)y = 19-6;	//C
				KeyNum = y + 6;                                             //7-9,与上一个相同,不解释           
			}
			
			//GPIO_SetBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11);
			//GPIO_ResetBits(GPIOB, GPIO_Pin_11);
			GPIO_SetBits(GPIOA, GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
			GPIO_ResetBits(GPIOA, GPIO_Pin_7);
			y = Key_LoopY(4);	//检测按键是否按下
			if(y)
			{
				if(y == 4)y = 20-9;	//D
				if(y == 3)y = 5;	//>   14 
				if(y == 2)y = 4;	//=   13   后面会转换为0 
				if(y == 1)y = 3;	//<   12
				KeyNum = y + 9;
			}
			
/****************************************************************************************************************     
			
			
			
			          操作主界面
			
			
			
*****************************************************************************************************************/
			if((KeyNum && (Key_error<4)))	     //如果按键按下(KeyNum!=0)或者接收到蓝牙的信号
			{									//如果输入的密码错误达到5次,蜂鸣器报警,按键无法再输入密码
				if(KeyNum == 13)KeyNum = 0;		//转换成0
				password_Bit = '0' + KeyNum;    //获取密码字节,将数字转换为字符串
				if(Num == 9)	                //锁屏界面,Num=9代表锁屏中,前面提到很多次了
				{
					if(card == 0)                //前面的卡错误标志位没有标为1(存疑)
					{
						Servo_SetAngle(0);
						OLED_Clear();
						OLED_ShowImage(50,8,32,32,lock);	
						OLED_ShowChinese(0,48,"关于");
						OLED_ShowChinese(96,48,"解锁");
						
						OLED_Update();                      //锁屏界面
					}

			
					if(password_Bit == 'D')             //按下D键(这里猜测是输入密码的功能)的情况
					{
						Num = 0;		                //输入密码界面,那么下面可以根据这个进入输入密码界面
						card = 0;                       //卡错误位置0(存疑)
						OLED_Clear();
						OLED_ShowChinese(0,0,"密码:");
						OLED_ShowChinese(0,48,"返回");
						OLED_ShowChinese(96,48,"确定");

						OLED_Update();
						password_Bit = ' ';              //将按键清空,防止再触发这个函数,其实这里根本不用解释
					}
					if(password_Bit == '<' && card == 0)
					{
						Num = 10;		                       //关于界面
					}
				}
				
				
				
				
				
				if(Num == 10)	                                     //*******项目信息界面**********
				{
					OLED_Clear();
					OLED_ShowChinese(0,0,"项目:");
					OLED_ShowChinese(16,24,"智能门禁锁系统");
					OLED_ShowChinese(96,48,"返回");
					OLED_Update();
					if(password_Bit == 'D')                        //按下D键进入锁屏界面,退出这个函数,也就是退出关于界面
					{
						Num = 9;		                           //锁屏界面              
						Return = 1;                                //退出函数
					}
					
				}
				
				
				
				
				if(Num == 0)		                              //***********输入密码界面******************************************
				{

					if((password_Bit < ':' && password_Bit > '/') || password_Bit == 'D')	//存储密码,根据ASSII码’/‘和’:‘中间对应的是0-9,也就是键盘输入的为0-9或者是D
					{
						password_Input[i] = password_Bit;	                                //存储输入的密码
						demo[i] = '*';		                                                //隐藏密码字符
						i++;                                                                //密码位数,存储一位之后+1
						
					}

					if(password_Bit == 'C')		                            //删除密码(删除输入的一位密码)
					{
						i--; //密码位数
						password_Input[i] = ' ';
						demo[i] = ' ';
					}
					
					if(i < 7)	                                        	//OLED屏显示
					{
						
						if(password_Bit == 'A')		                        //按下的是’A‘,切换显示和隐藏模式
						{
							demo_1++;                                       //demo_1的初始值为0,按一下就是1,这个作用就是每按一下都能切换一下显示和隐藏
						}
						if(demo_1 % 2 == 1)                                 //不显示密码的情况
						{
							OLED_ShowString(48,0,demo,OLED_8X16);	       //隐藏密码,横向偏移
							OLED_Update();

						}
						else                                                 //显示密码的情况
						{
							OLED_ShowString(48,0,password_Input,OLED_8X16);	//显示密码
							OLED_Update();
						}
						
					}
					if(password_Bit == '<')			          //返回键,按下后清除储存密码的数组和返回主屏界面(Num=9)
					{
						i = 0; //密码位数
						strcpy(password_Input,"       ");	  //清除输入的密码
						strcpy(demo,"       ");				  //清除输入的密码
						Num = 9;		//返回锁屏界面
						Return = 1;
					}
					if(password_Input[6] == 'D')	     	  //如果按下确定键
					{
						i = 0;                                       //密码位数
						if(strcmp(password_Input,password) == 0)	 //判断密码是否正确
						{
							strcpy(password_Input,"       ");	     //清除输入的密码
							strcpy(demo,"       ");				     //清除输入的密码
							Num = 1;	                             //密码正确,进入打开锁的界面(功能)
						}
						else
						{
							Key_error++;		                              //错误的次数
							if(Key_error > 3)                                 //密码输错三次就嗡嗡叫 
							{
								horn = 1;	                                   //蜂鸣器鸣叫
							}
							strcpy(password_Input,"       ");				   //清除输入的密码
							strcpy(demo,"       ");				               //清除输入的密码
							Num = 0;	                                       //密码错误    进入输密码界面(重新输入密码)
							OLED_ShowString(120,0,"!",OLED_8X16);
							OLED_ShowNum(112,0,Key_error,1,OLED_8X16);
							OLED_Update();
						}
					}

					
				}
				
				if(Num > 0 && Num < 8)		//密码输入正确,进入下面的这几个界面
				{
					if(password_Bit == '<')		//关锁模式
					{
						Servo_SetAngle(0);
						Num = 9;                //进入关锁界面
						ID_Start = 0;			//停止寻卡录卡
						i = 0;                                  //清除密码位数

						strcpy(password_Input,"       ");		//清除输入的密码
						OLED_Clear();
						OLED_ShowImage(50,8,32,32,lock);	
						OLED_ShowChinese(0,48,"关于");
						OLED_ShowChinese(96,48,"解锁");
						
						OLED_Update();

					}
				}
				
				if(Num == 1)		//密码输入正确后选择模式界面(之前做的步骤)
				{
					IC_ST = 1;      //允许IC卡解锁,之前可能错误太多把这个锁住了
					Num_demo = 1;
					OLED_Clear();                  //清屏
					Servo_SetAngle(180);	       //开锁
					Num = 2;	                  //进入选择模式
					password_Bit = ' ';           //键盘输入清零
					Key_error = 0;                //下面都是各种错误的次数清零
					card_error = 0;
					as608_SUO = 0;
				}
				if(Num == 2)		             //选择模式
				{
					if(password_Bit == 'B')		//切换键,向上
					{
						Num_demo--;
						if(Num_demo == 0)       //因为光标到1的位置就见底了所以只能减到1,所以到0了自动赋1
						{
							Num_demo = 1;
						}
						
					}				
					if(password_Bit == 'C')		//切换键,向下
					{
						Num_demo++;
						if(Num_demo == 7)       //同理
						{
							Num_demo = 6;
						}

					}	
					if(Num_demo < 4)	    //切换光标,可以理解为有两页,这是第一页的显示内容
					{
						OLED_Clear();
						OLED_ShowChinese(0,0,"模式选择:");
						OLED_ShowChinese(16*2,16,"更改密码");
						OLED_ShowChinese(16*2,32,"添加卡号");
						OLED_ShowChinese(16*2,48,"删除卡号");
						OLED_ShowImage(16*6,16*Num_demo,16,16,direction);    //16*Num_demo这里就改变了光标移动的位置
						OLED_Update();
					}
					if(Num_demo > 3)	  //切换光标 ,可以理解为这是第二页的显示内容
					{
						OLED_Clear();
						OLED_ShowChinese(0,0,"模式选择:");
						OLED_ShowChinese(16*2,16,"添加指纹");
						OLED_ShowChinese(16*2,32,"删除指纹");
						OLED_ShowChinese(16*2,48,"恢复出厂");
						OLED_ShowImage(16*6,16*(Num_demo-3),16,16,direction);  //同理改变光标移动的位置
						OLED_Update();
					}
					
					if(password_Bit == 'D')		//确认键
					{
						Num = Num_demo+2;        //按下确认键之后,光标位置+2就是要去的界面,然后根据前面显示信息不难判断功能呢,这里也很好理解,不解释
						password_Bit = ' ';      //键盘命令清0
					}	
				}
				if(Num == 3)		                                      //更改密码界面
				{
					OLED_Clear();
					OLED_ShowChinese(0,0,"新密码:");
					OLED_ShowChinese(54,48,"返回");
					OLED_ShowChinese(16*6,48,"确定");
					OLED_Update();
					if((password_Bit < ':' && password_Bit > '/') || password_Bit == 'D')	//如果输入的数值是0-9
					{
						password_Input[i] = password_Bit;	//存储输入的密码
						i++;		//密码位数
						
					}
					if(password_Bit == 'C')	                                            	//删除密码
					{
						i--;
						password_Input[i] = ' ';
					}					
					if(i < 7)                                                               //也就是密码没有输入完成的情况下
					{
						OLED_ShowString(4*16,0,password_Input,OLED_8X16);	//显示6位密码
						OLED_Update();
					}
					if(password_Input[6] == 'D')		//如果按下确定键,下面功能就是修改密码并清除输入的密码
					{
						i = 0;
						strcpy(password,password_Input);		//修改密码,将输入的密码赋值到保存密码password中
						OLED_ShowChinese(32,24,"修改成功!");
						OLED_Update();
						strcpy(password_Input,"       ");		//清除输入的密码
					}
					if(password_Bit == '>')	                    //返回键,按下后直接清除输入的密码且返回主页面
					{
						i = 0;
						strcpy(password_Input,"       ");		//清除输入的密码
						Num = 1;
						Return = 1;
					}

				}
				if(Num == 4)	                             	//输入卡号,添加IC卡(号)的界面
				{
                   /*
					***********************************************************************************************************************************************************************************************************
				                                                     	按下一个0-6的数字给ID_key,作为要存储到第几个卡号,然后只要输入的这个号码所代表的卡号没有存满,那么就显示录入成功,然后
					                                                     在不按返回的情况下让ID_Start等于1也就是开始寻卡,然后就跳转到前面233行ID卡号录入的函数中去了
                    ************************************************************************************************************************************************************************************************************					                                           
					*/
					if(password_Bit != ' ')                     //如果键盘输入的字符不等于''
					{
						if((KeyNum < 6) && (KeyNum > 0))	    //&!ID_Start   
						{
							OLED_ShowString(0,16,"ID:",OLED_8X16);
							OLED_ShowNum(8*3,16,KeyNum,1,OLED_8X16);		//显示输入的ID号	最多输入0-5    6个ID
							OLED_Update();
							ID_key = KeyNum;                                                                
						}
						if(password_Bit == 'D' && ID_key)		   //确认键
						{
							if(strcmp(ID_data[ID_key]," ") == 0)	        //如果ID不存在,以ID_key为下表的数组,内容为0,代表ID不存在
							{
								OLED_ShowChinese(16*2,16,"录入成功!");
								strcpy(ID_data[ID_key],ID_Data);			//存储ID号
								ID_i++;			                            //卡号+1
								OLED_ShowNum(16*7,0,ID_i,1,OLED_8X16);      //显示当前卡数
								OLED_Update();
								ID_key = 0;
							}
							else	                                    //如果ID已存在
							{
								OLED_ShowChinese(16*2,16,"录入失败!");
								OLED_ShowChinese(16*2,16*2,"卡号已存在!");
								OLED_ShowNum(16*7,0,ID_i,1,OLED_8X16);//显示当前卡数
								OLED_Update();
								ID_key = 0;
								
							}
						}
						if(password_Bit == '>')	//返回键
						{
							Num = 1;                              //去主界面
							Return = 1;
							ID_Start = 0;	          	          //停止寻卡
							repeat = 0;
						}
					}
					else
					{
						ID_Start = 1;		                       //开始寻卡录卡
						OLED_Clear();
						OLED_ShowChinese(0,0,"添加卡号:");
						OLED_ShowChinese(54,48,"返回");
						OLED_ShowChinese(16*6,48,"确定");
						OLED_Update();
					}
				}
				
				
				
				
				if(Num == 5)		//删除卡号界面*****************************************************************
				{
					if(password_Bit != ' ')
					{
						if((KeyNum < 6) && (KeyNum > 0))                 //删除卡号的ID
						{
							del_ID = KeyNum;                                 //将键盘输入的数字给del_ID
							OLED_ShowNum(8*3,16,KeyNum,1,OLED_8X16);		 //显示输入的ID号	最多输入0-5 6个ID
							OLED_Update();
						}
						if(password_Bit == 'D')		                         //确定
						{
							if(strcmp(ID_data[del_ID]," ") == 0)              //这个编号的代表的在密码集里面不存在,也就是等于空
							{
								
								OLED_ShowChinese(16,32,"卡号不存在!");
								OLED_Update();
							}
							else
							{
								OLED_ShowChinese(16*4,16,"删除成功");                //如果不为空就显示删除成功并删除ID号
								strcpy(ID_data[del_ID]," ");			             //删除ID号
								ID_i--;			                                     //卡号-1
								OLED_ShowNum(16*7,0,ID_i,1,OLED_8X16);               //显示当前卡数
								OLED_Update();
							}
						}
						if(password_Bit == '>')	                                     //返回,按下后,返回主界面并跳出函数
						{
							Num = 1;
							Return = 1;
						}
					}
					else                                                           //否则就一直显示着
					{
						OLED_Clear();
						OLED_ShowChinese(0,0,"删除卡号:");
						OLED_ShowChinese(54,48,"返回");
						OLED_ShowChinese(16*6,48,"确定");
						OLED_ShowString(0,16,"ID:             ",OLED_8X16);
						OLED_Update();
					}
				}
				if(Num == 6)		                                                  //添加指纹界面
				{
					if(password_Bit != ' ')                                          
					{
						if((KeyNum < 6) && (KeyNum > 0))	                          //获取指纹ID号,ID号的作用和指纹差不多
						{
							OLED_ShowNum(8*3,16,KeyNum,1,OLED_8X16);		          //显示输入的ID号    最多输入0-5 6个ID
							OLED_Update();
							as608_demo = KeyNum;		//获取ID值的临时变量
						}
						if(password_Bit == 'D' && as608_demo)		//确认键
						{
							as608_add = 1;		                    //开始录入指纹
							as608_ID = as608_demo;                  //获取ID值
							OLED_ClearArea(0,16,16*7,16);
							OLED_ShowChinese(16*2,16,"请放下手指");
							OLED_Update();
							as608_demo = 0;		                    //防止误按
						}
						if(password_Bit == '>')	                    //返回键
						{
							OLED_Clear();
							Num = 1;
							Return = 1;
							as608_demo = 0;
							as608_add = 0;		                    //停止录入指纹
						}
					}
					else
					{
						OLED_Clear();
						OLED_ShowChinese(0,0,"添加指纹:");
						OLED_ShowString(0,16,"ID:",OLED_8X16);
						OLED_ShowChinese(54,48,"返回");
						OLED_ShowChinese(16*6,48,"确定");
						OLED_Update();
					}
				}
				
				if(Num == 7)		//删除指纹
				{
					if(password_Bit != ' ')		//确定
					{
						if((KeyNum < 6) && (KeyNum > 0))//删除指纹的ID
						{
							del_ID = KeyNum;
							OLED_ShowNum(8*3,16,KeyNum,1,OLED_8X16);		//显示输入的ID号	最多输入0-5 6个ID
							OLED_Update();
						}
						if(password_Bit == 'D')		//确定
						{
							if(as608_ID_Date[del_ID] == 1)
							{
								as608_ID_Date[del_ID] = 0;
								as608_ID_i--;			//卡号-1
								Del_FR_One(del_ID);
								OLED_ShowChinese(32,32,"删除成功");
								OLED_ShowNum(16*7,0,as608_ID_i,2,OLED_8X16);//显示当前录入指纹数as608_ID_i
								OLED_Update();
							}
							else
							{
								OLED_ShowChinese(16,32,"指纹不存在!");
								OLED_Update();
							}
						}
						if(password_Bit == '>')	//返回
						{
							Num = 1;
							Return = 1;
						}
					}
					else
					{
						OLED_Clear();
						OLED_ShowChinese(0,0,"删除指纹:");
						OLED_ShowChinese(54,48,"返回");
						OLED_ShowChinese(16*6,48,"确定");
						OLED_ShowString(0,16,"ID:             ",OLED_8X16);
						OLED_Update();
					}
				}


				if(Num == 8)		//恢复出厂设置
				{
					OLED_Clear();
					OLED_ShowChinese(0,0,"正在恢复出厂:");
					OLED_ShowChinese(16*2,16,"请稍后。。。");
					OLED_Update();

					Num = 9;
					ID_Start = 0;					//停止录卡
					i = 0;
					Key_error = 0;
					card_error = 0;					
					strcpy(password,"123456D");		//恢复初值密码
					Del_FR_Whole();					//清空所有指纹
					for(z = 0;z<10;z++)				
					{
						as608_ID_Date[z] = 0;		//清空所有指纹ID
						ID_Data[z] = 0;				//清空所有卡号ID
						strcpy(ID_data[z]," ");		//清空所有卡号
					}
					Return = 1;
				}
				
				KeyNum = 0;
				if(Return == 1)		//返回键
				{
					KeyNum = 1;
					Return = 0;
				}
			}
		}
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
	}
}

上面代码为操作系统主界面的代码的编写,主要包含在一个1ms进行一次中断的函数中,通过if判断Num的值来判断应该去哪一个界面,所有的界面在参数解析中Num的解析中就可以看见

/*
蓝牙解锁模块
*/
char Serial_RxPacket[10];				//定义接收数据包数组,数据包格式"123456"

void USART1_IRQHandler(void)
{
	static uint8_t pRxPacket = 0;	                             	//定义表示当前接收数据位置的静态变量
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断
	{
		RxData = USART_ReceiveData(USART1);			         	//读取数据寄存器,存放在接收的数据变量
		Serial_RxPacket[pRxPacket] = RxData;	            	//将数据存入数据包数组的指定位置
		pRxPacket ++;			                                //数据包的位置自增
		if(pRxPacket == 6)
		{
			Serial_RxPacket[pRxPacket] = 'D';
			pRxPacket = 0;
			if(strcmp(Serial_RxPacket,password) == 0 || strcmp(Serial_RxPacket,password1) == 0)	//密码正确
			{
				Num = 1;
				KeyNum = 1;
				Num_demo = 1;
				Key_error = 0;
				strcpy(Serial_RxPacket,"       ");		//清空输入的密码
			}
		}
		
		
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);			//清除USART1的RXNE标志位
																//读取数据寄存器会自动清除此标志位
																//如果已经读取了数据寄存器,也可以不执行此代码
	}
}

以上代码是蓝牙解锁代码实现,因为蓝牙模块是和串口1相连接的,使用根据串口1接收的数据与与解锁密码(两个,包括管理员密码和密码锁密码)做对比,对比成功之后就解锁,这里对接收的格式并没有做严格的规范,且没有返回数据,只能根据蓝牙发送给MCU的数据来判断是否解锁

//录入指纹
uint8_t as608_add_fingerprint(uint16_t PageID)      //主要思路就是,按步骤一步步来,成功的话就显示OK,跳到下一步,某一个步骤不成功的话就,显示NO并且退出函数。
{
	uint8_t processnum = 0;   //录入的阶段
	uint8_t ensure;           //录入的结果
	while(1)
	{
		switch (processnum)
		{
			case 0:
				ensure=PS_GetImage();
			if(ensure==0x00)                                //至于为什么返回值为0x00就成功请追一下函数
				{
					OLED_ClearArea(0,16,16*7,16);
					ensure=PS_GenChar(CharBuffer1);//生成特征到CharBuffer1
					if(ensure==0x00)
					{
						OLED_ShowString(32,16,"No.1 OK!",OLED_8X16);
						OLED_Update();
						processnum=1;//跳到第二步
					}
					else
					{
						OLED_ShowString(32,16,"No.1 NO!",OLED_8X16);
						OLED_Update();
						return 1;
					}						
				}
				break;
			
			case 1:
				ensure=PS_GetImage();
				if(ensure==0x00) 
				{
					ensure=PS_GenChar(CharBuffer2);//生成特征到CharBuffer2
					if(ensure==0x00)
					{
						OLED_ShowString(32,16,"No.2 OK!",OLED_8X16);
						OLED_Update();
						processnum=2;//跳到第三步
					}
					else
					{
						OLED_ShowString(32,16,"No.2 NO!",OLED_8X16);
						OLED_Update();
						return 1;
					}					
				}
				else
				{
					OLED_ShowString(32,16,"No.2 NO!",OLED_8X16);
					OLED_Update();
					return 1;
				}
				break;

			case 2:		
				ensure=PS_Match();                              //比对刚刚存储的两个指纹的特征,可以的话跳到第四步
				if(ensure==0x00) 
				{
					OLED_ShowString(32,16,"No.3 OK!",OLED_8X16);
					OLED_Update();
					processnum=3;//跳到第四步
				}
				else 
				{
					OLED_ShowString(32,16,"No.3 NO!",OLED_8X16);
					OLED_Update();
					return 1;	
				}
				Delay_ms(1200);
				break;

			case 3:
				ensure=PS_RegModel();                      //将CharBuffer1与CharBuffer2中的特征文件合并生成 模板,结果存于CharBuffer1与CharBuffer2
				if(ensure==0x00) 
				{
					OLED_ShowString(32,16,"No.4 OK!",OLED_8X16);
					OLED_Update();
					processnum=4;//跳到第五步
				}
				else
				{
					OLED_ShowString(32,16,"No.4 NO!",OLED_8X16);
					OLED_Update();
					return 1;
				}
				Delay_ms(1200);
				break;
				
			case 4:	
				ensure=PS_StoreChar(CharBuffer2,PageID);     //储存模板将 CharBuffer1 或 CharBuffer2 中的模板文件存到 PageID 号flash数据库位置。    PageID(指纹库位置号)
				if(ensure==0x00) 
				{			
					OLED_ShowString(32,16,"No.5 OK!",OLED_8X16);
					OLED_Update();
					processnum = 5;
					Delay_ms(1500);
				}
				else
				{
					OLED_ShowString(32,16,"No.5 NO!",OLED_8X16);
					OLED_Update();
					return 1;
				}
				break;				
		}
		if(processnum==5)
		{
			break; 
		}				

	}
	return 0;
}

int press_FR(void)     
{
	SearchResult seach;
	u8 ensure;
	u8 ii = 0;
	while(1)
	{
		ensure=PS_GetImage();
		if(ensure==0x00)//获取图像成功 
		{
			ensure=PS_GenChar(CharBuffer1);
			if(ensure==0x00) //生成特征成功
			{
				
				ensure=PS_HighSpeedSearch(CharBuffer1,0,99,&seach);
				if(ensure==0x00)//搜索成功
				{			
					as608_SUO = 0;             
					return 1;
				}
				else 
				{
					Horn_ON();

					ii++;
					OLED_Clear();
					OLED_ShowNum(16*7,0,ii,2,OLED_8X16);
					OLED_ShowChinese(0,0,"解锁失败!");
					OLED_ShowChinese(0,16,"请再次放下手指");
					OLED_ShowChinese(16*4,48,"密码解锁");
					OLED_Update();
					card = 1;		
					Delay_ms(500);
					Horn_OFF();

					Delay_ms(1800);
					if(ii > 2)	                                //多次输入指纹失败之后,便不再允许指纹识别,同时蜂鸣器发出声响
					{
						as608_SUO = 1;                                           	//无法再输入指纹
						OLED_ShowChinese(0,16,"警告警告!!!");
						OLED_Update();
						Horn_ON();
						Delay_ms(3000);
						Horn_OFF();
						return 0;
					}
				}				
			}
			
		}
	}
}

//删除单个指纹
uint8_t Del_FR_One(u16 id)
{
	PS_DeletChar(id,1);//删除单个指纹
	return 0;
	
}
//删除全部指纹
uint8_t Del_FR_Whole(void)
{
	u8  ensure;
	ensure=PS_Empty();//清空指纹库
	if(ensure==0)
	{
		return 1;
	}
	return 0;
	
}

上面的代码主要包括录入指纹,检测指纹,删除(单个/全部)指纹,

AS608模块简单解析

首先,我们先初始化PA1为下拉输入,来检测是否有指纹按下(AS608有指纹按下时就显示为高电平状态),所以面在while循环里面PS_Sta就是代表的是PA1高电平(指纹按下)条件作为指纹识别的条件

//初始化PA1为下拉输入		    
//读摸出感应状态(触摸感应时输出高电平信号)
void PS_StaGPIO_Init(void)
{   
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
  //初始化读状态引脚GPIOA
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 		 //IPD
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
 
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO	
}

 写串口发送函数,利用USART2发送8字节数据

//串口发送一个字节
static void MYUSART_SendData(u8 data)
{
	while((USART2->SR&0X40)==0);   //0X40表示第六位(从第0位开始),TC发送完成标志位 
	USART2->DR = data;             //给DR中写数据
}

由于包头之类都是固定的,下面通过函数方便以后代码的书写,因为地址默认为0XFFFFFFFF,所以需要用串口发送四次,每一次都发送一个FF

//发送包头
static void SendHead(void)
{
	MYUSART_SendData(0xEF);
	MYUSART_SendData(0x01);
}
//发送地址
static void SendAddr(void)
{
	MYUSART_SendData(AS608Addr>>24);
	MYUSART_SendData(AS608Addr>>16);
	MYUSART_SendData(AS608Addr>>8);
	MYUSART_SendData(AS608Addr);
}
//发送包标识,
static void SendFlag(u8 flag)
{
	MYUSART_SendData(flag);
}
//发送包长度
static void SendLength(int length)
{
	MYUSART_SendData(length>>8);
	MYUSART_SendData(length);
}
//发送指令码
static void Sendcmd(u8 cmd)
{
	MYUSART_SendData(cmd);
}
//发送校验和
static void SendCheck(u16 check)
{
	MYUSART_SendData(check>>8);
	MYUSART_SendData(check);
}

下面是判断中断接收的数组有没有规定的应答包,注释写的已经很仔细了,主要是判断 返回代码中(存到了接收缓冲区USART2_RX_BUF)中有没有指定格式的应答包,这里需要我们知道strstr函数的意思,该函数返回在 A中第一次出现 B字符串的位置,如果未找到则返回 null。可以认为返回应答包的首地址,至于str为什么是前面那些数值,需要我们去AS608参考手册去看应答包的格式

其中str[0]和str[1]组成了应答包的包头,以此类推。。。。。。。。。。

//判断中断接收的数组有没有应答包
//waittime为等待中断接收数据的时间(单位1ms)
//返回值:数据包首地址
static u8 *JudgeStr(u16 waittime)
{
	char *data;
	u8 str[8];                                        //AS608向MCU发送的返回包格式,大概为2byt包头0xef01,4byt芯片地址,1byt包标识0x07,2byt包长度,1byt校验码,2byt校验和,
	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)                                   //接收到一次数据(最高位被置1)
		{
			USART2_RX_STA=0;                                       //串口2状态标志位置0,表示未被接收的状态,这样就可以去接收下一个了。
			data=strstr((const char*)USART2_RX_BUF,(const char*)str);    //strstr返回的是字符相同的首地址,也就是和str数组相同后,返回与USART2_RX_BUF中str[0]相同的地址。简而言之,这个的作用就是将接收缓冲区的与应答包相同的数据包的首地址给data ********              
			                                                             //由应答包格式可知 data[0]=0xef data[1]=0x01    .....data[9]是确认码,以后一般读取这个确认码来判断是否读取成功。     
			
			if(data)                                              //data不为0,就返回data
				return (u8*)data;	
		}
	}
	return 0;
}

其中以录入图像的函数为例,通过芯片手册上指令包格式提示,用串口发送一系列信息

下面代码的注释很详细,唯一可能不懂得是为什么data[9]是指令码,这个我们可以看前面应答包的数据,因为*data指向的是应答包的首地址,我们需要判断的是确认码,就是第十个字节的数据也就是data[9],根据数据手册,成功之后一般指令码为0x00,还有其他问题就去看看数据手册给的指令码

//录入图像 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; //temp是校验和,实际上这里应该是0x0005,包长度一个也占两个字节为0x0003,那么0x01+0x0003+0x01=0x0005
	SendCheck(temp);      //这里也可以证明temp实际上是两个Byt(0x0005),需要调用函数来发送 ,(因为串口只能发送一个字节的数据)
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];                     //***********************这里是一个重点,根据上面得知这是一个确认码,一般成功为0x00,具体看数据手册,最下面也有********
	else
		ensure=0xff;
	return ensure;
}

以下为内部FLASH存储的参考代码,可以参考一下利用内部FLASH包存密码,实现掉电不丢失

stmflash.h

//
//用户根据自己的需要设置
#define STM32_FLASH_SIZE 512 	 		//所选STM32的FLASH容量大小(单位为K)
#define STM32_FLASH_WREN 1              //使能FLASH写入(0,不是能;1,使能)
//

//FLASH起始地址
#define STM32_FLASH_BASE 0x08000000 	//STM32 FLASH的起始地址
//FLASH解锁键值
 

u16 STMFLASH_ReadHalfWord(u32 faddr);		  //读出半字  
void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);	//指定地址开始写入指定长度的数据
u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);						//指定地址开始读取指定长度数据
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);		//从指定地址开始写入指定长度的数据
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);   		//从指定地址开始读出指定长度的数据

//测试写入
void Test_Write(u32 WriteAddr,u16 WriteData);								   
#endif

stmfalsh.c

#include "stm32f10x.h"                  // Device header
#include "stmflash.h"
#include "Delay.h"
#include "USART1.h"
 
 
//读取指定地址的半字(16位数据)
//faddr:读地址(此地址必须为2的倍数!!)
//返回值:对应数据.
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
	return *(vu16*)faddr; 
}

#if STM32_FLASH_WREN	//如果使能了写   
//不检查的写入
//WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数   
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)   
{ 			 		 
	u16 i;
	for(i=0;i<NumToWrite;i++)
	{
		FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
	    WriteAddr+=2;//地址增加2.
	}  
} 
//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:数据指针
//NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //字节
#else 
#define STM_SECTOR_SIZE	2048
#endif		 
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)	
{
	u32 secpos;	   //扇区地址
	u16 secoff;	   //扇区内偏移地址(16位字计算)
	u16 secremain; //扇区内剩余地址(16位字计算)	   
 	u16 i;    
	u32 offaddr;   //去掉0X08000000后的地址
	if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
	FLASH_Unlock();						//解锁
	offaddr=WriteAddr-STM32_FLASH_BASE;		//实际偏移地址.
	secpos=offaddr/STM_SECTOR_SIZE;			//扇区地址  0~127 for STM32F103RBT6
	secoff=(offaddr%STM_SECTOR_SIZE)/2;		//在扇区内的偏移(2个字节为基本单位.)
	secremain=STM_SECTOR_SIZE/2-secoff;		//扇区剩余空间大小   
	if(NumToWrite<=secremain)secremain=NumToWrite;//不大于该扇区范围
	while(1) 
	{	
		STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;//需要擦除  	  
		}
		if(i<secremain)//需要擦除
		{
			FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
			for(i=0;i<secremain;i++)//复制
			{
				STMFLASH_BUF[i+secoff]=pBuffer[i];	  
			}
			STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区  
		}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
		if(NumToWrite==secremain)break;//写入结束了
		else//写入未结束
		{
			secpos++;				//扇区地址增1
			secoff=0;				//偏移位置为0 	 
		   	pBuffer+=secremain;  	//指针偏移
			WriteAddr+=secremain;	//写地址偏移	   
		   	NumToWrite-=secremain;	//字节(16位)数递减
			if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
			else secremain=NumToWrite;//下一个扇区可以写完了
		}	 
	};	
	FLASH_Lock();//上锁
}
#endif

//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)   	
{
	u16 i;
	for(i=0;i<NumToRead;i++)
	{
		pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
		ReadAddr+=2;//偏移2个字节.	
	}
}

//
//WriteAddr:起始地址
//WriteData:要写入的数据
void Test_Write(u32 WriteAddr,u16 WriteData)   	
{
	STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字 
}

STMFLASH_Write(SYS_SAVEADDR,(uint16_t*)&password,sizeof(password));写入FLASH

STMFLASH_Read(SYS_SAVEADDR,(uint16_t*)&password,3);读取FLASH

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值