RISC-V MCU 基于CH32V307的智能门锁设计

一、设计概述

1.1 设计目的

随着物联网、大数据等技术的快速发展,智能门锁应用广泛。但是传统智能门锁还存在着许多的问题,例如,能被特斯拉线圈开锁,机械强度不够容易被暴力强制开锁,忘记带钥匙,等等。这些问题给用户带来了巨大的隐患,同时也限制了智能门锁的市场发展,功能的不足也会影响门锁的便捷性。在此背景之下,打造一把安全的多功能密码锁,具有重大的现实意义,我们想到了一种多方位控制的智能锁系统的设计,极大减少了传统锁具中存在的不便性且保密性更强,更结合了蓝牙、服务器等方式实现了多方式、多途径控制。

1.2 设计主要功能

  1. 采用按键密码锁的方式进行解锁,它可以设计任意六位数作为解锁密码,极大的增加了安全性、可靠性;
  2. 增加了指纹解锁的方式,优化了密码锁结构,也减轻了用户开锁的困难;
  3. 通过蓝牙、服务器的方式远程监控其状态,并可以实现远程控制,给人们生活带来极大的便捷

二、系统设计组成

2.1 总体框图

毕设java智能门锁系统 智能门锁硬件设计方案_mcu

2.2 各模块介绍

  1. 门锁控制方式模块

毕设java智能门锁系统 智能门锁硬件设计方案_单片机_02


分为矩阵键盘模块、指纹模块与显示Led。 矩阵键盘原理图如下:

毕设java智能门锁系统 智能门锁硬件设计方案_毕设java智能门锁系统_03


矩阵键盘实物图如下:

毕设java智能门锁系统 智能门锁硬件设计方案_risc-v_04


本设计采用外部中断方式实现矩阵键盘扫描。

AS608指纹模块实物图如下:

毕设java智能门锁系统 智能门锁硬件设计方案_单片机_05


AS608指纹模块相关接线如下:

毕设java智能门锁系统 智能门锁硬件设计方案_毕设java智能门锁系统_06


AS608采用串口通信,本设计采用UART4作为通信串口。

  1. 远程控制模块
    蓝牙模块:
    实物图
  2. 毕设java智能门锁系统 智能门锁硬件设计方案_毕设java智能门锁系统_07

  3. 引脚及指令集
  4. 毕设java智能门锁系统 智能门锁硬件设计方案_嵌入式硬件_08

    毕设java智能门锁系统 智能门锁硬件设计方案_嵌入式硬件_09

  5. 蓝牙为串口通信,本设计采用UART5作为通信串口。
    esp8266 WiFi模块:
    接线示意:
  6. 毕设java智能门锁系统 智能门锁硬件设计方案_嵌入式硬件_10

  7. 相关指令集参考:

    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