基于51单片机的可改位数的记忆密码锁,有警报声和门铃(包含免费下载工程代码压缩包,持续更新)

视频演示

开机和门铃

修改密码

警报

密码锁功能

1.具备密码存储功能,即使断电也能保持密码不丢失。
2.配有各种按键音效,如普通按键提示音、正确输入提示音、错误输入提示音;并在连续输入错误时触发警报声;3.还具备门铃声与关闭按键音效功能。
4.支持修改密码、恢复原密码及清除密码等操作。
5.提供可调整的密码长度设置,以满足不同需求。
6.可设计一个开发者密码,防止密码忘记(输入对应位数的开发者密码也可以成功开锁),开发者密码只可在程序修改

提要

本人小白,刚接触51单片机不久,这是我第一次将自己的课程设计上传到博客,有诸多地方尚不了解,如果有哪里写法不对或者代码哪里出问题,欢迎各位指出!

文章部分代码解释

虽然文中我也有写注释,但是是思路的注释,如需了解原理请点击下面链接
AT24C02代码解释
点击link
http://t.csdn.cn/TZihH
定时器消抖解释
点击link
https://www.bilibili.com/video/BV1Mb411e7re?t=3197.8&p=28

下载代码工程压缩包

如要下载代码文件,即可在文章最上方点击下载,免费下载
或者 如下分享
百度网盘链接:
link
链接:https://pan.baidu.com/s/1c88CGpzYGu7_CdxwCmLH3Q?pwd=f5bp
提取码:f5bp
51单片机密码锁
link
https://www.aliyundrive.com/s/WLoKq5f9vH2
点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极速在线查看,视频原画倍速播放。

密码锁介绍

通过矩阵键盘,定时器,蜂鸣器,AT24C02,等等模块来实现密码锁功能
在这里插入图片描述

按键说明

在这里插入图片描述
密码按键一一对应上面的图片

使用方法

独立按键K1:门铃
独立按键K4:开关按键提示音
1~9和 * #:输入密码
Del::回退键,清除一位密码
Clear::清除键,清除已输入的密码
Mod::设置密码,在成功开锁后,按下此键修改密码并保持在单片机里面(断电不会擦除)
Deter::确认键,输入完密码,按下此键进行检验开锁或保存密码

可调密码位数
调节方式:

更改Length的大小即可
Administrator则是你自己设计的开发者密码

#define Length 3 							//长度
#define Administrator "2869902214"			//开发者密码设置(建议10位以内)

运行逻辑

extern 是类似在别的文件定义了共享使用,数据互通

开机后读取存放在AT24C02的密码
接着就输入Length位密码按下Deter,如果密码错误,则发出错误提示音,并且按下Clear清除后才可再次输入密码,但是连续输错三次则发出警报声
如果忘记了密码,则输入开发者密码进行重置密码

如果输入的密码正确会发出正确提示音则可以按下Mod进行更改密码,要输入两次更改后的密码,如果两次密码一致才会写入AT24C02,如果不一致则按下Clear重新进行更改

门锁密码为Length位(自己设置)初始密码开发者密码(自己设置需长度和Length一样),包含开发者密码(防止密码忘记)输入密码(在LCD1602屏幕上的密码都显示为*) 成功开锁将在屏幕上提示Success并发出开锁成功的音效,在开锁后按下Mod按键将进入密码设置模式,重新输入Length位密码,(需要输入两次修改后的密码,如果一致则修改成功,否则重新修改)按下Deter保存到单片机的AT24C02当中,此后的密码将以AT24C02的密码为准,输入错误将在屏幕上显示ERROR,并发出密码错误的音效,错误三次以上发出警报声。

大致的流程图(不是很会写,见谅)

在这里插入图片描述

工程代码压缩包

如果感觉看的内容过多,混乱,则可以下载这个工程压缩包,下载解压即用

代码实现(下面为用到的模块)

由于运用了多个模块,那么我将每个模块分别放出
用了LCD1602.c和.h文件 显示屏
MatriKey.c和.h文件 矩阵键盘
Timer0.c和.h文件 定时器
PassWord.c和.h文件 工程核心部分
Beep.c和.h文件蜂鸣器
Delay.c和.h文件1ms延时函数
AT24C02.c和.h文件

PassWord.c讲解

PassWord.c文件

