RISC-V MCU 基于CH32V307的智能门锁设计
一、设计概述
1.1 设计目的
随着物联网、大数据等技术的快速发展,智能门锁应用广泛。但是传统智能门锁还存在着许多的问题,例如,能被特斯拉线圈开锁,机械强度不够容易被暴力强制开锁,忘记带钥匙,等等。这些问题给用户带来了巨大的隐患,同时也限制了智能门锁的市场发展,功能的不足也会影响门锁的便捷性。在此背景之下,打造一把安全的多功能密码锁,具有重大的现实意义,我们想到了一种多方位控制的智能锁系统的设计,极大减少了传统锁具中存在的不便性且保密性更强,更结合了蓝牙、服务器等方式实现了多方式、多途径控制。
1.2 设计主要功能
- 采用按键密码锁的方式进行解锁,它可以设计任意六位数作为解锁密码,极大的增加了安全性、可靠性;
- 增加了指纹解锁的方式,优化了密码锁结构,也减轻了用户开锁的困难;
- 通过蓝牙、服务器的方式远程监控其状态,并可以实现远程控制,给人们生活带来极大的便捷
二、系统设计组成
2.1 总体框图
2.2 各模块介绍
- 门锁控制方式模块
分为矩阵键盘模块、指纹模块与显示Led。 矩阵键盘原理图如下:
矩阵键盘实物图如下:
本设计采用外部中断方式实现矩阵键盘扫描。
AS608指纹模块实物图如下:
AS608指纹模块相关接线如下:
AS608采用串口通信,本设计采用UART4作为通信串口。
- 远程控制模块
蓝牙模块:
实物图 - 引脚及指令集
- 蓝牙为串口通信,本设计采用UART5作为通信串口。
esp8266 WiFi模块:
接线示意: - 相关指令集参考:
WiFi模块采用串口通信,本设计采用UART6作为通信串口。
三、部分重要相关代码
3.1 矩阵键盘按键扫描
//key.c
#include "key.h"
#include <rtthread.h>
#include <rthw.h>
#include "debug.h"
#include "drivers/pin.h"
#include <rtdef.h>
#include "mg90s.h"
#include "esp8266.h"
#include "led.h"
char key_data[7] = {'1', '2', '3', '4', '5', '6', '\0'}; //密码设置
char key_input[7] = {0};
int count_t = 0;
extern rt_thread_t key_scancheck;
extern rt_sem_t sem_keych;
extern rt_sem_t sem_keyscan;
/*************************************************************
* name: thread_keychange_func
* function: 修改密码线程
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_keychange_func(void *parameter)
{
while (1)
{
rt_kprintf("keychangesem taking...\n");
rt_sem_take(sem_keych, RT_WAITING_FOREVER);
rt_thread_suspend(key_scancheck);
count_t = 0;
char ch[7] = {0};
char send_data[64] = {0};
rt_kprintf("keychange start...\n");
rt_strncpy(send_data, "password:", rt_strlen("password:"));
while (count_t != 6)
{
rt_strncpy(ch, key_input, 6);
}
rt_strncpy(key_data, key_input, 6);
for (int i = 0; i < 6; i++)
{
send_data[9 + i] = key_input[i];
}
esp8266_send(send_data, rt_strlen(send_data));
rt_kprintf("password has been changed!\n");
count_t = 0;
rt_thread_resume(key_scancheck);
}
}
/*************************************************************
* name: key1_init
* function: 按键扫描gpio初始化,使用中断方式
* input: 无
* return: 无
*************************************************************/
void key1_init()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
EXTI_InitTypeDef EXTI_InitStructure = {0};
NVIC_InitTypeDef NVIC_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = 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;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_ResetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
/* GPIOA ----> EXTI_Line0 */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource0);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource1);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3);
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2 | EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_Init(&NVIC_InitStructure);
}
/*************************************************************
* name: exti_disable
* function: 关按键扫描中断
* input: 无
* return: 无
*************************************************************/
void exti_disable()
{
EXTI_InitTypeDef EXTI_InitStructure = {0};
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2 | EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = DISABLE;
EXTI_Init(&EXTI_InitStructure);
}
/*************************************************************
* name: exti_enable
* function: 开按键扫描中断
* input: 无
* return: 无
*************************************************************/
void exti_enable()
{
EXTI_InitTypeDef EXTI_InitStructure = {0};
EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1 | EXTI_Line2 | EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
/*************************************************************
* name: scan
* function: 进中断后判断按键
* input: pin - 传送需要判断对应的列脚
* return: 无
*************************************************************/
int scan(uint16_t pin)
{
GPIO_SetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
for (int i = 0; i < 4; i++)
{
switch (i)
{
case 0:
GPIO_SetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
GPIO_ResetBits(GPIOE, GPIO_Pin_4);
break;
case 1:
GPIO_SetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
GPIO_ResetBits(GPIOE, GPIO_Pin_5);
break;
case 2:
GPIO_SetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
GPIO_ResetBits(GPIOE, GPIO_Pin_6);
break;
case 3:
GPIO_SetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
GPIO_ResetBits(GPIOE, GPIO_Pin_7);
break;
}
if (GPIO_ReadInputDataBit(GPIOE, pin) == 0)
{
while (GPIO_ReadInputDataBit(GPIOE, pin) == 0)
;
GPIO_ResetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
return (i + 1);
}
}
GPIO_ResetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
return 0;
}
//第一列有按键按下中断服务程序
void EXTI0_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void EXTI0_IRQHandler(void)
{
rt_kprintf("ex0.");
exti_disable();
switch (scan(GPIO_Pin_0))
{
case 1:
key_input[count_t++] = '1';
break;
case 2:
key_input[count_t++] = '4';
break;
case 3:
key_input[count_t++] = '7';
break;
case 4:
key_input[count_t++] = '*';
break;
}
rt_kprintf("%c\n", key_input[count_t - 1]);
exti_enable();
EXTI_ClearITPendingBit(EXTI_Line0);
}
//第二列有按键按下中断服务程序
void EXTI1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void EXTI1_IRQHandler(void)
{
rt_kprintf("ex1.");
exti_disable();
switch (scan(GPIO_Pin_1))
{
case 1:
key_input[count_t++] = '2';
break;
case 2:
key_input[count_t++] = '5';
break;
case 3:
key_input[count_t++] = '8';
break;
case 4:
key_input[count_t++] = '0';
break;
}
rt_kprintf("%c\n", key_input[count_t - 1]);
exti_enable();
EXTI_ClearITPendingBit(EXTI_Line1);
}
//第三列有按键按下中断服务程序
void EXTI2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void EXTI2_IRQHandler(void)
{
rt_kprintf("ex2.");
exti_disable();
switch (scan(GPIO_Pin_2))
{
case 1:
key_input[count_t++] = '3';
break;
case 2:
key_input[count_t++] = '6';
break;
case 3:
key_input[count_t++] = '9';
break;
case 4:
key_input[count_t++] = '#';
break;
}
rt_kprintf("%c\n", key_input[count_t - 1]);
exti_enable();
EXTI_ClearITPendingBit(EXTI_Line2);
}
//第四列有按键按下中断服务程序
void EXTI3_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void EXTI3_IRQHandler(void)
{
rt_kprintf("ex3.");
exti_disable();
switch (scan(GPIO_Pin_3))
{
case 1:
key_input[count_t++] = 'A';
break;
case 2:
key_input[count_t++] = 'B';
break;
case 3:
key_input[count_t++] = 'C';
break;
case 4:
key_input[count_t++] = 'D';
break;
}
rt_kprintf("%c\n", key_input[count_t - 1]);
exti_enable();
EXTI_ClearITPendingBit(EXTI_Line3);
}
/*************************************************************
* name: thread_keyscancheck_func
* function: 按键扫描判定,确定按键密码数量足够则发送信号量唤醒
* 密码判断线程
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_keyscancheck_func(void *parameter)
{
rt_kprintf("thread_keyscancheck_func entry...\n");
while (1)
{
while (count_t != 6)
{
rt_thread_mdelay(1000);
}
count_t = 0;
rt_kprintf("sem_keyscan release...\n");
rt_sem_release(sem_keyscan);
}
}
/*************************************************************
* name: thread_keyscan_func
* function: 按键扫描密码处理线程
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_keyscan_func(void *parameter)
{
int num = 0;
while (1)
{
rt_kprintf("sem_keyscan taking...\n");
rt_sem_take(sem_keyscan, RT_WAITING_FOREVER);
rt_kprintf("sem_keyscan toke...\n");
if (rt_strcmp(key_input, key_data) == 0)
{
num = 0;
rt_kprintf("password correct!\n");
led_crtl(1, 0);
rt_kprintf("open the door...\n");
mg90s_ctrl(OPEN_DOOR);
esp8266_send("door:1 ", rt_strlen("door:1 "));
rt_thread_mdelay(1000);
led_crtl(1, 1);
rt_thread_mdelay(5000);
rt_kprintf("close the door...\n");
mg90s_ctrl(CLOSE_DOOR);
esp8266_send("door:0 ", rt_strlen("door:0 "));
}
else
{
num++;
rt_kprintf("fingerprint is not been stored!\n");
mg90s_ctrl(CLOSE_DOOR);
esp8266_send("door:0 ", rt_strlen("door:0 "));
led_crtl(2, 0);
rt_thread_mdelay(1000);
led_crtl(2, 1);
if (num == 10)
{
esp8266_send("warn:1\n", rt_strlen("warn:1\n"));
num = 0;
}
rt_kprintf("password wrong...\n");
}
}
}
3.2 AS608指纹识别模块相关代码
3.2.1 指令集
u8 cmd_packagehead[6] = {0xef, 0x01, 0xff, 0xff, 0xff, 0xff}; //发送数据包头
u8 cmd_handshake[10] = {0x01, 0x00, 0x07, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b}; //握手口令,应答包12字节
u8 cmd_readmodenum[6] = {0x01, 0x00, 0x03, 0x1d, 0x00, 0x21}; //读当前存储的模板个数,应答包14字节
u8 cmd_getimage[6] = {0x01, 0x00, 0x03, 0x01, 0x00, 0x05}; //采集原始手指图像,应答包12字节
u8 cmd_genfeature1[7] = {0x01, 0x00, 0x04, 0x02, 0x01, 0x00, 0x08}; //从图像生成特征并存入CharBuffer1,应答包12字节
u8 cmd_genfeature2[7] = {0x01, 0x00, 0x04, 0x02, 0x02, 0x00, 0x09}; //从图像生成特征并存入CharBuffer2,应答包12字节
u8 cmd_commode[6] = {0x01, 0x00, 0x03, 0x05, 0x00, 0x09}; //通过CharBuffer1 2 中的特征生成模板,应答包12字节
u8 cmd_storedata[9] = {0x01, 0x00, 0x06, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00}; //存储模板至指定位置,应答包12字节
u8 cmd_search[11] = {0x01, 0x00, 0x08, 0x04, 0x01, 0x00, 0x00, 0x03, 0xe7, 0x00, 0xf8}; //通过CharBuffer1中的特征搜索匹配指纹,应答包16字节
u8 cmd_readfingerprint[9] = {0x01, 0x00, 0x06, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00}; //从flash中读取指定id号的指纹模板数据存入CharBuffer1,应答包12字节
u8 cmd_deletemode[10] = {0x01, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}; //删除指定id号的指纹模板,[4][5]是pageId,[8][9]为校验和,应答包12字节
u8 cmd_deleteall[6] = {0x01, 0x00, 0x03, 0x0d, 0x00, 0x11}; //删除所有指纹模板,应答包12字节
u8 cmd_readmode[9] = {0x01, 0x00, 0x06, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00}; //读出指定pageId的模板到buf1,最后四位分别为页码与校验和,应答包12字节
u8 rec_buf[32] = {0}; //接收buf
3.2.2 执行相关指令发包函数
/*************************************************************
* name: as608_FingerImageGet
* function: 录入指纹原始图像
* input: 无
* return: 无
*************************************************************/
void as608_FingerImageGet()
{
while (1)
{
while (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_9) != 1) //判断是否有手指按上,有手指按上跳出循环继续执行
;
count = 0;
num = 12;
usart_send(UART4, cmd_packagehead, sizeof(cmd_packagehead)); //发送包头
usart_send(UART4, cmd_getimage, sizeof(cmd_getimage)); //发送命令
while (count != num)
{
rt_thread_mdelay(100);
}
if (rec_buf[9] != 0x00) //判断应答包确认码是否成功采集
{
continue;
}
break;
}
}
/*************************************************************
* name: as608_GetFeature
* function: 将指纹图像提取特征存于对应buf
* input: buf_num - buf号码,1或2
* return: 成功返回0,失败返回对应错码
*************************************************************/
int as608_GetFeature(int buf_num)
{
while (1)
{
count = 0;
num = 12;
usart_send(UART4, cmd_packagehead, sizeof(cmd_packagehead)); //发送包头
if (buf_num == 1)
{
usart_send(UART4, cmd_genfeature1, sizeof(cmd_genfeature1)); //发送命令
}
else if (buf_num == 2)
{
usart_send(UART4, cmd_genfeature2, sizeof(cmd_genfeature2)); //发送命令
}
else
{
return -1;
}
while (count != num)
{
rt_thread_mdelay(100);
}
if (rec_buf[9] == 0x00)
{
return 0;
}
else
{
return (int)rec_buf[9];
}
}
}
/*************************************************************
* name: as608_FingerprintStore
* function: 存储指纹特征模板
* input: 无
* return: 成功返回0,失败返回对应错码
*************************************************************/
int as608_FingerprintStore()
{
while (1)
{
u8 tmp[4] = {0};
int t_num = fingerprint_num;
//判断存储位置是否有模板,修正位置
while (1)
{
u8 t_tmp[4] = {0};
t_tmp[0] = t_tmp[0] | (u8)(t_num >> 8);
t_tmp[1] = t_tmp[1] | (u8)t_num;
int s = 0;
s = 0x01 + 0x06 + 0x07 + 0x01 + (int)t_tmp[0] + (int)t_tmp[1];
t_tmp[2] = 0x00 | (u8)(s >> 8);
t_tmp[3] = 0x00 | (u8)s;
for (int i = 0; i < 4; i++)
{
cmd_readfingerprint[5 + i] = t_tmp[i];
}
count = 0;
num = 12;
usart_send(UART4, cmd_packagehead, sizeof(cmd_packagehead)); //发送包头
usart_send(UART4, cmd_readfingerprint, sizeof(cmd_readfingerprint)); //发送命令
while (count != num)
{
rt_thread_mdelay(100);
}
if (rec_buf[9] == 0x00) //成功读出代表里面有模板
{
t_num++;
if (t_num == 1000)
{
t_num = 0;
}
if (t_num == fingerprint_num) //扫描完全部存储位置仍未找到空位
{
return -RT_ENOMEM;
}
}
else if (rec_buf[9] == 0x0c) //无法读出代表内部无模板
{
fingerprint_num = t_num;
break;
}
else
{
return -1;
}
}
//计算位置号
tmp[0] = tmp[0] | (u8)(fingerprint_num >> 8);
tmp[1] = tmp[1] | (u8)fingerprint_num;
//计算校验和
int sum = 0;
sum = 0x01 + 0x06 * 2 + 0x01 + (int)tmp[0] + (int)tmp[1];
tmp[2] = tmp[2] | (u8)(sum >> 8);
tmp[3] = tmp[3] | (u8)sum;
//修改发送命令包
int i = 0;
for (i = 0; i < 4; i++)
{
cmd_storedata[5 + i] &= 0x00; //清零最后四字节
cmd_storedata[5 + i] |= tmp[i]; //修改
}
count = 0;
num = 12;
usart_send(UART4, cmd_packagehead, sizeof(cmd_packagehead)); //发送包头
usart_send(UART4, cmd_storedata, sizeof(cmd_storedata)); //发送命令
while (count != num)
{
rt_thread_mdelay(100);
}
if (rec_buf[9] == 0x00) //存储完成返回0,修正指纹序号
{
fingerprint_num++;
if (fingerprint_num == 1000)
{
fingerprint_num = 0;
}
return 0;
}
else if (rec_buf[9] == 0x01)
{
continue;
}
else
{
return (int)rec_buf[9];
}
}
}
/*************************************************************
* name: as608_deleteall
* function: 删除所有as608指纹模板
* input: 无
* return: 成功返回0,失败返回-1
*************************************************************/
int as608_deleteall()
{
for (int i = 1; i < 10; i++)
{
count = 0;
num = 12;
usart_send(UART4, cmd_packagehead, sizeof(cmd_packagehead)); //发送包头
usart_send(UART4, cmd_deleteall, sizeof(cmd_deleteall)); //发送命令
while (count != num)
{
rt_thread_mdelay(100);
}
if (rec_buf[9] == 0x00)
{
return 0;
}
}
return -1;
}
/*************************************************************
* name: as608_delete
* function: 删除as608内指定的指纹模板
* input: num - 指纹模板序号(0-999)
* return: 成功返回0,失败返回-1
*************************************************************/
int as608_delete(int num)
{
int tmp[4] = {0};
//计算位置号
tmp[0] = tmp[0] | (u8)(num >> 8);
tmp[1] = tmp[1] | (u8)num;
//计算校验和
int sum = 0;
sum = 0x01 + 0x07 + 0x0c + 0x01 + (int)tmp[0] + (int)tmp[1];
tmp[2] = tmp[2] | (u8)(sum >> 8);
tmp[3] = tmp[3] | (u8)sum;
//修正命令
cmd_deletemode[4] = tmp[0];
cmd_deletemode[5] = tmp[1];
cmd_deletemode[8] = tmp[2];
cmd_deletemode[9] = tmp[3];
count = 0;
num = 12;
usart_send(UART4, cmd_packagehead, sizeof(cmd_packagehead)); //发送包头
usart_send(UART4, cmd_deletemode, sizeof(cmd_deletemode)); //发送命令
while (count != num)
{
rt_thread_mdelay(100);
}
if (rec_buf[9] == 0x00)
{
return 0;
}
else
{
return -1;
}
}
3.2.3 指纹采集流程
1.录入图像: PS_GetImage
功能: 探测手指他,探测到后录入指纹图像存于ImageBuffer,返回确认码;
发送指令包格式;
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x01
包长度:0x00 0x03
指令码:0x01
校验和:0x00 0x05
{0xef, 0x01, 0xff, 0xff, 0xff, 0xff}
{0x01, 0x00, 0x03, 0x01, 0x00, 0x05}
应答包格式:12字节
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x07
包长度:0x00 0x03
确认码:0x** (0x00录入成功 0x01收包有错 0x02无手指 0x03录入不成功)
校验和:0x** 0x**(sum)
2.生成特征: PS_GenChar
功能:将 ImageBuffer 中的原始图像生成指纹特征文件存于 CharBuffer1 或 CharBuffer2;
发送指令包格式;
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x01
包长度:0x00 0x04
指令码:0x02
缓冲区号:0x01(CharBuffer1) / 0x02(CharBuffer2)
校验和:0x00 0x08(CharBuffer1) / 0x09(CharBuffer2)
{0x01, 0x00, 0x04, 0x02, 0x01, 0x00, 0x08}
{0x01, 0x00, 0x04, 0x02, 0x02, 0x00, 0x09}
应答包格式;12字节
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x07
包长度:0x00 0x03
确认码:0x** (0x00生成特征成功 0x01收包有错 0x06指纹图像太乱生不成特征 0x07指纹图像正常,特征点太少生不成特征 0x15图像缓冲区内无有效原始图)
校验和:0x** 0x**(sum)
3.合并特征(生成模板): PS_RegModel
功能:将CharBuffer1与CharBuffer2中的特征文件合并成模板,结果存于CharBuffer1与CharBuffer2;
发送指令包格式;
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x01
包长度:0x00 0x03
指令码:0x05
校验和;0x00 0x09
{0x01, 0x00, 0x03, 0x05, 0x00, 0x09}
应答包格式:12字节
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x07
包长度:0x00 0x03
确认码:0x**(0x00合并成功 0x01收包有误 0x0a合并失败)
校验和:0x** 0x**(sum)
4.存储模板: PS_StoreChar
功能:将CharBuffer1与CharBuffer2中的模板文件存到PageID号flash数据库位置;
指令包格式:
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x01
包长度:0x00 0x06
指令码:0x06
缓冲区号:0x01 / 0x02
位置号:0x** 0x** (0-999自行转换)
校验和:(sum 2bytes 自行相加)
{0x01, 0x00, 0x06, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00}
应答包格式:12字节
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x07
包长度:0x00 0x03
确认码:0x**(0x00存储成功 0x01收包有误 0x0bPageID超出范围 0x18写flash出错)
校验和:0x** 0x**(sum)
相关代码如下:
/*************************************************************
* name: as608_FingerprintEntry
* function: 录入指纹
* input: 无
* return: 成功返回0,失败返回对应错码
*************************************************************/
int as608_FingerprintEntry()
{
//第一次采集指纹特征
while (1)
{
//录入图像
rt_kprintf("please put your finger on it.[1]\n");
as608_FingerImageGet();
//生成特征存于CharBuffer1
int ret = as608_GetFeature(1);
if (ret == 0)
{
break;
}
else if (ret == 0x01)
{
continue;
}
else
{
return ret;
}
}
rt_kprintf("please release your finger.\n");
while (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_9) == 1) //判断手指是否松开,松开跳出循环
;
//第二次采集指纹特征
while (1)
{
//录入图像
rt_kprintf("please put your finger on it.[2]\n");
as608_FingerImageGet();
//生成特征存于CharBuffer2
int ret = as608_GetFeature(2);
if (ret == 0)
{
break;
}
else if (ret == 0x01)
{
continue;
}
else
{
return ret;
}
}
//合并特征生成模板
while (1)
{
count = 0;
num = 12;
usart_send(UART4, cmd_packagehead, sizeof(cmd_packagehead)); //发送包头
usart_send(UART4, cmd_commode, sizeof(cmd_commode)); //发送命令
while (count != num)
{
rt_thread_mdelay(100);
}
if (rec_buf[9] != 0x00)
{
if (rec_buf[9] == 0x0a)
{
return 0x0a;
}
continue;
}
break;
}
//存储模板
while (1)
{
int ret = as608_FingerprintStore();
return ret;
}
}
3.2.4 检测指纹流程
1.录入图像: PS_GetImage
功能: 探测手指他,探测到后录入指纹图像存于ImageBuffer,返回确认码;
发送指令包格式;
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x01
包长度:0x00 0x03
指令码:0x01
校验和:0x00 0x05
应答包格式:
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x07
包长度:0x00 0x03
确认码:0x** (0x00录入成功 0x01收包有错 0x02无手指 0x03录入不成功)
校验和:0x** 0x**(sum)
2.生成特征: PS_GenChar
功能:将 ImageBuffer 中的原始图像生成指纹特征文件存于 CharBuffer1 或 CharBuffer2;
发送指令包格式;
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x01
包长度:0x00 0x04
指令码:0x02
缓冲区号:0x01(CharBuffer1) 0x02(CharBuffer2)
校验和:0x08(CharBuffer1) / 0x09(CharBuffer2)
应答包格式;
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x07
包长度:0x00 0x03
确认码:0x** (0x00生成特征成功 0x01收包有错 0x06指纹图像太乱生不成特征 0x07指纹图像正常,特征点太少生不成特征 0x15图像缓冲区内无有效原始图)
校验和:0x** 0x**(sum)
3.搜索指纹: PS_Search
功能:以 CharBuffer1 或 CharBuffer2 中的特征文件搜索整个或部分指纹库,搜索到则返回页码;
指令包:
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x01
包长度:0x00 0x08
指令码:0x04
缓冲区号:0x01 / 0x02
起始页码:0x00 0x00
搜索页数:0x03 0xe7(0~999)
校验和:0x00 0xf8 / 0xf9
{0x01, 0x00, 0x08, 0x04, 0x01, 0x00, 0x00, 0x03, 0xe7, 0x00, 0xf8}
应答包:
包头:0xef 0x01
芯片地址:0xff 0xff 0xff 0xff
包标识:0x07
包长度:0x00 0x07
确认码:0x**(0x00搜索到 0x01收包有误 0x09没搜索到)
页码:0x** 0x**
得分:0x** 0x**
校验和:0x** 0x**(sum)
相关代码如下:
/*************************************************************
* name: as608_FingerprintCheck
* function: 匹配指纹
* input: 无
* return: 成功匹配返回0,失败返回对应错码,0x09表示未搜索到
*************************************************************/
int as608_FingerprintCheck()
{
//指纹图像采集
while (1)
{
//录入图像
as608_FingerImageGet();
//生成特征存于CharBuffer1
int ret = as608_GetFeature(1);
if (ret == 0)
{
break;
}
else if (ret == 0x01)
{
continue;
}
else
{
return ret;
}
}
//查找buf1中的特征匹配指纹
while (1)
{
int cnt = 0;
count = 0;
num = 16;
usart_send(UART4, cmd_packagehead, sizeof(cmd_packagehead)); //发送包头
usart_send(UART4, cmd_search, sizeof(cmd_search)); //发送命令
while (count != num)
{
rt_thread_mdelay(100);
}
if (rec_buf[9] == 0x00) //查找到返回0
{
return 0;
}
else if (rec_buf[9] == 0x09) //未查找到返回错码
{
return 0x09;
}
else
{
cnt++;
if (cnt <= 10)
{
continue;
}
return 0x01;
}
}
}
3.2.5 相关执行线程函数
/*************************************************************
* name: thread_fingerscan_func
* function: 手指扫描线程执行函数
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_fingerscan_func(void *parameter)
{
int num_touch = 0;
while (1)
{
if (as608_FingerprintCheck() == 0)
{
num_touch = 0;
rt_kprintf("fingerprint has been founded!\n");
led_crtl(1, 0);
rt_kprintf("open the door...\n");
mg90s_ctrl(OPEN_DOOR);
esp8266_send("door:1 ", rt_strlen("door:1 "));
rt_thread_mdelay(1000);
led_crtl(1, 1);
rt_thread_mdelay(5000);
rt_kprintf("close the door...\n");
mg90s_ctrl(CLOSE_DOOR);
esp8266_send("door:0 ", rt_strlen("door:0 "));
}
else
{
num_touch++;
rt_kprintf("fingerprint is not been stored!\n");
mg90s_ctrl(CLOSE_DOOR);
esp8266_send("door:0 ", rt_strlen("door:0 "));
led_crtl(2, 0);
rt_thread_mdelay(1000);
led_crtl(2, 1);
if (num_touch == 10)
{
esp8266_send("warn:1\n", rt_strlen("warn:1\n"));
num_touch = 0;
}
}
rt_kprintf("please release your finger.\n");
while (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_9) == 1) //判断手指是否松开,松开跳出循环
;
}
}
/*************************************************************
* name: thread_fingerprintstore_func
* function: 指纹录入线程执行函数
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_fingerprintstore_func(void *parameter)
{
while (1)
{
rt_kprintf("storethread start...\n");
rt_sem_take(sem_sc2st, RT_WAITING_FOREVER);
rt_thread_suspend(finger_scan);
rt_kprintf("storing your finger...\n");
if (as608_FingerprintEntry() == 0)
{
rt_kprintf("fingerprint has been stored!\n");
led_crtl(1, 0);
rt_thread_mdelay(1000);
led_crtl(1, 1);
}
rt_thread_resume(finger_scan);
}
}
3.3 蓝牙模块相关代码
//串口中断服务程序
void UART5_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void UART5_IRQHandler(void)
{
// rt_kprintf("uart5irq...\n");
static int i = 0;
if (USART_GetITStatus(UART5, USART_IT_RXNE) == SET)
{
blue_recbuf[i++] = USART_ReceiveData(UART5);
if (blue_recbuf[0] != 0xef)
{
i = 0;
}
}
if (i == 3)
{
i = 0;
flag = 1;
rt_kprintf("flag has been set!\n");
}
}
/*************************************************************
* name: thread_bluetoothscan_func
* function: 蓝牙接收串口数据中断判定线程,若接收数据满3字节则
* 唤醒处理线程
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_bluetoothscan_func(void *parameter)
{
while (1)
{
if (flag == 1)
{
rt_kprintf("%d,%d,%d\n", blue_recbuf[0], blue_recbuf[1], blue_recbuf[2]);
flag = 0;
rt_kprintf("sem_bthrec release\n");
rt_sem_release(sem_bthrec);
}
rt_thread_mdelay(1000);
}
}
/*************************************************************
* name: thread_bluetoothrec_func
* function: 蓝牙接收中断处理线程
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_bluetoothrec_func(void *parameter)
{
u8 err[3] = {0xef, 0xee, 0x0a};
while (1)
{
rt_kprintf("bluetoothrec_start\n");
rt_sem_take(sem_bthrec, RT_WAITING_FOREVER);
rt_kprintf("bluetoothrec take\n");
if (blue_recbuf[0] != 0xef || blue_recbuf[2] != 0x0a)
{
rt_kprintf("NONE\n");
continue;
}
else if (blue_recbuf[0] == 0xef && blue_recbuf[2] == 0x0a)
{
rt_kprintf("y\n");
switch ((int)blue_recbuf[1])
{
case 0x01:
rt_sem_release(sem_sc2st); // 0x01使能指纹录入线程
break;
case 0x02: //删除所有指纹模板
if (as608_deleteall() == 0)
{
rt_kprintf("fingerprints in as608 has been all deleted!\n");
}
else
{
rt_kprintf("delete failed...\n");
for (int i = 0; i < 3; i++)
{
while (USART_GetFlagStatus(UART5, USART_FLAG_TXE) == RESET)
;
USART_SendData(UART5, err[i]);
}
}
break;
case 0x03:
rt_kprintf("close the door...\n");
mg90s_ctrl(CLOSE_DOOR);
esp8266_send("door:0 ", rt_strlen("door:0 "));
break;
case 0x04:
rt_kprintf("open the door...\n");
mg90s_ctrl(OPEN_DOOR);
esp8266_send("door:1 ", rt_strlen("door:1 "));
break;
case 0x05: //更改密码
rt_kprintf("release sem_keych...\n");
rt_sem_release(sem_keych);
break;
default:
rt_kprintf("bluetooth command error...\n");
for (int i = 0; i < 3; i++)
{
while (USART_GetFlagStatus(UART5, USART_FLAG_TXE) == RESET)
;
USART_SendData(UART5, err[i]);
}
break;
}
}
}
}
3.4 ESP8266-01 WiFi模块相关代码
3.4.1 发送函数
/*************************************************************
* name: esp8266_send
* function: esp8266发送函数,通过esp8266发送信息给服务器
* input: data - 字符型指针,指向发送的数据数组
* data_size - 发送数据长度
* return: 无
*************************************************************/
void esp8266_send(char *data, int data_size)
{
char cmd_send[32] = "AT+CIPSEND=";
char num[8] = {0};
int tmp = data_size;
int i = 0;
rt_kprintf("sending...\n");
if (data_size <= 0)
{
rt_kprintf("can't send <=0 data...\n");
return;
}
while (tmp != 0)
{
num[i] = (tmp % 10) + 48; //解析data_size转换成ascii
tmp /= 10;
i++;
}
tmp = i - 1; //定位最高位
//组包
i = 11;
for (; tmp >= 0; tmp--)
{
cmd_send[i++] = num[tmp];
}
cmd_send[i++] = '\r';
cmd_send[i++] = '\n';
cmd_send[i] = '\0';
int sum = rt_strlen(cmd_send);
for (i = 0; i < sum; i++)
{
while (USART_GetFlagStatus(UART6, USART_FLAG_TXE) == RESET)
;
USART_SendData(UART6, cmd_send[i]);
}
//rt_kprintf("%s\n", cmd_send);
rt_thread_mdelay(10);
//发送数据
for (i = 0; i < data_size; i++)
{
while (USART_GetFlagStatus(UART6, USART_FLAG_TXE) == RESET)
;
USART_SendData(UART6, data[i]);
}
rt_kprintf("send finished!\n");
}
3.4.2 相关接收处理线程
/*************************************************************
* name: thread_connectrecscan_func
* function: esp8266接收扫描线程
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_connectrecscan_func(void *parameter)
{
while (1)
{
if (rec_flag == 1)
{
connect_scan_flag = 1;
rt_thread_mdelay(2000);
if (connect_scan_flag == 1)
{
rt_kprintf("release sem_esprec...\n");
rt_sem_release(sem_esprec);
}
}
rt_thread_mdelay(1000);
}
}
/*************************************************************
* name: thread_connectdatadeal_func
* function: esp8266接收数据处理线程
* input: parameter - 线程创建时传递的参数
* return: 无
*************************************************************/
void thread_connectdatadeal_func(void *parameter)
{
while (1)
{
rt_kprintf("connectrecdeal start...\n");
rt_sem_take(sem_esprec, RT_WAITING_FOREVER);
rt_kprintf("sem_esprec take...\n");
int i = 0;
if (recbuf[0] == '\r' && recbuf[1] == '\n' && recbuf[2] == '+' && recbuf[3] == 'I' && recbuf[4] == 'P' && recbuf[5] == 'D' && recbuf[6] == ',')
{
//判断为接收了数据
i = 7;
char tmp[8] = {0};
char data[64] = {0};
int n = 0, j = 0, sum = 0;
//解析数据长度
while (recbuf[i] != ':')
{
tmp[i - 7] = recbuf[i];
i++;
n++;
}
for (j = 0; j < n; j++)
{
sum = sum * 10 + (int)tmp[j] - 48;
}
//接收数据
i++;
for (j = 0; j < sum; j++)
{
data[j] = recbuf[i];
i++;
}
data[j] = '\0'; //结尾加上\0方便比较
rt_kprintf("%s\n", data);
//对比命令
if (rt_strcmp(data, "open door") == 0)
{
mg90s_ctrl(OPEN_DOOR);
esp8266_send("door:1 ", rt_strlen("door:1 "));
}
else if (rt_strcmp(data, "close door") == 0)
{
mg90s_ctrl(CLOSE_DOOR);
esp8266_send("door:0 ", rt_strlen("door:0 "));
}
else if (rt_strcmp(data, "connected") == 0)
{
rt_kprintf("connected to the server!\n");
}
else if (rt_strcmp(data, "clear all fingerprints") == 0)
{
if (as608_deleteall() == 0)
{
rt_kprintf("all fingerprints has been deleted!\n");
}
else
{
rt_kprintf("can't delete...\n");
}
}
else if (rt_strcmp(data, "record fingerprint") == 0)
{
rt_sem_release(sem_sc2st);
}
else if (rt_strcmp(data, "door:1 ") == 0)
{
mg90s_ctrl(OPEN_DOOR);
}
else if (rt_strcmp(data, "door:0 ") == 0)
{
mg90s_ctrl(CLOSE_DOOR);
}
else if (rt_strcmp(data, "change password") == 0)
{
rt_kprintf("release sem_keych...\n");
rt_sem_release(sem_keych);
}
else if (data[0] == 'p' && data[1] == 'a' && data[2] == 's' && data[3] == 's' && data[4] == 'w' && data[5] == 'o' && data[6] == 'r' && data[7] == 'd')
{
for (i = 0; i < 6; i++)
{
key_data[i] = data[i + 9];
}
key_data[7] = 0;
}
}
else
{
//rt_kprintf("%s\n", recbuf);
}
rec_flag = 0;
cnt = 0;
for (i = 0; i < 128; i++)
{
recbuf[i] = 0;
}
}
}
四、github仓库
rt_kprintf("all fingerprints has been deleted!\n");
}
else
{
rt_kprintf("can't delete...\n");
}
}
else if (rt_strcmp(data, "record fingerprint") == 0)
{
rt_sem_release(sem_sc2st);
}
else if (rt_strcmp(data, "door:1 ") == 0)
{
mg90s_ctrl(OPEN_DOOR);
}
else if (rt_strcmp(data, "door:0 ") == 0)
{
mg90s_ctrl(CLOSE_DOOR);
}
else if (rt_strcmp(data, "change password") == 0)
{
rt_kprintf("release sem_keych...\n");
rt_sem_release(sem_keych);
}
else if (data[0] == 'p' && data[1] == 'a' && data[2] == 's' && data[3] == 's' && data[4] == 'w' && data[5] == 'o' && data[6] == 'r' && data[7] == 'd')
{
for (i = 0; i < 6; i++)
{
key_data[i] = data[i + 9];
}
key_data[7] = 0;
}
}
else
{
//rt_kprintf("%s\n", recbuf);
}
rec_flag = 0;
cnt = 0;
for (i = 0; i < 128; i++)
{
recbuf[i] = 0;
}
}
}
# 四、github仓库
所有代码及核心板PCB设计位于:https://github.com/lcready/zhinengshuo