基于stm32智能门锁系统

一、项目背景

在这里插入图片描述

二、项目功能要求

1、可通过指纹模块增删查改家庭成员的指纹信息,增删查改是否成功的相关信息显示在OLED屏幕上
2、在指纹匹配过程中,如果采集的指纹与指纹模块库相匹配,OLED显示匹配成功,并转动步进电机一圈
3、可通过按键设定智能门锁密码,密码可设置为两个(密码六位),如果匹配两个中的一个成功,即可开锁,也可通过按键修改密码,所有的操作过程显示于OLED中
4、实现RFID与手机解锁

三、元件购买地址

1、继电器(锁)
https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-14787471870.9.1a966865lNZF6d&id=577356377112
2、4X4按键
https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-14787471870.9.40f96865MfVG6C&id=563379889617
3、指纹模块
https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-14787471870.9.16d06865bduv8I&id=563705918850
4、OLED(IIC)
https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-14787471870.15.44806865TkULtt&id=575274211818(白色)

5、蓝牙
https://item.taobao.com/item.htm?spm=a1z10.3-c-s.w4002-14787471870.13.471b6865OLuSU6&id=560220493066

四、开发项目

2、项目简介

模块使用:通过获取4x4按键来实现不同功能。本项目使用道IIC协议、SPI协议、RFID读卡模块、蓝牙模块、4x4按键、AS608指纹模块、OLED显示屏模块、STM32F4107开发板。

3、蓝牙模块

①、连接蓝牙

在这里插入图片描述

②注意事项

按住蓝牙模块的按键,再将USB转TTL插入电脑,插入电脑后,蓝牙模块的LED灯处于慢闪状态(慢闪状态表示处于AT指令操作模式)快闪处于数据透传模式(透传相当数据无障碍传输)

③、设置为主模块的步骤

①、 PIO11 置高。(根据自己的引脚接线)
②、上电,模块进入 AT 命令响应状态。
③、超级终端或其他串口工具,设置波特率 115200,数据位 8 位,停止位 1 位,无校验位,
无流控制。
④、串口发送字符“AT+ROLE=1\r\n”,成功返回“OK\r\n”,其中\r\n 为回车换行。
⑤、 PIO 置低,重新上电,模块为主模块,自动搜索从模块,建立连接。
⑥、修改蓝牙设备名称:AT+NAME=贵哥\r\n ——设置模块设备名为:“贵哥”
⑦、模块蓝牙设备地址: 00:02:72:od:22:24,设备名称为: Bluetoothat+rname? 0002, 72, od2224\r\n
+RNAME:Bluetooth
OK
⑧、手机下载”蓝牙调试器“APP,即可通过手机控制开发板的设备

④、蓝牙代码
/*
PB10 -- TX
PB11 -- RX
*/
/**************************************
	通过串口3,接收手机发送的指令,传递给蓝牙模块,蓝牙收到指令
	后,做出相应的动作,然后将指令通过串口3传递给开发板,执行指令操作。
************************************/
void Usart3_Init(void)
{
	 //结构体
	GPIO_InitTypeDef 			GPIO_InitStruct; 
	USART_InitTypeDef 			USART_InitStruct;
	NVIC_InitTypeDef    	 	NVIC_InitStruct;
	
	//使能串口3时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	//使能GPIO B组时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);	


	//PB10引脚映射到串口3
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3); 
	//PB11引脚映射到串口3
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3); 	

	GPIO_InitStruct.GPIO_Pin		= GPIO_Pin_10|GPIO_Pin_11;     //引脚10  11 
	GPIO_InitStruct.GPIO_Mode		= GPIO_Mode_AF;								//复用模式
	GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;    					//输出推挽
	GPIO_InitStruct.GPIO_PuPd		= GPIO_PuPd_UP;	    					//上拉
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz; 					//输出速度
	//3、初始化IO口为复用功能输出。
	GPIO_Init(GPIOB, &GPIO_InitStruct);	
	
	
	
	USART_InitStruct.USART_BaudRate 						= 57600;															//一般设置为 9600;
	USART_InitStruct.USART_WordLength 					= USART_WordLength_8b;							//字长为 8 位数据格式
	USART_InitStruct.USART_StopBits 						= USART_StopBits_1;									//一个停止位
	USART_InitStruct.USART_Parity 							= USART_Parity_No;									//无奇偶校验位
	USART_InitStruct.USART_HardwareFlowControl  = USART_HardwareFlowControl_None;		//无硬件控制流
	USART_InitStruct.USART_Mode 								= USART_Mode_Rx | USART_Mode_Tx;		//收发模式 全双工
	//初始化串口
	USART_Init(USART3, &USART_InitStruct); 
	
	NVIC_InitStruct.NVIC_IRQChannel										= USART3_IRQn;  //中断通道 
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= 0x01;       	//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority				= 0x01;		  		//抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd								= ENABLE;    	  //中断通道使能
	//3、启定时器中断,配置NVIC。
	NVIC_Init(&NVIC_InitStruct);	
	
	//开启中断,接收到数据中断
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
	
	USART_Cmd(USART3, ENABLE);
}

/*
	通过触发中断服务函数,来执行蓝牙模块的指令(中断服务函数不需要调用,写好就行)
*/
//串口3中断服务函数
void USART3_IRQHandler(void)
{
	char Usart_Data;
   //若是非空,则返回值为1,与RESET(0)判断,不相等则判断为真
   if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
   {	
   //判断为真后,为下次中断做准备,则需要对中断的标志清零
     USART_ClearITPendingBit(USART3,USART_IT_RXNE);
		 
		/* DR读取接受到的数据*/
		buffer[count++] = USART_ReceiveData(USART3);
	   
	   if(buffer[count-1] == '.')  //接受到一串字符串
	   {
		   //将有数据(去掉:)赋值到rx_buffer当中
			for(rx_i = 0; rx_i < (count-1); rx_i++)
		   {
				rx_buffer[rx_i] = buffer[rx_i];  
		   }
		   rx_flag = 1;  //接受完毕标志 
		   count = 0;    //以便下次字符串从buffer[0]开始存储
		   
		   memset(buffer, 0, sizeof(buffer));	   
	   }	  

	 }
}
//手机开门
void Phone_Open_Door(void)
{
	//	Usart3_Init();								//调用串口3
		OLED_ShowStr(20,3,(unsigned char *)"Enter Comment!!",2);//测试8*16字
	//	USART_Cmd(USART3, ENABLE);		//使能串口3
		if(rx_flag == 1)
		{
			
			printf("rx_buffer = %s\n", rx_buffer);
			//亮灯
			if(strcmp(rx_buffer, "open_door") == 0)//如果手机蓝牙输入的指令也是open_door,则触发中断,实现开门
			{
				GPIO_ResetBits(GPIOF, GPIO_Pin_10);
				OLED_Fill(0x00);//全屏灭
				delay_ms(500);
				OLED_ShowStr(20,3,(unsigned char *)"Open Success!!!",2);//测试8*16字
				delay_ms(500);
			}
			else
			{
					GPIO_SetBits(GPIOF, GPIO_Pin_10);
					OLED_Fill(0x00);//全屏灭
					delay_ms(500);
					OLED_ShowStr(20,3,(unsigned char *)"Error !!",2);//测试8*16字
					delay_ms(500);
			}
			 memset(rx_buffer, 0, sizeof(rx_buffer));	
			rx_flag = 0;
		}	
}

4、RFID

①、在使用之前,需要将弯排针焊接在RFID-RC522模块上,找准RFID的接口与开发板连接。

在这里插入图片描述

② 主要指标

. 容量为 8K 位 EEPROM
. 分为 16 个扇区,每个扇区为 4 块,每块 16 个字节,以块为存取单位
. 每个扇区有独立的一组密码及访问控制
. 每张卡有唯一序列号,为 32 位
. 具有防冲突机制,支持多卡操作
. 无电源,自带天线,内含加密控制逻辑和通讯逻辑电路
. 数据保存期为 10 年,可改写 10 万次,读无限次
. 工作温度: -20℃ ~50℃ (湿度为 90%)
. 工作频率: 13.56MHZ
. 通信速率: 106 KBPS
. 读写距离: 10 cm 以内(与读写器有关)

③、RFID代码
//.c文件
#include "MFRC522.h"
#include "sys.h"
#include "delay.h"
#include <stdio.h>
#include "OLED_I2C.h"
//test
u8  irq_regdata;
u16 wait_count;
u8  error_regdata;
u8  last_bitsdata;

//SPI3初始化
void STM32_SPI3_Init(void) 
{ 
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
	GPIO_InitTypeDef GPIO_InitStruct;
	//SPI3 pins: SCK=PD0,
	
	//MFRC522_CS    PD14
	GPIO_InitStruct.GPIO_Pin		= GPIO_Pin_0 | GPIO_Pin_9 | GPIO_Pin_14; 	//引脚D0 D14
	GPIO_InitStruct.GPIO_Mode		= GPIO_Mode_OUT;													//输出
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_100MHz;											//速度
	GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;													//推挽
	GPIO_InitStruct.GPIO_PuPd		= GPIO_PuPd_NOPULL;												//上拉
	GPIO_Init(GPIOD, &GPIO_InitStruct);
	
	//MFRC522_Reset PE15 , MOSI=PE7
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_7| GPIO_Pin_13 | GPIO_Pin_15; 	//引脚E7 E15
	GPIO_Init(GPIOE, &GPIO_InitStruct);	
	
	// MISO=PE9,
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_9; 							//引脚E9
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_IN;							//输入	
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;					//没有使能上下拉电阻	
	GPIO_Init(GPIOE, &GPIO_InitStruct);
	
	PDout(9)=1;//VCC
	PEout(13)=0;//GND
	
	MFRC522_CS(1);	//片选置高电平 																		
}

