【项目1】基于C51的电子密码锁

项目介绍:

  • 使用 Proteus 仿真软件开发,硬件包括 AT89C51LCD1602、红、绿 LED。具有清 0 和修改密码的功能。
  • 通过矩阵按键的输入,在 LCD1602 上显示,用 * 来遮盖用户输入的密码。LCD1602 和红、绿 LED 显示密码的正确性。

硬件连接:

  • 初始密码是0123. 运行后一次按下,绿灯常亮3s.

  • 按下密码设置后,可以修改密码

  • 修改完密码,初始密码0123就是错误的了。

流程图:

主要代码:

main.c

#include <reg52.h>
#include "LM016L.h"
#include "Password.h"
#include "key.h"

u8 equality();
void complete();
uchar code str1[]="input code";	//输入提示
u8 code str2 = '*';				//遮盖输入的密码
u8 code str3[]="- - - -";		//密码位
u8 code str4[]="ERROR!";		
u8 code str5[]="SUCCESS!";
u8 code str6[]="SetNewPassword..."; //设置新密码
u8 Set=0;

u8 code ch[]="0123456789";
u8 pos=4;		// 密码输入 从 4 开始显示
u8 input_data[]="####", k=0; //输入缓存区 索引
u8 password[]="0123",j=0;   //初始密码
u8 blank[]="                     ";

sbit LCD0 = P2^3;	// 红灯
sbit LCD1 = P2^4;	// 绿灯


void main(){
	u8 key_value=16;
	init_1602();
	wstr_1602(3,0,str1);	// 提示语 从 3 列 开始 显示
	wstr_1602(4,1,str3);
	LCD0=1;
	LCD1=1;
	while(1){

		key_value = keyscan();
		if(key_value<10){			
			
			if(Set==0)	// 密码输入  用“*” 遮住
			{
				wchar_1602(pos,1,&str2);			// 用 “*”遮盖住 密码的输入
				input_data[k++] = ch[key_value];	// 将按键值写入 缓冲区
				complete();							// 比较缓冲区 与 密码区
			}	
			if(Set==1)	//密码修改  不遮盖输入
			{
				wchar_1602(j*2+4,1,&ch[key_value]);	 //
				password[j++] = ch[key_value];		// 把按键值 写入到 密码区
				if(j==4)	// 4位密码设置完成
				{ 
					j=0; Set=0;
					delay_ms(1000);
					init_1602();
					wstr_1602(0,0,blank);
					wstr_1602(3,0,str1);
					wstr_1602(4,1,str3);
				}
			}
			
			if(k==4){ 	// 4位按键值输入完 将缓冲区的值 清除, 用 “#”来清除
				input_data[0]='#'; 
				input_data[1]='#'; 
				input_data[2]='#'; 
				input_data[3]='#'; 
				k=0;
			}
			if(pos==10){	//密码输入的索引值,最大为10,即4位密码输入完成
				pos=2;
			}
			pos = pos+2;	//pos从4开始,每一次输入的按键值都在 +2 处
		}				
		else if(key_value==10){	//清0
			wstr_1602(4,1,str3);
			pos=4;
			input_data[0]='#'; 
			input_data[1]='#'; 
			input_data[2]='#'; 
			input_data[3]='#'; 
			k=0;
		}
		else if(key_value==11){		//设置密码
			Set=1;
			wstr_1602(0,0,str6);
		}
	}
}

// 密码输入正确 返回1
u8 equality(){
	if((input_data[0]==password[0])&&
		(input_data[1]==password[1])&&
		(input_data[2]==password[2])&&
		(input_data[3]==password[3])
	){
		return 1;
	}
	else
		return 0;
}

/*  */
void complete(){
	if(equality()&&(pos==10)){		//正确
		LCD1 = 0;		// 绿灯 亮
		LCD0 = 1;		// 红灯 灭
		wstr_1602(3,0,blank);
		wstr_1602(4,1,blank);
		wstr_1602(3,0,str5);
		
		delay_ms(3000);
		init_1602();
		wstr_1602(3,0,str1);
		wstr_1602(4,1,str3);
		LCD0=1;			// 关闭 红灯
		LCD1=1;			// 关闭 绿灯
	}else if(~equality()&&(pos==10)){	//错误
		LCD0 = 0;
		LCD1 = 1;
		wstr_1602(3,0,blank);
		wstr_1602(4,1,blank);
		wstr_1602(3,0,str4);
		
		delay_ms(3000);
		init_1602();
		wstr_1602(3,0,str1);
		wstr_1602(4,1,str3);
		LCD0=1;
		LCD1=1;
	}
}


LM016L.h

#ifndef __LM016L_H__
#define __LM016L_H__

#include <reg52.h>
#include <intrins.h>
//#include "delay.h"



//定义使能端口
sbit RS = P2^0;
sbit RW = P2^1;
sbit E  = P2^2;
#define  DB  P0	//数据总线 8位


void init_1602();
void wdata_1602(u8 dat);
void wreg_1602(u8 com);
void busy_1602();
void wstr_1602(u8 x, u8 y, u8 *str);
void wchar_1602(u8 x, u8 y, u8 *ch);
void lcd_delay();


#endif

LM016.c

#include "LM016L.h"

void lcd_delay(){
	u16 i;
	for(i<=0;i<=10000;i++){
			_nop_();
	}
}


void busy_1602(){
	RS = 0;	//命令寄存器
	RW = 1; //读	
	E = 1;
	_nop_();
	while(DB&0x80);	
	E = 0;
	//bit7 为1表示忙
}

