当前位置: 首页 > news >正文

毕业设计 基于单片机的智能蓝牙密码锁设计与实现

文章目录

  • 0 前言
  • 1 简介
  • 2 主要器件
  • 3 实现效果
  • 4 设计原理
    • 4.1 硬件部分
    • 4.2 软件设计
  • 5 部分核心代码
  • 6 最后


0 前言

🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。

为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是

🚩 基于单片机的智能蓝牙密码锁设计与实现

🥇学长这里给一个题目综合评分(每项满分5分)

  • 难度系数:4分
  • 工作量:4分
  • 创新点:3分

🧿 选题指导, 项目分享:

https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md


1 简介

基于51单片机的按键蓝牙密码锁,可通过LCD1602显示屏显示密码信息。利用矩阵按键输入密码,当输入密码次数达到上限后蜂鸣器模块进行报警

2 主要器件

  • STC89C52单片机
  • 4×4矩阵键盘
  • LCD1602显示

3 实现效果

在这里插入图片描述

4 设计原理

4.1 硬件部分

硬件电路主要分为STC89C51单片机、AT24C02存储模块、按键电路、报警电路、继电器驱动模块五个部分。该控制系统的硬件设计以STC89C51单片机为主控芯片,利用单片机丰富的I/O端口将各个外围电路连接起来构成主系统,可以利用矩阵键盘实现密码输入与修改、关锁、复位等功能,并且通过单片机外接 LCD1602 液晶显示屏提示用户进行下一步操作。系统的硬件电路设计如图所示:

在这里插入图片描述
STC89C52单片机的最小系统

STC89C52单片机共有40只管脚,分为电源、时钟、控制和I/O引脚四类,它的优点是容易操作,原有程序可直接使用,硬件也无须改动,运行速度快且功耗低,而且成本低,抗干扰能力强,可提升产品性能,这使得在操作与成本方面都有极大的优势。单片机最小系统的工作由电源、晶振电路以及复位电路构成。

AT24C02存储模块

AT24C02存储器的数据传送率高且能与IIC总线兼容,功耗低,数据保存时间较长,还有一个专门的防误擦除写保护功能。芯片工作时有读和写两种操作,执行读操作时有当前地址读、随机读和顺序读三种方法;执行写操作时可根据数据量的大小选择字节写还是页写。在本设计中可直接将存储芯片的A0、A1、A2三个引脚连接至GND,为了方便读/写操作,将WP写保护引脚也连接到GND,最后将SDA、SCL两引脚分别接到单片机对应的两个引脚。

按键电路

该设计在操作过程中所需按键数目较多,所以采用矩阵式扫描的方法来作为键盘的输入形式且用4×4矩阵键盘可满足该设计所设定的功能。使用矩阵扫描法不仅可以减少单片机I/O端口的占用,也会降低电路连接的复杂程度。根据具体要实现的功能,密码锁的按键分布为数字键0-9、输入密码键、退格键、退出输入键、密码修改键、重置键、确认键。用户根据定义的按键功能实现输入,矩阵键盘直接连接单片机的P1口进行输入,通过输入高低电平判断键盘是否按下。

报警电路
报警电路由 LED 灯和蜂鸣报警器组成。这样可直观地观察密码锁的工作情况。本设计选用5V电磁式有源蜂鸣器,因为蜂鸣器工作时所需的电流较大,无法驱动单片机的I/O接口,电路中需要用一个三极管来驱动。当输入低电平时,三极管导通,蜂鸣器发出报警同时连接的红色LED灯亮;当输入高电平时,三极管截止,蜂鸣器停止鸣叫。

4.2 软件设计

电子密码锁控制系统的软件设计主要分为主程序、LCD1602显示程序、AT24C02存储程序、矩阵按键电路及中断服务程序的设计。为了实现密码锁的预期功能,软件设计部分以 STC89C51 单片机为核心编写程序,首先对整个系统程序进行初始化设置,开启电子密码锁的功能,采用4×4矩阵式键盘扫描方法来判断是否已按下按键,可通过LCD1602液晶显示屏清楚地看出当前已输入的密码位数,输入完成后按下确认键,密码锁会将输入的密码与事先存储在AT24C02芯片中的原密码进行比对,若密码一致则打开锁,若密码不一致则蜂鸣器报警且LED 灯亮,可选择重新输入,当密码错误三次则键盘将被锁定且报警。
软件设计流程如图所示:

在这里插入图片描述

5 部分核心代码

//MatrixKey.c文件
#include <REGX52.H>
#include "Delay.h"
#define FULL 4	//密码锁密码位数为4位
#define KEY_SCAN P1	//定义P1口