uint8_t rc522_send_byte(uint8_t byte)
{
  u8 i, rx_data=0;
	
	SCK = 0;
	//0 1 1 1 1 0 0 0
	for(i=0; i<8; i++)
	{
		/*准备数据*/
		
		//发数据1
		if(byte &  (1<<(7-i)))
		{
			MOSI = 1;  //引脚输出
		}
		//发数据0
		else
		{
			MOSI = 0; //引脚输出
		}	
		
		delay_us(5);
		SCK = 1;
		delay_us(5);
		
	    //接受数据
		if(MISO)  //引脚为电平为1
		{
			rx_data |= (1<<(7-i));
		}			
		
		SCK = 0;
	}	
	
  return rx_data; 
	
}
void SPI3_Send(u8 val)  
{ 
	rc522_send_byte(val);
}
//
u8 SPI3_Receive(void)  
{ 

	return rc522_send_byte(0x00);
}
//功能描述向MFRC522的某一寄存器写一个字节数据
//输入参数addr--寄存器地址val--要写入的值
void Write_MFRC522(u8 addr, u8 val) 
{
	//地址格式0XXXXXX0  
	MFRC522_CS(0);	
	SPI3_Send((addr<<1)&0x7E);  
	SPI3_Send(val); 
	MFRC522_CS(1); 
}
//功能描述从MFRC522的某一寄存器读一个字节数据
//输入参数addr--寄存器地址
//返 回 值返回读取到的一个字节数据 
u8 Read_MFRC522(u8 addr) 
{  
	u8 val;
	//地址格式1XXXXXX0   
	MFRC522_CS(0); 
	SPI3_Send(((addr<<1)&0x7E)|0x80);   
	val=SPI3_Receive();    
	MFRC522_CS(1); 
	//   
	return val;  
}
//下面两个函数只对能读写位有效
//功能描述置RC522寄存器位
//输入参数reg--寄存器地址;mask--置位值
void SetBitMask(u8 reg, u8 mask)   
{     
	u8 tmp=0;
	//     
	tmp=Read_MFRC522(reg);     
	Write_MFRC522(reg,tmp|mask);  // set bit mask 
}
//功能描述清RC522寄存器位
//输入参数reg--寄存器地址;mask--清位值
void ClearBitMask(u8 reg, u8 mask)   
{     
	u8 tmp=0;
	//     
	tmp=Read_MFRC522(reg);     
	Write_MFRC522(reg,tmp&(~mask));  //clear bit mask 
}
//功能描述开启天线,每次启动或关闭天线发射之间应至少有1ms的间隔
void AntennaOn(void) 
{  
	u8 temp;
	//   
	temp=Read_MFRC522(TxControlReg);  
	if ((temp&0x03)==0)  
	{   
		SetBitMask(TxControlReg,0x03);  
	}
}
//功能描述关闭天线,每次启动或关闭天线发射之间应至少有1ms的间隔
void AntennaOff(void) 
{  
	ClearBitMask(TxControlReg,0x03);
}
//功能描述复位MFRC522
void MFRC522_Reset(void) 
{ 
	//外复位可以不用
	MFRC522_Rst(1);
	delay_us(1);
	MFRC522_Rst(0);
	delay_us(1);
	MFRC522_Rst(1); 
	delay_us(1);
	//内复位   
	Write_MFRC522(CommandReg, PCD_RESETPHASE); 
}
//
void MFRC522_Initializtion(void) 
{
	STM32_SPI3_Init();  
	MFRC522_Reset();         
	//Timer: TPrescaler*TreloadVal/6.78MHz = 0xD3E*0x32/6.78=25ms     
	Write_MFRC522(TModeReg,0x8D);				//TAuto=1为自动计数模式,受通信协议影向。低4位为预分频值的高4位
	//Write_MFRC522(TModeReg,0x1D);				//TAutoRestart=1为自动重载计时,0x0D3E是0.5ms的定时初值//test    
	Write_MFRC522(TPrescalerReg,0x3E); 	//预分频值的低8位     
	Write_MFRC522(TReloadRegL,0x32);		//计数器的低8位                
	Write_MFRC522(TReloadRegH,0x00);		//计数器的高8位       
	Write_MFRC522(TxAutoReg,0x40); 			//100%ASK     
	Write_MFRC522(ModeReg,0x3D); 				//CRC初始值0x6363
	Write_MFRC522(CommandReg,0x00);			//启动MFRC522  
	//Write_MFRC522(RFCfgReg, 0x7F);    //RxGain = 48dB调节卡感应距离      
	AntennaOn();          							//打开天线 
}
//功能描述RC522和ISO14443卡通讯
//输入参数command--MF522命令字
//					sendData--通过RC522发送到卡片的数据
//					sendLen--发送的数据长度
//					BackData--接收到的卡片返回数据
//					BackLen--返回数据的位长度
//返 回 值成功返回MI_O
u8 MFRC522_ToCard(u8 command, u8 *sendData, u8 sendLen, u8 *backData, u16 *backLen) 
{
	u8  status=MI_ERR;
	u8  irqEn=0x00;
	u8  waitIRq=0x00;
	u8  lastBits;
	u8  n;
	u16 i;
	//根据命预设中断参数
	switch (command)     
	{         
		case PCD_AUTHENT:  		//认证卡密   
			irqEn 	= 0x12;			//    
			waitIRq = 0x10;			//    
			break;
		case PCD_TRANSCEIVE: 	//发送FIFO中数据      
			irqEn 	= 0x77;			//    
			waitIRq = 0x30;			//    
			break;      
		default:    
			break;     
	}
	//
	Write_MFRC522(ComIEnReg, irqEn|0x80);		//允许中断请求     
	ClearBitMask(ComIrqReg, 0x80);  				//清除所有中断请求位               	
	SetBitMask(FIFOLevelReg, 0x80);  				//FlushBuffer=1, FIFO初始化
	Write_MFRC522(CommandReg, PCD_IDLE); 		//使MFRC522空闲   
	//向FIFO中写入数据     
	for (i=0; i<sendLen; i++)
		Write_MFRC522(FIFODataReg, sendData[i]);
	//执行命令
	Write_MFRC522(CommandReg, command);
	//天线发送数据     
	if (command == PCD_TRANSCEIVE)					//如果是卡片通信命令,MFRC522开始向天线发送数据      
		SetBitMask(BitFramingReg, 0x80);  		//StartSend=1,transmission of data starts      
	//等待接收数据完成     
	i = 10000; //i根据时钟频率调整操作M1卡最大等待时间25ms     
	do      
	{        
		n = Read_MFRC522(ComIrqReg);
		//irq_regdata=n;	//test         
		i--;
		//wait_count=i;		//test		     
	}while ((i!=0) && !(n&0x01) && !(n&waitIRq));	//接收完就退出n=0x64
	//停止发送
	ClearBitMask(BitFramingReg, 0x80);   		//StartSend=0
	//如果在25ms内读到卡
	if (i != 0)     
	{            
		if(!(Read_MFRC522(ErrorReg) & 0x1B)) //BufferOvfl Collerr CRCErr ProtecolErr         
		{            
			if (n & irqEn & 0x01)			//                  
				status = MI_NOTAGERR;		//
			//
			if (command == PCD_TRANSCEIVE)             
			{                 
				n = Read_MFRC522(FIFOLevelReg);		//n=0x02                
				lastBits = Read_MFRC522(ControlReg) & 0x07;	//lastBits=0               
				if (lastBits!=0)                         
					*backLen = (n-1)*8 + lastBits; 
				else
					*backLen = n*8;									//backLen=0x10=16
				//
				if (n == 0)                         
				 	n = 1;                        
				if (n > MAX_LEN)         
				 	n = MAX_LEN;
				//
				for (i=0; i<n; i++)                 
					backData[i] = Read_MFRC522(FIFODataReg); 
			}
			//
			status = MI_OK;		
		}
		else
			status = MI_ERR;
	}	
	//
	Write_MFRC522(ControlReg,0x80);				//timer stops     
	Write_MFRC522(CommandReg, PCD_IDLE);	//
	//
	return status;
}
//功能描述寻卡读取卡类型号
//输入参数reqMode--寻卡方式
//					TagType--返回卡片类型
//					0x4400 = Mifare_UltraLight
//					0x0400 = Mifare_One(S50)
//					0x0200 = Mifare_One(S70)
//					0x0800 = Mifare_Pro(X)
//					0x4403 = Mifare_DESFire
//返 回 值成功返回MI_OK	
u8 MFRC522_Request(u8 reqMode, u8 *TagType)
{  
	u8  status;    
	u16 backBits;   //接收到的数据位数
	//   
	Write_MFRC522(BitFramingReg, 0x07);  //TxLastBists = BitFramingReg[2..0]   
	TagType[0] = reqMode;  
	status = MFRC522_ToCard(PCD_TRANSCEIVE, TagType, 1, TagType, &backBits); 
	// 
	if ((status != MI_OK) || (backBits != 0x10))  
	{       
		status = MI_ERR;
	}
	//  
	return status; 
}
//功能描述防冲突检测读取选中卡片的卡序列号
//输入参数serNum--返回4字节卡序列号,第5字节为校验字节
//返 回 值成功返回MI_OK
u8 MFRC522_Anticoll(u8 *serNum) 
{     
	u8  status;     
	u8  i;     
	u8  serNumCheck=0;     
	u16 unLen;
	//           
	ClearBitMask(Status2Reg, 0x08);  			//TempSensclear     
	ClearBitMask(CollReg,0x80);   				//ValuesAfterColl  
	Write_MFRC522(BitFramingReg, 0x00);  	//TxLastBists = BitFramingReg[2..0]
	serNum[0] = PICC_ANTICOLL1;     
	serNum[1] = 0x20;     
	status = MFRC522_ToCard(PCD_TRANSCEIVE, serNum, 2, serNum, &unLen);
	//      
	if (status == MI_OK)
	{   
		//校验卡序列号   
		for(i=0;i<4;i++)   
			serNumCheck^=serNum[i];
		//
		if(serNumCheck!=serNum[i])        
			status=MI_ERR;
	}
	SetBitMask(CollReg,0x80);  //ValuesAfterColl=1
	//      
	return status;
}
//功能描述用MF522计算CRC
//输入参数pIndata--要读数CRC的数据len--数据长度pOutData--计算的CRC结果
void CalulateCRC(u8 *pIndata, u8 len, u8 *pOutData) 
{     
	u16 i;
	u8  n;
	//      
	ClearBitMask(DivIrqReg, 0x04);   			//CRCIrq = 0     
	SetBitMask(FIFOLevelReg, 0x80);   		//清FIFO指针     
	Write_MFRC522(CommandReg, PCD_IDLE);   
	//向FIFO中写入数据      
	for (i=0; i<len; i++)
		Write_MFRC522(FIFODataReg, *(pIndata+i));
	//开始RCR计算
	Write_MFRC522(CommandReg, PCD_CALCCRC);
	//等待CRC计算完成     
	i = 1000;     
	do      
	{         
		n = Read_MFRC522(DivIrqReg);         
		i--;     
	}while ((i!=0) && !(n&0x04));   //CRCIrq = 1
	//读取CRC计算结果     
	pOutData[0] = Read_MFRC522(CRCResultRegL);     
	pOutData[1] = Read_MFRC522(CRCResultRegH);
	Write_MFRC522(CommandReg, PCD_IDLE);
}
//功能描述选卡读取卡存储器容量
//输入参数serNum--传入卡序列号
//返 回 值成功返回卡容量
u8 MFRC522_SelectTag(u8 *serNum) 
{     
	u8  i;     
	u8  status;     
	u8  size;     
	u16 recvBits;     
	u8  buffer[9];
	//     
	buffer[0] = PICC_ANTICOLL1;	//防撞码1     
	buffer[1] = 0x70;
	buffer[6] = 0x00;						     
	for (i=0; i<4; i++)					
	{
		buffer[i+2] = *(serNum+i);	//buffer[2]-buffer[5]为卡序列号
		buffer[6]  ^=	*(serNum+i);	//卡校验码
	}
	//
	CalulateCRC(buffer, 7, &buffer[7]);	//buffer[7]-buffer[8]为RCR校验码
	ClearBitMask(Status2Reg,0x08);
	status = MFRC522_ToCard(PCD_TRANSCEIVE, buffer, 9, buffer, &recvBits);
	//
	if ((status == MI_OK) && (recvBits == 0x18))    
		size = buffer[0];     
	else    
		size = 0;
	//	     
	return size; 
}
//功能描述验证卡片密码
//输入参数authMode--密码验证模式
//					0x60 = 验证A密钥
//					0x61 = 验证B密钥
//					BlockAddr--块地址
//					Sectorkey--扇区密码
//					serNum--卡片序列号4字节
//返 回 值成功返回MI_OK
u8 MFRC522_Auth(u8 authMode, u8 BlockAddr, u8 *Sectorkey, u8 *serNum) 
{     
	u8  status;     
	u16 recvBits;     
	u8  i;  
	u8  buff[12];    
	//验证模式+块地址+扇区密码+卡序列号     
	buff[0] = authMode;		//验证模式     
	buff[1] = BlockAddr;	//块地址     
	for (i=0; i<6; i++)
		buff[i+2] = *(Sectorkey+i);	//扇区密码
	//
	for (i=0; i<4; i++)
		buff[i+8] = *(serNum+i);		//卡序列号
	//
	status = MFRC522_ToCard(PCD_AUTHENT, buff, 12, buff, &recvBits);
	//      
	if ((status != MI_OK) || (!(Read_MFRC522(Status2Reg) & 0x08)))
		status = MI_ERR;
	//
	return status;
}
//功能描述读块数据
//输入参数blockAddr--块地址;recvData--读出的块数据
//返 回 值成功返回MI_OK
u8 MFRC522_Read(u8 blockAddr, u8 *recvData) 
{     
	u8  status;     
	u16 unLen;
	//      
	recvData[0] = PICC_READ;     
	recvData[1] = blockAddr;     
	CalulateCRC(recvData,2, &recvData[2]);     
	status = MFRC522_ToCard(PCD_TRANSCEIVE, recvData, 4, recvData, &unLen);
	//
	if ((status != MI_OK) || (unLen != 0x90))
		status = MI_ERR;
	//
	return status;
}
//功能描述写块数据
//输入参数blockAddr--块地址;writeData--向块写16字节数据
//返 回 值成功返回MI_OK
u8 MFRC522_Write(u8 blockAddr, u8 *writeData) 
{     
	u8  status;     
	u16 recvBits;     
	u8  i;  
	u8  buff[18];
	//           
	buff[0] = PICC_WRITE;     
	buff[1] = blockAddr;     
	CalulateCRC(buff, 2, &buff[2]);     
	status = MFRC522_ToCard(PCD_TRANSCEIVE, buff, 4, buff, &recvBits);
	//
	if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A))
		status = MI_ERR;
	//
	if (status == MI_OK)     
	{         
		for (i=0; i<16; i++)  //向FIFO写16Byte数据                     
			buff[i] = *(writeData+i);
		//                     
		CalulateCRC(buff, 16, &buff[16]);         
		status = MFRC522_ToCard(PCD_TRANSCEIVE, buff, 18, buff, &recvBits);           
		if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A))               
			status = MI_ERR;         
	}          
	return status;
}
//功能描述命令卡片进入休眠状态
void MFRC522_Halt(void) 
{    
	u16 unLen;     
	u8  buff[4];
	//       
	buff[0] = PICC_HALT;     
	buff[1] = 0;     
	CalulateCRC(buff, 2, &buff[2]);       
	MFRC522_ToCard(PCD_TRANSCEIVE, buff, 4, buff,&unLen);
}