#include <REGX52.H>
#include "MatriKey.h"
#include "LCD1602.h"
#include "String.h"
#include "Delay.h"
#include "AT24C02.h"
#include "PassWord.h"
#include "Beep.h"
#define Length 3 							//长度
#define Administrator "2869902214"			//开发者密码设置(建议10位以内)

extern unsigned char Number;						//键值
unsigned char Count_Input,Count_ERROR;				//目前输入的位数,错误次数
unsigned char Write_LCD_PassWord[Length + 1];		//输入的密码	
unsigned char Temp_Read_AT[Length + 1];				//读取过渡字符串
unsigned char First_PassWord[Length + 1];			//改密码第一次的字符串
unsigned char Second_PassWord[Length + 1];			//第二次的字符串
unsigned char Administrator_PassWorld[Length + 1];	//忘记密码(Length为目前设置的密码长度)
unsigned char Write_Count;							//写入的次数
bit ERROR,Wirte_Flag,Success,Success_Flag;			//密码错误、写入的标志、密码正确、蜂鸣器正确音效标志
	
void Set_PassWord()		  	//设置密码
{
		Count_Input = 0;
		Wirte_Flag = 1;		//进入写入模式
		LCD_Init();
		LCD_ShowString(1,1,"Write:");
		
}
void Init_PassWord()		//初始化密码
{	
		LCD_Init();
	if(Wirte_Flag == 0)			//如果不为写入状态则
		LCD_ShowString(1,1,"PassWord:");
	else if(Wirte_Flag == 1)	//写入状态
	{
		if(Write_Count == 0)
			LCD_ShowString(1,1,"Write:");
		else  LCD_ShowString(1,1,"Again:");
	}
		
	
		strcpy(Write_LCD_PassWord," ");			//清空密码
		Count_Input = 0;						//回到密码输入最初位置
		Success = 0;							//返回后不可修改密码
}
void Back_PassWord()		//回退密码
{
		Count_Input = Count_Input - 1;		//输入回退一个
		Write_LCD_PassWord[Count_Input] = ' ';			//赋值空格
		LCD_ShowChar(2,Count_Input+1,Write_LCD_PassWord[Count_Input]);		//空格覆盖	
}
void Judge_PassWord()		//输入密码的判断结果
{
	 if (strcmp(Write_LCD_PassWord, Temp_Read_AT) == 0 || strcmp(Write_LCD_PassWord, Administrator_PassWorld) == 0)			
		 {
			LCD_ShowString(1,10,"Success");
			 Success = 1;				//正确则赋值1
			 Success_Flag = 1;		 	//蜂鸣器的赋值
			 strcpy(Write_LCD_PassWord," ");
			 Count_Input = Length + 1;			//确认后不再输入,不再回退
			 Count_ERROR = 0;			//清除
		 }
		 else 								//错误
		 {
			 LCD_ShowString(1,10,"ERROR!!");
			 ERROR = 1;
			 Count_Input = Length + 1;			//确认后不再输入,不再回退,因为i=Length 是最后一位
			 Count_ERROR++;				//如果确认后i=Length 或者0都可以再输入密码
		 }
}
void Read_PassWord()		//读取正确密码
{
	unsigned char a;
			for(a = 0;a<Length ;a++)
		{
			Temp_Read_AT[a] = AT24C02_ReadByte(a);
		}
		
		for(a = 0;a<Length ;a++)
		{
			Administrator_PassWorld[a] = Administrator[a];
		}
		
}

void Write_PassWord()		//写入正确密码
{
	unsigned char a;
	for(a = 0;a<Length ;a++)
		{
			AT24C02_WriteByte(a,Write_LCD_PassWord[a]);
			Delay(5);
		}
}
void Change_PassWord()		//更改密码
{

			unsigned char i;
		if(Write_Count == 0)
		{
			for(i=0;i<Length;i++)
			{
				First_PassWord[i] = Write_LCD_PassWord[i];   遍历length次传递第一次密码
			}
			LCD_Init();
			LCD_ShowString(1,1,"Again:");
			Count_Input = 0;
			Write_Count++;
		}
		else if(Write_Count == 1)
		{
			for(i=0;i<Length;i++)  Second_PassWord[i] = Write_LCD_PassWord[i];   //遍历length次传递第二次密码
			if(strcmp(Second_PassWord, First_PassWord) == 0)		//如果一样
			{
				LCD_Init();
				LCD_ShowString(1,1,"Success!");
				Success = 0;//改完密码后置0,必须再输入正确密码才可以改
				Wirte_Flag = 0;				//存入后退出修改密码模式
				Write_PassWord();			//存入AT24C02
				Count_Input = Length + 1;			//防误操作
				Success_Flag = 1;
				 Write_Count = 0;
			}
			else 
			{
				LCD_Init();
				LCD_ShowString(1,1,"Start Again!");
				Write_Count = 0;
				Count_Input = 0;			//防误操作
				ERROR = 1;
			}	
		}							
}
void LCD_PassWord()			//LCD显示
{
	if(Count_Input!=0 && Count_Input <= Length )		//按键之后开始输入密码
	{
		//LCD_ShowChar(2,Count_Input,Write_LCD_PassWord[Count_Input-1]);
		LCD_ShowChar(2,Count_Input,'*');	
	}
}

