基于STM32F4的智能门锁超详细解析(矩阵键盘、OLED、舵机、HC

您所在的位置:网站首页 stm32密码锁课程设计 基于STM32F4的智能门锁超详细解析(矩阵键盘、OLED、舵机、HC

基于STM32F4的智能门锁超详细解析(矩阵键盘、OLED、舵机、HC

2024-05-30 15:57| 来源: 网络整理| 查看: 265

前言:

        对于刚学习STM32单片机的小伙伴,学习了理论知识和部分可驱动的模块,但是综合项目还没有触碰过多少。所以本文已最简单的模块进行简单的知识梳理和疑问解答。本文有参考其他博主内容,会在使用这些内容时附加原网址,以便向这些最牛博主学习!

 实现功能:

        可以使用蓝牙进行开锁,可以使用4*4矩阵键盘进行开锁。在锁定界面时可按下*号键查看按键表,在锁定界面按下#号键可进行密码输入,在锁定界面除*号和#号键以外其他按键均不起作用。当密码输入时,可有删除、清除、锁定、确认四种按键进行操作。当密码正确进入系统时,可进行系统锁定和更改密码操作。其他类似操作可在此基础上进行增加。

        注:本文将讲述了很多模块的使用方法,这些模块的使用方法都是在本人整理过之后的驱动下写的,如果有需要改动的或者移植时,请修改模块驱动程序。

目录

前言:

 实现功能:

F407ZG最小系统

矩阵键盘

OLED显示

舵机

HC-05蓝牙模块

连线说明

代码解析

总代码下载网址

以下为总体展示图:

F407ZG最小系统

        作者本人采用的是正点原子的STM32F4ZGT6带SRAM版。因为带SRAM没有片选SRAM使用的IO口所以选用几个独立的IO口进行操作。选取IO口的方法为先查看数据手册还有正点原子增值资料中的IO口表格文档,查找适合本次实验所需的IO口。

        例如我需要一个USART3的串口进行蓝牙通信,我在芯片手册中查找USART3_TX/RX这两个IO口,查找到之后去IO口表格文档中查找这两个引脚对应的连接属性,经本次操作可知,USART3_TX/RX两个口完全独立并且IO口已引出,可以进行外设连接操作。

        其中相应的数据手册和IO口表格,可根据不同开发板提供的资料进行查找。

矩阵键盘

        作者本人采用的是最普通的4*4矩阵键盘,某宝几块钱就可以获取的。

        线路连接:(上四IO口为列,下四IO口为行,表格以从下到上说明)

        IO选择原因:此次程序操作了IDR和ODR寄存器,如此选择IO口,可方便寄存器的操作,以便实现代码。

线路连接列表 矩阵键盘IO口行IO4行IO3行IO2行IO1列IO1列IO2列IO3列IO4单片机IO口PC0PC1PC2PC3PC4PC5PG6PG7

   

         矩阵键盘的操作原理:上四列IO口设置为输入模式,下四行IO口设置为输出模式。由原理图可知当按下其中一个键时,其中两个IO口短接。输入一端的IO口读取到了输出一段的IO口的电平,单片机会将这一采取结果返回给程序进行判断。具体操作与代码流程如下。

1.写44KEY头文件44KEY.h的代码,其中包含两个函数一个GPIO初始化函数一个按键操作函数。

#ifndef _44KEY_H #define _44KEY_H #include "sys.h" void gpio_init_key4(void);//矩阵键盘引脚初始化 u16 key_init_44(void);//4*4矩阵键盘函数(单次按键模式) #endif

2.写44KEY.c中gpio_init_key4的函数,此函数的目的是为了初始化矩阵键盘相关的GPIO。正如本小结开头所说,其中PC的0、1、2、3设置成为了推挽输出模式,其余的PC4、5、PG6、7设置成为了输入模式。

GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOG, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOG,&GPIO_InitStructure);//初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_Init(GPIOC,&GPIO_InitStructure);//初始化

        3.写44KEY.c中key_init_44的函数,此函数的目的是为了读取相关的IO口,并进行判断是哪一个按键按下的,使用的方法为扫描法。以按下S1按键为例(第一行第一列的按键)进行第一行的扫描,由于没有按键按下时列输入IO读取到的值为初始的全高(IO引脚悬空时默认为高),以此为方法进行判断。令第一行输出低电平,如果某处按键按下则对应列输入信号为低电平,其余因为没有按键按下造成短路所以依旧为高电平(IO引脚悬空时默认为高)。此时PC0、1、2、3、4、5和PG6、7的GPIO的值为1110 0111,在寄存器储存值为0xE7,则判断成功为S1按键按下。

8个IO口的储存=(GPIOC->IDR&0x37)|(GPIOG->IDR&0xC0);

GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2); GPIO_ResetBits(GPIOC,GPIO_Pin_3); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { delay_ms(10); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { temp=(GPIOC->IDR&0x37)|(GPIOG->IDR&0xC0); switch(temp) { case 0xE7:Key_val=1; break; case 0xD7:Key_val=2; break; case 0xB7:Key_val=3; break; case 0x77:Key_val=4; break; default :Key_val=0; break; } } }

      矩阵键盘部分主要参考:https://blog.csdn.net/Kevin_8_Lee/article/details/88084516

OLED显示

        作者本人采用的是某宝较为便宜的0.96寸7针OLED显示使用的是SPI通信协议的。

        线路连接:

OLED的IOGNDVCCDODIRESDCCS单片机的IOGND+5VPF8PF7PF6PB13PB12

        

        OLED的原理:根据所给数据的16进制数进行点亮和熄灭,例如逐列式顺式第一列点阵(一列16个点),如果给了一个16进制数为0x00ff,则表示前8个点不点亮后8个点点亮。

        取字模的方法:使用PCtoLCD2002.exe取模软件进行文字取模,主要设置如下阴码、列行式、逆向。格式处为C51的格式,其余可以不做改动。过程如下图所示。(主要生成16*16的字模,其余大小字体方法类似)

 

 

         字模生成以后将其复制到主程序字库中,作者本人字库是放在oledfone.h头文件中定义的,其中如果字体数量超过了你能保存的大小则需要修改数组的大小。如下图所示,我定义了一个密字,则将代码复制于此。此字库中一共保存了50个字(每个字需要两行16进制组成,则每两个数组保存一个字)每个字的生成大小为16*16。

        取图片模的方法:使用PCtoLCD2002.exe取模软件进行图片取模,首先需要先切换到图片模式(取模软件处点击模式,选择图片模式)。

        在图片取模之前需要先对图片进行处理,首先选择一张颜色单一的图片(也就是只有深色和白色的但不能包含浅色的图片,不然识别不出来图片的质量会下降)以画图的方式打开选择重新调整大小,调整大小范围在长128宽64之内(作者本人使用的是128*64的OLED屏幕,如果其他屏幕可更改此处设置)。然后就是保存图片,保存图片的方法为点击另存为选择BMP类图片保存,然后在选择为单色图。如图所示。

        之后就是将图片在PCtoLCD2002.exe取模软件中打开进行图片取模,跟文字取模一样需要对取模方式进行一些设置,其中设置阴码、列行式、逆向,格式为C51格式并且去除两个大括号。如图所示。

        最后就是将取模成功后的图片放置到程序中,我保存在bmp.h的头文件中,方法与取字模方法相同。如图所示。

 

 

 

驱动程序保存在程序总代码中,如有需要请自行提取。

舵机

        作者本人使用的是TD-7015MG舵机,因为手头只有这一种舵机。

定时器控制PWM输出知识参考此博客:PWM知识学习

舵机构造和简单原理参考:http://www.geek-workshop.com/thread-70-1-1.html

线路连接:

舵机IO红线棕线黄线单片机IO+5VGNDPC6

驱动程序讲解:

        由舵机转动的原理,我们将频率设置为50HZ即0.002秒一个周期(使用定时器),我们设置的脉冲为0.0019时为顺时针转动,设置的脉冲为0.0018时为逆时针转动,符合我们开关门锁的要求。具体代码操作如下。

//进行结构体声明 GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3); //GPIO初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOC,&GPIO_InitStructure); //定时器计时初始化 TIM_TimeBaseStructure.TIM_Prescaler=8399; TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_Period=200; TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //输出PWM设置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; TIM_OC1Init(TIM3, &TIM_OCInitStructure); //使能 TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM3,ENABLE); TIM_Cmd(TIM3, ENABLE); HC-05蓝牙模块

        本文作者使用的时某宝购买的HC-05蓝牙模块,如下图所示。

        原理:我们使用蓝牙模块当作媒介来使用单片机的某个串口进行串口通信。

        线路连接:(因为只使用建议的蓝牙从模式所以STATE和EN接口未连接)

蓝牙与单片机 蓝牙IOSTATERXDTXDGNDVCCEN单片机IO未连PB10PB11GND+5V未连 蓝牙IOSTATERXDTXDGNDVCCENTTLUSBIO未连TXDRXDGND+5V未连

        蓝牙模块模式设置:

蓝牙模块常用指令集 操作指令格式/备注是否连接成功AT返回值为OK获取串口信息AT+UART?返回值为串口信息修改串口信息AT+UART=115200,0,0返回值为OK,修改串口波特率为115200,并且一位停止位,没有校验位获取连接密码AT+PSWD?返回值为连接密码修改连接密码AT+PSWD=1234返回值为OK,修改连接密码为1234获取蓝牙设备名称AT+NAME?一般会获取失败修改蓝牙设备名称AT+NAME=BEIJING返回值为OK,修改名字为BEIJING修改主从模式AT+ROLE=0返回值为OK,设置为从模式

        将蓝牙模块与TTLUSB进行连接进行蓝牙设置,蓝牙设置模式需要在蓝牙上电之前将KEY小开关按住,使KEY为高进入AT设置模式。或者在上电之后按一下KEY键进入AT模式。

        进入AT模式之后,打开XCOM.exe,进行波特率为38400和串口的设置。

逐次发送:AT、AT+NAME=BEIJING、AT+ROLE=0、AT+PSWD=1234、AT+UART=115200,0,0命令进行蓝牙模块作为从设备的设置

 蓝牙模块与手机连接、代码分析

        在蓝牙模块设置好模式之后,使用手机随便打开一个蓝牙串口助手,等待搜索到蓝牙并进行连接。串口和定时器的初始化,不再过多的赘述,都是简单的配置,如果有知识不明白,可参考这位博主的文章:其他博主串口知识讲解

 以下是串口3的初始化和中断服务函数

void usart3_init(u32 bound) { NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; USART_DeInit(USART3); //复位串口3 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);//使能USART3时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_10; //GPIOB11和GPIOB10初始化 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB11,和GPIOB10 GPIO_PinAFConfig(GPIOB,GPIO_PinSource11,GPIO_AF_USART3); //GPIOB11复用为USART3 GPIO_PinAFConfig(GPIOB,GPIO_PinSource10,GPIO_AF_USART3); //GPIOB10复用为USART3 USART_InitStructure.USART_BaudRate = bound;//波特率一般设置为9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 USART_Init(USART3, &USART_InitStructure); //初始化串口3 USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启中断 USART_Cmd(USART3, ENABLE); //使能串口 NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器 TIM7_Int_Init(100-1,8400-1); //10ms中断一次 TIM_Cmd(TIM7, DISABLE); //关闭定时器7 USART3_RX_STA=0; //清零 } void USART3_IRQHandler(void) { u8 res; if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)//接收到数据 { res =USART_ReceiveData(USART3); if((USART3_RX_STA&(1IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { delay_ms(10); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { temp=(GPIOC->IDR&0x3B)|(GPIOG->IDR&0xC0); switch(temp) { case 0xEB:Key_val=5; break; case 0xDB:Key_val=6; break; case 0xBB:Key_val=7; break; case 0x7B:Key_val=8; break; default :Key_val=0; break; } } } GPIO_ResetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); GPIO_SetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_3|GPIO_Pin_2); GPIO_ResetBits(GPIOC,GPIO_Pin_1); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { delay_ms(10); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { temp=(GPIOC->IDR&0x3D)|(GPIOG->IDR&0xC0); switch(temp) { case 0xED:Key_val=9; break; case 0xDD:Key_val=10; break; case 0xBD:Key_val=11; break; case 0x7D:Key_val=12; break; default :Key_val=0; break; } } } GPIO_ResetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); GPIO_SetBits(GPIOC,GPIO_Pin_3|GPIO_Pin_1|GPIO_Pin_2); GPIO_ResetBits(GPIOC,GPIO_Pin_0); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { delay_ms(10); if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))!=0xF0) { temp=(GPIOC->IDR&0x3E)|(GPIOG->IDR&0xC0); switch(temp) { case 0xEE:Key_val=13; break; case 0xDE:Key_val=14; break; case 0xBE:Key_val=15; break; case 0x7E:Key_val=16; break; default :Key_val=0; break; } } } if(Key_val!=0) { t=0; } return Key_val; } else if(((GPIOC->IDR&0x30)|(GPIOG->IDR&0xC0))==0xF0) { t=1; } return 0; }

OLED.C代码:OLED驱动显示屏部分

u8 OLED_GRAM[144][8]; //反显函数 void OLED_ColorTurn(u8 i) { if(i==0) { OLED_WR_Byte(0xA6,OLED_CMD);//正常显示 } if(i==1) { OLED_WR_Byte(0xA7,OLED_CMD);//反色显示 } } //屏幕旋转180度 void OLED_DisplayTurn(u8 i) { if(i==0) { OLED_WR_Byte(0xC8,OLED_CMD);//正常显示 OLED_WR_Byte(0xA1,OLED_CMD); } if(i==1) { OLED_WR_Byte(0xC0,OLED_CMD);//反转显示 OLED_WR_Byte(0xA0,OLED_CMD); } } void OLED_WR_Byte(u8 dat,u8 cmd) { u8 i; if(cmd) OLED_DC_Set(); else OLED_DC_Clr(); OLED_CS_Clr(); for(i=0;i


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3