//数字的ASCII码
uc8 numberascii[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};

//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];
u8  comper_card_ID[5] = {0x59,0x94,0x67,0xb3 ,0x19};

//MFRC522测试函数
u8 MFRC522Test(void)
{
	
	u8 i,j,status,card_size;
	//
	status=MFRC522_Request(0x52, card_pydebuf);			//寻卡 查看读卡器旁边是否有卡
		printf("%d\n",status);
	//
	if(status==0)		//如果读到卡
	{
		status=MFRC522_Anticoll(card_numberbuf);			//防撞处理	 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("read card ok\n");
											
		//卡类型显示
		printf("卡类型:0x");
		printf("%x%x\n",card_pydebuf[0], card_pydebuf[1] );
		
		//卡序列号显,最后一字节为卡的校验码
		printf("卡序列号:");
		for(i=0;i<5;i++)
		{
			printf(" %#x ",card_numberbuf[i]);
		}
		printf("\n");
		
		//卡容量显示,单位为Kbits
		printf("卡容量 : %dKbits\n",card_size);
		//读卡状态显示,正常为0
		//读一个块的数据显示
		
		printf("读一个块的数据显示\n");
		for(i=0;i<18;i++)
				printf(" %x",card_readbuf[i]);

		printf("\n");

			if(comper_card_ID[0] == card_numberbuf[0] &&comper_card_ID[1] == card_numberbuf[1]&&comper_card_ID[2] == card_numberbuf[2] &&comper_card_ID[3] == card_numberbuf[3]&&comper_card_ID[4] == card_numberbuf[4])
			{
					PFout(8)=1;PFout(9)=0;delay_ms(80);
					PFout(8)=0;PFout(9)=1;delay_ms(80);		
					PFout(8)=1;PFout(9)=0;delay_ms(80);
					PFout(8)=0;PFout(9)=1;delay_ms(80);		
					return 1;
			}	
			else
				return 0;
				
	}	
}

.h文件
//
#ifndef _MFRC522_H_
#define _MFRC522_H_
#include "stm32f4xx.h"
//
//定义MFRC522的CS引脚操作,x=1时CS=1,x=0时CS=0
#define MFRC522_CS(x) x ? GPIO_SetBits(GPIOD,GPIO_Pin_14):GPIO_ResetBits(GPIOD,GPIO_Pin_14)
#define MFRC522_Rst(x) x ? GPIO_SetBits(GPIOE,GPIO_Pin_15):GPIO_ResetBits(GPIOE,GPIO_Pin_15)
/
//MF522命令字
/
#define PCD_IDLE              0x00               //取消当前命令
#define PCD_AUTHENT           0x0E               //验证密钥
#define PCD_RECEIVE           0x08               //接收数据
#define PCD_TRANSMIT          0x04               //发送数据
#define PCD_TRANSCEIVE        0x0C               //发送并接收数据
#define PCD_RESETPHASE        0x0F               //复位
#define PCD_CALCCRC           0x03               //CRC计算

/
//Mifare_One卡片命令字
/
#define PICC_REQIDL           0x26               //寻天线区内未进入休眠状态
#define PICC_REQALL           0x52               //寻天线区内全部卡
#define PICC_ANTICOLL1        0x93               //防冲撞
#define PICC_ANTICOLL2        0x95               //防冲撞
#define PICC_AUTHENT1A        0x60               //验证A密钥
#define PICC_AUTHENT1B        0x61               //验证B密钥
#define PICC_READ             0x30               //读块
#define PICC_WRITE            0xA0               //写块
#define PICC_DECREMENT        0xC0               //扣款
#define PICC_INCREMENT        0xC1               //充值
#define PICC_RESTORE          0xC2               //调块数据到缓冲区
#define PICC_TRANSFER         0xB0               //保存缓冲区中数据
#define PICC_HALT             0x50               //休眠

