一、介绍系统
1.功能介绍
本系统由指纹模块、RC522模块、矩阵键盘、LCD1602显示屏、ESP8266模块几个部分构成。该系统功能可以通过指纹、刷卡、输入密码和APP远程解锁这四种解锁开门的方式,并且APP将会记录门锁状态和各个模块解锁的次数。
2.系统软件框架图
二、模块程序
1.矩阵键盘初始化配置程序
//KEY初始化
void KEY_GPIO_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO |RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //关闭JTAG模式 使A15,PB3,PB4变成普通IO口
}
//A15,B3,B4,B5为输出,B6,B7,B8,B9为输入
void PA15_AND_PB3_5OUT_AND_PB6_9IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE); //使能PA,PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
}
//A15,B3,B4,B5为输入,B6,B7,B8,B9为输出
void PA15_AND_PB3_5IN_AND_PB6_9OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //下拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
}
//消抖
void KEY_DELAY(void)
{
u16 i = 10000;
while(i--);
}
2.矩阵键盘判断是否按下程序
//矩阵键盘初始化
u8 KEYSCAN(void)
{
u8 temp = 0,num = 99;
PA15_AND_PB3_5OUT_AND_PB6_9IN(); //A15,B3,B4,B5为输出,B6,B7,B8,B9为输入
GPIO_WriteBit(GPIOA,GPIO_Pin_15,1); //A15输出高电平
GPIO_Write(GPIOB,(GPIO_ReadOutputData(GPIOB)|0X0038)); // B3,B4,B5输出高电平
KEY_DELAY();//去抖动
if((GPIO_ReadInputData(GPIOB) & 0X03C0 )!= 0) // B6,B7,B8,B9检测到高电平,说明有按键按下
{
temp = (u8)((GPIO_ReadInputData(GPIOB) & 0X03C0) >> 2); // temp: XXXX 0000 temp: 0010 0000
PA15_AND_PB3_5IN_AND_PB6_9OUT(); //A15,B3,B4,B5为输入,B6,B7,B8,B9为输出
GPIO_Write(GPIOB,(GPIO_ReadOutputData(GPIOB)|0X03C0));//B6,B7,B8,B9输出高电平
KEY_DELAY();//去抖动
if((GPIO_ReadInputData(GPIOB) & 0X0038) != 0 || GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15))//A15,B3,B4,B5检测到高电平,说明有按键按下
{
temp += (u8)((GPIO_ReadInputData(GPIOB) & 0X0038) >> 2) + GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15);// temp: XXXX XXXX
//return temp; // temp: 0000 0001
while((GPIO_ReadInputData(GPIOB) & 0X0038) != 0 || GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_15));
switch(temp)// temp数据对应引脚:B9,B8,B7,B6,B5,B4,B3,A15
{
case 33: num = 15; break;
case 36: num = 6; break;
case 65: num = 0; break;
case 66: num = 2; break;
case 68: num = 5; break;
case 24: num = 10; break;
case 20: num = 11; break;
case 132: num = 4; break;
case 130: num = 1; break;
case 18: num = 12; break;
case 34: num = 3; break;
case 17: num = 13; break;
case 40: num = 9; break;
case 72: num = 8; break;
case 136: num = 7; break;
case 129: num = 14; break;
default : break;
}
}
}
return num;
}
3.LCD1602模块程序
u8 Dao_xu(u8 data)//倒叙函数
{
u8 i = 0 ,temp = 0;;
for(i = 0; i < 8; i++)
{
temp += (((data >> i) & 0x01) << (7 - i));
}
return temp;
}
/*------------------------------------------------
写入命令函数
------------------------------------------------*/
void LCD_Write_Com(unsigned char com)
{
LCD1602_RS=0; //命令
delay_us(1);
LCD1602_RW=0;
delay_us(1);
LCD1602_EN=1;
delay_us(1);
GPIO_Write(GPIOA,(GPIO_ReadOutputData(GPIOA)&0XFF00)+Dao_xu(com)); //在不影响A8-A15引脚的前提下,把数据写到A0-A7引脚
delay_us(100);
LCD1602_EN=0;
}
/*------------------------------------------------
写入数据函数
------------------------------------------------*/
void LCD_Write_Data(unsigned char Data)
{
LCD1602_RS=1; //数据
delay_us(1);
LCD1602_RW=0;
delay_us(1);
LCD1602_EN=1;
delay_us(1);
GPIO_Write(GPIOA,(GPIO_ReadOutputData(GPIOA)&0XFF00)+Dao_xu(Data)); //在不影响A8-A15引脚的前提下,把数据写到A0-A7引脚
delay_us(100);
LCD1602_EN=0;
}
/*------------------------------------------------
写入字符串函数
------------------------------------------------*/
void LCD_Write_String(unsigned char x,unsigned char y,unsigned char *s)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x); //第一行
}
else
{
LCD_Write_Com(0xC0 + x); //第二行
}
while (*s) //判断是否检测到结尾符
{
LCD_Write_Data( *s);//显示字符
s ++; //指针加1
}
}
/*------------------------------------------------
写入字符函数
------------------------------------------------*/
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data)
{
if (y == 0)
{
LCD_Write_Com(0x80 + x); //第一行
}
else
{
LCD_Write_Com(0xC0 + x); //第二行
}
LCD_Write_Data( Data); //显示字符
}
/*------------------------------------------------
清屏函数
------------------------------------------------*/
void LCD_Clear(void)
{
LCD_Write_Com(0x01);
delay_ms(5);
}
/*------------------------------------------------*/
void LCD_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //开启GPIOA GPIOB GPIOC时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 |
GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出速度50MHZ
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1; // LCD1602 RS-RW-EN
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出速度50MHZ
GPIO_Init(GPIOB, &GPIO_InitStructure); //GPIOB
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; // LCD1602 RS-RW-EN
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //输出速度50MHZ
GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOB
LCD_Write_Com(0x38);
delay_ms(5);
LCD_Write_Com(0x38);
delay_ms(5);
LCD_Write_Com(0x38);
delay_ms(5);
LCD_Write_Com(0x08); /*显示关闭*/
delay_ms(5);
LCD_Write_Com(0x01); /*显示清屏*/
delay_ms(5);
LCD_Write_Com(0x06); /*显示光标移动设置*/
delay_ms(5);
LCD_Write_Com(0x0C); /*显示开及光标设置*/
delay_ms(5);
}
4.RC522初始化程序
void RC522_Init ( void )
{
RC522_SPI_Config ();
macRC522_Reset_Disable();
macRC522_CS_Disable();
}
static void RC522_SPI_Config ( void )
{
/* SPI_InitTypeDef SPI_InitStructure */
GPIO_InitTypeDef GPIO_InitStructure;
/*!< Configure SPI_RC522_SPI pins: CS */
macRC522_GPIO_CS_CLK_FUN ( macRC522_GPIO_CS_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = macRC522_GPIO_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = macRC522_GPIO_CS_Mode;
GPIO_Init(macRC522_GPIO_CS_PORT, &GPIO_InitStructure);
/*!< Configure SPI_RC522_SPI pins: SCK */
macRC522_GPIO_SCK_CLK_FUN ( macRC522_GPIO_SCK_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = macRC522_GPIO_SCK_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = macRC522_GPIO_SCK_Mode;
GPIO_Init(macRC522_GPIO_SCK_PORT, &GPIO_InitStructure);
/*!< Configure SPI_RC522_SPI pins: MOSI */
macRC522_GPIO_MOSI_CLK_FUN ( macRC522_GPIO_MOSI_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = macRC522_GPIO_MOSI_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = macRC522_GPIO_MOSI_Mode;
GPIO_Init(macRC522_GPIO_MOSI_PORT, &GPIO_InitStructure);
/*!< Configure SPI_RC522_SPI pins: MISO */
macRC522_GPIO_MISO_CLK_FUN ( macRC522_GPIO_MISO_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = macRC522_GPIO_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = macRC522_GPIO_MISO_Mode;
GPIO_Init(macRC522_GPIO_MISO_PORT, &GPIO_InitStructure);
/*!< Configure SPI_RC522_SPI pins: RST */
macRC522_GPIO_RST_CLK_FUN ( macRC522_GPIO_RST_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = macRC522_GPIO_RST_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = macRC522_GPIO_RST_Mode;
GPIO_Init(macRC522_GPIO_RST_PORT, &GPIO_InitStructure);
}
5.指纹模块AS608程序
#define STM32_RX1_BUF Usart1RecBuf
#define STM32_Rx1Counter RxCounter
#define STM32_RX1BUFF_SIZE USART1_RXBUFF_SIZE
unsigned char AS608_RECEICE_BUFFER[24];//指纹数据存储数组
//FINGERPRINT通信协议定义
unsigned char AS608_Pack_Head[6] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF}; //协议包头
unsigned char AS608_Get_Img[6] = {0x01,0x00,0x03,0x01,0x0,0x05}; //获得指纹图像
unsigned char FP_Search[11]= {0x01,0x0,0x08,0x04,0x01,0x0,0x0,0x03,0xA1,0x0,0xB2}; //搜索指纹搜索范围0 - 929
unsigned char FP_Img_To_Buffer1[7]= {0x01,0x0,0x04,0x02,0x01,0x0,0x08}; //将图像放入到BUFFER1
unsigned char FP_Img_To_Buffer2[7]= {0x01,0x0,0x04,0x02,0x02,0x0,0x09}; //将图像放入到BUFFER2
unsigned char FP_Reg_Model[6]= {0x01,0x0,0x03,0x05,0x0,0x09}; //将BUFFER1跟BUFFER2合成特征模版
unsigned char FP_Delet_All_Model[6]= {0x01,0x0,0x03,0x0d,0x00,0x11}; //删除指纹模块里所有的模版
unsigned char FP_Save_Finger[9]= {0x01,0x00,0x06,0x06,0x01,0x00,0x0B,0x00,0x19}; //将BUFFER1中的特征码存放到指定的位置
unsigned char FP_Delete_Model[10]= {0x01,0x00,0x07,0x0C,0x0,0x0,0x0,0x1,0x0,0x0}; //删除指定的模版
//读出感应状态(有指纹按下时,输出高电平)
void PS_StaGPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//时钟开启
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//下拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
unsigned char uart_recv(unsigned char *bufs, unsigned short timeout)//接收数据
{
unsigned char i;
unsigned short RX_TimeOut;
RX_TimeOut = timeout;
do
{
delay_ms(1);
if(RX_TimeOut > 0) //超时计数
{
if(--RX_TimeOut == 0)//等待时间到
{
unsigned char ret;
ret = STM32_Rx1Counter;//获取字节数
if(STM32_Rx1Counter > 0)
{
for(i=0; i<STM32_Rx1Counter; i++)
{
*bufs = STM32_RX1_BUF[i];
bufs ++;
}
}
STM32_Rx1Counter = 0;
return ret;//返回字节数,并跳出while
}
}
}
while(1);
}
/*------------------ FINGERPRINT命令字 --------------------------*/
//接收反馈数据缓冲
unsigned char AS608_Receive_Data(void)
{
memset(AS608_RECEICE_BUFFER,0,24);//清空数据
return uart_recv(AS608_RECEICE_BUFFER,1000);
}
void AS608_Cmd_Send_Pack_Head(void) //发送包头
{
uart1_send(AS608_Pack_Head,6);
}
//FINGERPRINT_获得指纹图像命令判断接收到的确认码,等于0指纹获取成功
unsigned char AS608_Cmd_Get_Img(void)
{
AS608_Cmd_Send_Pack_Head(); //发送通信协议包头
uart1_send(AS608_Get_Img,6);
if (AS608_Receive_Data() > 0)
{
return AS608_RECEICE_BUFFER[9];
}
else
{
return 0xFE;
}
}
//讲图像转换成特征码存放在Buffer1中
unsigned char FINGERPRINT_Cmd_Img_To_Buffer1(void)
{
AS608_Cmd_Send_Pack_Head(); //发送通信协议包头
uart1_send(FP_Img_To_Buffer1,7);//发送命令 将图像转换成 特征码 存放在 CHAR_buffer1
if (AS608_Receive_Data() > 0)
{
return AS608_RECEICE_BUFFER[9];
}
else
{
return 0xFE;
}
}
//将图像转换成特征码存放在Buffer2中
unsigned char FINGERPRINT_Cmd_Img_To_Buffer2(void)
{
AS608_Cmd_Send_Pack_Head(); //发送通信协议包头
uart1_send(FP_Img_To_Buffer2,7);
if (AS608_Receive_Data() > 0)
{
return AS608_RECEICE_BUFFER[9];
}
else
{
return 0xFE;
}
}
//将BUFFER1 跟 BUFFER2 中的特征码合并成指纹模版
unsigned char FINGERPRINT_Cmd_Reg_Model(void)
{
AS608_Cmd_Send_Pack_Head(); //发送通信协议包头
uart1_send(FP_Reg_Model,6);
if (AS608_Receive_Data() > 0)
{
return AS608_RECEICE_BUFFER[9];
}
else
{
return 0xFE;
}
}
//删除指纹模块里的所有指纹模版
unsigned char FINGERPRINT_Cmd_Delete_All_Model(void)
{
AS608_Cmd_Send_Pack_Head(); //发送通信协议包头
uart1_send(FP_Delet_All_Model,6);
if (AS608_Receive_Data() > 0)
{
return AS608_RECEICE_BUFFER[9];
}
else
{
return 0xFE;
}
}
//删除指纹模块里的指定指纹模版
void FINGERPRINT_Cmd_Delete_Model(unsigned short uiID_temp)
{
unsigned short uiSum_temp = 0;
unsigned char i;
FP_Delete_Model[4]=(uiID_temp&0xFF00)>>8;
FP_Delete_Model[5]=(uiID_temp&0x00FF);
for(i=0; i<8; i++)
uiSum_temp = uiSum_temp + FP_Delete_Model[i];
FP_Delete_Model[8]=(uiSum_temp&0xFF00)>>8;
FP_Delete_Model[9]=uiSum_temp&0xFF;
AS608_Cmd_Send_Pack_Head(); //发送通信协议包头
uart1_send(FP_Delete_Model,10);
}
//搜索全部用户999枚
unsigned short FINGERPRINT_Cmd_Search_Finger(void)
{
AS608_Cmd_Send_Pack_Head(); //发送通信协议包头
uart1_send(FP_Search,11);
if (AS608_Receive_Data() > 0 && AS608_RECEICE_BUFFER[9] == 0)
{
return (AS608_RECEICE_BUFFER[10]*256 + AS608_RECEICE_BUFFER[11]);
}
else
{
return 0xFFFE;
}
}
unsigned char FINGERPRINT_Cmd_Save_Finger(unsigned short storeID)
{
unsigned short temp = 0;
unsigned char i;
FP_Save_Finger[5] =(storeID&0xFF00)>>8;
FP_Save_Finger[6] = (storeID&0x00FF);
for(i=0; i<7; i++) //计算校验和
temp = temp + FP_Save_Finger[i];
FP_Save_Finger[7]=(temp & 0xFF00) >> 8; //存放校验数据
FP_Save_Finger[8]= temp & 0xFF;
AS608_Cmd_Send_Pack_Head(); //发送通信协议包头
uart1_send(FP_Save_Finger,9);
if (AS608_Receive_Data() > 0)
{
return AS608_RECEICE_BUFFER[9];
}
else
{
return 0xFE;
}
}
//返回指纹ID 0xFFFE错误
unsigned short AS608_Find_Fingerprint(void)
{
if(AS608_Cmd_Get_Img() == 0)
{
delay_ms(100);
if (FINGERPRINT_Cmd_Img_To_Buffer1() == 0)
{
return FINGERPRINT_Cmd_Search_Finger();
}
}
return 0xFFFE;
}
//指纹添加新用户
unsigned short AS608_Add_Fingerprint(unsigned short ID)
{
if(AS608_Cmd_Get_Img() == 0)
{
//delay_ms(100);
if (FINGERPRINT_Cmd_Img_To_Buffer1() == 0)
{
if(AS608_Cmd_Get_Img() == 0)
{
//delay_ms(100);
if (FINGERPRINT_Cmd_Img_To_Buffer2() == 0)
{
if (FINGERPRINT_Cmd_Reg_Model() == 0)
{
return FINGERPRINT_Cmd_Save_Finger(ID) ;
}
}
}
}
}
return 0xFFFE;
}
5.往STM32内部FLASH写入数据
void WRITE_IC_NUM_TO_FLASH(u8* ID_Buffer,u8 space)//IC卡号写入STM32内部FLASH
{
STMFLASH_Write(FLASH_SAVE_ADDR + 0x10 * space,(u16*)ID_Buffer,SIZE);
delay_ms(100); //内部FLASH寿命不长,防止误操作反复擦鞋
}
void READ_IC_NUM_FOR_FLASH(u8* ID_TEMP_Buffer ,u8 space)//从STM32内部FLASH读出IC卡号
{
STMFLASH_Read(FLASH_SAVE_ADDR + 0x10 * space,(u16*)ID_TEMP_Buffer,SIZE);
}
void WRITE_DATA_TO_FLASH(u8* ID_Buffer,u8 LEN)//密码写入STM32内部FLASH
{
STMFLASH_Write(FLASH_SAVE_ADDR + 0x10*MAX_PEOPLE*2,(u16*)ID_Buffer,LEN);
delay_ms(100); //内部FLASH寿命不长,防止误操作反复擦写
}
void READ_DATA_FOR_FLASH(u8* ID_TEMP_Buffer ,u8 LEN)//从STM32内部FLASH读出密码
{
STMFLASH_Read(FLASH_SAVE_ADDR + 0x10*MAX_PEOPLE*2,(u16*)ID_TEMP_Buffer,LEN);
}
void CHECK_NEW_MCU(void) // 检查是否是新的单片机,是的话清空存储区,否则保留
{
u8 comper_str[6],i=0;
u8 clear_str[10] = {0};
memset(clear_str,'0',sizeof(clear_str));//把clear_str这个数组全部清 ‘0’
STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)comper_str,5); //从0X0801F010这个地址读出数据
comper_str[5] = '\0';
if(strstr((char*)comper_str,"FDYDZ") == NULL) //新的单片机
{
WRITE_DATA_TO_FLASH(initpassword,6); //单片机第一次使用时,密码为6个0
for(i = 0; i < MAX_PEOPLE; i++)//把存卡内部缓存全部清零
{
WRITE_IC_NUM_TO_FLASH(clear_str,i);//存IC的地址内容全部清零
}
STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)"FDYDZ",5); //写入“FDYDZ”,方便下次校验
}
READ_DATA_FOR_FLASH(CurrentPassword,6); //从STM32内部FLASH里读出存储密码
}
6.ESP8266初始化程序
//==========================================================
// 函数名称: ESP8266_Init
//
// 函数功能: 初始化ESP8266
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Init(void)
{
ESP8266_Clear();
while(ESP8266_SendCmd("AT\r\n", "OK"))
delay_ms(500);
ESP8266_SendCmd("AT+RST\r\n", "");
delay_ms(500);
ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");
delay_ms(500);
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
delay_ms(500);
while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
delay_ms(500);
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
delay_ms(500);
while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
delay_ms(500);
}
7.继电器和蜂鸣器初始化程序
#define BEEP PCout(13)
#define RELAY PBout(12)
void BEEP_AND_RELAY_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_12); //输出低电平
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_ResetBits(GPIOC,GPIO_Pin_13); //输出低电平
}
三、核心执行程序
1.矩阵按键核心程序
//==========================确认键,并通过相应标志位执行相应功能===============================
void Ensure(void)
{
if(PressNum == 6)//必须输入6位数密码才有效
{
if((InputData[0]==0)&&(InputData[1]==3)&&(InputData[2]==1)&&(InputData[3]==4)&&(InputData[4]==0)&&(InputData[5]==6))//031406密码初始化为000000
{
WRITE_DATA_TO_FLASH(initpassword,6); // 强制将初始密码写入STM32内部存储
delay_ms(100);
READ_DATA_FOR_FLASH(CurrentPassword,6); //从STM32内部FLASH里读出存储密码
LCD_Write_String(0,1,"Init password...");
BEEP = 1;
delay_ms(1000);
BEEP = 0;
LCD_Write_String(0,1,"password: ");
LCD_Write_Com(0x80+0x40+9);
LCD_Write_Com(0x0F);//光标闪烁
}
else if((InputData[0]==0)&&(InputData[1]==3)&&(InputData[2]==1)&&(InputData[3]==4)&&(InputData[4]==0)&&(InputData[5]==7))//031407注册指纹、注册卡、删除卡
{
LCD_Write_String(0,1," ");
LCD_Write_String(0,2," manager ");
BuzzerRingsNum(2); //提示音,响两声
zc=1;
}
else if((InputData[0]==0)&&(InputData[1]==3)&&(InputData[2]==1)&&(InputData[3]==4)&&(InputData[4]==0)&&(InputData[5]==8))//031408指纹清空
{
if (FINGERPRINT_Cmd_Delete_All_Model() == 0)
{
LCD_Write_String(0,1," clear finger ok");
BuzzerRingsNum(2); //提示音,响两声
}
user_id = 0;
delay_ms(1000);
InitDisplay = 1;
}
else
{
if((InputData[0]==CurrentPassword[0])&&(InputData[1]==CurrentPassword[1])&&(InputData[2]==CurrentPassword[2])&&
(InputData[3]==CurrentPassword[3])&&(InputData[4]==CurrentPassword[4])&&(InputData[5]==CurrentPassword[5])) //密码匹配正确
{
CorrectCont++;
/*==========================按键解锁成功==========================*/
if(CorrectCont==1) //正确输入计数,当只有一次正确输入时,开锁,
{
LCD_Write_String(0,1," open "); //显示开锁
Sample_Upload_Fun(3);
RELAY = 1; //继电器开启
RELAY_TIME = 15; //继电器开启15秒
pass = 1; //密码正确标志
BuzzerRingsNum(2); //提示音,响两声
}
else //当两次正确输入时,开启重置密码功能
{
LCD_Write_String(0,1,"SetNewWordEnable");
BuzzerRingsNum(2); //提示音,响两声
ReInputEn=1; //允许重置密码输入
CorrectCont=0; //正确计数器清零
}
}
else
{
ErrorCont++;
LCD_Write_String(0,1," error "); //显示错误
if(ErrorCont==3)//错误输入计数达三次时,报警
{
do
{
LCD_Write_String(0,1,"Keyboard Locked!");
RELAY=0; //关闭锁
BEEP = !BEEP;
delay_ms(55);
}while(1);
}
else
{
/密码输错一次,蜂鸣器长响一声
BEEP=1;
delay_ms(1000);
BEEP=0;
LCD_Write_String(0,1,"password: ");
LCD_Write_Com(0x80+0x40+9);
LCD_Write_Com(0x0F);//光标闪烁
}
}
}
DataInit(); //将输入数据计数器清零,为下一次输入作准备
}
}
2.指纹模块核心程序
void finger_ctrl(void)
{
u8 ret;
if(PS_Sta == 1)//感应到有手指按上去
{
if (RegFingerprint == 1)//指纹录入
{
ret = AS608_Add_Fingerprint(user_id);//指纹添加新用户
if (ret== 0)
{
user_id ++;
if (user_id > 9)//最多录9个指纹
user_id = 0;
display_user_id();//显示
BuzzerRingsNum(1);//蜂鸣器响
}
}
if (RegFingerprint ==0)
{
unsigned short ret_userid = 0;
ret_userid = AS608_Find_Fingerprint();//扫描指纹
/*==========================指纹解锁成功==========================*/
if (ret_userid != 0xFFFE)//说明识别到指纹
{
LCD_Write_String(0,1," open "); //显示开锁
Sample_Upload_Fun(1);
RELAY = 1; //继电器开启
RELAY_TIME = 15; //继电器开启15秒
pass = 1; //密码正确标志
BuzzerRingsNum(2); //提示音,响两声
}
else
{
ErrorCont++;
LCD_Write_String(0,1," error "); //显示错误
if(ErrorCont==3)//错误输入计数达三次时,报警
{
do
{
LCD_Write_String(0,1,"Keyboard Locked!");
RELAY=0; //关闭锁
BEEP = !BEEP;
delay_ms(55);
}while(1);
}
else
{
/密码输错一次,蜂鸣器长响一声
BEEP=1;
delay_ms(1000);
BEEP=0;
LCD_Write_String(0,1,"password: ");
LCD_Write_Com(0x80+0x40+9);
LCD_Write_Com(0x0F);//光标闪烁
}
}
}
}
}
3.RC522核心程序
//正常待机刷卡函数
void COMPER_ID_MODE(void)
{
if(RC522_SCAN(ID_BUF)) //检测到有卡刷入
{
u8 i = 0;
ICpass = 0;
LCD_Write_Com(0x0C);//关闭光标
LCD_Write_String(0,1," IC: ");
LCD_Write_String(4,1,ID_BUF);//显示卡号
BEEP = 1; //蜂鸣器响
delay_ms(500);
BEEP = 0;
for(i = 0; i < MAX_PEOPLE; i++)//从5张IC卡中扫描
{
READ_IC_NUM_FOR_FLASH(ID_TEMP_Buffer,i);//读取STM32内部FLASH存储的卡号
/*==========================FRID解锁成功==========================*/
if(strstr((char*)ID_TEMP_Buffer,(char*)ID_BUF) != NULL ) //查找匹配正确
{
LCD_Write_String(0,1," open "); //显示开锁
Sample_Upload_Fun(2);
RELAY = 1; //继电器开启
RELAY_TIME = 15; //继电器开启15秒
ICpass = 1; //IC正确标志
ErrorCont=0;
break;
}
else
{
ICpass = 0;
}
}
if(ICpass == 0)
{
ErrorCont++;
LCD_Write_String(0,1," error "); //显示错误
if(ErrorCont==3)//错误输入计数达三次时,报警
{
do
{
LCD_Write_String(0,1,"Keyboard Locked!");
RELAY=0; //关闭锁
BEEP = !BEEP;
delay_ms(55);
}while(1);
}
else
{
/密码输错一次,蜂鸣器长响一声
BEEP=1;
delay_ms(1000);
BEEP=0;
LCD_Write_String(0,1,"password: ");
LCD_Write_Com(0x80+0x40+9);
LCD_Write_Com(0x0F);//光标闪烁
}
//break;
InitDisplay = 1;
}
}
}
4.APP远程开门函数
void OneNet_RevPro(unsigned char *cmd)
{
MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0}; //协议包
char *req_payload = NULL;
char *cmdid_topic = NULL;
unsigned short topic_len = 0;
unsigned short req_len = 0;
unsigned char type = 0;
unsigned char qos = 0;
static unsigned short pkt_id = 0;
short result = 0;
char *dataPtr = NULL;
char numBuf[10];
int num = 0;
type = MQTT_UnPacketRecv(cmd);
switch(type)
{
case MQTT_PKT_CMD: //命令下发
result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len); //解出topic和消息体
if(result == 0)
{
// UsartPrintf(USART_DEBUG, "cmdid: %s, req: %s, req_len: %d\r\n", cmdid_topic, req_payload, req_len);
if(MQTT_PacketCmdResp(cmdid_topic, req_payload, &mqttPacket) == 0) //命令回复组包
{
// UsartPrintf(USART_DEBUG, "Tips: Send CmdResp\r\n");
ESP8266_SendData(mqttPacket._data, mqttPacket._len); //回复命令
MQTT_DeleteBuffer(&mqttPacket); //删包
}
}
break;
case MQTT_PKT_PUBLISH: //接收的Publish消息
result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);
if(result == 0)
{
// UsartPrintf(USART_DEBUG, "topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",
// cmdid_topic, topic_len, req_payload, req_len);
switch(qos)
{
case 1: //收到publish的qos为1,设备需要回复Ack
if(MQTT_PacketPublishAck(pkt_id, &mqttPacket) == 0)
{
// UsartPrintf(USART_DEBUG, "Tips: Send PublishAck\r\n");
ESP8266_SendData(mqttPacket._data, mqttPacket._len);
MQTT_DeleteBuffer(&mqttPacket);
}
break;
case 2: //收到publish的qos为2,设备先回复Rec
//平台回复Rel,设备再回复Comp
if(MQTT_PacketPublishRec(pkt_id, &mqttPacket) == 0)
{
// UsartPrintf(USART_DEBUG, "Tips: Send PublishRec\r\n");
ESP8266_SendData(mqttPacket._data, mqttPacket._len);
MQTT_DeleteBuffer(&mqttPacket);
}
break;
default:
break;
}
}
break;
case MQTT_PKT_PUBACK: //发送Publish消息,平台回复的Ack
if(MQTT_UnPacketPublishAck(cmd) == 0)
// UsartPrintf(USART_DEBUG, "Tips: MQTT Publish Send OK\r\n");
break;
case MQTT_PKT_PUBREC: //发送Publish消息,平台回复的Rec,设备需回复Rel消息
if(MQTT_UnPacketPublishRec(cmd) == 0)
{
// UsartPrintf(USART_DEBUG, "Tips: Rev PublishRec\r\n");
if(MQTT_PacketPublishRel(MQTT_PUBLISH_ID, &mqttPacket) == 0)
{
// UsartPrintf(USART_DEBUG, "Tips: Send PublishRel\r\n");
ESP8266_SendData(mqttPacket._data, mqttPacket._len);
MQTT_DeleteBuffer(&mqttPacket);
}
}
break;
case MQTT_PKT_PUBREL: //收到Publish消息,设备回复Rec后,平台回复的Rel,设备需再回复Comp
if(MQTT_UnPacketPublishRel(cmd, pkt_id) == 0)
{
// UsartPrintf(USART_DEBUG, "Tips: Rev PublishRel\r\n");
if(MQTT_PacketPublishComp(MQTT_PUBLISH_ID, &mqttPacket) == 0)
{
// UsartPrintf(USART_DEBUG, "Tips: Send PublishComp\r\n");
ESP8266_SendData(mqttPacket._data, mqttPacket._len);
MQTT_DeleteBuffer(&mqttPacket);
}
}
break;
case MQTT_PKT_PUBCOMP: //发送Publish消息,平台返回Rec,设备回复Rel,平台再返回的Comp
if(MQTT_UnPacketPublishComp(cmd) == 0)
{
// UsartPrintf(USART_DEBUG, "Tips: Rev PublishComp\r\n");
}
break;
case MQTT_PKT_SUBACK: //发送Subscribe消息的Ack
if(MQTT_UnPacketSubscribe(cmd) == 0);
// UsartPrintf(USART_DEBUG, "Tips: MQTT Subscribe OK\r\n");
else;
// UsartPrintf(USART_DEBUG, "Tips: MQTT Subscribe Err\r\n");
break;
case MQTT_PKT_UNSUBACK: //发送UnSubscribe消息的Ack
if(MQTT_UnPacketUnSubscribe(cmd) == 0);
// UsartPrintf(USART_DEBUG, "Tips: MQTT UnSubscribe OK\r\n");
else;
// UsartPrintf(USART_DEBUG, "Tips: MQTT UnSubscribe Err\r\n");
break;
default:
result = -1;
break;
}
ESP8266_Clear(); //清空缓存
if(result == -1)
return;
dataPtr = strchr(req_payload, '}'); //搜索'}'
if(dataPtr != NULL && result != -1) //如果找到了
{
dataPtr++;
while(*dataPtr >= '0' && *dataPtr <= '9') //判断是否是下发的命令控制数据
{
numBuf[num++] = *dataPtr++;
}
num = atoi((const char *)numBuf); //转为数值形式
}
//判断下滑指令
if(strstr((char *)req_payload, "flag:1")) //开锁
{
LCD_Write_String(0,1," open "); //显示开锁
RELAY = 1;
pass = 1;
BuzzerRingsNum(2);
}
else if(strstr((char *)req_payload, "flag:0")) //关锁
{
Cancel();
}
if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH)
{
MQTT_FreeBuffer(cmdid_topic);
MQTT_FreeBuffer(req_payload);
}
}
四、Android studio程序
这里只展示远程开门锁和接收各个模块开门锁的程序,连接OneNET程序这些前几个文章有说明,这里就不在说明了。
1.远程开门锁
private void publishDataToMQTT() {
String topic = "cmd/water";
String payload = "flag:"+count;
int qos = 1;
try {
MqttMessage message = new MqttMessage(payload.getBytes());
message.setQos(qos);
mqttClient.publish(topic, message);
} catch (MqttException e) {
e.printStackTrace();
}
}
Lock_open.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView3.setText("开");
if(count != 1) count=1;
count_app++;
View_App.setText(" "+count_app+" 次");
publishDataToMQTT();
}
});
//close-----0
Lock_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView3.setText("关");
if(count != 0) count=0;
publishDataToMQTT();
}
});
2.接收各个模块开门锁以及次数
public void messageArrived(String topic, MqttMessage message) throws Exception {
// 处理收到的消息
String data = new String(message.getPayload());
if(data.indexOf("fing")!=-1)
{
count_fing++;
count_open=1;
}
else if(data.indexOf("FRID")!=-1)
{
count_FRID++;
count_open=1;
}
else if(data.indexOf("Key")!=-1)
{
count_Key++;
count_open=1;
}
else if(data.indexOf("close")!=-1)
{
count_open=0;
}
if(count_open==0) textView3.setText("关");
else if(count_open==1) textView3.setText("开");
View_fingerprint.setText(" "+count_fing+" 次");
View_FRID.setText(" "+count_FRID+" 次");
View_Key.setText(" "+count_Key+" 次");
}
@Override
public void deliveryComplete(IMqttDeliveryToken token) {
// 处理消息发送完成的情况
}
});
} catch (MqttException e) {
e.printStackTrace();
}
}