/**
  * @brief  传回输入键值0-10,及输入的有效密码位数
  * @param  *keyValue:0-11,*times:1-4
  * @retval 无
  */

void MatrixKey(unsigned char* keyValue,unsigned char* times)
{
	KEY_SCAN = 0X0F;	//置行为0,进行列扫描
	if(KEY_SCAN!=0X0F)	//若有键按下
	{
		Delay(10);	//消抖
		
		KEY_SCAN = 0X0F;	//再次进行列扫描
		switch(KEY_SCAN)
		{	//若有某一列值为0,表示该列有按键按下
			case 0X07:*keyValue = 0;break;
			case 0X0B:*keyValue = 1;break;
			case 0X0D:*keyValue = 2;break;
			case 0X0E:*keyValue = 3;break;
		}
		
		KEY_SCAN = 0XF0; //行扫描
		switch(KEY_SCAN)
		{	//若某行有值为0表示该行有键按下
			case 0X70:*keyValue = *keyValue;break;
			case 0XB0:*keyValue += 4;break;
			case 0XD0:*keyValue += 8;break;
			case 0XE0:*keyValue += 12;break;
		}
		if(*keyValue>=0 && *keyValue<=9){	//按下为有效密码
			*times = *times % 4 + 1;
		}
		while(KEY_SCAN!=0XF0)
		{	//检测到按键松开时,退出循环
			Delay(10);
		}
	}	
}

#include <REGX52.H>
 
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_E=P2^7;
#define LCD_DataPort P0
 
//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()		//@12.000MHz  1ms 如果是更快的单片机,这里延时要长一点
{
	unsigned char i, j;
 
	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
}
 
/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
	LCD_RS=0;//写指令
	LCD_RW=0;
	LCD_DataPort=Command;
	LCD_E=1;//这里置高又置低,高电平速度太快,反应不过来,因此进行延时
	LCD_Delay();
	LCD_E=0;
	LCD_Delay();
}
 
/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;//写数据
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_E=1;//这里置高又置低,高电平速度太快,反应不过来,因此进行延时
	LCD_Delay();
	LCD_E=0;
	LCD_Delay();
	
}
 
/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init(void)
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0C);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//清屏
 
}
 
/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
	if(Line==1)
	{
		LCD_WriteCommand(0x80|(Column-1));//0x80是确定光标位置的指令,A0-A6是确定地址
	}
	else
	{
		LCD_WriteCommand(0x80|(Column-1)+0x40);//换行所以要加一个基地址0x40
	}	
}
 
/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{
  LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);       //char x=‘A’;(等效于char x=0x41;)
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,unsigned char String[])
{
	//这里用到指针的作用,String[]指针指向该字符组的第一个地址,依次显示出来,直到'\0'的出现
	unsigned char i;
  LCD_SetCursor(Line,Column);
	for(i=0;String[i]!='\0';i++)
	{
		LCD_WriteData(String[i]);
	}
}
 
/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)//x的y次方
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}
/*
                    i
789/100%10    7     3     10^(3-1)
789/10%10     8     2     10^(2-1)
789/1%10      9     1     10^(1-1)
*/
/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
	unsigned char i;
  LCD_SetCursor(Line,Column);
	//根据长度确定所要显示的位数,通过计算将Number中从高位到低位依次显示出来,对照ASCII码表0的基地址
	//为0x30,显示几就在这个地址上加几就可以
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(0x30+Number/LCD_Pow(10,i-1)%10);//将数字转化为ASCII码表
	}
}
 
/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column, int Number,unsigned char Length)
{
	unsigned char i;
	unsigned int Number1;
  LCD_SetCursor(Line,Column);
	if(Number>=0)
	{
		LCD_WriteData('+');
		Number1=Number;
	}
	else
	{
		LCD_WriteData('-');
		Number1=-Number;
	}
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(0x30+Number1/LCD_Pow(10,i-1)%10);//将数字转化为ASCII码表
	}
}
 
/*
输入0xA0                   i
160/16%16     10     2     16^(2-1)
160/1%16       0     1     16^(1-1)
*/
/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column, int Number,unsigned char Length)
{
	unsigned char i;
	unsigned char SingleNumber;
  LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		//将16进制转化为ASCII码表,这里输入16进制,但进行计算的是十进制
		if(SingleNumber<10)
		{
			LCD_WriteData('0'+SingleNumber);
		}
		else
		{
			LCD_WriteData('A'+SingleNumber-10);//这里的‘A’相当于是一个基地址
		}
	}	
}
 