/
//MF522 FIFO长度定义
/
#define DEF_FIFO_LENGTH       64                 //FIFO size=64byte

/
//MF522寄存器定义
/
// PAGE 0
#define     RFU00                 0x00    
#define     CommandReg            0x01    
#define     ComIEnReg             0x02    
#define     DivlEnReg             0x03    
#define     ComIrqReg             0x04    
#define     DivIrqReg             0x05
#define     ErrorReg              0x06    
#define     Status1Reg            0x07    
#define     Status2Reg            0x08    
#define     FIFODataReg           0x09
#define     FIFOLevelReg          0x0A
#define     WaterLevelReg         0x0B
#define     ControlReg            0x0C
#define     BitFramingReg         0x0D
#define     CollReg               0x0E
#define     RFU0F                 0x0F
// PAGE 1     
#define     RFU10                 0x10
#define     ModeReg               0x11
#define     TxModeReg             0x12
#define     RxModeReg             0x13
#define     TxControlReg          0x14
#define     TxAutoReg             0x15
#define     TxSelReg              0x16
#define     RxSelReg              0x17
#define     RxThresholdReg        0x18
#define     DemodReg              0x19
#define     RFU1A                 0x1A
#define     RFU1B                 0x1B
#define     RFU1C             		0x1C
#define     RFU1D                 0x1D
#define     RFU1E                 0x1E
#define     SerialSpeedReg        0x1F
// PAGE 2    
#define     RFU20                 0x20  
#define     CRCResultRegH         0x21
#define     CRCResultRegL         0x22
#define     RFU23                 0x23
#define     ModWidthReg           0x24
#define     RFU25                 0x25
#define     RFCfgReg              0x26
#define     GsNReg                0x27
#define     CWGsCfgReg            0x28
#define     ModGsCfgReg           0x29
#define     TModeReg              0x2A
#define     TPrescalerReg         0x2B
#define     TReloadRegH           0x2C
#define     TReloadRegL           0x2D
#define     TCounterValueRegH     0x2E
#define     TCounterValueRegL     0x2F
// PAGE 3      
#define     RFU30                 0x30
#define     TestSel1Reg           0x31
#define     TestSel2Reg           0x32
#define     TestPinEnReg          0x33
#define     TestPinValueReg       0x34
#define     TestBusReg            0x35
#define     AutoTestReg           0x36
#define     VersionReg            0x37
#define     AnalogTestReg         0x38
#define     TestDAC1Reg           0x39  
#define     TestDAC2Reg           0x3A   
#define     TestADCReg            0x3B   
#define     RFU3C                 0x3C   
#define     RFU3D                 0x3D   
#define     RFU3E                 0x3E   
#define     RFU3F		  						0x3F

/
//和MF522通讯时返回的错误代码
/
#define MI_OK                     0
#define MI_NOTAGERR               1
#define MI_ERR                    2
//
#define MAX_LEN										18

//#define F_CS    PDout(14)
#define SCK		PDout(0)
#define MISO	PEin(9)
#define MOSI	PEout(7)

//MFRC522 test
u8 MFRC522Test(void);
extern u8  irq_regdata;
extern u16 wait_count;
extern u8  error_regdata;
extern u8  last_bitsdata;
//void MFRC522Test(void);
void Delay1_us(vu16 count);
void STM32_SPI3_Init(void);
void SPI2_Send(u8 val);
u8 SPI2_Receive(void);
void SPI3_Send(u8 val);
u8 SPI3_Receive(void);
void MFRC522_Initializtion(void);
void Write_MFRC522(u8 addr, u8 val);
u8 Read_MFRC522(u8 addr);
void SetBitMask(u8 reg, u8 mask);
void ClearBitMask(u8 reg, u8 mask);
void AntennaOn(void);
void AntennaOff(void);
void MFRC522_Reset(void);
void MFRC522_Init(void);
u8 MFRC522_ToCard(u8 command, u8 *sendData, u8 sendLen, u8 *backData, u16 *backLen);
u8 MFRC522_Request(u8 reqMode, u8 *TagType);
u8 MFRC522_Anticoll(u8 *serNum);
void CalulateCRC(u8 *pIndata, u8 len, u8 *pOutData);
u8 MFRC522_SelectTag(u8 *serNum);
u8 MFRC522_Auth(u8 authMode, u8 BlockAddr, u8 *Sectorkey, u8 *serNum);
u8 MFRC522_Read(u8 blockAddr, u8 *recvData);
u8 MFRC522_Write(u8 blockAddr, u8 *writeData);
void MFRC522_Halt(void); 
//
#endif

5、指纹模块

①AS608原理图

在这里插入图片描述

②、PCB图

在这里插入图片描述

③、 ATK-AS608 指纹识别模块简介
④、引脚介绍

在这里插入图片描述

根据自己的开发板,选择引脚的连接

⑤、代码实现
.c文件
#include <string.h>
#include "delay.h" 	
#include "usart.h"
#include "finger.h"



u32 AS608Addr = 0XFFFFFFFF; //默认

u8 flag = 0;								//用来当作按下手指和未按下的标志位,1表示按下,0表示未按下
//初始化PA6为下拉输入		    
//读摸出感应状态(触摸感应时输出高电平信号)
void PS_StaGPIO_Init(void)
{   
	EXTI_InitTypeDef 	EXTI_InitStruct;
  GPIO_InitTypeDef  GPIO_InitStructure;
	NVIC_InitTypeDef    NVIC_InitStruct;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA时钟
  //初始化读状态引脚GPIOA
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//下拉模式
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO	
	
	//3、设置IO口与中断线的映射关系。EXTI0有16个引脚与之对应,这里的映射选择其中一个引脚(PA0)(PE2)(PE3)(PE4)
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource6);
	
	EXTI_InitStruct.EXTI_Line	= EXTI_Line6;   		//中断线6//
	EXTI_InitStruct.EXTI_Mode	= EXTI_Mode_Interrupt;	//中断模式
	EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Rising; //上升沿
	EXTI_InitStruct.EXTI_LineCmd= ENABLE;               //中断线使能
	//4、初始化线上中断,设置触发条件等。
	EXTI_Init(&EXTI_InitStruct);
	
	NVIC_InitStruct.NVIC_IRQChannel						= EXTI9_5_IRQn; //中断通道 
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority	= 0x01;       //抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority			= 0x01;		  //抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd					= ENABLE;     //中断通道使能
	//5、配置中断分组(NVIC),并使能中断。
	NVIC_Init(&NVIC_InitStruct);
}

//6、 编写中断服务函数。满足中断条件后,CPU自动去执行的代码,不需要程序在主函数或者其它函数进行调用。
void EXTI9_5_IRQHandler(void)
{
	//判断中断是否为1
	if(EXTI_GetITStatus(EXTI_Line6)  == SET)
	{
		delay_ms(10);
		if(PS_Sta)
		{
			flag = 1;//说明按下了手指
		}
		//7、清除中断标志位,防止一直进入中断
		EXTI_ClearITPendingBit(EXTI_Line6);
	}
	//7、清除中断标志位,防止一直进入中断
	EXTI_ClearITPendingBit(EXTI_Line6);
}


//串口发送一个字节
static void MYUSART_SendData(u8 data)
{
	while((USART2->SR&0X40)==0); 
	USART2->DR = data;
}
//发送包头
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);
}

//判断中断接收的数组有没有应答包
//waittime为等待中断接收数据的时间(单位1ms)
//返回值:数据包首地址
static u8 *JudgeStr(u16 waittime)
{
	USART2_RX_STA=0;
	while(--waittime)
	{	
		delay_us(500);
	}
	if(USART2_RX_BUF[9] == 0x00)//确认字为0x00表示正确
	{
		USART2_RX_STA=0;//下标置零,为了下次重新接受返回过来的数据
		return USART2_RX_BUF;//返回存从指纹模块返回过来数据的首地址
	}
	else
		return 0;
}

/*****************************************************************************************
录入指纹:
		 ①、首先要是实现开锁,需要在系统里面录入指纹信息,需要:
		 ②、录入图像->生成特征->快速查找图像->自动注册模板
录入图像 PS_GetImage
功能:探测手指,探测到后录入指纹图像存于ImageBuffer。 
模块返回确认字
*****************************************************************************************/

u8 PS_GetImage(void)
{
	//printf("111 录入图像 PS_GetImage\r\n");
 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;
}

/*****************************************************************************
生成特征指令-> 存入指纹图像,存入单片机给定的缓冲区号BufferID中
生成特征 PS_GenChar
功能:将ImageBuffer中的原始图像生成指纹特征文件存于CharBuffer1或CharBuffer2			 
参数:BufferID --> charBuffer1:0x01	charBuffer1:0x02												
模块返回确认字
******************************************************************************/

u8 PS_GenChar(u8 BufferID)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x04);
	Sendcmd(0x02);
	MYUSART_SendData(BufferID);
	temp = 0x01+0x04+0x02+BufferID;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;
}
//精确比对两枚指纹特征 PS_Match
//功能:精确比对CharBuffer1 与CharBuffer2 中的特征文件 
//模块返回确认字
u8 PS_Match(void)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x03);
	temp = 0x01+0x03+0x03;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;
}