void Key_Number()			//数字和 * # 按键
{
	if(Count_Input < Length )
			{
				switch(Number)
				{
					case 16: Write_LCD_PassWord[Count_Input] = '0';
							Count_Input++;
						break;
					case 10:Write_LCD_PassWord[Count_Input] = '*';
							Count_Input++;
						break;
					case 11:Write_LCD_PassWord[Count_Input] = '#';
							Count_Input++;
						break;
					case 9:Write_LCD_PassWord[Count_Input] = '9';Count_Input++;break;
					case 8:Write_LCD_PassWord[Count_Input] = '8';Count_Input++;break;
					case 7:Write_LCD_PassWord[Count_Input] = '7';Count_Input++;break;
					case 6:Write_LCD_PassWord[Count_Input] = '6';Count_Input++;break;
					case 5:Write_LCD_PassWord[Count_Input] = '5';Count_Input++;break;
					case 4:Write_LCD_PassWord[Count_Input] = '4';Count_Input++;break;
					case 3:Write_LCD_PassWord[Count_Input] = '3';Count_Input++;break;
					case 2:Write_LCD_PassWord[Count_Input] = '2';Count_Input++;break;
					case 1:Write_LCD_PassWord[Count_Input] = '1';Count_Input++;break;
					default :break;
				}
			}
}
void Function_Key()			//功能按键
{	
	if(Count_Input == Length  && Number == 15 && Wirte_Flag == 0)		//确认密码,和确认设置密码
	{
		Judge_PassWord();		//判断密码	
	}
	else if(Count_Input == Length  && Number == 15 && Wirte_Flag == 1)	//确认修改密码
	{
		Change_PassWord();	//修改密码
	}
	
	if(Count_Input != 0 && Number == 12 && Count_Input != Length + 1) 		//回退一个密码
	{	
		Back_PassWord();		
	}
	if(Number == 13)		//初始化密码
	{
		Init_PassWord();
	}
	if((Count_Input == Length + 1 || Count_Input == 0) && Number == 14 && Success == 1 )			//设置密码
	{
		Set_PassWord();
	}
	Number = 99;//如果为0,那么0不会进入Key_Flag();
}
void PassWord()				//执行
{
	if(Key() != 0)			//调用密码键
	{		
		if(Number == 101)	//如果是101门铃,则不进行下面的操作,防止蜂鸣器再次发声
			DoorBell(1);
		else
		{			
		if(Wirte_Flag == 0)	//输入密码
		{
			Key_Number();
		}
		if(Wirte_Flag == 1)	//改写密码
		{
			Key_Number();
		}	
		Function_Key();		//调用功能键
		Buzzer();			//按键蜂鸣器	
	}
	}	
}

Timer0.c 定时器和中断

#include <REGX52.H>  //头文件
#include "PassWord.h"	
unsigned int T0Count,T0Count_LCD,T0Count_Read;//分别定义了定时器中断变量
extern bit Success;		//extern 是类似在别的文件定义了共享使用,数据互通,
void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;
	TMOD |= 0x01;		
	TL0 = 0x66;		
	TH0 = 0xFC;		
	EA = 1;
	PT0 = 0;
	TF0 = 0;		
	TR0 = 1;		
	ET0 = 1;

}

