基于STM32的智能门锁/门禁系统多功能设计解析

(蓝牙、AS608指纹、RFID刷卡PN532、键盘、LCD、界面友好)单片机嵌入式开发(项目开源)

程序链接:智能锁-作者:STM时
提取码:1234 
–来自百度网盘超级会员V5的分享

B站视频:基于STM32的智能锁(蓝牙、指纹、RFID刷卡磁卡、LCD显示)

工具包、原理图、PCB及其他文件我整理好后就发上来…

前言

  • 本次设计是基于STM32F103C8T6(以下C8T6等同)开发的智能锁,支持多种方式对系统进行操作:蓝牙、指纹、RFID刷卡、4×4键盘输入,拥有友好的蓝牙收发界面和LCD交互界面。
  • 蓝牙:作为总系统的管理员,有主管理和次管理,主管理只能有一个,副管理员可以有多个。主管理员拥有全部权限(包括修改管理员数据);而副管理员不能修改管理员数据,但可以修改指纹、RFID刷卡、键盘密码的数据。蓝牙交互界面提供了多种选项,同时兼顾ID密码登数据发送和指令发送,功能齐全,蓝牙界面通过管理员账号登录。
  • 指纹:在锁屏界面下可以通过验证指纹来解锁智能锁,可以通过主/副管理员来添加指纹和删除指纹。
  • RFID刷卡:在锁屏界面下可以通过验证ID/IC卡来进行智能锁解锁,可以通过主/副管理员来添加ID/IC卡和删除ID/IC卡。
  • 4×4键盘:4×4键盘既可以作为系统的选项按钮,也可以作为ID和密码输入的按钮,在锁屏界面可以选择进入键盘输入功能,进行键盘用户ID和密码认证,认证通过后开启智能锁。
  • LCD:LCD提供了友好的交互界面,可以通过4×4键盘来进行功能选择。
  • 收发协议:蓝牙、指纹、RFID刷卡使用USART(刚好用完C8T6的三个USART串口),LCD使用SPI(如果你用的是STM32F4或其他的系列,那么你需要对LCD的代码驱动进行修改,因为F1和F4的SPI代码程序不一样),4×4键盘使用推挽输出和上拉输入(4×4扫描,必是一边输出、一边输入)。
  •  模块介绍

    序号

    模块

    型号规格

    数量

    1

    微控制器

    STM32F103C8T6

    1

    2

    指纹

    AS608

    1

    3

    RFID射频读卡模块

    PN532 NFC RFID V3

    1

    4

    蓝牙

    汇承HC-08

    1

    5

    LCD显示

    TFT-ST7735S

    1

    6

    电机驱动

    两路直流双H桥L298N

    1

    7

    按键

    6*6*4.3轻触按键

    16

    8

    下载器

    ST-LINK V2

    1

    9

    电源

    DC12-3.3/5/7V

    1

    10

    电池

    DC12-2800mAh

    1

    STM32F103C8T6

    提醒:我们经常使用的STM32F103C8T6,有一些是用兼容芯片做的,虽然99%的功能都差不多,但是这次开发我遇到了那1%,我试了几个不同店铺的STM32F103C8T6,发现很多都不能正常使用AS608指纹模块(个人猜测可能是因为AS608识别不到指令…),因为在USART串口发送时,总是会有那么一点错误,我用蓝牙模块对串口进行了测试,发现第一次发送数据总是会带有一小点乱码。

    我买的店铺是touglesy,不是说其他的店铺不好(我买东西一般不会拘泥于一家店铺,其他模块也会换换家买,毕竟每个店铺都有自己的干货),这次是无奈了,而是我又不知道哪些能用,撞了很多次脚才发现问题所在,然后AS608要求的串口稳定性也太高(如果是大板子,那么不用担心这个,我用STM32103ZET6和STM32F407ZGT6大板子都可以驱动AS608),希望建议有帮助。
    (甚至有可能是AS608的问题,仅供参考)

    蓝牙 

    蓝牙我买的是汇承的HC-08,主从一体,比较方便,使用的时候拿官方的蓝牙测试架设置为从机就行了,官方网站可以下载蓝牙模块使用手册、上位机调试、手机蓝牙助手APP。(当然我也会打包到分享资料中)

    AS608

    我买的是光学AS608,支持USB D- D+、USART传输模式,我这里走的是USART,没什么好说的,直接上图。

    RFID射频读卡模块

    RFID射频刷卡模块有很多种型号,我这里买的是PN532 NFC RFID V3,支持多种类型的卡(根据自身需求,这个可以自己去商家哪里查,买其他的比如522也可以的)。支持I2C、SPI、高速USART多种传输模式,我这里走的是USART。配套的应该会送两张卡,试过用校园卡也能刷,没什么好说的,直接上图。

    LCD显示

    LCD屏幕我买的1.8寸 TFT-ST7735S,正面管脚顺序G,V,SCL,SDA,RST,DC,CS,BLK(具体买什么型号看需求吧)

    电源

    电源我买的是12V转3.3V、5V、12V,实际只需要3.3V和5V的并没有使用,实际看个人需求。

    电池

    电池我用的是12V,2800mAh,DC公母头锂电池,实际看个人需求。(图片来自淘宝商家,侵权删)

    其他模块

    按键:按键买什么都行,看个人需求。

    电机驱动和电机:这个都行,我用的是两路直流双H桥L298N,加一个减速电机。(视频没有演示电机部分,程序里写有电机驱动,本次设计只介绍系统设计而不介绍电机)
    下载器(下载方式):我用的是ST LINK V2下载器,闪存flash烧录,实际看个人习惯。

    程序设计部分

    ID、密码和卡号

    本次设计中,如果仅用程序模拟数据的方式,则机器掉电后将会使数据丢失。
    所以我的设计思路是,将蓝牙管理员、键盘使用者、指纹ID、卡号全部保存近闪存中。

    在验证身份时,调用闪存中的数据进行认证。

     结构体

     定义结构体,方便管理ID、密码和卡号:

    struct CODE_STU//自由ID
    {
        int ID;
        char CODE[CODE_LEN];
    };
    
    extern struct CODE_STU USER_CODE[CN];//用户密码,用于按键
    extern struct CODE_STU MASTER_CODE[CN];//管理员密码,用于蓝牙
    
    struct RFID_STU//自动分配ID号
    {
        int ID;
        uint8_t CODE[RFID_LEN];
    };
    extern u16 FR_ID[300];//指纹ID,指纹只需要ID,就不定义结构体了
    extern struct RFID_STU USER_RFID[CN];//卡号

    闪存的读写 

     编写程序,用来闪存读写:

    void flash_write(uint32_t address, void *data, uint32_t size)//闪存写
    {
        uint32_t *flash_ptr = (uint32_t *)data;
        FLASH_Status flash_status = FLASH_COMPLETE;
        
        FLASH_Unlock(); // 解锁Flash
        
        // 擦除扇区
        FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 清除Flash标志位
        flash_status = FLASH_ErasePage(address);
        if (flash_status != FLASH_COMPLETE)
        {
            // 擦除失败处理
        }
        
        // 写入数据
        for (uint32_t i = 0; i < size / 4; i++)
        {
            flash_status = FLASH_ProgramWord(address, *flash_ptr);
            if (flash_status != FLASH_COMPLETE)
            {
                // 写入失败处理
            }
            
            address += 4;
            flash_ptr++;
        }
        FLASH_Lock(); // 锁定Flash
    }
    
    void flash_read(uint32_t address, void *data, uint32_t size)//闪存读
    {
        uint32_t *flash_ptr = (uint32_t *)data;
        
        for (uint32_t i = 0; i < size / 4; i++)
        {
            *flash_ptr = *(__IO uint32_t*)address;
            address += 4;
            flash_ptr++;
        }
    }

    定义闪存地址,用来分配蓝牙管理员、键盘使用者、指纹ID、卡号的闪存地址:

    #define VAR_FLASH_ADDR ((uint32_t)0x08014000) // 第一管理员ID,用来记录主管理员的ID号
    #define MASTER_FLASH_ADDR ((uint32_t)0x08015000) // 管理员数据存储地址
    #define USER_FLASH_ADDR ((uint32_t)0x08017000) // 用户数据存储地址C
    #define FR_FLASHID_ADDR ((uint32_t)0x08018000) // 指纹ID
    #define RFID_FLASHID_ADDR ((uint32_t)0x08019000) // 刷卡ID索引
    #define RFID_FLASH_ADDR ((uint32_t)0x0801B000) // 卡号

    函数的用法:

    //以蓝牙管理员和键盘使用者为例:
    
    //要寻找时
    flash_read(VAR_FLASH_ADDR, &first_masterID, sizeof(first_masterID));//第一管理员ID
    flash_read(USER_FLASH_ADDR, USER_CODE, sizeof(USER_CODE));//使用者
    flash_read(MASTER_FLASH_ADDR, MASTER_CODE, sizeof(MASTER_CODE));//管理员
    /*
    一系列查找结构体成员的程序
    */
    
    //要更新结构体时
    /*
    一系列对结构体修改的程序
    */
    flash_write(VAR_FLASH_ADDR, &first_masterID, sizeof(first_masterID));//第一管理员ID
    flash_write(USER_FLASH_ADDR, USER_CODE, sizeof(USER_CODE));//使用者
    flash_write(MASTER_FLASH_ADDR, MASTER_CODE, sizeof(MASTER_CODE));//管理员

    LCD程序设计

    程序所需文件:
    Lcd_Driver.c、Lcd_Driver.h、LCD_Config.h、GUI.c、GUI.h 、Font.h
    Lcd_Driver.c初始化驱动,Lcd_Driver.h管脚宏定义,LCD_Config.h最大分辨率,GUI.c显示函数,GUI.h显示函数定义,Font.h字符模编码

    LCD的程序直接用商家提供的就行了,不过商家提供的不能直接显示数字,需要用C函数转换为字符类型。(或者自己设计也行)

    int i=0;
    char LCD_id[4];
    sprintf(LCD_id,"%d",i);//i转换为LCD_id字符串

    然后讲一下LCD显示函数的使用,如下所示,Gui_DrawFont_GBK16和24为商家提供,32是我自己魔改出来的,也能使用。*s必须是u8[ ]类型、字符串char[ ]类型或者直接使用 "锁屏" 。

    Gui_DrawFont_GBK16(u16 x, u16 y, u16 fc, u16 bc, u8 *s);
    Gui_DrawFont_GBK24(u16 x, u16 y, u16 fc, u16 bc, u8 *s);
    Gui_DrawFont_GBK32(u16 x, u16 y, u16 fc, u16 bc, u8 *s);
    //Gui_DrawFont_GBK16后面的数字表示16x16显示
    //x表示行分辨率,y表示列分辨率,fc表示颜色,bc表示背光灰度
    //*s能使用的类型:char CODE[100] 、uint8_t CODE[100]和"锁屏界面"都可以用
    //例如:Gui_DrawFont_GBK16(28,0,BLUE,GRAY0,"锁屏界面");
    //表示在28,0的地方开始显示“锁屏界面”,字体颜色为蓝色,背景灰度为白色,若GRAY1则颜色偏灰
    /*又例如:
    int i=15;
    char LCD_id[4];
    sprintf(LCD_id,"%d",i);//i转换为LCD_id字符串
    Gui_DrawFont_GBK16(28,0,BLUE,GRAY0,LCD_id);
    这样就可以显示15
    */
    /*
    #define RED  	0xf800  //红色
    #define GREEN	0x07e0  //绿色
    #define BLUE 	0x001f  //蓝色
    #define WHITE	0xffff  //白色
    #define BLACK	0x0000  //黑色
    #define PINK 0xFFC011   //粉色
    #define YELLOW  0xFFE0  //黄色
    #define GRAY0   0xEF7D  //灰色0 3165 00110 001011 00101
    #define GRAY1   0x8410  //灰色1      00000 000000 00000
    #define GRAY2   0x4208  //灰色2  1111111111011111
    */

    特别说明:该函数不能直接使用中文,若要使用中文,则需要使用LCD商家提供的文字取模工具导出中文编码(配套我会全部放到资料中),代码示例如下(中文编码在front.h中)。

    这是16×16的编码存放结构体,hz16_num代表文字数量,若超出200,则需要修改,其他数值不用动。
    如果是24×24,则需要找另一个结构体,也在front.h中。
    中文的编码存放方式如下,如果要增加新的中文字符,则使用文字取模工具取码。

    文字取模示例:实际用法放在资料包里,下面放一张图

    LCD模块特别说明:
    如果是STM32F103,则不需要修改程序,如果是STM32F4系列,则需要进行细微的修改,F4和F1的SPI传输方式不一样,F4需要修改SPI发送函数,当然F4使用LCD的程序我也放在了资料包里。

    蓝牙程序设计

    程序所需文件:
    usart2_bluetooth.c蓝牙初始化+蓝牙程序设计

    usart2_bluetooth.h蓝牙收发缓冲定义和函数定义

    蓝牙测试

    蓝牙首先需要自己用官方的工具蓝牙测试架进行测试和参数调整,我这里是波特率115200,从机模式,名称也可以通过蓝牙测试架修改。
    注:在测试蓝牙串口时,同样需要测试MCU单片机的串口(使用TTL转USB就行了),测试单片机的方法不多讲,教程很多了。

    蓝牙程序

    测试好MCU的串口USART和蓝牙的串口USART之后,就可以让MCU和蓝牙模块进行通信了。
    手机给蓝牙发送数据之后,数据会通过串口直接发给MCU,然后程序对其进行处理。
    蓝牙模块各函数的意义:

    //以下函数存于usart2_bluetooth.c文件
    void usart2_init(u32 bound2);//串口2初始化
    /*
    用法:
    usart2_init(115200);//115200设置波特率
    */
    u8 bluetooth(void);
    /*
    用法:
    蓝牙功能界面,直接进入就行。bluetooth();
    */
    void u2_printf(char* fmt, ...);//发送数据用的函数
    /*
    用法:
    蓝牙发送函数,发送数据类型与上面说的LCD类似
    一、
    u2_printf("%s","蓝牙界面\n");
    二、
    int i=15;
    char LCD_id[4];
    sprintf(LCD_id,"%d",i);//i转换为LCD_id字符串
    u2_printf("%s",LCD_id);//发送15
    */
    u8 add_blue_code(struct CODE_STU* code);//蓝牙添加管理员/键盘用户函数
    u8 delete_blue_code(struct CODE_STU* code);//蓝牙删除管理员/键盘用户函数(也可转让主管理员)
    u8 errror_blue_code(struct CODE_STU* code);//蓝牙验证ID密码函数
    /*
    用法:
    add_blue_code(MASTER_CODE);//MASTER_CODE为管理员的结构体
    add_blue_code(USER_CODE);//USER_CODE为键盘密码的结构体
    三个函数都是这么调用
    关于结构体,后面会介绍
    */
    void USART2_SendData(USART_TypeDef* USARTx, uint16_t Data);
    void send2HexData(uint8_t data[], uint16_t length);
    /*
    用法:
    这两句是十六进制串口发送函数,蓝牙模块暂时用不到
    uint8_t data[] = {0x11, 0xAB, 0xFE, 0x42};
    send2HexData(data, sizeof(data)/sizeof(data[0]));
    */
    int isUART2Idle(void);
    /*
    用法:
    判断串口是否为空闲状态
    配合RX2K来判断数据是否接收完毕
    if(RX2K == 1 && isUART2Idle())//判断是否收到数据,是否接收完毕
    {
      //程序.....
      //末尾必须放这三句清空状态
    RX2K=0;
    rxs2_index=0;
    memset(RX2DATA, 0, sizeof(RX2DATA));
    }
    */
    void USART2_IRQHandler(void)
    /*
    用法:
    中断函数,根据中断线触发,不需要调用
    蓝牙接收中断,收到数据自动将数据保存到缓存USART2_RX_BUF和RX2DATA中
    USART2_RX_BUF为总缓存,RX2DATA为单次缓存
    每次接收数据,处理完这批数据之后,都需要运行下面三句代码删除数据
    不然下次收到数据会保留有上一次的数据
    RX2K=0;
    rxs2_index=0;
    memset(RX2DATA, 0, sizeof(RX2DATA));
    */

    蓝牙界面开发

    首先,蓝牙模块只能是收或者发,并没有界面一说(喜欢折腾的小伙伴可以自行开发一个APP),我这里是基于手机蓝牙助手的收发界面来做个人定制。
    既然没有界面,那就用收发来做界面。
    界面:蓝牙给手机发数据来当做类似界面的东西,如下图(或者视频演示)
    功能键和ID密码输入:手机给蓝牙发数据来当做功能键或者ID密码


    使用APP自带的自定义按钮定义%A到%F作为功能键(这些功能键的功能与LCD+键盘交互界面一致,也可以通过键盘来选择功能)

    蓝牙界面开发代码程序框架:
    实际程序设计看我的程序资源包或者自行定制

    //这里举例一个简单的框架
    //LCD界面
    Gui_DrawFont_GBK16(0,20,RED,GRAY0,"A:功能一");
    Gui_DrawFont_GBK16(0,40,RED,GRAY0,"B:功能二");
    Gui_DrawFont_GBK16(0,60,RED,GRAY0,"C:功能三");
    //蓝牙界面
    u2_printf("%s","%A功能一\n");
    delay_ms(100);
    u2_printf("%s","%B功能二\n");
    delay_ms(100);
    u2_printf("%s","%C功能三\n");
    delay_ms(100);
    while (1)
       {
    			key4_4_Scan();//键盘值读取
    /功能选择行///
                //一级标题
    			if (keyv == 4 || memcmp("%A",RX2DATA,4)==0)//键盘A或者蓝牙%A
    			{ //功能一
    			    RX2K = 0;
    				rxs2_index = 0;
    				memset(RX2DATA, 0, sizeof(RX2DATA));
    			}
    			//一级标题
    			else if (keyv == 8 | memcmp("%B",RX2DATA,4)==0)//键盘B或者蓝牙%B
    			{ //功能二
    			    RX2K = 0;
    				rxs2_index = 0;
    				memset(RX2DATA, 0, sizeof(RX2DATA));
    			}
    			//一级标题
    			else if(keyv == 12 || memcmp("%C",RX2DATA,4)==0)//键盘C或者蓝牙%C
    			{  //功能三
    			    RX2K = 0;
    				rxs2_index = 0;
    				memset(RX2DATA, 0, sizeof(RX2DATA));
    			}
                //其他更多功能.........
                //....................
    /数据收发行
    			//一级标题				
    			else if(RX2K == 1 && isUART2Idle())//既判断已经发送,又判断发送完成
    			{
    					RX2K = 0;
    					//二级标题					
    					if (IDorMI == 0)
    					{   
    						//对蓝牙发送的ID号进行临时保存
                            //ID号一定是临时保存,不能直接用RX2DATA
                            //因为每次进入if都需要清除RX2DATA(前面说的那三句代码)
                            //memset(RX2DATA, 0, sizeof(RX2DATA));
                            //这样避免数据冲突
    					}
    					else if (IDorMI == 1)
    					{ 
    					    //对蓝牙发送的密码进行临时保存
                            //密码一定是临时保存,不能直接用RX2DATA
                            //因为每次进入if都需要清除RX2DATA(前面说的那三句代码)
                            //memset(RX2DATA, 0, sizeof(RX2DATA));
                            //这样避免数据冲突
    					}
                        rxs2_index = 0;
    					memset(RX2DATA, 0, sizeof(RX2DATA));//清除RX2DATA
    			}
                //一级标题
    			else if(RX2K == 1 && isUART2Idle())//发送无关的数据则不进行处理 
    	  {
    	     RX2K=0;
    		 rxs2_index=0;
    		 memset(RX2DATA, 0, sizeof(RX2DATA));
    	  }
      }
    }

    4×4键盘程序设计

    注意:4×4键盘使用键盘扫描的方式,一边用上拉输入,一边用推挽输出,但不是所有的管脚都有上拉输入和推挽输出的功能,有些管脚无法上拉输入,有些管脚无法推挽输出。
    无法上拉的情况表现为:无论你管脚输入啥,管脚状态都读取到0
    无法推挽的情况表现为:管脚输出状态单一,可能只能输出1或者只能输出0

    特别说明:
    上拉输入为,MCU单片机自己设置了一个上拉电阻,在没有任何输入的情况下管脚状态为1,当管脚输入0(接地)时,管脚状态读取到0,实现扫描读取按键的目的。
    推挽输出为,MCU单片机可以根据程序来决定管脚输出0还是输出1。
    这里说下我用的管脚:
    X1     X2     X3     X4
    PA11 PA12 PA15 PB3
    Y1   Y2    Y3   Y4

    PA0 PB5 PB6 PB7

         //X为上拉输入,Y为推挽输出(管脚定义)
        /*
        X1 X2 X3 X4    ————————-
        ↑    ↑    ↑    ↑                                   |
        ——————                                 |
        1    2   3    A  | ← Y1                       |
        4    5   6    B  | ← Y2  ———–MCU单片机
        7    8   9    C  | ← Y3
         *    0   #    D  | ← Y4
        */

    4×4键盘扫描原理,首先我们的按键是有两头的,以下称为左边和右边。

    方便理解:
    单个按键的扫描方式是,MCU<—-按键—–GND
    左边接MCU,右边接一个地,当按键按下,就相当于MCU上拉输入端接GND,该管脚状态变为0
    然后键盘扫描的方式是单个按键扫描的改良版
    每个按键的左边接X,右边接Y,根据按键的排/列号来确定接的是X几/Y几,原理图如下。
    Y端从MCU循环输出0来模拟单个按键的右边GND状态。
    X端给MCU给MCU读取状态,按下哪个X就是哪个X输入0。
    例如,Y1输出0,X1按下,则为按键1
    Y输出循环:
    Y1   Y2    Y3   Y4
    0      1      1     1     row=0 
    1      0      1     1     row=1
    1      1      0     1     row=2
    1      1      1     0     row=3
    使用for循环for (row = 0; row < 4; row++)来定位Y的输出位置。
    使用管脚状态查询来获取X的位置,当某个按键按下,GPIO_ReadInputDataBit(X_KEYα_PORT,X_KEYα) α为1、2、3、4
    X1   X2    X3   X4
    0      1      1     1     col=1 
    1      0      1     1     col=2
    1      1      0     1     col=3
    1      1      1     0     col=4
    读取得到按键的值keyv=(row * 4 + col)(1到16)


    程序使用方法:

    键盘交互界面程序

    void key4_4_Init(void);//4x4键盘初始化程序
    void key4_4_Scan(void);
    //在while循环里使用key4_4_Scan();就可以更新键盘状态
    //键盘值keyv持续更新
    //例:
    //下面这段程序可以用于界面开发,功能选项
    /*
    key4_4_Init();
    Gui_DrawFont_GBK16(0,20,RED,GRAY0,"A:功能一");
    Gui_DrawFont_GBK16(0,40,RED,GRAY0,"B:功能二");
    Gui_DrawFont_GBK16(0,60,RED,GRAY0,"C:功能三");
    Gui_DrawFont_GBK16(0,80,RED,GRAY0,"D:功能四");
    while(1)
    {
    	key4_4_Scan();
    	if(keyv==4)//A键
    		{ 
               //程序 }
    	if(keyv==8)//B键
    		{  
               //程序 }
    	if(keyv==12)//C键
    		{ 
               //程序 }
    	if(keyv==16)//D键
    		{ 
                //程序 }
    }
    */

    指纹程序设计

    程序所需文件:
    as608.c,as608.h,usart1_fr.c,usart1_fr.h

    测试指纹模块

    准备一个TTL转USB(用于串口USART转成USB,然后用电脑上位机进行串口测试指纹模块),使用配套资料里的指纹上位机进行指纹模块功能测试。
    注:在测试指纹串口时,同样需要测试MCU单片机的串口(使用TTL转USB就行了),测试单片机的方法不多讲,教程很多了。

    上位机程序:

    上位机连接:

    指纹录入测试:

    指纹对比测试:

    测试完指纹模块没问题就可以进行程序设计了

    程序说明部分

    指纹程序设计可以直接参考ALIENTEK给出的函数(仅供学习,请勿用于其他用途)
    ALIENTEK的程序存放在as608.c和as608.h中
    或者可以自己查找AS608指纹模块的数据手册,里面有指令,根据指令,可以自行完成指纹模块功能函数的编写。

    //ALIENTEK给出的函数
    void PS_StaGPIO_Init(void);//初始化PA6读状态引脚
    u8 PS_GetImage(void); //录入图像 
    u8 PS_GenChar(u8 BufferID);//生成特征 
    u8 PS_Match(void);//精确比对两枚指纹特征 
    u8 PS_Search(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p);//搜索指纹 
    u8 PS_RegModel(void);//合并特征(生成模板) 
    u8 PS_StoreChar(u8 BufferID,u16 PageID);//储存模板 
    u8 PS_DeletChar(u16 PageID,u16 N);//删除模板 
    u8 PS_Empty(void);//清空指纹库 
    u8 PS_WriteReg(u8 RegNum,u8 DATA);//写系统寄存器 
    u8 PS_ReadSysPara(SysPara *p); //读系统基本参数 
    u8 PS_SetAddr(u32 addr);  //设置模块地址 
    u8 PS_WriteNotepad(u8 NotePageNum,u8 *content);//写记事本 
    u8 PS_ReadNotepad(u8 NotePageNum,u8 *note);//读记事 
    u8 PS_HighSpeedSearch(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p);//高速搜索  
    u8 PS_ValidTempleteNum(u16 *ValidN);//读有效模板个数 
    u8 PS_HandShake(u32 *PS_Addr); //与AS608模块握手

    有了ALIENTEK的函数,我们就可以自行定制AS608指纹模块的功能了,比如指纹录入、刷指纹对比、指纹删除等操作。
    但只有as608.c和as608.h我们是不能直接调用的,我们还得使MCU单片机和as608模块建立通信,以便MCU单片机通过USART或USB将指令传输给as608。(类似的操作同样可以在ALIENTEK的教程里面找),我们可以根据需求进行修改,定制出适合自己方案的功能。

    我这里是根据需求定义了三个函数,添加指纹、刷指纹、删除指纹(操作见于视频)
    程序具体实现自行下载我的程序资源包,在usart1_fr.c和usart1_fr.h里面
    有串口初始化、添加指纹、刷指纹、删除指纹的函数
    满足这几个功能,需要的ALIENTEK函数有:

    u8 PS_ValidTempleteNum(u16 *ValidN);//读有效模板个数,三个函数都用到 
    //添加指纹函数void Add_FR(void);
    u8 PS_GetImage(void); //录入图像 
    u8 PS_GenChar(u8 BufferID);//生成特征 
    u8 PS_Match(void);//精确比对两枚指纹特征
    u8 PS_RegModel(void);//合并特征(生成模板)
    u8 PS_StoreChar(u8 BufferID,u16 PageID);//储存模板 
    //刷指纹void press_FR(void);
    u8 PS_GetImage(void); //录入图像 
    u8 PS_GenChar(u8 BufferID);//生成特征
    u8 PS_HighSpeedSearch(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p);//高速搜索
    //删除指纹void Del_FR(void);
    u8 PS_DeletChar(u16 PageID,u16 N);//删除单个模板 
    u8 PS_Empty(void);//清空指纹库

    这里我使用c语言对每个添加进来的新指纹进行ID分配,ID保存到u16 FR_ID[300];中,指纹添加时,自动分配到空的ID上,比如现有ID:1,3,4,则新指纹自动分配ID:2
    u16 FR_ID[300];掉电后数据不会丢失。
    每次添加新指纹自动更新u16 FR_ID[300];到STM32的闪存中。

    RFID刷卡程序设计

    程序所需文件:
    usart3_rfid.c和usart3_rfid.h

    测试PN532刷卡模块

    与指纹模块的测试类似,拿一个TTL转USB就能使用电脑进行测试了,不过这里使用的上位机与指纹的上位机不一样,这里要使用串口助手sscom进行测试

    串口连接上电脑后,使用PN532的指令代码对PN532模块进行测试
    具体的指令可以查PN532的数据手册,也可以跳转下面这篇文章,讲得比较好。
    NFC芯片–PN532的使用
    上位机测试:

    PN532程序设计思路

    本次设计我只使用了PN532激活指令和寻卡指令
    激活指令是必须要发送的,寻卡指令我用于开启寻卡状态并获取读取到的卡号
    本次智能锁设计仅需要卡号就行了,不需要进入卡认证读写,ID号和卡号的保存和蓝牙、键盘用户一致,使用结构体保存。
    使用结构体保存卡号主要是为了方便设计和数据管理,将蓝牙管理员、键盘使用者、卡号都使用结构体保存,有利于统一管理数据。
    结构体有ID和卡号
    struct RFID_STU
    {
        int ID;  //ID自动分配到空闲的ID中
        uint8_t CODE[RFID_LEN]; //卡号通过刷卡获取
    };
    添加刷卡时,寻卡后将卡号保存到结构,同时写入闪存。
    对比刷卡时,从闪存中读取出结构体,寻卡后获得卡号对比结构体中卡号,完成开锁。
    删除刷卡时,从闪存中读取出结构体,可以直接通过蓝牙管理员发送ID号进行删除,也可以通过刷卡对比卡号来删除。然后更新结构体到闪存。

    函数功能:

    void usart3_init(u32 bound3); //串口3初始化				
    u8 add_rfid(void);//加卡
    u8 press_rfid(void);//刷卡
    u8 del_rfid(void);//删卡
    
    void USART3_SendData(USART_TypeDef* USARTx, uint16_t Data);
    void send3HexData(uint8_t data[], uint16_t length);
    //用来发送十六进制数,PN532只能接收十六进制的指令
    /*
    刷卡流程:
    uint8_t a1[40] =//唤醒
    	{0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    	 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, 0xFD, 0xD4, 0x14, 0x01, 0x17, 0x00};
    uint8_t a2[40]=//寻卡
        {0x00, 0x00, 0xFF, 0x04, 0xFC, 0xD4, 0x4A, 0x01, 0x00, 0xE1, 0x00};
    a1和a2必须是全局变量
    send3HexData(a1, sizeof(a1) / sizeof(a1[0]));//唤醒
    rxs3_index=0;
    memset(RX3DATA, 0, sizeof(RX3DATA));//清一次以免数据冲突
    delay_ms(100);
    send3HexData(a2, sizeof(a2) / sizeof(a2[0]));//寻卡
    delay_ms(100);
    RX3K=0;
    rxs3_index=0;
    memset(RX3DATA, 0, sizeof(RX3DATA));//清一次以免数据冲突
    //.......等待寻卡,当有卡刷到PN532时,卡号将通过串口发送给RX3DATA
    //然后就可以对接收到的RX3DATA进行处理
    */

    以上是简单的函数使用和寻卡流程,根据这个寻卡流程,就可以自定义各种刷卡操作类的函数了
    我这里是定义了三个函数:
    u8 add_rfid(void);//加卡
    u8 press_rfid(void);//刷卡
    u8 del_rfid(void);//删卡
    具体的,请看我分享的程序资源或者自行定制。

    程序链接:智能锁-作者:STM时 
    提取码:1234 
    –来自百度网盘超级会员V5的分享
    B站实物演示视频链接:基于STM32的智能锁(蓝牙、指纹、RFID刷卡磁卡、LCD显示)

    工具包、原理图、PCB及其他文件我整理好后就发上来…

    物联沃分享整理
    物联沃-IOTWORD物联网 » 基于STM32的智能门锁/门禁系统多功能设计解析

    发表评论