/*************************************************************************************
快速查找图像生成图像后要验证是否有在Buffer里面
搜索指纹 PS_Search
功能:以CharBuffer1或CharBuffer2中的特征文件搜索整个或部分指纹库.若搜索到,则返回页码。			
参数:  BufferID @ref CharBuffer1	CharBuffer2
说明:  模块返回确认字,页码(相配指纹模板)
**************************************************************************************/

u8 PS_Search(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x08);
	Sendcmd(0x04);
	MYUSART_SendData(BufferID);
	MYUSART_SendData(StartPage>>8);
	MYUSART_SendData(StartPage);
	MYUSART_SendData(PageNum>>8);
	MYUSART_SendData(PageNum);
	temp = 0x01+0x08+0x04+BufferID
	+(StartPage>>8)+(u8)StartPage
	+(PageNum>>8)+(u8)PageNum;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
	{
		ensure = data[9];
		p->pageID   =(data[10]<<8)+data[11];
		p->mathscore=(data[12]<<8)+data[13];	
	}
	else
		ensure = 0xff;
	return ensure;	
}


//合并特征(生成模板)PS_RegModel
//功能:将CharBuffer1与CharBuffer2中的特征文件合并生成 模板,结果存于CharBuffer1与CharBuffer2	
//说明:  模块返回确认字
u8 PS_RegModel(void)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x05);
	temp = 0x01+0x03+0x05;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;		
}
//储存模板 PS_StoreChar
//功能:将 CharBuffer1 或 CharBuffer2 中的模板文件存到 PageID 号flash数据库位置。			
//参数:  BufferID @ref charBuffer1:0x01	charBuffer1:0x02
//       PageID(指纹库位置号)
//说明:  模块返回确认字
u8 PS_StoreChar(u8 BufferID,u16 PageID)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x06);
	Sendcmd(0x06);
	MYUSART_SendData(BufferID);
	MYUSART_SendData(PageID>>8);
	MYUSART_SendData(PageID);
	temp = 0x01+0x06+0x06+BufferID
	+(PageID>>8)+(u8)PageID;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;	
}
//删除模板 PS_DeletChar
//功能:  删除flash数据库中指定ID号开始的N个指纹模板
//参数:  PageID(指纹库模板号),N删除的模板个数。
//说明:  模块返回确认字
u8 PS_DeletChar(u16 PageID,u16 N)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x07);
	Sendcmd(0x0C);
	MYUSART_SendData(PageID>>8);
	MYUSART_SendData(PageID);
	MYUSART_SendData(N>>8);
	MYUSART_SendData(N);
	temp = 0x01+0x07+0x0C
	+(PageID>>8)+(u8)PageID
	+(N>>8)+(u8)N;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;
}
//清空指纹库 PS_Empty
//功能:  删除flash数据库中所有指纹模板
//参数:  无
//说明:  模块返回确认字
u8 PS_Empty(void)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x0D);
	temp = 0x01+0x03+0x0D;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;
}
//写系统寄存器 PS_WriteReg
//功能:  写模块寄存器
//参数:  寄存器序号RegNum:4\5\6
//说明:  模块返回确认字
u8 PS_WriteReg(u8 RegNum,u8 DATA)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x05);
	Sendcmd(0x0E);
	MYUSART_SendData(RegNum);
	MYUSART_SendData(DATA);
	temp = RegNum+DATA+0x01+0x05+0x0E;
	SendCheck(temp);
	data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	if(ensure==0)
		printf("\r\n设置参数成功!");
	else
		printf("\r\n%s",EnsureMessage(ensure));
	return ensure;
}
//读系统基本参数 PS_ReadSysPara
//功能:  读取模块的基本参数(波特率,包大小等)
//参数:  无
//说明:  模块返回确认字 + 基本参数(16bytes)
u8 PS_ReadSysPara(SysPara *p)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x0F);
	temp = 0x01+0x03+0x0F;
	SendCheck(temp);
	data=JudgeStr(1000);
	if(data)
	{
		ensure = data[9];
		p->PS_max = (data[14]<<8)+data[15];
		p->PS_level = data[17];
		p->PS_addr=(data[18]<<24)+(data[19]<<16)+(data[20]<<8)+data[21];
		p->PS_size = data[23];
		p->PS_N = data[25];
	}		
	else
		ensure=0xff;
	if(ensure==0x00)
	{
		printf("\r\n模块最大指纹容量=%d",p->PS_max);
		printf("\r\n对比等级=%d",p->PS_level);
		printf("\r\n地址=%x",p->PS_addr);
		printf("\r\n波特率=%d",p->PS_N*9600);
	}
	else 
			printf("\r\n%s",EnsureMessage(ensure));
	return ensure;
}
//设置模块地址 PS_SetAddr
//功能:  设置模块地址
//参数:  PS_addr
//说明:  模块返回确认字
u8 PS_SetAddr(u32 PS_addr)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x07);
	Sendcmd(0x15);
	MYUSART_SendData(PS_addr>>24);
	MYUSART_SendData(PS_addr>>16);
	MYUSART_SendData(PS_addr>>8);
	MYUSART_SendData(PS_addr);
	temp = 0x01+0x07+0x15
	+(u8)(PS_addr>>24)+(u8)(PS_addr>>16)
	+(u8)(PS_addr>>8) +(u8)PS_addr;				
	SendCheck(temp);
	AS608Addr=PS_addr;//发送完指令,更换地址
  data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;	
		AS608Addr = PS_addr;
	if(ensure==0x00)
		printf("\r\n设置地址成功!");
	else
		printf("\r\n%s",EnsureMessage(ensure));
	return ensure;
}
//功能: 模块内部为用户开辟了256bytes的FLASH空间用于存用户记事本,
//	该记事本逻辑上被分成 16 个页。
//参数:  NotePageNum(0~15),Byte32(要写入内容,32个字节)
//说明:  模块返回确认字
u8 PS_WriteNotepad(u8 NotePageNum,u8 *Byte32)
{
	u16 temp;
  u8  ensure,i;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(36);
	Sendcmd(0x18);
	MYUSART_SendData(NotePageNum);
	for(i=0;i<32;i++)
	 {
		 MYUSART_SendData(Byte32[i]);
		 temp += Byte32[i];
	 }
  temp =0x01+36+0x18+NotePageNum+temp;
	SendCheck(temp);
  data=JudgeStr(2000);
	if(data)
		ensure=data[9];
	else
		ensure=0xff;
	return ensure;
}
//读记事PS_ReadNotepad
//功能:  读取FLASH用户区的128bytes数据
//参数:  NotePageNum(0~15)
//说明:  模块返回确认字+用户信息
u8 PS_ReadNotepad(u8 NotePageNum,u8 *Byte32)
{
	u16 temp;
  u8  ensure,i;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x04);
	Sendcmd(0x19);
	MYUSART_SendData(NotePageNum);
	temp = 0x01+0x04+0x19+NotePageNum;
	SendCheck(temp);
  data=JudgeStr(2000);
	if(data)
	{
		ensure=data[9];
		for(i=0;i<32;i++)
		{
			Byte32[i]=data[10+i];
		}
	}
	else
		ensure=0xff;
	return ensure;
}
//高速搜索PS_HighSpeedSearch
//功能:以 CharBuffer1或CharBuffer2中的特征文件高速搜索整个或部分指纹库。
//		  若搜索到,则返回页码,该指令对于的确存在于指纹库中 ,且登录时质量
//		  很好的指纹,会很快给出搜索结果。
//参数:  BufferID, StartPage(起始页),PageNum(页数)
//说明:  模块返回确认字+页码(相配指纹模板)
u8 PS_HighSpeedSearch(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x08);
	Sendcmd(0x1b);
	MYUSART_SendData(BufferID);
	MYUSART_SendData(StartPage>>8);
	MYUSART_SendData(StartPage);
	MYUSART_SendData(PageNum>>8);
	MYUSART_SendData(PageNum);
	temp = 0x01+0x08+0x1b+BufferID
	+(StartPage>>8)+(u8)StartPage
	+(PageNum>>8)+(u8)PageNum;
	SendCheck(temp);
	data=JudgeStr(2000);
 	if(data)
	{
		ensure=data[9];
		p->pageID 	=(data[10]<<8) +data[11];
		p->mathscore=(data[12]<<8) +data[13];
	}
	else
		ensure=0xff;
	return ensure;
}
//读有效模板个数 PS_ValidTempleteNum
//功能:读有效模板个数
//参数: 无
//说明: 模块返回确认字+有效模板个数ValidN
u8 PS_ValidTempleteNum(u16 *ValidN)
{
	u16 temp;
  u8  ensure;
	u8  *data;
	SendHead();
	SendAddr();
	SendFlag(0x01);//命令包标识
	SendLength(0x03);
	Sendcmd(0x1d);
	temp = 0x01+0x03+0x1d;
	SendCheck(temp);
  data=JudgeStr(2000);
	if(data)
	{
		ensure=data[9];
		*ValidN = (data[10]<<8) +data[11];
	}		
	else
		ensure=0xff;
	
	if(ensure==0x00)
	{
		printf("\r\n有效指纹个数=%d",(data[10]<<8)+data[11]);
	}
	else
		printf("\r\n%s",EnsureMessage(ensure));
	return ensure;
}
//与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;		
}
//模块应答包确认码信息解析
//功能:解析确认码错误信息返回信息
//参数: ensure
const char *EnsureMessage(u8 ensure) 
{
	const char *p;
	switch(ensure)
	{
		case  0x00:
			p="OK";break;		
		case  0x01:
			p="数据包接收错误";break;
		case  0x02:
			p="传感器上没有手指";break;
		case  0x03:
			p="录入指纹图像失败";break;
		case  0x04:
			p="指纹图像太干、太淡而生不成特征";break;
		case  0x05:
			p="指纹图像太湿、太糊而生不成特征";break;
		case  0x06:
			p="指纹图像太乱而生不成特征";break;
		case  0x07:
			p="指纹图像正常,但特征点太少(或面积太小)而生不成特征";break;
		case  0x08:
			p="指纹不匹配";break;
		case  0x09:
			p="没搜索到指纹";break;
		case  0x0a:
			p="特征合并失败";break;
		case  0x0b:
			p="访问指纹库时地址序号超出指纹库范围";
		case  0x10:
			p="删除模板失败";break;
		case  0x11:
			p="清空指纹库失败";break;	
		case  0x15:
			p="缓冲区内没有有效原始图而生不成图像";break;
		case  0x18:
			p="读写 FLASH 出错";break;
		case  0x19:
			p="未定义错误";break;
		case  0x1a:
			p="无效寄存器号";break;
		case  0x1b:
			p="寄存器设定内容错误";break;
		case  0x1c:
			p="记事本页码指定错误";break;
		case  0x1f:
			p="指纹库满";break;
		case  0x20:
			p="地址错误";break;
		default :
			p="模块返回确认码有误";break;
	}
 return p;	
}
//.h文件
#ifndef __AS608_H
#define __AS608_H
#include <stdio.h>
#include "stm32f4xx.h" 
#include "sys.h"
#define PS_Sta   PAin(6)//读指纹模块状态引脚
#define CharBuffer1 0x01
#define CharBuffer2 0x02