void Timer0_Routine() interrupt 1
{
	TL0 = 0x66;
	TH0 = 0xFC;		//定时器
	T0Count++;			//频率
	T0Count_LCD++;
	T0Count_Read++;
	if(T0Count_Read == 1000)	//每1000ms读取一次保存的密码
	{
		Read_PassWord();
		T0Count_Read = 0;
	}
	//下面是按键定时器消抖
	if(T0Count >= 20) //20s断一次
	{
		T0Count = 0;	//断一次后重置
		MatriKey();	//20ms调用一次按键驱动函数
	}
	//下面是每100ms更新一次显示屏显示的内容,防止太快出错,则选择100ms一次
	if(T0Count_LCD >= 100) //100ms断一次
	{
		T0Count_LCD = 0;	//断一次后重置
		LCD_PassWord();
	}
		PassWord();
}

Timer0.h文件

#ifndef __Timer0_H_
#define __Timer0_H_

void Timer0_Init(void);

#endif

LCD1602.c文件

#include <REGX52.H>

//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0

//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
	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_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
	LCD_RS=1;
	LCD_RW=0;
	LCD_DataPort=Data;
	LCD_EN=1;
	LCD_Delay();
	LCD_EN=0;
	LCD_Delay();
}

/**
  * @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));
	}
	else if(Line==2)
	{
		LCD_WriteCommand(0x80|(Column-1+0x40));
	}
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
	LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
	LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
	LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
	LCD_WriteCommand(0x01);//光标复位,清屏
}

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
	LCD_SetCursor(Line,Column);
	LCD_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
	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)
{
	unsigned char i;
	int Result=1;
	for(i=0;i<Y;i++)
	{
		Result*=X;
	}
	return Result;
}

/**
  * @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);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @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(Number1/LCD_Pow(10,i-1)%10+'0');
	}
}

/**
  * @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,unsigned int Number,unsigned char Length)
{
	unsigned char i,SingleNumber;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		SingleNumber=Number/LCD_Pow(16,i-1)%16;
		if(SingleNumber<10)
		{
			LCD_WriteData(SingleNumber+'0');
		}
		else
		{
			LCD_WriteData(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,unsigned int Number,unsigned char Length)
{
	unsigned char i;
	LCD_SetCursor(Line,Column);
	for(i=Length;i>0;i--)
	{
		LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
	}
}

PassWord .h文件

#ifndef __PassWord_H_
#define __PassWord_H_

void PassWord(void);	//执行
void LCD_PassWord(void);//LCD显示密码
void Function_Key(void);//功能按键
void Write_PassWord(void);//写入密码
void Read_PassWord(void);//读取密码
void Key_Number(void);//输入的密码
void Judge_PassWord(void);//判断密码
void Change_PassWord(void);//更改密码
void Back_PassWord(void);//回退密码
void Init_PassWord(void);//初始化清除密码
void Set_PassWord(void);//进入设置密码

#endif

LCD1602.h文件

#ifndef __LCD1602_H__
#define __LCD1602_H__

//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);

#endif

Beep.c文件

#include <REGX52.H>
#include "Delay.h"
#include "Beep.h"
#include "MatriKey.h"
#define LED P2
extern unsigned char Count_ERROR,Number;
extern bit ERROR,Success,Success_Flag;
bit Buzzer_Flag;
sbit Beep = P2^5;

void Key_Buzzer(unsigned char k)				//按键
{
		while(k--)
		{
			Beep = 0;
			Delay(1);			
			Beep = 1;
			Delay(1);
		}
}
void ERROR_Buzzer(unsigned char k,unsigned char Count)
{
			while(--k)
			{
				Beep = 0;
				Delay(1);				//音效
				Beep = 1;
				Delay(1);
				if(Count  && k%20 == 0 )
				{
					Delay(100);
					Count--;
				}
			}
}
void Success_Buzzer(unsigned char k,unsigned char Count)
{
		while(k--)
			{
				Beep = 0;
				Delay(1);				//音效
				Beep = 1;
				Delay(1);			
				if(Count == 2 && k == 20 )
					{
						Delay(300);
						Count--;
					}
				if(Count == 1 && k == 10 )
					{
						Delay(100);
						Count--;
					}	
			}	
}				
			
void Warning(unsigned char Count)
{
	unsigned int  i, j;
		 while(Count--)
			{
				for(i = 0; i < 300; i++)
					{
						for(j = 0; j < 150; j++); 
							Beep=~Beep; 
					}
		  LED=0xAA;
				for(i = 0; i < 300; i++)
					{
						for(j = 0; j < 300; j++);
							Beep = ~Beep;
					}
		  LED=0x55;
		 }
}
void DoorBell(unsigned char Count)
{
	unsigned int  i, j;
		 while(Count--)
			{
				for(i = 0; i < 500; i++)
					{
						for(j = 0; j < 130; j++); 
							Beep=~Beep; 
					}
		  LED=0xAA;
				for(i = 0; i < 200; i++)
					{
						for(j = 0; j < 300; j++);
							Beep = ~Beep;
					}
		  LED=0x55;
		 }
}
void Buzzer() //蜂鸣器提示音
{
	Key_Flag();
	if(Buzzer_Flag == 1)
	{	
		Key_Buzzer(20);
		Buzzer_Flag = 0;
	}
	if(ERROR == 1)
	{
		if(Count_ERROR != 3)
		{
			ERROR_Buzzer(40,1);
		}
		 if(Count_ERROR == 3)
		{
			Warning(5);			
			LED = 0xFF;
			Count_ERROR = 0;
		}	
		ERROR = 0;
	}
	if(Success_Flag == 1)
	{
		Success_Buzzer(40,2);
		Success_Flag = 0;
	}
}

Beep.h文件

#ifndef __Beep_H_
#define __Beep_H_

void Buzzer(void);
void Key_Buzzer(unsigned char k);
void ERROR_Buzzer(unsigned char k,unsigned char Count);
void Warning(unsigned char Count);
void DoorBell(unsigned char Count);
#endif

Delay.c文件

void Delay(unsigned int xms)
{
 	unsigned char i ,j;
	while(xms--)
	{
		i = 2;j = 239;
		do
		{
			while(--j);
		}	while(--i);
	}
}

Delay.h文件

#ifndef __DELAY_H__
#define __DELAY_H__ 

void Delay(unsigned int xms);

#endif

AT24C02.h包含I2C 文件

#include <REGX52.H>
sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;
// SLAVE ADDRESS+W为0xA0,SLAVE ADDRESS+R为0xA1
#define AT24C02_ADDRESS_READ		0xA0
#define AT24C02_ADDRESS_WRITE		0xA1

void I2C_Start(void)	//I2C开始
{
	//初始化全部置为高电平
	I2C_SDA = 1;
	I2C_SCL = 1;
	//先SDA,再SCL
	I2C_SDA = 0;
	I2C_SCL = 0;
}

void I2C_Stop(void)		//I2C停止
{	
	//SDA从低电平开始,所以=0,终止条件先SCL置高电平,SDA再高电平
	I2C_SDA = 0;
	I2C_SCL = 1;
	I2C_SDA = 1;
}

void I2C_SendByte(unsigned char Byte) //发送一个bite
{
	unsigned char i;
	for(i=0;i<8;i++)	//八次
	{
		I2C_SDA = Byte & (0x80 >> i);//寻位
		I2C_SCL = 1;	//切换
		I2C_SCL = 0;
	}
}

unsigned char I2C_ReceiveByte()	//接受Byte
{
	unsigned char i,Byte = 0x00;
	I2C_SDA = 1;//释放SDA
	
	for(i=0;i<8;i++)
	{
		I2C_SCL = 1;
		if(I2C_SDA)
		{
			Byte |= (0x80 >> i);
		}
		I2C_SCL = 0;
	}
	return Byte;
}

void I2C_SendAck(unsigned char AckBit) //发送答应
{
	I2C_SDA = AckBit;
	I2C_SCL = 1;
	I2C_SCL = 0;
}

unsigned char I2C_ReceiveAck(void)//接受答应
{
	unsigned char AckBit;
	I2C_SDA = 1;//释放SDA
	I2C_SCL = 1;
	AckBit = I2C_SDA;
	I2C_SCL = 0;
	return AckBit;
}


/**
  * @brief  AT24C02写入一个字节
  * @param  WordAddress 要写入字节的地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS_READ);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();
	I2C_SendByte(Data);
	I2C_ReceiveAck();
	I2C_Stop();
}

/**
  * @brief  AT24C02读取一个字节
  * @param  WordAddress 要读出字节的地址
  * @retval 读出的数据
  */
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
	unsigned char Data;
	I2C_Start();
	I2C_SendByte(AT24C02_ADDRESS_READ);
	I2C_ReceiveAck();
	I2C_SendByte(WordAddress);
	I2C_ReceiveAck();
	I2C_Start();
	// 读地址
	I2C_SendByte(AT24C02_ADDRESS_WRITE);
	I2C_ReceiveAck();
	Data=I2C_ReceiveByte();
	I2C_SendAck(1);
	I2C_Stop();
	return Data;
}

