当前位置: 首页 > news >正文

基于STM32的智能门锁安防系统(开源)

目录

项目演示

项目概述

硬件组成:

功能实现

1. 开锁模式

1.1 按键密码开锁

1.2 门禁卡开锁

1.3 指纹开锁

2. 功能备注

3. 硬件模块工作流程

3.1 步进电机控制

3.2 蜂鸣器提示

3.3 OLED显示

3.4 指纹与卡片管理

项目源代码分析

1. 主程序流程 (main.c)

1.1 开机界面

1.2 主界面

1.3 开锁功能

2. 指纹录入与删除

3.门禁卡的录入

4. 数据存储与恢复

完整的main.c文件

总结


项目演示

基于STM32的智能门锁安防系统-CSDN直播

项目概述

智能门锁安防系统是一种集成了多种开锁方式的安全系统,采用STM32单片机作为核心控制器,配备多种硬件设备,如OLED显示器、RC522门禁卡模块、AS608指纹模块、4x4矩阵按键、步进电机、蜂鸣器等,旨在为家庭或企业提供更加安全和便利的门禁系统。该系统支持三种开锁方式:密码锁、门禁卡、指纹识别。

硬件组成:

  • STM32F103C8T6:主控芯片,处理所有的控制任务。
  • OLED显示器:用于显示系统状态、信息、时间等。
  • RC522门禁卡模块:实现RFID卡的读写,支持门禁卡开锁。
  • AS608指纹模块:用于指纹识别,实现指纹开锁。
  • 4x4矩阵按键模块:用于输入密码,实现密码开锁。
  • 步进电机模块:驱动门锁的开关。
  • 蜂鸣器模块:用于提醒用户操作结果,如密码错误、开锁成功等。

功能实现

1. 开锁模式

本系统支持三种开锁方式,每种方式都具有一定的安全性,用户可以根据实际需求选择合适的方式进行解锁:

1.1 按键密码开锁

用户通过按键输入密码进行开锁,密码是可以修改的,用户可以根据需要设置和修改密码。每次输入密码后,系统会对比预设的密码,并决定是否开锁。

1.2 门禁卡开锁

使用RC522门禁卡模块,通过读取门禁卡的信息进行开锁。用户可以录入新的门禁卡,系统会根据用户输入的卡号进行验证,如果匹配成功,则开锁。

1.3 指纹开锁

使用AS608指纹模块,通过指纹识别实现开锁。用户可以录入多个指纹,并通过指纹验证进行开锁。如果验证通过,系统自动解锁。

2. 功能备注

  • 修改密码:用户可以随时修改开锁密码,提供更好的安全性。
  • 添加/删除功能:可以添加新的指纹、门禁卡、密码,支持删除已录入的指纹、门禁卡、密码。
  • 内部Flash存储:系统支持通过内部Flash存储数据,保证即使在掉电情况下,密码、指纹和门禁卡信息仍然能够保留,下次上电时可以继续使用。

3. 硬件模块工作流程

3.1 步进电机控制

步进电机负责锁的物理开关,通过与STM32的GPIO端口连接,可以在接收到开锁命令时,驱动电机打开或关闭锁。

3.2 蜂鸣器提示

蜂鸣器用于提供音响提示,比如开锁成功、密码错误等操作结果。蜂鸣器会根据不同的状态发出不同的声音。

3.3 OLED显示

OLED显示器显示系统的当前状态,包括时间、密码输入界面、开锁提示等信息,增强了用户的交互体验。

3.4 指纹与卡片管理

指纹模块(AS608)与RFID卡模块(RC522)是门锁系统的两大身份验证方式。系统通过这些模块读取指纹和门禁卡信息,并将其与预存的数据库进行比对,判断是否开锁。

项目源代码分析

1. 主程序流程 (main.c)

系统启动后,主程序执行硬件初始化,包括初始化OLED显示器、步进电机、蜂鸣器、按键、指纹模块、门禁卡模块等。然后系统进入主循环,根据用户输入的操作,进行密码验证、指纹识别、门禁卡识别等。

1.1 开机界面
void starting(void)
{OLED_Clear();Show_Str(16,12,128,16,"智能门锁系统",16,0);OLED_Refresh_Gram();delay_ms(2000);
}

在开机时,OLED显示器显示“智能门锁系统”,并延时2秒。

1.2 主界面
			/********************主界面**************************/MENU:OLED_Clear();
MENUNOCLR:OLED_Fill(0,0,20,48,0);//主页菜单显示if(arrow<3){Show_Str(5,arrow*16,128,16,"->",16,0);//显示箭头set=0;}else {Show_Str(5,(arrow-3)*16,128,16,"->",16,0);set=3;}Show_Str(25,0,128,16,setup[set],16,0);Show_Str(25,16,128,16,setup[set+1],16,0);Show_Str(25,32,128,16,setup[set+2],16,0);Show_Str(0,52,128,12,"上    下     确定",12,0);OLED_Refresh_Gram();//更新显示time2=0;while(1){//超时锁屏time2++;if(time2>10000 | key_num==4){  OLED_Clear();DisLock();if(time2>10000)beep_on_mode2();time2 =0;
//								delay_ms(1000);OLED_Clear();goto MAIN;}//手机蓝牙锁定if(memcmp(USART_RX_BUF,"LOCK",4)==0)	{
//							USART_RX_STA=0;
//							memset(USART_RX_BUF,0,USART_REC_LEN);DisLock();goto MAIN;}//功能选项选择key_num=Button4_4_Scan();	if(key_num){if(key_num==13){if(arrow>0)arrow--;goto MENUNOCLR;}if(key_num==15){if(arrow<MaxParaNum-1)arrow++;goto MENUNOCLR;}if(key_num==16){switch(arrow){
#if USE_FINGERPRINTcase 0:Add_FR();		break;//录指case 1:Del_FR();		break;//删指纹case 2:SetPassworld();break;//修改密码case 3:Set_Time(); break;  //设置时间case 4:Add_Rfid(); break;  //录入卡片case 5:Massige(); break;  //显示信息
#elsecase 0:SetPassworld();break;//修改密码case 1:Set_Time(); break;  //设置时间case 2:Add_Rfid(); break;  //录入卡片case 3:Massige(); break;  //显示信息
#endif//									}goto MENU;}		}delay_ms(1);}	}

在主界面,用户可以选择不同的功能选项。界面通过按键的输入切换,显示相应的功能选项,如修改密码、录入指纹、查看信息等。

1.3 开锁功能

系统实现了三种开锁模式,分别是密码开锁、指纹开锁和门禁卡开锁。根据用户输入的方式,系统会调用相应的函数来完成开锁操作。

  • 密码开锁:用户输入密码后,系统会与预设密码进行对比,如果匹配成功,则开锁。
  • 指纹开锁:用户将手指放在指纹模块上,系统通过指纹识别来判断是否开锁。
  • 门禁卡开锁:通过刷卡的方式,系统会验证门禁卡是否匹配,如果匹配成功,则开锁。

蓝牙密码开锁:

						//手机蓝牙解锁密码1Error=usart1_cherk((char*)sys.passwd1);         if(Error==0){ OLED_Clear_NOupdate();Show_Str(12,13,128,20,"蓝牙密码1:正确",12,0); OLED_Refresh_Gram();//更新显示delay_ms(800);DisUnLock();goto MENU;	}else {
//							OLED_Clear_NOupdate();
//							Show_Str(12,13,128,12,"蓝牙密码:错误!",12,0); 
//							OLED_Refresh_Gram();//更新显示
//							delay_ms(800);
//							OLED_Show_Font(56,48,0);//锁}//手机蓝牙解锁密码2Error=usart1_cherk((char*)sys.passwd2);         if(Error==0){sys.errCnt = 0;OLED_Clear_NOupdate();Show_Str(12,13,128,12,"蓝牙密码2:正确",12,0); OLED_Refresh_Gram();//更新显示delay_ms(800);DisUnLock();goto MENU;	}else {//OLED_Show_Font(56,48,0);//锁}

按键密码开锁: 