extern u32 AS608Addr;//模块地址

typedef struct  
{
	u16 pageID;//指纹ID
	u16 mathscore;//匹配得分
}SearchResult;

typedef struct
{
	u16 PS_max;//指纹最大容量
	u8  PS_level;//安全等级
	u32 PS_addr;
	u8  PS_size;//通讯数据包大小
	u8  PS_N;//波特率基数N
}SysPara;

void PS_StaGPIO_Init(void);//初始化PA6读状态引脚
	
u8 PS_GetImage(void); //录入图像 
 
u8 PS_GenChar(u8 BufferID);//生成特征 

u8 PS_Match(void);//精确比对两枚指纹特征 

u8 PS_Search(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p);//搜索指纹 
 
u8 PS_RegModel(void);//合并特征(生成模板) 
 
u8 PS_StoreChar(u8 BufferID,u16 PageID);//储存模板 

u8 PS_DeletChar(u16 PageID,u16 N);//删除模板 

u8 PS_Empty(void);//清空指纹库 

u8 PS_WriteReg(u8 RegNum,u8 DATA);//写系统寄存器 
 
u8 PS_ReadSysPara(SysPara *p); //读系统基本参数 

u8 PS_SetAddr(u32 addr);  //设置模块地址 

u8 PS_WriteNotepad(u8 NotePageNum,u8 *content);//写记事本 

u8 PS_ReadNotepad(u8 NotePageNum,u8 *note);//读记事 

u8 PS_HighSpeedSearch(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p);//高速搜索 
  
u8 PS_ValidTempleteNum(u16 *ValidN);//读有效模板个数 

u8 PS_HandShake(u32 *PS_Addr); //与AS608模块握手

const char *EnsureMessage(u8 ensure);//确认码错误信息解析
#endif

6、OLED显示屏模块

①接线图

(再次声明,我的开发板是基于STMF4 107系列,要根据自己的开发板来连接)
在这里插入图片描述

② OLED 简介:
③、IIC 电路连接

GND: 电源地
VCC: 2.2V~5.5V
SCL: CLK 时钟 (高电平 2.2V~5.5V)
SDA: MOSI 数据 (高电平 2.2V~5.5V)
(注意事项:
OLED 显示屏不同于 LCD,OLED 上电是没有反应的,需要程序驱动才会有显示!)

④、代码实现
//.c文件
//因为我是项目需要,所以会有多个函数,起始都大同小异,还有,需要使用取字模软件,来实现汉字的显示
/************************************************************************************
*  Copyright (c), 2014, HelTec Automatic Technology co.,LTD.
*            All rights reserved.
*
* Http:    www.heltec.cn
* Email:   cn.heltec@gmail.com
* WebShop: heltec.taobao.com
*
* File name: OLED_I2C.c
* Project  : HelTec.uvprij
* Processor: STM32F103C8T6
* Compiler : MDK fo ARM
* 
* Author : 小林
* Version: 1.00
* Date   : 2014.4.8
* Email  : hello14blog@gmail.com
* Modification: none
* 
* Description:128*64点阵的OLED显示屏驱动文件,仅适用于惠特自动化(heltec.taobao.com)的SD1306驱动IIC通信方式显示屏
*
* Others: none;
*
* Function List:
*	1. void I2C_Configuration(void) -- 配置CPU的硬件I2C
* 2. void I2C_WriteByte(uint8_t addr,uint8_t data) -- 向寄存器地址写一个byte的数据
* 3. void WriteCmd(unsigned char I2C_Command) -- 写命令
* 4. void WriteDat(unsigned char I2C_Data) -- 写数据
* 5. void OLED_Init(void) -- OLED屏初始化
* 6. void OLED_SetPos(unsigned char x, unsigned char y) -- 设置起始点坐标
* 7. void OLED_Fill(unsigned char fill_Data) -- 全屏填充
* 8. void OLED_CLS(void) -- 清屏
* 9. void OLED_ON(void) -- 唤醒
* 10. void OLED_OFF(void) -- 睡眠
* 11. void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize) -- 显示字符串(字体大小有6*8和8*16两种)
* 12. void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N) -- 显示中文(中文需要先取模,然后放到codetab.h中)
* 13. void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]) -- BMP图片
*
* History: none;
*
*************************************************************************************/

#include "OLED_I2C.h"
#include "delay.h"
#include "codetab.h"
#include "sys.h"


void I2C_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;  //结构体
	
	//使能GPIO D E组时钟

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
	
	GPIO_InitStruct.GPIO_Pin		= GPIO_Pin_1|GPIO_Pin_15;     	//引脚1 15
	GPIO_InitStruct.GPIO_Mode		= GPIO_Mode_OUT;								//输出模式
	GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;    						//输出推挽
	GPIO_InitStruct.GPIO_PuPd		= GPIO_PuPd_UP;	    						//上拉
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz; 						//输出速度
	
	GPIO_Init(GPIOD, &GPIO_InitStruct);
	

	GPIO_InitStruct.GPIO_Pin		= GPIO_Pin_8|GPIO_Pin_10;     	//引脚8 10
	GPIO_InitStruct.GPIO_Mode		= GPIO_Mode_OUT;								//输出模式
	GPIO_InitStruct.GPIO_OType	= GPIO_OType_PP;  	  					//输出推挽
	GPIO_InitStruct.GPIO_PuPd		= GPIO_PuPd_UP;	    						//上拉
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz; 						//输出速度
	
	GPIO_Init(GPIOE, &GPIO_InitStruct);
	
	//电源供电
	PDout(1) = 1;
	PDout(15) = 0;
	

	//总线空闲
	OLED_SCL = 1;
	OLED_SDA_OUT = 1;
	
}

//引脚模式变更
void OLED_Iic_Sda_Mode(GPIOMode_TypeDef mode)
{
	GPIO_InitTypeDef  	GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;				//第10号引脚
	GPIO_InitStructure.GPIO_Mode  = mode;								//输入/输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;			//推挽输出,增强驱动能力,引脚的输出电流更大
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		//引脚的速度最大为100MHz
	GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;		//没有使用内部上拉电阻
	GPIO_Init(GPIOE, &GPIO_InitStructure);	
}

//启动信号
void OLED_Iic_Start(void)
{
	OLED_Iic_Sda_Mode(GPIO_Mode_OUT);
	
	//总线空闲
	OLED_SCL = 1;
	OLED_SDA_OUT = 1;	
	delay_us(5);
	
	//启动信号
	OLED_SDA_OUT = 0;
    delay_us(5);	
	OLED_SCL = 0;

}

//停止信号
void OLED_Iic_Stop(void)
{
	OLED_Iic_Sda_Mode(GPIO_Mode_OUT);	

	OLED_SCL = 0;
	OLED_SDA_OUT = 0;
	delay_us(5);
	
	OLED_SCL = 1;
	delay_us(5);
	OLED_SDA_OUT = 1;
	
}

//引脚发送一位数据
void OLED_Iic_Send_Ack(u8 ack)
{
	OLED_Iic_Sda_Mode(GPIO_Mode_OUT);

	OLED_SCL = 0;
	
	/*准备数据*/
	
	//发数据1
	if(ack == 1)
	{
		OLED_SDA_OUT = 1;  //引脚输出
	}
	//发数据0
	if(ack == 0)
	{
		OLED_SDA_OUT = 0; //引脚输出
	}	
	
	
	delay_us(5);
	OLED_SCL = 1;
	delay_us(5);
	OLED_SCL = 0;
}


//引脚发送一个字节数据
void OLED_Iic_Send_Byte(u8 data)
{
	u8 i;
	OLED_Iic_Sda_Mode(GPIO_Mode_OUT);
	
	OLED_SCL = 0;
	
	//0 1 1 1 1 0 0 0
	for(i=0; i<8; i++)
	{
		/*准备数据*/
		
		//发数据1
		if(data &  (1<<(7-i)))
		{
			OLED_SDA_OUT = 1;  //引脚输出
		}
		//发数据0
		else
		{
			OLED_SDA_OUT = 0; //引脚输出
		}	
		
		
		delay_us(5);
		OLED_SCL = 1;
		delay_us(5);
		OLED_SCL = 0;
	}
}