AT24C02.h文件

#ifndef __AT24C02_H_
#define __AT24C02_H_

void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char Byte);
unsigned char I2C_ReceiveByte(void);
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveAck(void);

void AT24C02_WriteByte(unsigned char WordAddress,Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);

#endif

MatriKey.c

#include <REGX52.H>
#include "Beep.h"
unsigned char Button,Number;
extern bit Buzzer_Flag;
bit Sound_Switch;
unsigned char Key(void)
{
	unsigned char Temp=0;
	Temp=Button;
	Number = Button;
	
	if(Number == 104)
		{
		Sound_Switch = ~Sound_Switch;
		}
	Button=0;
	return Temp;
}
bit Key_Flag(void)
{
		if(Sound_Switch == 0)//控制按键音效
		Buzzer_Flag = 1;
	return Buzzer_Flag;
}
unsigned char Key_GetState()
{
	unsigned char KeyNumber=0;
	
	P1 = 0xFF;
	P1_3 = 0;
	if(P1_7 == 0) {KeyNumber = 1;}
	if(P1_6 == 0) {KeyNumber = 5;}
	if(P1_5 == 0) {KeyNumber = 9;}
	if(P1_4 == 0) {KeyNumber = 13;}//"Clear" == 13

	P1 = 0xFF;
	P1_2 = 0;
	if(P1_7 == 0) {KeyNumber = 2;}
	if(P1_6 == 0) {KeyNumber = 6;}
	if(P1_5 == 0) {KeyNumber = 10;}//"*" == 10
	if(P1_4 == 0) {KeyNumber = 14;}//"Mod" == 14

	P1 = 0xFF;
	P1_1 = 0;
	if(P1_7 == 0) {KeyNumber = 3;}
	if(P1_6 == 0) {KeyNumber = 7;}
	if(P1_5 == 0) {KeyNumber = 11;}//"#" == 11
	if(P1_4 == 0) {KeyNumber = 15;}//"Deter" == 15
	
	P1 = 0xFF;
	P1_0 = 0;
	if(P1_7 == 0) {KeyNumber = 4;}
	if(P1_6 == 0) {KeyNumber = 8;}	
	if(P1_5 == 0) {KeyNumber = 12;}//"DEL" == 12
	if(P1_4 == 0) {KeyNumber = 16;}	
	
	
	if(P3_3 == 0) {KeyNumber = 104;}//K4
	if(P3_1 == 0) {KeyNumber = 101;}//K1
	
	return KeyNumber;
}