//密码锁
int password(void)
{int  key_num=0,i=0,satus=0;u16 num=0,num2=0,time3=0,time;u8 pwd[11]="          ";u8 hidepwd[11]="          ";u8 buf[64];OLED_Clear();//清屏if(DisErrCnt())return -1;//错误次数超限OLED_Clear();//清屏Show_Str(5,0,128,16,"密码:",16,0);Show_Str(10,16,128,12," 1   2   3  Bck",12,0);Show_Str(10,28,128,12," 4   5   6  Del",12,0);Show_Str(10,40,128,12," 7   8   9  Dis",12,0);Show_Str(10,52,128,12,"Clr  0  Clr  OK",12,0);OLED_Refresh_Gram();//更新显示
//	Show_Str(102,36,128,12,"显示",12,0);
//	Show_Str(0,52,128,12,"删除 清空   返回 确认",12,0);while(1){key_num=Button4_4_Scan();	if(key_num != -1){	printf("key=[%d]\r\n",key_num);DisFlag = 1;time3=0;if(key_num != -1){	DisFlag = 1;time3=0;switch(key_num){case 1:case 2:case 3:pwd[i]=key_num+0x30; //1-3hidepwd[i]='*';i++;break;case 4://返回OLED_Clear();delay_ms(500);return -1;break;case 5:case 6:case 7:pwd[i]=key_num+0x30-1; //4-6hidepwd[i]='*';i++;break;case 8:if( i > 0){pwd[--i]=' ';  //‘del’键hidepwd[i]=' '; }break;case 9:case 10:case 11:pwd[i]=key_num+0x30-2; //4-6hidepwd[i]='*';i++;break;case 12:satus=!satus; break;//DIScase 13:case 15:while(i--){pwd[i]=' ';  //‘清空’键hidepwd[i]=' '; }i=0;break;case 14:pwd[i]=0x30; //4-6hidepwd[i]='*';i++;break;case 16:goto UNLOCK;break;}}if(DisFlag == 1){if(satus==0)OLED_ShowString(53,0,hidepwd,12);else OLED_ShowString(53,0,pwd,12);OLED_Refresh_Gram();//更新显示}time3++;if(time3%1000==0){OLED_Clear();//清屏return -1;}}}UNLOCK:	for(i=0; i<10; i++){   //验证虚伪密码if(pwd[i]==sys.passwd1[num])num++;else num=0;if(num==6)break;}for(i=0; i<10; i++){   //验证密码if(pwd[i]==sys.passwd2[num2])num2++;else num2=0;if(num2==6)break;}if(num==6 | num2==6){DisUnLock();OLED_Clear();//清屏sys.errCnt = 0;return 0;}else {sys.errCnt++;//错误次数计数if(sys.errCnt>MAXERRTIMES)sys.errTime = 30; //30秒不能再解锁OLED_Clear();//清屏Show_Str(45,48,128,16,"密码错误!",16,0);OLED_Refresh_Gram();//更新显示beep_on_mode1();delay_ms(1500);OLED_Clear();//清屏return -1;}}

指纹开锁:

//刷指纹
int press_FR(void)
{SearchResult seach;u8 ensure;char str[256];if(DisErrCnt())return -1;//错误次数超限ensure=PS_GetImage();OLED_Clear_NOupdate();Show_Str(0,0,128,16,"正在检测指纹",16,0);OLED_Refresh_Gram();//更新显示if(ensure==0x00)//获取图像成功 {	ensure=PS_GenChar(CharBuffer1);if(ensure==0x00) //生成特征成功{		ensure=PS_HighSpeedSearch(CharBuffer1,0,AS608Para.PS_max,&seach);if(ensure==0x00)//搜索成功{				OLED_Clear_NOupdate();Show_Str(20,10,128,24,"解锁中...",24,0);	OLED_Refresh_Gram();//更新显示Walkmotor_ON();Show_Str(20,10,128,24,"已解锁!",24,0);OLED_Refresh_Gram();//更新显示OLED_Show_Font(112,18,1);//开锁				//str=mymalloc(SRAMIN,2000);sprintf(str,"ID:%d     匹配分",seach.pageID);Show_Str(0,52,128,12,(u8*)str,12,0);	sprintf(str,":%d",seach.mathscore);Show_Str(96,52,128,12,(u8*)str,12,0);	//myfree(SRAMIN,str);OLED_Refresh_Gram();//更新显示delay_ms(1800);OLED_Clear();return 0;}else {sys.errCnt++;//错误次数计数if(sys.errCnt>MAXERRTIMES)sys.errTime = 30; //30秒不能再解锁ShowErrMessage(ensure);	OLED_Refresh_Gram();//更新显示beep_on_mode1();OLED_Clear();return -1;}				}elseShowErrMessage(ensure);OLED_Refresh_Gram();//更新显示delay_ms(2000);OLED_Clear();}return -1;	
}

门禁卡开锁:

MFRC522_Initializtion();			
Error=MFRC522_lock();
if(Error==0)
{goto MENU;	
}
else 
{OLED_Show_Font(56,48,0);//锁
}

2. 指纹录入与删除

指纹的录入与删除功能也在系统中得到了实现,用户可以通过界面选择录入或删除指纹,并且在录入时会进行指纹的匹配与验证,确保指纹信息的正确性。

void Add_FR(void)
{// 录入指纹逻辑...
}
//删除指纹
void Del_FR(void)
{//删除指纹逻辑......MENU:	OLED_Clear();
}

3.门禁卡的录入

//录入新卡
u8 Add_Rfid(void)
{//录入卡片逻辑......
}

4. 数据存储与恢复

系统使用STM32的Flash存储用户信息(如指纹、密码和门禁卡信息),确保即使系统掉电,数据不会丢失。系统会在每次开机时检查Flash中的数据,恢复上次的配置。

void SysPartInit(void )
{STMFLASH_Read(SYS_SAVEADDR, (u16*)&sys, sizeof(sys));if(sys.HZCFlag != 980706){// 初始化系统配置}
}

完整的main.c文件

#include "main.h"
#include <string.h>SysTemPat sys;#define USE_FINGERPRINT  1
#define MAXERRTIMES 5
#define usart2_baund  57600//串口2波特率,根据指纹模块波特率更改//要写入到STM32 FLASH的字符串数组
const u8 TEXT_Buffer[]={0x17,0x23,0x6f,0x60,0,0};
#define TEXT_LENTH sizeof(TEXT_Buffer)	 		  	//数组长度	
#define SIZE TEXT_LENTH/4+((TEXT_LENTH%4)?1:0)
#define FLASH_SAVE_ADDR  0X0802C124 	//设置FLASH 保存地址(必须为偶数,且所在扇区,要大于本代码所占用到的扇区.//否则,写操作的时候,可能会导致擦除整个扇区,从而引起部分程序丢失.引起死机.SysPara AS608Para;//指纹模块AS608参数
u16 ValidN;//模块内有效指纹个数
u8** kbd_tbl;void Display_Data(void);//显示时间
void Add_FR(void);	//录指纹
void Del_FR(void);	//删除指纹
int press_FR(void);//刷指纹
void ShowErrMessage(u8 ensure);//显示确认码错误信息
int password(void);//密码锁
void SetPassworld(void);//修改密码
void starting(void);//开机界面信息
u8 MFRC522_lock(void);//刷卡解锁
u8 Add_Rfid(void);		//录入
void Set_Time(void);
void Massige(void);
void SysPartInit(void );   //系统参数初始化 
//u8 Pwd[7]="      ";  //解锁密码1
//u8 Pwd2[7]="      ";  //解锁密码2
//u8 cardid[6]={0,0,0,0,0,0};  //卡号1
int Error;  //密码验证信息u8 DisFlag = 1;//数字的ASCII码
uc8 numberascii[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
//显示缓冲区
u8  dispnumber5buf[6];
u8  dispnumber3buf[4];
u8  dispnumber2buf[3];
//MFRC522数据区
u8  mfrc552pidbuf[18];
u8  card_pydebuf[2];
u8  card_numberbuf[5];
u8  card_key0Abuf[6]={0xff,0xff,0xff,0xff,0xff,0xff};
u8  card_writebuf[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
u8  card_readbuf[18];
//SM05-S数据区
u8  sm05cmdbuf[15]={14,128,0,22,5,0,0,0,4,1,157,16,0,0,21};
//extern声明变量已在外部的C文件里定义,可以在主文件中使用
extern u8  sm05receivebuf[16];	//在中断C文件里定义
extern u8  sm05_OK;							//在中断C文件里定义//u8 * week[7]={"Mon","Tue","Wed","Thu","Fri","Sat","Sun"};
u8 * week[7]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};#if USE_FINGERPRINT
#define MaxParaNum 6
u8 * setup[7]={"1、录入指纹","2、删除指纹","3、修改密码","4、修改时间","5、录入卡片","6、查看信息"};
#else
#define MaxParaNum 4
u8 * setup[7]={"1、修改密码","2、修改时间","3、录入卡片","4、查看信息","           ","           "};
#endifvoid DisUnLock(void )
{OLED_Clear();Show_Str(20,10,128,24,"解锁中...",24,0);	OLED_Refresh_Gram();//更新显示Walkmotor_ON();Show_Str(20,10,128,24,"已解锁!",24,0);OLED_Refresh_Gram();//更新显示delay_ms(1500);
}void DisLock(void )
{OLED_Clear();Show_Str(30,20,128,16,"锁定中!",16,0);OLED_Refresh_Gram();//更新显示Walkmotor_OFF();Show_Str(30,20,128,16,"已锁定!",16,0);OLED_Show_Font(56,48,0);//锁OLED_Refresh_Gram();//更新显示delay_ms(1000);
}int main(void)
{			u16 set=0;u8 err=0;int key_num;int time1;int time2;		//锁屏时间char arrow=0;  //箭头位子//SysHSI_Init();delay_init();	    	 //延时函数初始化	  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级uart_init(9600);	 //串口初始化为9600printf("串口功能正常\r\n");Button4_4_Init();          //初始化与按键连接的硬件接口OLED_Init();    			//显示初始化Walkmotor_Init();        //步进电机初始化BEEP_Init();			//蜂鸣器初始化usart2_init(usart2_baund);           //初始化指纹模块PS_StaGPIO_Init();OLED_Clear(); starting();//开机信息  logoerr = RTC_Init();	  			//RTC初始化if(err){OLED_Clear(); Show_Str(12,13,128,20,"RTC CRY ERR!",12,0); OLED_Refresh_Gram();//更新显示delay_ms(3000);}SysPartInit();   //系统参数初始化 while(1){
//锁屏界面
MAIN:OLED_Clear(); OLED_Show_Font(56,48,0);//显示锁图标while(1){time1++;Display_Data();//时间显示:每1000ms更新一次显示数据if(DisFlag == 1){DisFlag = 0;OLED_Fill(0,24,16,63,0);OLED_Refresh_Gram();//更新显示}if((time1%100)==1){//MFRC522解锁time1=0;MFRC522_Initializtion();			Error=MFRC522_lock();if(Error==0){goto MENU;	}else {OLED_Show_Font(56,48,0);//锁}//手机蓝牙解锁密码1Error=usart1_cherk((char*)sys.passwd1);         if(Error==0){ OLED_Clear_NOupdate();Show_Str(12,13,128,20,"蓝牙密码1:正确",12,0); OLED_Refresh_Gram();//更新显示delay_ms(800);DisUnLock();goto MENU;	}else {
//							OLED_Clear_NOupdate();
//							Show_Str(12,13,128,12,"蓝牙密码:错误!",12,0); 
//							OLED_Refresh_Gram();//更新显示
//							delay_ms(800);
//							OLED_Show_Font(56,48,0);//锁}//手机蓝牙解锁密码2Error=usart1_cherk((char*)sys.passwd2);         if(Error==0){sys.errCnt = 0;OLED_Clear_NOupdate();Show_Str(12,13,128,12,"蓝牙密码2:正确",12,0); OLED_Refresh_Gram();//更新显示delay_ms(800);DisUnLock();goto MENU;	}else {//OLED_Show_Font(56,48,0);//锁}}//指纹解锁if(PS_Sta)	 //检测PS_Sta状态,如果有手指按下{while(PS_Sta){Error=press_FR();//刷指纹if(Error==0){//DisUnLock();goto MENU;   //跳到解锁界面}								else {OLED_Show_Font(56,48,0);//锁}}}//密码锁key_num=Button4_4_Scan();	//按键扫描if(key_num!=-1){Error=password();//密码解锁函数if(Error==0){goto MENU;	//跳到解锁界面}else {OLED_Show_Font(56,48,0);//锁}}delay_ms(1);}/********************主界面**************************/MENU:OLED_Clear();
MENUNOCLR:OLED_Fill(0,0,20,48,0);//主页菜单显示if(arrow<3){Show_Str(5,arrow*16,128,16,"->",16,0);//显示箭头set=0;}else {Show_Str(5,(arrow-3)*16,128,16,"->",16,0);set=3;}Show_Str(25,0,128,16,setup[set],16,0);Show_Str(25,16,128,16,setup[set+1],16,0);Show_Str(25,32,128,16,setup[set+2],16,0);Show_Str(0,52,128,12,"上    下     确定",12,0);OLED_Refresh_Gram();//更新显示time2=0;while(1){//超时锁屏time2++;if(time2>10000 | key_num==4){  OLED_Clear();DisLock();if(time2>10000)beep_on_mode2();time2 =0;
//								delay_ms(1000);OLED_Clear();goto MAIN;}//手机蓝牙锁定if(memcmp(USART_RX_BUF,"LOCK",4)==0)	{
//							USART_RX_STA=0;
//							memset(USART_RX_BUF,0,USART_REC_LEN);DisLock();goto MAIN;}//功能选项选择key_num=Button4_4_Scan();	if(key_num){if(key_num==13){if(arrow>0)arrow--;goto MENUNOCLR;}if(key_num==15){if(arrow<MaxParaNum-1)arrow++;goto MENUNOCLR;}if(key_num==16){switch(arrow){
#if USE_FINGERPRINTcase 0:Add_FR();		break;//录指case 1:Del_FR();		break;//删指纹case 2:SetPassworld();break;//修改密码case 3:Set_Time(); break;  //设置时间case 4:Add_Rfid(); break;  //录入卡片case 5:Massige(); break;  //显示信息
#elsecase 0:SetPassworld();break;//修改密码case 1:Set_Time(); break;  //设置时间case 2:Add_Rfid(); break;  //录入卡片case 3:Massige(); break;  //显示信息
#endif//									}goto MENU;}		}delay_ms(1);}	}//while}u8 DisErrCnt(void){int time=0;u8 buf[64];if(sys.errTime>0)//错误次数计数{OLED_Clear();while(1){if(time++ == 1000){time = 0;if(sys.errTime==0){OLED_Clear();break;}Show_Str(0,16,128,16,"密码错误次数过多",16,0);sprintf(buf,"请%02d秒后重试", sys.errTime);Show_Str(20,32,128,16,buf,16,0);OLED_Refresh_Gram();//更新显示}delay_ms(1);if(4 == Button4_4_Scan())//返回{OLED_Clear();return 1;}}}}//获取键盘数值
u16 GET_NUM(void)
{u8  key_num=0;u16 num=0;OLED_ShowNum(78,32,num,3,12);OLED_Refresh_Gram();//更新显示while(1){key_num=Button4_4_Scan();	if(key_num != -1){
//			if(key_num==13)return 0xFFFF;//‘返回’键
//			if(key_num==14)return 0xFF00;//		
//			if(key_num>0&&key_num<10&&num<99)//‘1-9’键(限制输入3位数)
//				num =num*10+key_num;		
//			if(key_num==15)num =num/10;//‘Del’键			
//			if(key_num==10&&num<99)num =num*10;//‘0’键
//			if(key_num==16)return num;  //‘Enter’键switch(key_num){case 1:case 2:case 3:if(key_num>0&&key_num<10&&num<99)//‘1-9’键(限制输入3位数)num =num*10+key_num;		break;case 4://返回return 0xFFFF;return -1;break;case 5:case 6:case 7:if(key_num>0&&key_num<10&&num<99)//‘1-9’键(限制输入3位数)num =num*10+key_num-1;break;case 8:num =num/10;//‘del’键break;case 9:case 10:case 11:if(key_num>0&&key_num<10&&num<99)//‘1-9’键(限制输入3位数)num =num*10+key_num-2;break;case 12: break;//DIScase 13:case 15:return 0xFF00;break;case 14:num =num*10;break;case 16:return num;break;}OLED_ShowNum(78,32,num,3,12);OLED_Refresh_Gram();//更新显示}}	
}
//密码锁
int password(void)
{int  key_num=0,i=0,satus=0;u16 num=0,num2=0,time3=0,time;u8 pwd[11]="          ";u8 hidepwd[11]="          ";u8 buf[64];OLED_Clear();//清屏if(DisErrCnt())return -1;//错误次数超限OLED_Clear();//清屏Show_Str(5,0,128,16,"密码:",16,0);Show_Str(10,16,128,12," 1   2   3  Bck",12,0);Show_Str(10,28,128,12," 4   5   6  Del",12,0);Show_Str(10,40,128,12," 7   8   9  Dis",12,0);Show_Str(10,52,128,12,"Clr  0  Clr  OK",12,0);OLED_Refresh_Gram();//更新显示
//	Show_Str(102,36,128,12,"显示",12,0);
//	Show_Str(0,52,128,12,"删除 清空   返回 确认",12,0);while(1){key_num=Button4_4_Scan();	if(key_num != -1){	printf("key=[%d]\r\n",key_num);DisFlag = 1;time3=0;if(key_num != -1){	DisFlag = 1;time3=0;switch(key_num){case 1:case 2:case 3:pwd[i]=key_num+0x30; //1-3hidepwd[i]='*';i++;break;case 4://返回OLED_Clear();delay_ms(500);return -1;break;case 5:case 6:case 7:pwd[i]=key_num+0x30-1; //4-6hidepwd[i]='*';i++;break;case 8:if( i > 0){pwd[--i]=' ';  //‘del’键hidepwd[i]=' '; }break;case 9:case 10:case 11:pwd[i]=key_num+0x30-2; //4-6hidepwd[i]='*';i++;break;case 12:satus=!satus; break;//DIScase 13:case 15:while(i--){pwd[i]=' ';  //‘清空’键hidepwd[i]=' '; }i=0;break;case 14:pwd[i]=0x30; //4-6hidepwd[i]='*';i++;break;case 16:goto UNLOCK;break;}}if(DisFlag == 1){if(satus==0)OLED_ShowString(53,0,hidepwd,12);else OLED_ShowString(53,0,pwd,12);OLED_Refresh_Gram();//更新显示}time3++;if(time3%1000==0){OLED_Clear();//清屏return -1;}}}UNLOCK:	for(i=0; i<10; i++){   //验证虚伪密码if(pwd[i]==sys.passwd1[num])num++;else num=0;if(num==6)break;}for(i=0; i<10; i++){   //验证密码if(pwd[i]==sys.passwd2[num2])num2++;else num2=0;if(num2==6)break;}if(num==6 | num2==6){DisUnLock();OLED_Clear();//清屏sys.errCnt = 0;return 0;}else {sys.errCnt++;//错误次数计数if(sys.errCnt>MAXERRTIMES)sys.errTime = 30; //30秒不能再解锁OLED_Clear();//清屏Show_Str(45,48,128,16,"密码错误!",16,0);OLED_Refresh_Gram();//更新显示beep_on_mode1();delay_ms(1500);OLED_Clear();//清屏return -1;}}//显示确认码错误信息
void ShowErrMessage(u8 ensure)
{Show_Str(0,48,128,12,(u8*)EnsureMessage(ensure),12,0);	OLED_Refresh_Gram();//更新显示delay_ms(1000);OLED_ShowString(0,48,"                   ",12);	OLED_Refresh_Gram();//更新显示
}
//录指纹
void Add_FR(void)
{u8 i,ensure ,processnum=0;int key_num;u16 ID;OLED_Clear();//清屏while(1){key_num=Button4_4_Scan();	if(key_num==16){OLED_Clear();//清屏return ;}switch (processnum){case 0://OLED_Clear();//清屏i++;Show_Str(0,0,128,16,"=== 录入指纹 ===",16,0);Show_Str(0,24,128,12,"请按指纹!  ",12,0);	Show_Str(104,52,128,12,"返回",12,0);		OLED_Refresh_Gram();//更新显示	ensure=PS_GetImage();if(ensure==0x00) {BEEP=0;ensure=PS_GenChar(CharBuffer1);//生成特征BEEP=1;if(ensure==0x00){Show_Str(0,24,128,12,"指纹正常!    ",12,0);OLED_Refresh_Gram();//更新显示	i=0;processnum=1;//跳到第二步						}else ShowErrMessage(ensure);				}else ShowErrMessage(ensure);//OLED_Clear();//清屏break;case 1:i++;Show_Str(0,24,128,12,"请再按一次指纹",12,0);OLED_Refresh_Gram();//更新显示		ensure=PS_GetImage();if(ensure==0x00) {BEEP=0;ensure=PS_GenChar(CharBuffer2);//生成特征BEEP=1;if(ensure==0x00){Show_Str(0,24,128,12,"指纹正常!",12,0);	OLED_Refresh_Gram();//更新显示i=0;processnum=2;//跳到第三步}else ShowErrMessage(ensure);	}else ShowErrMessage(ensure);		//OLED_Clear();//清屏break;case 2:		Show_Str(0,24,128,12,"对比两次指纹        ",12,0);OLED_Refresh_Gram();//更新显示ensure=PS_Match();if(ensure==0x00) {Show_Str(0,24,128,12,"两次指纹一样       ",12,0);OLED_Refresh_Gram();//更新显示processnum=3;//跳到第四步}else {Show_Str(0,24,128,12,"对比失败 请重录    ",12,0);	OLED_Refresh_Gram();//更新显示ShowErrMessage(ensure);i=0;OLED_Clear();//清屏processnum=0;//跳回第一步		}delay_ms(1200);//OLED_Clear();//清屏break;case 3:Show_Str(0,24,128,12,"生成指纹模板...    ",12,0);OLED_Refresh_Gram();//更新显示	ensure=PS_RegModel();if(ensure==0x00) {
//					Show_Str(0,24,128,12,"生成指纹模板成功!",12,0);OLED_Refresh_Gram();//更新显示processnum=4;//跳到第五步}else {processnum=0;ShowErrMessage(ensure);}delay_ms(1200);break;case 4:	//OLED_Clear();//清屏Show_Str(0,24,128,12,"请输入储存ID:        ",12,0);Show_Str(122,52,128,12," ",12,0);Show_Str(0,52,128,12,"删除 清空      确认",12,0);OLED_Refresh_Gram();//更新显示doID=GET_NUM();while(!(ID<AS608Para.PS_max));//输入ID必须小于模块容量最大的数值ensure=PS_StoreChar(CharBuffer2,ID);//储存模板if(ensure==0x00) {			OLED_Clear_NOupdate();//清屏Show_Str(0,30,128,16,"录指纹成功!",16,0);	PS_ValidTempleteNum(&ValidN);//读库指纹个数Show_Str(66,52,128,12,"剩余",12,0);OLED_ShowNum(90,52,AS608Para.PS_max-ValidN,3,12);OLED_Refresh_Gram();//更新显示delay_ms(1500);OLED_Clear();	return ;}else {processnum=0;ShowErrMessage(ensure);}OLED_Clear();//清屏					break;				}delay_ms(400);if(i==10)//超过5次没有按手指则退出{OLED_Clear();break;}				}
}//刷指纹
int press_FR(void)
{SearchResult seach;u8 ensure;char str[256];if(DisErrCnt())return -1;//错误次数超限ensure=PS_GetImage();OLED_Clear_NOupdate();Show_Str(0,0,128,16,"正在检测指纹",16,0);OLED_Refresh_Gram();//更新显示if(ensure==0x00)//获取图像成功 {	ensure=PS_GenChar(CharBuffer1);if(ensure==0x00) //生成特征成功{		ensure=PS_HighSpeedSearch(CharBuffer1,0,AS608Para.PS_max,&seach);if(ensure==0x00)//搜索成功{				OLED_Clear_NOupdate();Show_Str(20,10,128,24,"解锁中...",24,0);	OLED_Refresh_Gram();//更新显示Walkmotor_ON();Show_Str(20,10,128,24,"已解锁!",24,0);OLED_Refresh_Gram();//更新显示OLED_Show_Font(112,18,1);//开锁				//str=mymalloc(SRAMIN,2000);sprintf(str,"ID:%d     匹配分",seach.pageID);Show_Str(0,52,128,12,(u8*)str,12,0);	sprintf(str,":%d",seach.mathscore);Show_Str(96,52,128,12,(u8*)str,12,0);	//myfree(SRAMIN,str);OLED_Refresh_Gram();//更新显示delay_ms(1800);OLED_Clear();return 0;}else {sys.errCnt++;//错误次数计数if(sys.errCnt>MAXERRTIMES)sys.errTime = 30; //30秒不能再解锁ShowErrMessage(ensure);	OLED_Refresh_Gram();//更新显示beep_on_mode1();OLED_Clear();return -1;}				}elseShowErrMessage(ensure);OLED_Refresh_Gram();//更新显示delay_ms(2000);OLED_Clear();}return -1;	
}//删除指纹
void Del_FR(void)
{u8  ensure;u16 num;OLED_Clear();Show_Str(0,0,128,16,"=== 删除指纹 ===",16,0);	Show_Str(0,16,128,12,"输入指纹ID:",12,0);Show_Str(0,52,128,12,"返回 清空    确认删除",12,0);OLED_Refresh_Gram();//更新显示delay_ms(50);
//	AS608_load_keyboard(0,170,(u8**)kbd_delFR);num=GET_NUM();//获取返回的数值if(num==0xFFFF)goto MENU ; //返回主页面else if(num==0xFF00)ensure=PS_Empty();//清空指纹库else ensure=PS_DeletChar(num,1);//删除单个指纹if(ensure==0){OLED_Clear();Show_Str(0,20,128,12,"删除指纹成功!",12,0);		Show_Str(80,48,128,12,"剩余",12,0);		OLED_Refresh_Gram();//更新显示}elseShowErrMessage(ensure);	OLED_Refresh_Gram();//更新显示PS_ValidTempleteNum(&ValidN);//读库指纹个数OLED_ShowNum(110,48,AS608Para.PS_max-ValidN,3,12);delay_ms(1200);MENU:	OLED_Clear();
}
//修改密码
void SetPassworld(void)
{int pwd_ch=0;int  key_num=0,i=0,satus=0;u16 time4=0;u8 pwd[6]="      ";u8 hidepwd[6]="      ";u8 buf[10];OLED_Clear();//清屏Show_Str(10,16,128,12," 1   2   3  Bck",12,0);Show_Str(10,28,128,12," 4   5   6  Del",12,0);Show_Str(10,40,128,12," 7   8   9  Dis",12,0);Show_Str(10,52,128,12,"Clr  0  Chg  OK",12,0);Show_Str(5,0,128,16,"新密码",16,0);sprintf((char*)buf,"%d:",pwd_ch+1);Show_Str(5,48,128,16,buf,16,0);OLED_Refresh_Gram();//更新显示while(1){key_num=Button4_4_Scan();	if(key_num != -1){	DisFlag = 1;time4=0;switch(key_num){case 1:case 2:case 3:pwd[i]=key_num+0x30; //1-3hidepwd[i]='*';i++;break;case 4://返回OLED_Clear();delay_ms(500);return ;break;case 5:case 6:case 7:pwd[i]=key_num+0x30-1; //4-6hidepwd[i]='*';i++;break;case 8:if( i > 0){pwd[--i]=' ';  //‘del’键hidepwd[i]=' '; }break;case 9:case 10:case 11:pwd[i]=key_num+0x30-2; //4-6hidepwd[i]='*';i++;break;case 12:satus=!satus; break;//DIScase 13:sprintf((char*)buf,"%d:",pwd_ch+1);Show_Str(5,48,128,16,buf,16,0);pwd_ch = !pwd_ch;case 15:while(i--){pwd[i]=' ';  //‘清空’键hidepwd[i]=' '; }i=0;break;case 14:pwd[i]=0x30; //4-6hidepwd[i]='*';i++;break;case 16:goto MODIF;break;}}if(DisFlag == 1)if(satus==0){OLED_ShowString(70,0,hidepwd,12);OLED_Refresh_Gram();//更新显示}else {OLED_ShowString(70,0,pwd,12);OLED_Refresh_Gram();//更新显示}time4++;if(time4%1000==0){OLED_Clear();//清屏DisFlag = 1;return ;}}	MODIF:if(pwd_ch==0){memcpy(sys.passwd1,pwd,7);STMFLASH_Write(SYS_SAVEADDR,(u16*)&sys,sizeof(sys));//保存到内部FLASH//STMFLASH_Read(SYS_SAVEADDR,(u16*)&sys,sizeof(sys)); //读取printf("pwd=%s",sys.passwd1);}else{		memcpy(sys.passwd2,pwd,7);STMFLASH_Write(SYS_SAVEADDR,(u16*)&sys,sizeof(sys));//保存密码到内部FLASH
//		STMFLASH_Write(0X08090004,(u32*)pwd,2);//保存密码到内部eeprom//STMFLASH_Read(SYS_SAVEADDR,(u16*)&sys,sizeof(sys)); //读取密码2printf("pwd2=%s",sys.passwd1);}OLED_Clear();//清屏Show_Str(0,48,128,16,"密码修改成功 !",16,0);OLED_Refresh_Gram();//更新显示delay_ms(1000);
}
//设置时间
void Set_Time(void)
{
//	RTC_TimeTypeDef RTC_TimeStruct;
//	RTC_DateTypeDef calendar;u16 year;u8 mon,dat,wek,hour,min,sec;u16 time5=0;u8 tbuf[40];int key_num;int st=0;//	RTC_GetTime(RTC_Format_BIN,&RTC_TimeStruct);
//	RTC_GetDate(RTC_Format_BIN, &calendar);year=calendar.w_year;mon=calendar.w_month;dat=calendar.w_date;wek=calendar.week;hour=calendar.hour;min=calendar.min;sec=calendar.sec;OLED_Clear();Show_Str(98,38,128,12,"<--",12,0);Show_Str(0,52,128,12,"减  加   切换  确定",12,0);OLED_Refresh_Gram();//更新显示while(1){time5++;key_num=Button4_4_Scan();	if(key_num==12 | time5==3000){OLED_Clear();//清屏return ;}if(key_num==13){switch(st){case 0:if(hour>0)hour--;break;case 1:if(min>0)min--;break;case 2:if(sec>0)sec--;break;case 3:if(wek>0)wek--;break;case 4:if(year>0)year--;break;case 5:if(mon>0)mon--;break;case 6:if(dat>0)dat--;break;}}if(key_num==14){switch(st){case 0:if(hour<23)hour++;break;case 1:if(min<59)min++;break;case 2:if(sec<59)sec++;break;case 3:if(wek<7)wek++;break;case 4:if(year<2099)year++;break;case 5:if(mon<12)mon++;break;case 6:if(dat<31)dat++;break;}}if(key_num==15){if(st<7)st++;if(st==7)st=0;}if(key_num==16){break;}if(time5%250==0){switch(st)			//闪烁{case 0:OLED_ShowString(0,0,"  ",24);break;case 1:OLED_ShowString(36,0,"  ",24);break;case 2:OLED_ShowString(72,0,"  ",24);break;case 3:OLED_ShowString(110,12,"   ",12);break;case 4:OLED_ShowString(68,26,"    ",12);break;case 5:OLED_ShowString(98,26,"  ",12);break;case 6:OLED_ShowString(116,26,"  ",12);break;}OLED_Refresh_Gram();//更新显示}if(time5%500==0){time5=0;sprintf((char*)tbuf,"%02d:%02d:%02d",hour,min,sec); OLED_ShowString(0,0,tbuf,24);	//RTC_GetDate(RTC_Format_BIN, &calendar);sprintf((char*)tbuf,"%04d-%02d-%02d",year,mon,dat); OLED_ShowString(68,26,tbuf,12);		sprintf((char*)tbuf,"%s",week[wek]); OLED_ShowString(110,12,tbuf,12);	OLED_Refresh_Gram();//更新显示}delay_ms(1);}
//	RTC_Set_Time(hour,min,sec,RTC_H12_AM);	//设置时间
//	RTC_Set_Date(year,mon,dat,wek);		//设置日期RTC_Set(year,mon,dat,hour, min,sec);OLED_Clear();Show_Str(20,48,128,16,"设置成功!",16,0);OLED_Refresh_Gram();//更新显示delay_ms(1000);
}//录入新卡
u8 Add_Rfid(void)
{u8 ID;u16 time6=0;u8 i,key_num,status=1,card_size;OLED_Clear();Show_Str(0,0,128,16,"=== 录入卡片 ===",16,0);	Show_Str(0,20,128,12,"请放入新卡片:",12,0);	Show_Str(0,52,128,12,"返回",12,0);OLED_Refresh_Gram();//更新显示MFRC522_Initializtion();			//初始化MFRC522while(1){AntennaOn();status=MFRC522_Request(0x52, card_pydebuf);			//寻卡if(status==0)		//如果读到卡{printf("rc522 ok\r\n");Show_Str(0,38,128,12,"读卡成功!",12,0);OLED_Refresh_Gram();//更新显示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);					//读卡//printf("卡的类型:%#x %#x",card_pydebuf[0],card_pydebuf[1]);//卡序列号显,最后一字节为卡的校验码printf("卡的序列号:");for(i=0;i<5;i++){printf("%#x ",card_numberbuf[i]);}printf("\r\n");//卡容量显示,单位为Kbits//printf("卡的容量:%dKbits\n",card_size);AntennaOff();OLED_Clear_NOupdate();Show_Str(0,12,128,12,"请输入储存ID(0-9):  ",12,0);Show_Str(122,52,128,12," ",12,0);Show_Str(0,52,128,12,"删除 清空      确认",12,0);OLED_Refresh_Gram();//更新显示doID=GET_NUM();while(!(ID<10));//输入ID必须小于最大容量printf("正在录入卡片:%d\r\n",ID);OLED_Clear_NOupdate();Show_Str(0,38,128,12,"正在录入.",12,0);OLED_Refresh_Gram();//更新显示
//			memcpy(sys.cardid[ID],card_numberbuf,5);for(i=0;i<5;i++){sys.cardid[ID][i] = card_numberbuf[i];}STMFLASH_Write(SYS_SAVEADDR,(u16*)&sys,sizeof(sys));//保存到内部FLASH
//			STMFLASH_Write(0X080f0004,(u32*)card_numberbuf,2);
//			STMFLASH_Read(0X080f0004,(u32*)cardid,1); //读取卡号1for(i=0;i<10;i++)printf("cardid={%X,%X,%X,%X}\r\n",sys.cardid[i][0],sys.cardid[i][1],sys.cardid[i][2],sys.cardid[i][3]);Show_Str(0,38,128,12,"录入成功!",12,0);OLED_Refresh_Gram();//更新显示delay_ms(1000);OLED_Clear();return 0;}key_num=Button4_4_Scan();	time6++;if(time6%5000==0 | key_num==13){OLED_Clear();return 1;}}
}
//rfid卡锁
u8 MFRC522_lock(void)
{u8 i,j,status=1,card_size;u8 count;u8 prtfbuf[64];AntennaOn();status=MFRC522_Request(0x52, card_pydebuf);			//寻卡if(status==0)		//如果读到卡{if(DisErrCnt())return -1;//错误次数超限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("卡的类型:%#x %#x",card_pydebuf[0],card_pydebuf[1]);//卡序列号显,最后一字节为卡的校验码count=0;for(j=0;j<10;j++){printf("\r\n卡%d 的序列号:",j);for(i=0;i<5;i++){printf("%x=%x    ",card_numberbuf[i],sys.cardid[j][i]);if(card_numberbuf[i]==sys.cardid[j][i])count++;}printf("\r\n");if(count>=4){sys.errCnt = 0;OLED_Clear_NOupdate();sprintf(prtfbuf,"RFID:%d匹配成功",j);Show_Str(12,13,128,20,prtfbuf,12,0); OLED_Refresh_Gram();//更新显示delay_ms(500);DisUnLock();return 0;}else count=0;}{sys.errCnt++;//错误次数计数if(sys.errCnt>MAXERRTIMES)sys.errTime = 30; //30秒不能再解锁OLED_Clear(); Show_Str(12,13,128,20,"卡片错误",12,0); OLED_Refresh_Gram();//更新显示beep_on_mode1();OLED_Clear(); OLED_Show_Font(56,48,0);//锁DisFlag = 1;}printf("\n");//卡容量显示,单位为Kbits//printf("卡的容量:%dKbits\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();return 1;
}
//显示信息
void Massige(void)
{OLED_Clear();Show_Str(0,0,128,12,"智能能门锁系统",12,0); OLED_Refresh_Gram();//更新显示delay_ms(3000);
}//显示时间
void Display_Data(void)
{static u8 t=1;	u8 tbuf[40];if(t!=calendar.sec){t=calendar.sec;sprintf((char*)tbuf,"%02d:%02d:%02d",calendar.hour,calendar.min,calendar.sec); OLED_ShowString(0,0,tbuf,24);	//printf(tbuf);sprintf((char*)tbuf,"%04d-%02d-%02d",calendar.w_year,calendar.w_month,calendar.w_date); OLED_ShowString(68,26,tbuf,12);		//printf(tbuf);sprintf((char*)tbuf,"%s",week[calendar.week]); //printf(tbuf);OLED_ShowString(110,12,tbuf,12);DisFlag = 1;//更新显示}
}//开机信息
void starting(void)
{u8 cnt = 0;u8 ensure;char str[64];			  u8 key;
//	OLED_Show_Image(0);	//image
//	OLED_Refresh_Gram();//更新显示Show_Str(16,12,128,16,"智能门锁系统",16,0);OLED_Refresh_Gram();//更新显示delay_ms(2000);/*********************************开机信息提示***********************************/
#if USE_FINGERPRINTOLED_Clear();Show_Str(0,0,128,12,"fingerprint system!",12,0); Show_Str(0,12,128,12,"connect to as608",12,0);OLED_Refresh_Gram();//更新显示while(PS_HandShake(&AS608Addr))//与AS608模块握手{cnt++;if(cnt>10)break;delay_ms(400);Show_Str(0,24,128,12,"connect failed! ",12,0);OLED_Refresh_Gram();//更新显示delay_ms(800);Show_Str(0,24,128,12,"connect to as608  ",12,0);	printf("connect to as608..\r\n");OLED_Refresh_Gram();//更新显示}if(cnt>10)Show_Str(0,24,128,12,"connect failed!",12,0);	OLED_Refresh_Gram();//更新显示sprintf(str,"baud:%d  addr:%x",usart2_baund,AS608Addr);Show_Str(0,36,128,12,(u8*)str,12,0);OLED_Refresh_Gram();//更新显示ensure=PS_ValidTempleteNum(&ValidN);//读库指纹个数if(ensure!=0x00)printf("ERR:010\r\n");//ShowErrMessage(ensure);//显示确认码错误信息	ensure=PS_ReadSysPara(&AS608Para);  //读参数 
//	if(ensure==0x00)
//	{sprintf(str,"capacity:%d  Lv: %d",AS608Para.PS_max-ValidN,AS608Para.PS_level);Show_Str(0,48,128,12,(u8*)str,12,0);OLED_Refresh_Gram();//更新显示
//	}
//	else
//		ShowErrMessage(ensure);	//显示确认码错误信息	//	delay_ms(1000);
//
#endifOLED_Clear();
}void SysPartInit(void )   //系统参数初始化 
{STMFLASH_Read(SYS_SAVEADDR,(u16*)&sys,sizeof(sys)); //读取if(sys.HZCFlag != 980706){memset(&sys,0,sizeof(sys));sys.HZCFlag = 980706;strcpy((char *)sys.passwd1, "123456");//密码strcpy((char *)sys.passwd2, "980706");//密码STMFLASH_Write(SYS_SAVEADDR,(u16*)&sys,sizeof(sys));//保存到内部FLASHprintf("初始化配置成功\r\n");}else{printf("欢迎使用智能门锁\r\n");}
}

总结

本项目通过STM32控制多个外设,结合指纹识别、门禁卡与密码开锁三种开锁方式,构建了一个智能门锁系统。系统实现了多种安全措施,如数据存储、指纹验证、密码保护、门禁卡管理等,能够有效地提升安全性和用户体验。需要获取完整资料的,可以私信留言我,记得先点赞关注收藏先喔,完整资料如图所示:

相关文章:

基于STM32的智能门锁安防系统(开源)

目录 项目演示 项目概述 硬件组成&#xff1a; 功能实现 1. 开锁模式 1.1 按键密码开锁 1.2 门禁卡开锁 1.3 指纹开锁 2. 功能备注 3. 硬件模块工作流程 3.1 步进电机控制 3.2 蜂鸣器提示 3.3 OLED显示 3.4 指纹与卡片管理 项目源代码分析 1. 主程序流程 (main…...

浅谈云端编辑器,分析其亮点与不足

浅谈云端编辑器&#xff0c;分析其亮点与不足 这个云端编辑器界面可以分为左侧题目筛选栏、中间题目描述与代码编辑区域、右侧AI提示功能三部分。以下是详细的分析&#xff1a; 1. 左侧题目筛选栏 层次结构清晰&#xff1a;左侧栏展示了一个层级结构&#xff0c;题目按主题分…...

Python字符串引号的嵌套问题

目录 1、使用不同类型的引号 2、使用转义字符 3、使用三重引号 4、嵌套三重引号 5、注意事项 在Python中&#xff0c;字符串可以使用单引号 () 或双引号 (") 来定义&#xff0c;但是如果我们要定义的字符串内也包含引号&#xff0c;字符串界定符的不正确使用会导致语法…...

latex如何让目录后面有点

使用前效果 在导言区引入以下代码 \usepackage[subfigure]{tocloft} \usepackage{subfigure} % 设置目录中 section 条目前导符号为连续点 \renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}}编译后的效果...

【力扣系列题目】不同路径 组合总和 最大连续1个数 打家劫舍{持续更新中...}

文章目录 不同路径不同路径[不同路径 II](https://leetcode.cn/problems/unique-paths-ii/)[不同路径 III](https://leetcode.cn/problems/unique-paths-iii/) 组合总和组合总和 【无重复数字无限制选择次数】[组合总和 II](https://leetcode.cn/problems/combination-sum-ii/)…...

方法建议ChatGPT提示词分享

方法建议 ChatGPT能够根据您的具体需求提供针对性的建议&#xff0c;帮助您选择最合适的研究方法。通过清晰的提示&#xff0c;ChatGPT可以精准地为您提供最契合的研究方案。此外&#xff0c;它还能协助您将这些方法灵活地应用于新的研究环境&#xff0c;提出创新的技术解决方案…...

Cursor的详细使用指南

以下是一份关于 Cursor 的详细使用指南&#xff1a; 一、安装与设置 下载与安装&#xff1a; 首先&#xff0c;访问 Cursor 的官方网站&#xff0c;根据你的操作系统&#xff08;Windows、Mac 或 Linux&#xff09;下载相应的安装程序。运行安装程序&#xff0c;按照屏幕上的提…...

Python----Python高级(正则表达式:语法规则,re库)

一、正则表达式 1.1、概念 正则表达式&#xff0c;又称规则表达式,&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、 regexp或RE&#xff09;&#xff0c;是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff0…...

电脑如何访问手机文件?

手机和电脑已经深深融入了我们的日常生活&#xff0c;无时无刻不在为我们提供服务。除了电脑远程操控电脑外&#xff0c;我们还可以在电脑上轻松地访问Android或iPhone手机上的文件。那么&#xff0c;如何使用电脑远程访问手机上的文件呢&#xff1f; 如何使用电脑访问手机文件…...

计算最接近的数

计算最接近的数 真题目录: 点击去查看 E B卷 100分题型 题目描述 给定一个数组X和正整数K&#xff0c;请找出使表达式&#xff1a; X[i] - X[i 1] - … - X[i K - 1] 结果最接近于数组中位数的下标 i &#xff0c;如果有多个 i 满足条件&#xff0c;请返回最大的 i. 其中&…...

Ubuntu离线docker compose安装DataEase 2.10.4版本笔记

1、先准备一个可以正常上网的相同版本的Ubuntu系统&#xff0c;可以使用虚拟机。Ubuntu系统需要安装好docker compose或docker-compose 2、下载dataease-online-installer-v2.10.4-ce.tar在线安装包&#xff0c;解压并执行install.sh进行安装和启动 3、导出docker镜像 sudo d…...

C#使用WMI获取控制面板中安装的所有程序列表

C#使用WMI获取控制面板中安装的所有程序列表 WMI 全称Windows Management Instrumentation,Windows Management Instrumentation是Windows中用于提供共同的界面和对象模式以便访问有关操作系统、设备、应用程序和服务的管理信息。如果此服务被终止&#xff0c;多数基于 Windo…...

WPF2-1在xaml为对象的属性赋值.md

1. AttributeValue方式 1.1. 简单属性赋值1.2. 对象属性赋值 2. 属性标签的方式给属性赋值3. 标签扩展 (Markup Extensions) 3.1. StaticResource3.2. Binding 3.2.1. 普通 Binding3.2.2. ElementName Binding3.2.3. RelativeSource Binding3.2.4. StaticResource Binding (带参…...

社区版Dify实现文生视频 LLM+ComfyUI+混元视频

社区版Dify实现文生视频 LLMComfyUI混元视频 一、 社区版Dify实现私有化混元视频效果二、为什么社区版Dify可以在对话框实现文生视频&#xff1f;LLMComfyUI混元视频 实现流程图&#xff08;重点&#xff09;1. 文生视频模型支持ComfyUI2. ComfyUI可以轻松导出API实现封装3. Di…...

QT调用OpenSceneGraph

OSG和osgQt编译教程&#xff0c;实测通过 一、下载OpenSceneGraph OpenSceneGraphhttps://github.com/openscenegraph/OpenSceneGraph 二、使用CMAKE编译OpenSceneGraph 1.打开cmake&#xff0c;配置源代码目录 2. CMAKE_INSTALL_PREFIX设置为install文件夹&#xff0c;生…...

Qt基础项目篇——Qt版Word字处理软件

一、核心功能 本软件为多文档型程序&#xff0c;界面是标准的 Windows 主从窗口 拥有&#xff1a;主菜单、工具栏、文档显示区 和 状态栏。 所要实现的东西&#xff0c;均在下图了。 开发该软件&#xff0c;主要分为下面三个阶段 1&#xff09;界面设计开发 多窗口 MDI 程序…...

【Postgres_Python】使用python脚本批量创建和导入多个PG数据库

之前批量创建和导入数据库分为2个python脚本进行&#xff0c;现整合优化代码合并为一个python脚本&#xff0c;可同步实现数据库的创建和数据导入。之前的文章链接&#xff1a; 【Postgres_Python】使用python脚本批量创建PG数据库 【Postgres_Python】使用python脚本将多个.S…...

消息队列篇--原理篇--RabbitMQ和Kafka对比分析

RabbitMQ和Kafka是两种非常流行的消息队列系统&#xff0c;但它们的设计哲学、架构特点和适用场景存在显著差异。对比如下。 1、架构设计 RabbitMQ&#xff1a; 基AMQP协议&#xff1a;RabbitMQ是基于AMQP&#xff08;高级消息队列协议&#xff09;构建的&#xff0c;支持多…...

俄语画外音的特点

随着全球媒体消费的增加&#xff0c;语音服务呈指数级增长。作为视听翻译和本地化的一个关键方面&#xff0c;画外音在确保来自不同语言和文化背景的观众能够以一种真实和可访问的方式参与内容方面发挥着重要作用。说到俄语&#xff0c;画外音有其独特的特点、挑战和复杂性&…...

【机器学习实战中阶】音乐流派分类-自动化分类不同音乐风格

音乐流派分类 – 自动化分类不同音乐风格 在本教程中,我们将开发一个深度学习项目,用于自动化地从音频文件中分类不同的音乐流派。我们将使用音频文件的频率域和时间域低级特征来分类这些音频文件。 对于这个项目,我们需要一个具有相似大小和相似频率范围的音频曲目数据集…...

Keil5 IDE使用笔记

1 Keil生成bin文件 $K\ARM\ARMCLANG\bin\fromelf.exe --bin --outputL/L.bin !L $K: 表示 Keil 5的安装路径 L: 表示 工程名 !L: 表示 工程名.arf 后缀的文件 可根据实际需要修改 --output 的值调整生成的bin文件的存放路径。 2 下载程序报错 No ST-LINK detected Error: Fla…...

自动化办公|使用Python重命名并移动文件到对应文件夹

在日常的文件管理和处理过程中&#xff0c;我们可能会遇到需要将文件整理到不同文件夹中的需求。例如&#xff0c;我们有一个包含多个文件的目录&#xff0c;文件名的首字符表示文件应该存放在哪个文件夹中。我们可以使用Python脚本来自动完成这个任务&#xff0c;实现文件的分…...

【全栈】SprintBoot+vue3迷你商城(5)

【全栈】SprintBootvue3迷你商城&#xff08;5&#xff09; 上一期我们基本完成了与用户相关的接口&#xff0c;而这些接口都是用户才能干的事情&#xff0c;如果你没登录&#xff0c;那么这些接口功能你都不能实现。 那么如何做到这一步呢&#xff1f; 1.Token 作用 身份…...

Java 并发编程:Java 中的乐观锁与 CAS

大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 025 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自…...

模拟飞行入坑(五) P3D 多通道视角配置 viewgroup

背景&#xff1a; P3D进行多个屏幕显示的时候&#xff0c;如果使用英伟达自带的屏幕融合成一个屏&#xff0c;或者使用P3D单独拉伸窗口&#xff0c;会使得P3D的画面被整体拉伸&#xff0c;又或者,当使用Multichannel进行多个设备联动时&#xff0c;视角同步组合需要配置&#…...

react中hooks之 React 19 新 Hooks useActionState useFormStatus用法总结

React 19 新 Hooks 使用指南: useActionState & useFormStatus 目录 useActionStateuseFormStatus最佳实践 useActionState 概述 useActionState 是 React 19 引入的新 Hook&#xff0c;用于处理表单 action 的状态更新。它允许你基于表单 action 的结果来更新组件状态…...

为AI聊天工具添加一个知识系统 之48 蒙板程序设计(第二版):Respect九宫格【社会形态:治理】

本文要点 1、词汇表Vocabulary &#xff08;普通名词&#xff09; 1) 三组词&#xff08;数据库支持的三个数字散列&#xff09;&#xff1a; 工作&#xff0c;工件&#xff0c;工具。论题&#xff0c;主题词&#xff0c;关键字。口号&#xff0c;符号&#xff0c;编号。 2…...

靠右行驶数学建模分析(2014MCM美赛A题)

笔记 题目 要求分析&#xff1a; 比较规则的性能&#xff0c;分为light和heavy两种情况&#xff0c;性能指的是 a.流量与安全 b. 速度限制等分析左侧驾驶分析智能系统 论文 参考论文 两类规则分析 靠右行驶&#xff08;第一条&#xff09;2. 无限制&#xff08;去掉了第一条…...

6.5、密集波分复用系统(DWDM)/OTN

图中从左到右分为多个部分&#xff0c;分别代表了信号的输入、传输和输出过程。 左侧是客户侧&#xff0c;有普通接口和彩色接口&#xff0c;分别连接到光转发单元&#xff08;OTU&#xff09;。 中间部分是传输线路&#xff0c;包含多个光放大器&#xff08;OBA、OLA、OPA&…...

Unity3D基于Unity整合BEPUphysicsint物理引擎实战详解

引言 Unity3D是一款流行的游戏引擎&#xff0c;提供了丰富的功能和工具&#xff0c;使开发者能够轻松创建各种类型的游戏。其中&#xff0c;帧同步技术是游戏开发中至关重要的一环&#xff0c;它能确保多个玩家在同一时间内看到的游戏状态是一致的。BEPUphysicsint是一个基于U…...

《探秘鸿蒙Next:如何保障AI模型轻量化后多设备协同功能一致》

在鸿蒙Next的多设备协同场景中&#xff0c;确保人工智能模型轻量化后功能的一致性是一项极具挑战性但又至关重要的任务。以下是一些关键的方法和策略。 统一的模型架构与标准 采用标准化框架&#xff1a;选择如TensorFlow Lite、PyTorch Mobile等在鸿蒙Next上适配良好的轻量化…...

微服务知识——4大主流微服务架构方案

文章目录 1、微服务聚合模式2、微服务共享模式3、微服务代理模式4、微服务异步消息模式 微服务是大型架构的必经之路&#xff0c;也是大厂重点考察对象&#xff0c;下面我就重点详解4大主流微服务架构方案。 1、微服务聚合模式 微服务聚合设计模式&#xff0c;解决了如何从多个…...

Java 方法重写

目录 一、什么是方法重写&#xff0c;为什么需要它 二、方法重写的规则 三、方法重写的实际应用场景 四、方法重写与重载的区别 五、总结 在 Java 编程的精彩世界里&#xff0c;方法重写是一项极为重要且实用的特性&#xff0c;它犹如一把神奇的钥匙&#xff0c;为我们开启…...

华为E9000刀箱服务器监控指标解读

美信监控易内置了数千种常见设备监测器&#xff0c;能够监测超过20万项指标。这些指标涵盖了从硬件设备到软件系统&#xff0c;从网络性能到安全状态等各个方面。如下基于美信监控易——IT基础监控模块&#xff0c;对华为E9000刀箱服务器部分监控指标进行解读。 一、华为E9000…...

正则表达式基础与应用

什么是正则表达式&#xff1f; 正则表达式&#xff08;Regular Expression&#xff0c;简称regex&#xff09;是一种用于描述字符串结构的语法规则。它定义了一个搜索模式&#xff0c;可以用来匹配、替换或提取文本中的子串。正则表达式广泛应用于文本处理、数据验证、查找和替…...

微信小程序使用上拉加载onReachBottom。页面拖不动。一直无法触发上拉的事件。

1&#xff0c;可能是原因是你使用了scroll-view的标签&#xff0c;用onReachBottom触发加载事件。这两个是有冲突的。没办法一起使用。如果页面的样式是滚动的是无法去触发页面的onReachBottom的函数的。因此&#xff0c;你使用overflow:auto.来使用页面的某些元素滚动&#xf…...

9. 神经网络(一.神经元模型)

首先&#xff0c;先看一个简化的生物神经元结构&#xff1a; 生物神经元有多种类型&#xff0c;内部也有复杂的结构&#xff0c;但是可以把单个神经元简化为3部分组成&#xff1a; 树突&#xff1a;一个神经元往往有多个树突&#xff0c;用于接收传入的信息。轴突&#xff1a;…...

Mysql安装,mysql-installer-community-8.0.41.0

“windowR"键弹出运行框&#xff0c;输入”cmd"进入window命令提示符&#xff0c;输入“mysql -uroot -p"按下回车&#xff0c;再输入密码&#xff0c;按下回车&#xff0c;出现下面界面则是配置成功。 默认会在 C:\Program Files\MySQL\MySQL Server 8.0\bin …...

吴恩达深度学习——神经网络介绍

文章内容来自BV11H4y1F7uH&#xff0c;仅为个人学习所用。 文章目录 什么是神经网络引入神经网络神经元激活函数ReLU隐藏单元 用神经网络进行监督学习监督学习与无监督学习举例 什么是神经网络 引入 已经有六个房子的数据集&#xff0c;横轴为房子大小&#xff0c;纵轴为房子…...

【SpringBoot】SpringBoot中分页插件(PageHelper)的使用

目录 1.分页概念 2.原生写法 3.PageHelper插件分页查询 3.1 介绍 3.2?使用 3.3 Page对象和PageInf对象 1.分页概念 用户查询的数据不可能一次性全部展示给用户&#xff08;如果用户有一万条数据呢&#xff09;&#xff0c;而是分页展示给用户&#xff0c;这就是分页查询…...

JavaScript DOM 操作与事件处理

Hi&#xff0c;我是布兰妮甜 &#xff01;在现代Web开发中&#xff0c;JavaScript不仅是用来增强用户体验的工具&#xff0c;它更是创建动态、交互式网页的关键。通过操作文档对象模型&#xff08;DOM&#xff09;和处理用户事件&#xff0c;开发者能够构建出响应迅速且功能丰富…...

rstrip 方法是 Python 字符串的一个内置方法,用于 删除字符串右边(末尾)的指定字符

rstrip 方法是 Python 字符串的一个内置方法&#xff0c;用于 删除字符串右边&#xff08;末尾&#xff09;的指定字符。 语法&#xff1a; string.rstrip([chars])string&#xff1a;原始字符串。chars&#xff1a;可选参数&#xff0c;指定要删除的字符。默认为 None&#…...

【Elasticsearch】腾讯云安装Elasticsearch

Elasticsearch 认识Elasticsearch安装Elasticsearch安装Kibana安装IK分词器分词器的作用是什么&#xff1f;IK分词器有几种模式&#xff1f;IK分词器如何拓展词条&#xff1f;如何停用词条&#xff1f; 认识Elasticsearch Elasticsearch的官方网站如下 Elasticsearch官网 Ela…...

rsync结合inotify实现文件实时同步

rsync 1.复制工具 本地复制 远程复制 cp dd 跨主机传递文件 rz sz ftp scp rsync nfs samba drdb 2.rsync作用 实现文件的备份&#xff0c;可以是当前主机&#xff0c;也可以是远程主机&#xff1b;可以完全备份&#xff0c;也可以是增量备份 2.1功能 类似于cp的复制功能…...

浅谈 PID 控制算法

PID 控制算法概念 在我们的生活中可能大家都没有听说过 PID 控制算法&#xff0c;但它可以说是无处不在&#xff0c;小到空调的温度控制、无人机的精准悬停、机器人运作系统&#xff0c;大到飞机和火箭的飞行姿态控制都有 PID 的身影。 PID 控制算法&#xff0c;即比例 - 积分…...

react中hooks之useId用法总结以及与useRef用法区别

React useId Hook 使用指南 概述 useId 是 React 18 引入的新 Hook&#xff0c;用于生成唯一的 ID&#xff0c;主要用于可访问性&#xff08;accessibility&#xff09;属性。它在服务端和客户端渲染时都能保持一致性。 useId vs useRef useId: 生成稳定的唯一标识符&#…...

Spring Boot AOP实现动态数据脱敏

依赖&配置 <!-- Spring Boot AOP起步依赖 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency>/*** Author: 说淑人* Date: 2025/1/18 23:03* Desc…...

AutoGen入门——快速实现多角色、多用户、多智能体对话系统

1.前言 如https://github.com/microsoft/autogen所述&#xff0c;autogen是一多智能体的框架&#xff0c;属于微软旗下的产品。 依靠AutoGen我们可以快速构建出一个多智能体应用&#xff0c;以满足我们各种业务场景。 本文将以几个示例场景&#xff0c;使用AutoGen快速构建出…...

.NET Framework

.NET Framework 是微软推出的一个软件开发平台&#xff0c;主要用于构建和运行 Windows 应用程序。它是 .NET 生态系统的早期版本&#xff0c;专注于 Windows 平台&#xff0c;并提供了丰富的类库和运行时环境。 注意事项 跨平台限制&#xff1a;.NET Framework 主要适用于 W…...

算法中的移动窗帘——C++滑动窗口算法详解

1. 滑动窗口简介 滑动窗口是一种在算法中常用的技巧&#xff0c;主要用来处理具有连续性的子数组或子序列问题。通过滑动窗口&#xff0c;可以在一维数组或字符串上维护一个固定或可变长度的窗口&#xff0c;逐步移动窗口&#xff0c;避免重复计算&#xff0c;从而提升效率。常…...