//接受一位数据
u8 OLED_Iic_Rcv_Ack(void)
{
	u8 ack;
	
	OLED_Iic_Sda_Mode(GPIO_Mode_IN);

	
	OLED_SCL = 0;
	delay_us(5);
	OLED_SCL = 1;	
	delay_us(5);
	if(OLED_SDA_IN == 1)  //引脚为电平为1
	{
		ack = 1;
	}
	
	if(OLED_SDA_IN == 0)  //引脚为电平为1
	{
		ack = 0;
	}	
	
	OLED_SCL = 0;	

	
	return ack;
}

//接受一个字节数据
u8 OLED_Iic_Rcv_Byte(void)
{
	u8 i, data = 0; //0 0 0 0 0 0 0 0   比如有数据:1 1 0 0 1 0 0 0 
	OLED_Iic_Sda_Mode(GPIO_Mode_IN);
	
	OLED_SCL = 0;
	
	//0 1 1 1 1 0 0 0
	for(i=0; i<8; i++)
	{

		delay_us(5);
		OLED_SCL = 1;
		delay_us(5);
		
	    //接受数据
		if(OLED_SDA_IN == 1)  //引脚为电平为1
		{
			data |= (1<<(7-i));
		}	
		OLED_SCL = 0;
	}

	return data;
}




void I2C_WriteByte(uint8_t addr,uint8_t data)
{
	u8 ack;
	
//	I2C_GenerateSTART(I2C1, ENABLE);//开启I2C1
//	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));/*EV5,主模式*/
	//启动信号
	OLED_Iic_Start();
	
	

//	I2C_Send7bitAddress(I2C1, OLED_ADDRESS, I2C_Direction_Transmitter);//器件地址 -- 默认0x78
//	while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
	OLED_Iic_Send_Byte(OLED_ADDRESS);
	ack = OLED_Iic_Rcv_Ack();
	if(ack == 1)
	{
		printf("ack failure\n");
		return ;
	}		
	
//	I2C_SendData(I2C1, addr);//寄存器地址
//	while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	OLED_Iic_Send_Byte(addr);
	ack = OLED_Iic_Rcv_Ack();
	if(ack == 1)
	{
		printf("ack failure\n");
		return ;
	}	
	
//	I2C_SendData(I2C1, data);//发送数据
//	while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	OLED_Iic_Send_Byte(data);
	ack = OLED_Iic_Rcv_Ack();
	if(ack == 1)
	{
		printf("ack failure\n");
		return ;
	}	
	
	
//	I2C_GenerateSTOP(I2C1, ENABLE);//关闭I2C1总线
	
	OLED_Iic_Stop();
	
	
	
}

void WriteCmd(unsigned char I2C_Command)//写命令
{
	I2C_WriteByte(0x00, I2C_Command);
}

void WriteDat(unsigned char I2C_Data)//写数据
{
	I2C_WriteByte(0x40, I2C_Data);
}

void OLED_Init(void)
{
	delay_ms(100); //这里的延时很重要
	
	WriteCmd(0xAE); //display off
	WriteCmd(0x20);	//Set Memory Addressing Mode	
	WriteCmd(0x10);	//00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
	WriteCmd(0xb0);	//Set Page Start Address for Page Addressing Mode,0-7
	WriteCmd(0xc8);	//Set COM Output Scan Direction
	WriteCmd(0x00); //---set low column address
	WriteCmd(0x10); //---set high column address
	WriteCmd(0x40); //--set start line address
	WriteCmd(0x81); //--set contrast control register
	WriteCmd(0xff); //亮度调节 0x00~0xff
	WriteCmd(0xa1); //--set segment re-map 0 to 127
	WriteCmd(0xa6); //--set normal display
	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
	WriteCmd(0x3F); //
	WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
	WriteCmd(0xd3); //-set display offset
	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
	WriteCmd(0xf0); //--set divide ratio
	WriteCmd(0xd9); //--set pre-charge period
	WriteCmd(0x22); //
	WriteCmd(0xda); //--set com pins hardware configuration
	WriteCmd(0x12);
	WriteCmd(0xdb); //--set vcomh
	WriteCmd(0x20); //0x20,0.77xVcc
	WriteCmd(0x8d); //--set DC-DC enable
	WriteCmd(0x14); //
	WriteCmd(0xaf); //--turn on oled panel
}

void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{ 
	WriteCmd(0xb0+y);
	WriteCmd(((x&0xf0)>>4)|0x10);
	WriteCmd((x&0x0f)|0x01);
}

void OLED_Fill(unsigned char fill_Data)//全屏填充
{
	unsigned char m,n;
	for(m=0;m<8;m++)
	{
		WriteCmd(0xb0+m);		//page0-page1
		WriteCmd(0x00);		//low column start address
		WriteCmd(0x10);		//high column start address
		for(n=0;n<128;n++)
			{
				WriteDat(fill_Data);
			}
	}
}

void OLED_CLS(void)//清屏
{
	OLED_Fill(0x00);
}

//--------------------------------------------------------------
// Prototype      : void OLED_ON(void)
// Calls          : 
// Parameters     : none
// Description    : 将OLED从休眠中唤醒
//--------------------------------------------------------------
void OLED_ON(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X14);  //开启电荷泵
	WriteCmd(0XAF);  //OLED唤醒
}

//--------------------------------------------------------------
// Prototype      : void OLED_OFF(void)
// Calls          : 
// Parameters     : none
// Description    : 让OLED休眠 -- 休眠模式下,OLED功耗不到10uA
//--------------------------------------------------------------
void OLED_OFF(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X10);  //关闭电荷泵
	WriteCmd(0XAE);  //OLED休眠
}

//--------------------------------------------------------------
// Prototype      : void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
// Calls          : 
// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description    : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
//--------------------------------------------------------------
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
	unsigned char c = 0,i = 0,j = 0;
	switch(TextSize)
	{
		case 1:
		{
			while(ch[j] != '\0')
			{
				c = ch[j] - 32;
				if(x > 126)
				{
					x = 0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<6;i++)
					WriteDat(F6x8[c][i]);
				x += 6;
				j++;
			}
		}break;
		case 2:
		{
			while(ch[j] != '\0')
			{
				c = ch[j] - 32;
				if(x > 120)
				{
					x = 0;
					y++;
				}
				OLED_SetPos(x,y);
				for(i=0;i<8;i++)
					WriteDat(F8X16[c*16+i]);
				OLED_SetPos(x,y+1);
				for(i=0;i<8;i++)
					WriteDat(F8X16[c*16+i+8]);
				x += 8;
				j++;
			}
		}break;
	}
}

//--------------------------------------------------------------
// Prototype      : void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
// Calls          : 
// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引
// Description    : 显示codetab.h中的汉字,16*16点阵
//--------------------------------------------------------------
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
	unsigned char wm=0;
	unsigned int  adder=32*N;
	OLED_SetPos(x , y);//	
	for(wm = 0;wm < 16;wm++)
	{
		WriteDat(F16x16[adder]);
		adder += 1;
	}
	OLED_SetPos(x,y + 1);
	for(wm = 0;wm < 16;wm++)
	{
		WriteDat(F16x16[adder]);
		adder += 1;
	}
}


//void OLED_ShowCN1(unsigned char x, unsigned char y, unsigned char N)
//{
//	unsigned char wm=0;
//	unsigned int  adder=32*N;
//	OLED_SetPos(x , y);//	
//	for(wm = 0;wm < 16;wm++)
//	{
//		WriteDat(E16x16[adder]);
//		adder += 1;
//	}
//	OLED_SetPos(x,y + 1);
//	for(wm = 0;wm < 16;wm++)
//	{
//		WriteDat(E16x16[adder]);
//		adder += 1;
//	}
//}
//--------------------------------------------------------------
// Prototype      : void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);
// Calls          : 
// Parameters     : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
// Description    : 显示BMP位图
//--------------------------------------------------------------
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
	unsigned int j=0;
	unsigned char x,y;

  if(y1%8==0)
		y = y1/8;
  else
		y = y1/8 + 1;
	for(y=y0;y<y1;y++)
	{
		OLED_SetPos(x0,y);
    for(x=x0;x<x1;x++)
		{
			WriteDat(BMP[j++]);
		}
	}
}


显示登陆界面
//void load_shou(void)
//{
//		unsigned char i;
//	
//	Delay_Init();
//	I2C_Configuration();
//	OLED_Init();	
//	
//		OLED_Fill(0xFF);//全屏点亮
//		
//		delay_ms(500);
//		OLED_Fill(0x00);//全屏灭
//		delay_ms(500);	
//		delay_ms(500);
//		
//		for(i=0;i<5;i++)
//		{
//			OLED_ShowCN1(29+i*16,11,i);//测试显示中文
//		}
//		OLED_OFF();//测试OLED休眠
//		delay_ms(500);
//		delay_ms(500);
//		delay_ms(500);
//		delay_ms(500);
//		OLED_ON();//测试OLED休眠后唤醒
//		delay_ms(500);
//		delay_ms(500);
//		delay_ms(500);
//		delay_ms(500);
//}

//显示主界面
void Show_main(void)
{
		unsigned char i;
	
	Delay_Init();
	I2C_Configuration();
	OLED_Init();	
	
		OLED_Fill(0xFF);//全屏点亮
		
		delay_ms(500);
		OLED_Fill(0x00);//全屏灭
		delay_ms(500);	
		delay_ms(500);
		
		for(i=0;i<5;i++)
		{
			OLED_ShowCN(29+i*16,11,i);//测试显示中文
		}
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
				OLED_OFF();//测试OLED休眠
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
		OLED_ON();//测试OLED休眠后唤醒
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
		delay_ms(500);
}