unsigned char MatriKey()
{
	static unsigned char NowState,LastState;
	LastState=NowState;				//按键状态更新
	NowState=Key_GetState();		//获取当前按键状态
	//如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
	if(NowState == 0)
	{
		switch(LastState)
		{
			case 1 	: Button=7; 	break;
			case 2 	: Button=8; 	break;
			case 3 	: Button=9; 	break;
			case 4 	: Button=12; 	break;
			case 5 	: Button=4; 	break;
			case 6 	: Button=5; 	break;
			case 7 	: Button=6; 	break;
			case 8 	: Button=13;	break;
			case 9 	: Button=1; 	break;
			case 10 : Button=2; 	break;
			case 11 : Button=3; 	break;
			case 12 : Button=14;	break;
			case 13 : Button=10;	break;
			case 14 : Button=16; 	break;
			case 15 : Button=11; 	break;
			case 16 : Button=15; 	break;
			case 104: Button=104;	break;
			case 101: Button=101;	break;
		}
	}
	return Button;
}

}

MatriKey.h文件

#ifndef __MatriKey_H_
#define	__MatriKey_H_

unsigned char MatriKey(void);
unsigned char Key(void);
unsigned char Key_GetState(void);
bit Key_Flag(void);

#endif

main主函数

#include <REGX52.H>
#include "Timer0.h"
#include "LCD1602.h"
void main()
{
	Timer0_Init();
	LCD_Init();
	LCD_ShowString(1,1,"PassWord:");
	while(1)
	{
		
	}
}

总结

这是我第一次写比较长的代码,变量命名或者函数命名可能不太规范或者不明了,希望各位见谅,慢慢提升自己,包括博客写作内容。大家如果需要参考个别代码,则可以在K5上创建两个文件,一个.c一个.h复制进去再引用头文件即可

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

送外卖的CV工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值