void init_1602()			 //函数功能:设置LCD_1602的开显示 光标不闪烁等的功能 
{
   wreg_1602(0x38);			//指令6,8位数据总线,双行显示,每位采用5*7点阵  指令6
   wreg_1602(0x08);			//指令4,关显示,关光标,无闪烁                  指令4	
   wreg_1602(0x06);			//指令3,光标自动右移,文字不移动                指令3
   wreg_1602(0x01);			//指令1,清显示                                  指令1
   wreg_1602(0x0c);			//指令4,开显示                                  指令4

}




void wreg_1602(u8 com)	 //函数功能:写指令函数 
{

	//busy_1602();		//不注释掉,LCD不显示
   RS=0;		//当RS=0,RW=0时,表明写入的是命令
   RW=0;
   DB=com;		//当使能由高到低时,LCD执行相应命令
   _nop_();
   E=1;
   _nop_();
   E=0;
}



void wdata_1602(u8 dat)	 //函数功能:写数据函数 
{
   busy_1602();
   RS=1;		//当RS=1,RW=0时,表明写入的是数据
   RW=0;
   DB=dat;		//当使能由高到低时,LCD执行相应命令
   _nop_();
   E=1;
   _nop_();
   E=0;
}




/*
 * x为横坐标:0~40  y为纵坐标0~1
 *  *str是需要显示的字符串
*/
void wstr_1602(u8 x, u8 y, u8 *str){	
	busy_1602();
	if(y)	x |= 0xC0;	//第二行p
	 	x |= 0x80;	//第一行
	wreg_1602(x);	
	while(*str != '\0'){
		wdata_1602( *str++ );//str先取值*str, 再str++
	}
}



void wchar_1602(u8 x, u8 y, u8 *ch){
	busy_1602();
	if(y)	x |= 0xc0;	//第二行p
	 	x |= 0x80;	//第一行
	wreg_1602(x);	//写命令和写数据之间,必须要延时,并且足够大。
	lcd_delay();
	//lcd_delay();
	wdata_1602(*ch);
}

	

key.h

#ifndef __KEY_H__
#define __KEY_H__

#include <reg52.h>
#include <stdio.h>
#include <intrins.h>
#include "delay.h"


u8 keyscan();
u8 keyscan1();
u8 keydown(void);

#endif

key.c

#include "key.h"


// 老师的按键扫描函数
//所以返回的是按键按下后的P1端口的值。 若按键按下,8位有2位是0,6位是1.
/*
u8 keyscan1(){	//P1 低位控制行,高位控制列
	u8 sccode, recode;
	//u8 i=0;	//按键按下的是第几行,即while循环到第几次
	P1 = 0xf0;	//行扫描
	if( (P1&0xf0) != 0xf0){	//有按键按下 P1就会变化,与原来相与 就会变化   P1和0xf不一样就会进入
		delay_ms(20);		//消抖
		if( (P1&0xf0) != 0xf0){	//消抖后依然保持按下的状态,确实是按下了
			sccode = 0xfe;		//逐行扫描初值	
			while( (sccode&0x10) != 0){	//sccode第5位不为0就进入		sccode!=0x10
				P1 = sccode;			//输出行扫描码
				if( (P1&0xf0) != 0xf0){	//本行有按键按下 	P1的高四位有1变成0
					recode = (P1&0xf0) | 0x0f;		//保留P1的高4位,低四位强制置1
					return ( (~sccode)+(~recode) );		//sccode高四位全1,第四位是按键按下的那一时刻的值。 recode高四位是按下按键后的,第四位全1.
				}
				else {
					sccode = (sccode<<1)|0x01;	//行扫描码左移一位。 循环左移,与_ctol_函数功能一样
					//i++;
					//i%=4;
				}					
			}
		}
	}
	return 0;	//无按键按下返回0
	//return 16;	//无按键按下返回16
}
*/



//	0 1 2 3				返回值 0  1  2  3
//	4 5 6 7			  		  4  5  6  7
//	8 9 清0 密码设置			  8  9  10 11
//	-- -- -- --		  		  12 13 14 15
u8 keyscan()//按键扫描函数:要去抖,若有按键按下,返回对应的按键值(0-15),没有按键按下返回16
{
    u8 i,row,temp;
    u8 key=16;  //按键号,初值设置为16,目的是:没有按键按下时返回16;
                //若不设初值(默认值为0),没有按键按下时,将返回0,会误认为0被按下  
    row=0xef; //从第一列开始  
	P1=0xff;       
    for(i=0;i<4;i++)
    {
	    //P1=0xff;  
	    P1=row;	//第i列信号,对应列为低,其他全为高
	    row=_crol_(row,1); 	  //生成下一列信号
        temp=P1; //读入扫描信号
        temp=temp&0x0f; //屏蔽高4位列信号,只保留低4位行信号 
        if(temp!=0x0f)//有按键被按下,因为第i列某行有按键按下,则低4位中有一位为低  
        {  
            delay_ms(20);  //延时去抖
            temp=P1;  
            temp=temp&0x0f;  
            if(temp!=0x0f)   //再次确认有按键被按下
            {  
                switch(temp)  //根据低4位行信号,判断哪个按键被按下
                {  
                    case 0x0e:key=0+i;break; //第i列第1行按键被按下 
                    case 0x0d:key=4+i;break; //第i列第2行按键被按下  
                    case 0x0b:key=8+i;break; //第i列第3行按键被按下
                    case 0x07:key=12+i;      //第i列第4行按键被按下 
                }                   
                do
                {
                        temp=P1;  	    //再次扫描按键
                        temp=temp&0x0f;  
                }
                while(temp!=0x0f); //等待按键释放   
            }  
        }
    }  
    return(key);//扫描结束,返回按键值
}





delay.c

#include "delay.h"


void delay_us(u16 t){	//us级	4.5*t us
	while(t--);	
}


void delay_ms(u16 x){	//ms
	int j,i;
	for(i=0;i<x;i++)
    {
	    for(j=0;j<115;j++);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值