/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column, int Number,unsigned char Length)
{
	unsigned char i;
  LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(0x30+Number/LCD_Pow(2,i-1)%2);//将数字转化为ASCII码表
	}
}
//main.c文件
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "MatrixKey.h"

#define DEL 10	//矩阵键盘键值为10的键表示删除键
#define SURE 11	//矩阵键盘键值为11的键表示确认键
#define TRUE 1	//真值
#define FALSE 0	//假值
#define NONE 20	//初始化键值,用非0-9数字表示无效值

unsigned char value = NONE;	//键值,并初始化为无效值NONE
unsigned char times;	//输入有效的密码位数

unsigned char password[] = {0,1,0,0,7};	//密码“1007”,第0位不用
unsigned char identify = TRUE;	//用于判断密码是否输入正确
unsigned char input[5]={0,0,0,0,0};	//用于存放输入的密码

void main()
{
	LCD_Init();	//初始化LCD屏幕
	LCD_ShowString(1,1,"Password:");
	LCD_ShowString(2,1,"XXXX");
	
	while(1)
	{
		MatrixKey(&value,&times);	//获得输入的键值和输入密码位数
		if(value>=0 && value <=9){	//判断是否为有效键值(密码)
			LCD_ShowNum(2,times,value,1);	//对应位输出密码
			input[times] = value;	//保存输入密码
			if(times==FULL){	//输入密码为4位时,判断是否密码正确
				unsigned int i;
				for(i=1; i<=4; i++){	//遍历密码password进行判断
					if(input[i] != password[i])
							identify = FALSE;
				}
			}
		}
		if(value == DEL){	//若输入删除键
			LCD_ShowString(2,times,"X");	//在删除位输出X表示对应位删除
			times--;	//清空删除的密码,表示当前输入密码位数
			value = NONE;	//调用前value=10,执行后value值为无效值
		}
		if(identify==FALSE && times==FULL && value==SURE ){	//输入4位密码不正确
			LCD_ShowString(1,12,"Error");
			LCD_ShowString(2,1,"XXXX");
			identify = TRUE;
			value = NONE;
		}else if(identify==TRUE && times==FULL && value==SURE){	//输入4位密码正确
			LCD_ShowString(1,12,"Right");
		}
	}
}


6 最后

相关文章:

  • 【线上实习项目】助力你的校招!
  • 吊打面试官系列之--吃透Spring ioc 和 aop (中)
  • Matlab制作GUI
  • Spring Data JPA或Spring Data JDBC中Like和Containing区别
  • SpringMVC04之JSON和全局异常处理
  • <C++> list容器本质|常用接口|自定义排序规则
  • 【Matlab】简单控制系统建模(控制系统工具箱)
  • 设计模式——模板模式
  • 倍投技巧 - 凯利公式教你如何用正确的方法投资
  • SpringBoot restful api接口设计
  • 软件测试高薪“骗局”软件测试入门就月薪过万,还包就业。别再上当受骗了、清醒点吧
  • 【DP 动态规划 | 精选推荐】持续更新
  • 专利的要求-需要什么条件?
  • Google Earth Engine (GEE)——GEE制作gif动态图(北京市为例)
  • Spring-Framework-ioc-1
  • [译]CSS 居中(Center)方法大合集
  • 《用数据讲故事》作者Cole N. Knaflic:消除一切无效的图表
  • ➹使用webpack配置多页面应用(MPA)
  • 07.Android之多媒体问题
  • Java Agent 学习笔记
  • vue总结
  • 第三十一到第三十三天:我是精明的小卖家(一)
  • 缓存与缓冲
  • 聊聊directory traversal attack
  • 七牛云假注销小指南
  • 前端面试之闭包
  • 深度学习在携程攻略社区的应用
  • 使用 Docker 部署 Spring Boot项目
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 小程序01:wepy框架整合iview webapp UI
  • 一道闭包题引发的思考
  • 云大使推广中的常见热门问题
  • C# - 为值类型重定义相等性
  • 积累各种好的链接
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • #大学#套接字
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (c语言)strcpy函数用法
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (力扣)循环队列的实现与详解(C语言)
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (中等) HDU 4370 0 or 1,建模+Dijkstra。
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • .NET Core中Emit的使用
  • .net mvc部分视图
  • .NET 发展历程
  • .NET 回调、接口回调、 委托
  • .net图片验证码生成、点击刷新及验证输入是否正确
  • ?.的用法
  • @transactional 方法执行完再commit_当@Transactional遇到@CacheEvict,你的代码是不是有bug!...
  • @软考考生,这份软考高分攻略你须知道
  • [Android]Android P(9) WIFI学习笔记 - 扫描 (1)
  • [Android]How to use FFmpeg to decode Android f...