//输入密码解锁界面
void show_unlock()
{
	OLED_Fill(0x00);//全屏灭
	delay_ms(500);
	
	OLED_ShowStr(0,3,(unsigned char *)"Please input password",1);//测试8*16字符
}
//密码正确
void show_right()
{
	OLED_Fill(0x00);//全屏灭
	delay_ms(500);
	
	OLED_ShowStr(45,3,(unsigned char *)"Right!",2);//测试8*16字符
}
//刷卡失败
void show_RFID_Failer()
{
	OLED_Fill(0x00);//全屏灭
	delay_ms(500);
	
	OLED_ShowStr(45,3,(unsigned char *)"Error!",2);//测试8*16字符
}

//刷卡成功
void show_RFID_Success()
{
	OLED_Fill(0x00);//全屏灭
	delay_ms(500);
	
	OLED_ShowStr(45,3,(unsigned char *)"Open Door",2);//测试8*16字符
}


//密码错误
void show_error()
{
	OLED_Fill(0x00);//全屏灭
	delay_ms(500);
	
	OLED_ShowStr(45,3,(unsigned char *)"Error!",2);//测试8*16字符
}
//0.返回(显示)
void set_return()
{
	OLED_Fill(0x00);//全屏灭
	delay_ms(500);
	
	//返回(显示)
	for(int i=11; i<15 ;i++)
	{
		OLED_ShowCN(0+(i-11)*16,0,i);//测试显示中文
	}
	OLED_ShowStr(8,0,(unsigned char *)"0",2);//测试8*16字符
}
//1.密码修改(显示)
void set_password()
{
	//密码修改(显示)
	for(int i=15; i<21 ;i++)
	{
		OLED_ShowCN(0+(i-15)*16,2,i);//测试显示中文
	}
	OLED_ShowStr(8,2,(unsigned char *)"1",2);//测试8*16字符
}

//2.添加指纹(显示)
void set_add()
{
	//添加指纹(显示)
	for(int i=21; i<27 ;i++)
	{
		OLED_ShowCN(0+(i-21)*16,4,i);//测试显示中文
	}
	OLED_ShowStr(8,4,(unsigned char *)"2",2);//测试8*16字符
}

//3.删除指纹(显示)
void set_delete()
{	
	//删除指纹(显示)
	for(int i=27; i<33 ;i++)
	{
		OLED_ShowCN(0+(i-27)*16,6,i);//测试显示中文
	}
	OLED_ShowStr(8,6,(unsigned char *)"3",2);//测试8*16字符
}

//展示设置界面
void show_set()
{
	set_return();//返回显示
	set_password();//修改密码显示
	set_add();//添加指纹显示
	set_delete();//删除指纹显示
}

//请按下手指界面
void show_please_press()
{
	OLED_Fill(0x00);//全屏灭
	//delay_ms(500);
	
	OLED_ShowStr(20,4,(unsigned char *)"Please press",2);//测试8*16字符
}

//请再次按下手指界面
void show_press_again()
{
	OLED_Fill(0x00);//全屏灭
	//delay_ms(500);
	
	OLED_ShowStr(20,2,(unsigned char *)"Please press",2);//测试8*16字符
	OLED_ShowStr(30,4,(unsigned char *)"again",2);//测试8*16字符
}
.h文件
#ifndef __OLED_I2C_H
#define	__OLED_I2C_H

#include "stm32f4xx.h"


#define OLED_SCL  		PEout(8)
#define OLED_SDA_IN		PEin(10)
#define OLED_SDA_OUT	PEout(10)



#define OLED_ADDRESS	0x78 //通过调整0R电阻,屏可以0x78和0x7A两个地址 -- 默认0x78

void I2C_Configuration(void);
void I2C_WriteByte(uint8_t addr,uint8_t data);
void WriteCmd(unsigned char I2C_Command);
void WriteDat(unsigned char I2C_Data);
void OLED_Init(void);
void OLED_SetPos(unsigned char x, unsigned char y);
void OLED_Fill(unsigned char fill_Data);
void OLED_CLS(void);
void OLED_ON(void);
void OLED_OFF(void);
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize);
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N);
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);

#endif

7、 4x4按键

①按键原理图

在这里插入图片描述
在这里插入图片描述
有4根线控制行、4根线控制列,然后实现16个按键的功能

②、代码实现
//.c文件
#include "key.h"
#include "delay.h"
#include "sys.h"

/*
C4-->PE6
C3-->PB6
C2-->PC9
C1-->PC7

R1-->PC6
R2-->PC8
R3-->PC11
R4-->PE5
*/
extern u8 flag;
void Key_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	//打开GPIOC时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
	//打开GPIOB时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	//打开GPIOE时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
	
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;//输出模式
	GPIO_InitStructure.GPIO_OType=GPIO_OType_OD;//开漏
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_6|GPIO_Pin_7;//6 7 8 9引脚
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_SetBits(GPIOC, GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9);//将所有的按键的电平置位
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;//6引脚
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB, GPIO_Pin_6);//将所有的按键的电平置位
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_6;//5 6引脚
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	GPIO_SetBits(GPIOE, GPIO_Pin_5|GPIO_Pin_6);//将所有的按键的电平置位
	
}

//判断是哪个按键按下函数
u8 Key_Press(void)
{
	while(1)
	{
		//第四列
		C4 = 0;
		//读取各列的值
		if(R1 == 0)//第一行
		{
			delay_ms(10);//按键消抖
			if(R1 == 0)
			{
				C4 = 1;
				//第一行第四列按下
				return 4;
			}
		}
		if(R2 == 0)//第二行
		{
			delay_ms(10);//按键消抖
			if(R2 == 0)
			{
				C4 = 1;
				//第二行第四列按下
				return 8;
			}
		}
		if(R3 == 0)//第三行
		{
			delay_ms(10);//按键消抖
			if(R3 == 0)
			{
				C4 = 1;
				//第三行第四列按下
				return 12;
			}
		}
		if(R4 == 0)//第四行
		{
			delay_ms(10);//按键消抖
			if(R4 == 0)
			{
				C4 = 1;
				//第四行第四列按下
				return 16;
			}
		}
		C4 = 1;
		
		//第三列
		C3 = 0;
		//读取各列的值
		if(R1 == 0)//第一行
		{
			delay_ms(10);//按键消抖
			if(R1 == 0)
			{
				C4 = 1;
				//第一行第三列按下
				return 3;
			}
		}
		if(R2 == 0)//第二行
		{
			delay_ms(10);//按键消抖
			if(R2 == 0)
			{
				C4 = 1;
				//第二行第三列按下
				return 7;
			}
		}
		if(R3 == 0)//第三行
		{
			delay_ms(10);//按键消抖
			if(R3 == 0)
			{
				C4 = 1;
				//第三行第三列按下
				return 11;
			}
		}
		if(R4 == 0)//第四行
		{
			delay_ms(10);//按键消抖
			if(R4 == 0)
			{
				C4 = 1;
				//第四行第三列按下
				return 15;
			}
		}
		C3 = 1;
		
		
		//第二列
		C2 = 0;
		//读取各列的值
		if(R1 == 0)//第一行
		{
			delay_ms(10);//按键消抖
			if(R1 == 0)
			{
				C2 = 1;
				//第一行第二列按下
				return 2;
			}
		}
		if(R2 == 0)//第二行
		{
			delay_ms(10);//按键消抖
			if(R2 == 0)
			{
				C2 = 1;
				//第二行第二列按下
				return 6;
			}
		}
		if(R3 == 0)//第三行
		{
			delay_ms(10);//按键消抖
			if(R3 == 0)
			{
				C2 = 1;
				//第三行第二列按下
				return 10;
			}
		}
		if(R4 == 0)//第四行
		{
			delay_ms(10);//按键消抖
			if(R4 == 0)
			{
				C2 = 1;
				//第四行第二列按下
				return 14;
			}
		}
		C2 = 1;
		
		
		//第一列
		C1 = 0;
		//读取各列的值
		if(R1 == 0)//第一行
		{
			delay_ms(10);//按键消抖
			if(R1 == 0)
			{
				C1 = 1;
				//第一行第一列按下
				return 1;
			}
		}
		if(R2 == 0)//第二行
		{
			delay_ms(10);//按键消抖
			if(R2 == 0)
			{
				C1 = 1;
				//第二行第一列按下
				return 5;
			}
		}
		if(R3 == 0)//第三行
		{
			delay_ms(10);//按键消抖
			if(R3 == 0)
			{
				C1 = 1;
				//第三行第一列按下
				return 9;
			}
		}
		if(R4 == 0)//第四行
		{
			delay_ms(10);//按键消抖
			if(R4 == 0)
			{
				C1 = 1;
				//第四行第一列按下
				return 13;
			}
		}
		C1 = 1;
	}
}
.h文件
//这里我使用的是位带操作,来实现的引脚配置
#ifndef __KEY_H
#define __KEY_H

#include "stm32f4xx.h"
/*

C4-->PE6		
C3-->PB6
C2-->PC9
C1-->PC7			

R1-->PC6
R2-->PC8
R3-->PC11
R4-->PE5

*/
#define C4 PEout(6)
#define C3 PBout(6)
#define C2 PCout(9)
#define C1 PCout(7)

#define R1 PCin(6)
#define R2 PCin(8)
#define R3 PCin(11)
#define R4 PEin(5)


void Key_Init(void);
u8 Key_Press(void);


#endif

9、项目总结

通过调用这些函数,就可以实现智能门锁的系统开发,有需要源码的小伙伴,我阔以给你提供。

评论 79
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_44585751

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值