基于STM32的智能门锁/智能门禁多功能系统 |
您所在的位置:网站首页 › 智能锁带蓝牙有什么用处 › 基于STM32的智能门锁/智能门禁多功能系统 |
(蓝牙、AS608指纹、RFID刷卡PN532、键盘、LCD、界面友好)单片机嵌入式开发(项目开源)
程序链接:智能锁-作者:STM时 PCB和小工具链接:智能锁-PCB和工具-PCB制作者:STM时 PN532的资料:PN532的资料(资料比较大,不一定用到) 提取码:1234 --来自百度网盘超级会员V5的分享
B站视频:基于STM32的智能锁(蓝牙、指纹、RFID刷卡磁卡、LCD显示)
TODO:0x2B开头烧录地址的F103C8T6芯片为M4内核爆改来的,可以用TIM7(比如我这个)。但是一般的F103C8T6是0x1B开头也就是M3内核,只有TIM1到TIM4,假如发现上电指纹卡死,那么多半是定时器的问题,需要修改程序设置为TIM1到TIM4。 前言 本次设计是基于STM32F103C8T6(以下C8T6等同)开发的智能锁,支持多种方式对系统进行操作:蓝牙、指纹、RFID刷卡、4x4键盘输入,拥有友好的蓝牙收发界面和LCD交互界面。蓝牙:作为总系统的管理员,有主管理和次管理,主管理只能有一个,副管理员可以有多个。主管理员拥有全部权限(包括修改管理员数据);而副管理员不能修改管理员数据,但可以修改指纹、RFID刷卡、键盘密码的数据。蓝牙交互界面提供了多种选项,同时兼顾ID密码登数据发送和指令发送,功能齐全,蓝牙界面通过管理员账号登录。指纹:在锁屏界面下可以通过验证指纹来解锁智能锁,可以通过主/副管理员来添加指纹和删除指纹。RFID刷卡:在锁屏界面下可以通过验证ID/IC卡来进行智能锁解锁,可以通过主/副管理员来添加ID/IC卡和删除ID/IC卡。4x4键盘:4x4键盘既可以作为系统的选项按钮,也可以作为ID和密码输入的按钮,在锁屏界面可以选择进入键盘输入功能,进行键盘用户ID和密码认证,认证通过后开启智能锁。LCD:LCD提供了友好的交互界面,可以通过4x4键盘来进行功能选择。收发协议:蓝牙、指纹、RFID刷卡使用USART(刚好用完C8T6的三个USART串口),LCD使用SPI(如果你用的是STM32F4或其他的系列,那么你需要对LCD的代码驱动进行修改,因为F1和F4的SPI代码程序不一样),4x4键盘使用推挽输出和上拉输入(4x4扫描,必是一边输出、一边输入)。 模块介绍序号 模块 型号规格 数量 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的问题,仅供参考) 这里再讲一个推测,某些C8T6直接烧录我的代码不能驱动指纹模块,可能是因为内核问题。0x2B开头烧录地址的F103C8T6芯片为M4内核爆改来的,可以用TIM7(比如我这个)(烧录地址在那个魔法棒那里查看,在Debug的地方)。但是一般的F103C8T6是0x1B开头也就是M3内核,只有TIM1到TIM4,假如发现上电指纹卡死,那么多半是定时器的问题,需要修改程序设置为TIM1到TIM4。也就是说,只需要对TIM的配置程序进行相应的修改就行了。 蓝牙我买的是汇承的HC-08,主从一体,比较方便,使用的时候拿官方的蓝牙测试架设置为从机就行了,官方网站可以下载蓝牙模块使用手册、上位机调试、手机蓝牙助手APP。(当然我也会打包到分享资料中) 我买的是光学AS608,支持USB D- D+、USART传输模式,我这里走的是USART,没什么好说的,直接上图。 RFID射频刷卡模块有很多种型号,我这里买的是PN532 NFC RFID V3,支持多种类型的卡(根据自身需求,这个可以自己去商家哪里查,买其他的比如522也可以的)。支持I2C、SPI、高速USART多种传输模式,我这里走的是USART。配套的应该会送两张卡,试过用校园卡也能刷,没什么好说的,直接上图。 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中)。 文字取模示例:实际用法放在资料包里,下面放一张图 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密码
蓝牙界面开发代码程序框架: 实际程序设计看我的程序资源包或者自行定制 //这里举例一个简单的框架 //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)); } } } 4x4键盘程序设计注意:4x4键盘使用键盘扫描的方式,一边用上拉输入,一边用推挽输出,但不是所有的管脚都有上拉输入和推挽输出的功能,有些管脚无法上拉输入,有些管脚无法推挽输出。 无法上拉的情况表现为:无论你管脚输入啥,管脚状态都读取到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 */ 4x4键盘扫描原理,首先我们的按键是有两头的,以下称为左边和右边。 方便理解: 单个按键的扫描方式是,MCU |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |