C51 Microcomputer Combination Lock Course Design

Tip: After the article is written, the table of contents can be automatically generated in bold style. How to generate it can refer to the help document on the right


Design requirements

The course design of microcomputer principle and single chip application technology, C51 design a combination lock;

Requirements: There are 0-9 number keys on the keyboard, function keys: confirm and cancel, etc., and compound keys can be set. The number of digits of the password and the password can be set arbitrarily. When the input number is the same as the set password, the lock will be opened, otherwise it cannot be opened.
Basic implementation: the password will not be saved after power failure, and the initial password will be restored;
improved implementation: the password will not be lost after power failure, and any password can be entered through the keyboard

1. System model design

Proteus simulation diagram
insert image description here
The attached picture is to improve the simulation in the implementation, the difference of the basic implementation is that there is one less AT24c02 chip in the lower right corner.

2. Basic implementation

2.1 Program structure

The program mainly has four functions:

void delay()					//延时函数
void display()					//数码管显示函数
void key_scan()					//键盘扫描函数
void main()						//
void time_count() interrupt 1	//中断定时函数

Initialization code:

#include <reg51.h>
#define keyboard P2

unsigned char code table[]={
    
    0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
unsigned char code lig[]={
    
    0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};			//P1引脚选位
unsigned int code number[4][3]={
    
    {
    
    7,8,9},{
    
    4,5,6},{
    
    1,2,3},{
    
    0,0,0}};			//键盘查表
unsigned char show[8]={
    
    0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf,0xbf};			//数码管只显示show中的内容,数码管默认显示‘横杠’
unsigned char  keywords[21] = {
    
    1,2,3,4};						//初始密码
unsigned char input[21] = {
    
    0};								//承载输入密码
unsigned char a=0,b,c,d,j,k,temp,key,M,N,clc,enter,change,count=0,number_1=60,z,pp = 3;	//寄存器的定义	**clc,enter,change 这三个我将其称为状态寄存器,当对应按键按下的时候置1;pp密码位数,初始密码更改时要同步更改
signed int num=-1,lock=1;	//lock和上述的clc一样,都是状态寄存器,lock=1时锁定,0解锁;num为密码输入时候的位数辅助计数,注意初始为-1

sbit led1=P3^3;			//红灯--锁定指示灯
sbit led2=P3^4;			//绿灯--解锁指示灯

void delay(unsigned char o)
{
    
    
	while(o--);
}

void display()					//8位数码管显示输出
{
    
    
		unsigned int i;
		
		for(i=0;i<8;i++)
		{
    
    	
			P1 = lig[i];		//多位数码管选位
			P0 = show[i];		//数码管显示内容
			delay(100);
			P0 =0xff;
		}
}

2.2 4×4 keyboard scan

Keyboard scan code:

void key_scan()						//键盘按键检测
{
    
    	
	P2=0x0f;						
	delay(10);
	if(keyboard == 0x0f)			//a用作按钮松手检测
		a=0;
	if(a == 0)		
	{
    
    
		P2=0x0f;
		temp=P2;
		temp=temp&0x0f;
		if(temp!=0x0f)
		{
    
    
			delay(10);
			switch(temp)
			{
    
    
				case 0x0e:N=0;break;
				case 0x0d:N=1;break;
				case 0x0b:N=2;break;
				case 0x07:N=3;break;
			 }
			P2=0xf0;
			temp=P2;
			temp=temp&0xf0;
			if(temp!=0xf0)
			{
    
    
				delay(10);
				switch(temp)
				{
    
    
					case 0xe0:M=0;a=1;break;
					case 0xd0:M=1;a=1;break;
					case 0xb0:M=2;a=1;break;
					case 0x70:M=3;a=1;break;
				}
				if(M<3)
				{
    
    
					key=number[N][M];		//查表确定按键对应的数
					num++;					//输入位数统计
				}
				else
				{
    
    
					switch(N)				//对应最后一列按键
					{
    
    
						case 0:change=1;break;		//功能按键置位对应的状态寄存器
						case 1:clc=1;break;
						case 2:enter=1;break;
						case 3:lock=1;break;
					}
				}
			}
		}
	}				

}

2.3 Processing function (data processing function)

The content of data processing is placed in this function

void processing()
{
    
    
	if(num>-1)																						//输入input数组转换为显示数组
	{
    
    	
		input[num]=key;				//输入密码进入input数组中
		
		if(num>-1 && num<7)			
		{
    
    
			show[num]=table[key];	//将输入的密码实时放入show数组中并显示在数码管上;**注意放入show中的是对应显示数字的8位二进制数不是密码本身
		}
		if(num>7 && num != b)
		{
    
    
			for(c=0;c<8;c++)
			{
    
    
				show[c] = table[input[num-7+c]];	//在密码超出8位后数码管显示内容跟着滚动
			}
		}
		b = num;						//实现每按一次数码管内容刷新一次
	}
	
	if(lock == 1 && enter == 1 )			//在锁定状态下,按确认键开锁
	{
    
    
		for(z = 0;z < pp+1;z++)				
		{
    
    
			if(input[z] == keywords[z])		//比较输入的密码与设定的是否一致
				d = 1;
			if(input[z] != keywords[z])		
			{
    
    
				d = 0;
				break;
			}
		}
		if(pp != num)						//判断密码位数是否一样
			d = 0;
		
		if(d)								//设备解锁
		{
    
    
			lock = 0;
			number_1 = 30;					//启动30秒倒计时锁定
		}
		for( z = 0;z<21;z++)				//无论解锁与否都对input和show复位
			{
    
    input[z] = 0;}
		for( z = 0;z<8;z++)
			{
    
    show[z]=0xbf;}
		num = -1;
		d = 0;
		enter = 0;							//状态寄存器复位
	}
	
	if(clc)									//清除键按下																//清除键
	{
    
    
		for( z = 0;z<21;z++)
			{
    
    input[z] = 0;}
		for( z = 0;z<8;z++)
			{
    
    show[z]=0xbf;}

		num = -1;
		clc = 0;
	}
	if(lock == 0 && change == 1)			//在开锁情况下按更改密码
	{
    
    	
		for(z=0;z<21;z++)
		{
    
    
			keywords[z] = input[z];
		}
		lock = 1;							//更改密码后设备自动锁定
		for( k = 0;k<8;k++)
			{
    
    show[k]=0xbf;}
		for( k = 0;k<21;k++)
			{
    
    input[k] = 0;}
		pp = num;							//更改密码位数
		num = -1;
		change = 0;	
	}
	
	if(lock == 0 && number_1 == 0)			//倒计时结束后锁定
		lock = 1;
}

2.4 Main function and interrupt timing function

void main()
{
    
    	
	EA=1;								//系统初始化寄存器
	ET0=1;
	TMOD=0x01;
	TH0=(65536-50000)/256;
	TL0=(65536-50000)%256;
	TR0=1;
	while(1)
	{
    
    
		display();
		key_scan();
		processing();
		if(lock == 1)					//指示灯
		{
    
    
			led1 = 0;
			led2 = 1;}
		else
		{
    
    
			led1 = 1;
			led2 = 0;}
	}
}

void time_count() interrupt 1		//定时器中断实现延时
{
    
    
	TH0=(65536-50000)/256;
	TL0=(65536-50000)%256;
	count++;
	if(count==20)
	{
    
    
		count=0;
		number_1--;
		if(number_1<0)
			number_1=59;
	}
}

3. Improve the realization

illustrate

The boost implementation differs from the base implementation in two ways.
(1) In the main function, the system starts and reads the password from AT24C02 and puts it into keywords;
(2) When changing the password, there is an additional step of writing the password to AT24C02
(3) Write the function of reading and writing AT24C02
Attach the above code

change password section

if(lock == 0 && change == 1)									//在开锁情况下按更改密码
	{
    
    	
		number_1 = 20;
		for(z=0;z<8;z++)
		{
    
    
			keywords[z] = input[z];
		}
		qq = num;
		
		At24c02Write(0x50, keywords);							//密码写入24C02
		delay(1000);
		
		lock = 1;
		for( k = 0;k<8;k++)
			{
    
    show[k]=0xbf;}
		for( k = 0;k<8;k++)
			{
    
    input[k] = 0;}
		num = -1;
		change = 0;
	}

main function part

void main()							//主程序
{
    
    	
	EA=1;							//系统初始化寄存器
	ET0=1;
	IT0=0;
	TMOD=0x01;
	TH0=(65536-50000)/256;
	TL0=(65536-50000)%256;
	TR0=1;
	
//	At24c02Write(0x50, keys);		//keys[]为写入芯片的初始密码,应当在程序开头定义
//	Delay10us();					//用来第一次运行的时候写进24C02(由于AT24C02芯片第一次没有密码数据在里面),在第一次运行后将这两行注释,从新编译一次就可以正常运行
	At24c02Read(keywords,0x50);		//启动读取密码
	
	while(1)
	{
    
    
		display();
		key_scan();
		processing();
		if(lock == 1)				//指示灯程序
		{
    
    
			led1 = 0;
			led2 = 1;}
		else
		{
    
    
			led1 = 1;
			led2 = 0;}
	}
}

AT24C02 read and write code

There is no direct function call for reading and writing 89C51 of AT24C02, you can only write communication functions through pins according to the manual.
Reading and writing of AT24C02 The following two blogs are well written. Readers who want to learn AT24C02 can move to

https://blog.csdn.net/snyanglq/article/details/50408489?
https://blog.csdn.net/zbl12345678910/article/details/106029054

The read and write code is attached below

/**********************************
**	函数名称 :ACK
**	函数功能 : 应答信号
**********************************/
void ACK()			//发送应答A子函数ACK
{
    
    
	unsigned char i;
	SCL = 1;		//时钟线发出时钟脉冲
	Delay10us();
	while((SDA == 1) && (i < 200))i++ ;
	SCL = 0;		//与SCL=1组成时钟脉冲
	Delay10us();
}
/**********************************
**	函数名称 :NACK
**	函数功能 : 非应答信号
**********************************/
void NACK()			//发送应答A子函数NACK,见实例354
{
    
    

	SDA = 1;		//数据线高电平(发送数据1)
	SCL = 0;
	Delay10us();
	SCL = 1;		//时钟线发出时钟脉冲
	Delay10us();
	SCL = 0;		//与SCL=1组成时钟脉冲
	SDA = 0;		//数据线低电平复位
}
 
/*******************************************************************************
* 函数名         : Start_signal(void)
* 函数功能		 : I2C总线起始信号
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
 
void I2C_Start_signal(void)
{
    
    
	 SDA = 1 ;
	 Delay10us();
	 SCL = 1 ;
	 Delay10us();
	 SDA = 0 ;
	 Delay10us();
	 SCL = 0 ;
	 Delay10us();
}
 
/*******************************************************************************
* 函数名         : Start_signal(void)
* 函数功能		 : I2C总线终止信号
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
 
void I2C_Stop_signal(void)
{
    
    
	SDA = 0 ;
	Delay10us();
   	SCL = 1 ;
	Delay10us();
	Delay10us();
	SDA=1;
	Delay10us();
}
 
/*******************************************************************************
* 函数名         : I2C_SendByte(unsigned char dat, unsigned char ack)
* 函数功能		 : I2C总线发送数据
* 输入           : dat,一个字节的数据
* 输出         	 : 发送成功返回1,发送失败返回0
* 备    注       : 发送完一个字节I2C_SCL=0, 需要应答则应答设置为1,否则为0
*******************************************************************************/
 
unsigned char I2C_SendByte(unsigned char dat, unsigned char ack)
{
    
    
 
 
 
	unsigned char a = 0,b = 0;//最大255,一个机器周期为1us,最大延时255us。
Replay:
    b = 0 ;			
	for(a=0; a<8; a++)//要发送8位,从最高位开始
	{
    
    
		SDA = dat >> 7;	 //起始信号之后I2C_SCL=0,所以可以直接改变I2C_SDA信号
		dat = dat << 1;
		Delay10us();
		SCL = 1;
		Delay10us();//建立时间>4.7us
		SCL = 0;
		Delay10us();//时间大于4us		
	}
 
	SDA = 1;
	Delay10us();
	SCL = 1;
	while(SDA && (ack == 1))//等待应答,也就是等待从设备把I2C_SDA拉低
	{
    
    
		b++;
		if(b > 200)	 //如果超过200us没有应答发送失败,或者为非应答,表示接收结束
		{
    
    
			SCL = 0;
			Delay10us();
		//	return 0;
			goto Replay ;   //如果超过200us没有应答则发送失败,或者为非应答,这时候系统启动重发机制
							//使用goto语句返回到上面接着发
		}
	}
 
	SCL = 0;
	Delay10us();
 	return 1;		
}
 
/*******************************************************************************
* 函数名         : I2cReadByte()
* 函数功能		 : I2C总线接收数据
* 输入           : 无
* 输出         	 : dat,数据
*******************************************************************************/
 
unsigned char I2cReadByte()
{
    
    
	unsigned char a=0,dat=0;
	SDA=1;			
	Delay10us();
	for(a=0;a<8;a++)//接收8个字节
	{
    
    
		SCL=1;
		Delay10us();
		dat<<=1;
		dat|=SDA;
		Delay10us();
		SCL=0;
		Delay10us();
	}
	return dat;	
}
 
/*******************************************************************************
* 函数名         : Delay10us()
* 函数功能		 : 延时
* 输入           : 无
* 输出         	 : 无
*******************************************************************************/
 
void Delay10us()
{
    
    
   unsigned char a,b;
	for(b=1;b>0;b--)
		for(a=2;a>0;a--);
}
 
/*******************************************************************************
* 函 数 名         : void At24c02Write(unsigned char addr,unsigned char dat)
* 函数功能		   : 往24c02的8个地址写入8个数据
* 输    入         : 地址和数据
* 输    出         : 无
*******************************************************************************/
 
void At24c02Write(unsigned char addr,unsigned char dat[])
{
    
    
	unsigned char i;
   	I2C_Start_signal();
	I2C_SendByte(0xa0, 1);//发送写器件地址
	I2C_SendByte(addr, 1);//发送要写入内存地址
	for(i = 0; i < 8; i++)
		I2C_SendByte(dat[i], 0);	//发送数据
	I2C_Stop_signal();
 
}
 
/*******************************************************************************
* 函 数 名         : unsigned char At24c02Read(unsigned char addr)
* 函数功能		   : 读取24c02的一个地址的一个数据
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/


void At24c02Read(unsigned char b[], unsigned char addr)
{
    
    
	unsigned char i;
	for(i = 0; i < 7; i++)
	{
    
    
		I2C_Start_signal();
		I2C_SendByte(0xa0, 1); 			//发送写器件地址	   1010   0000
		I2C_SendByte(addr + i, 1); 		//发送要读取的地址
		I2C_Start_signal();
		I2C_SendByte(0xa1, 1); 			//发送读器件地址
		b[i] = I2cReadByte();	 		//读取数据
		ACK();	
	}
	I2C_Start_signal();
	I2C_SendByte(0xa0, 1); 				//发送写器件地址	   1010   0000
	I2C_SendByte(addr + i, 1);			 //发送要读取的地址
	I2C_Start_signal();
	I2C_SendByte(0xa1, 1); 				//发送读器件地址
	b[i] =  I2cReadByte();
	NACK();
	I2C_Stop_signal();
	Delay10us();
}

*Because the AT24C02 read and write function only reads and writes 8 bits at a time, so the password in the implementation is only set to 8 bits by default. If more passwords are needed, more passwords can be easily set by multiple reads and splicing.

Summarize

The complete code and simulation files of the above-mentioned course design can be downloaded. The parts that are not cleared in the article can be further understood in combination with the program. I hope it will be helpful to everyone.

https://download.csdn.net/download/canmianlaida/85656284

Guess you like

Origin blog.csdn.net/canmianlaida/article/details/125312169