基于51单片机的密码锁程序

基于51单片机的密码锁程序,第1张

用STC52编的,下面是C程序,调试已经成功,自己看程序吧……

#include<reg52.h>

#include <intrins.h>

#define uchar unsigned char

#define uint unsigned int

#define LCD_data P0

sbit SDA=P3^5

sbit SCL=P3^4//24C08控制口设置

sbit LCD_RS = P3^3 //寄存器选择输入

sbit LCD_RW = P3^6 //液晶读/写控制

sbit LCD_EN = P3^7 //液晶使能控制

sbit LCD_PSB = P3^2 //串/并方式控制

sbit FM=P2^4//蜂鸣器控制口

sbit RS=P2^5

sbit T_CLK = P2^0//实时时钟时钟线引脚 //

sbit T_IO = P2^1//实时时钟数据线引脚 //

sbit T_RST = P2^2//实时时钟复位线引脚 //

sbit ds=P2^3

sbit EN=P2^6

sbit ZZ=P2^7

sbit FZ=P3^1

sbit ACC0=ACC^0

sbit ACC7=ACC^7

uint temp1,s_temp //定义整形变量

float f_temp //定义浮点型变量

uchar time[]=" : : "

uchar day[]=" 20 / / ( ) "

uchar temp0[]=" 温度: . 度 "

uchar num,num1,flag,count,a,b

uchar unlock_i//解密标志位

uchar t[4]

uchar t1[4]

void delay_ms(uint z)//长延时

{

uint x,y

for(x=zx>0x--)

for(y=110y>0y--)

}

void delay() //短延时,大约5us

{

}

void reshi()

{

if(RS==1)

{ unlock_i=1

}

else

{

unlock_i=0

}

}

uchar code mima[]={'0','1','2','3','4','5','6','7','8','9','0','*'}

void lcd_xieping0(uchar x,uchar y,uchar date)

void lcd_xieping(uchar x,uchar y,uchar *str)

//********************************************************

//开机显示

//********************************************************

void kjxs()

{

uint i,j

lcd_xieping(0,0,"****************")

lcd_xieping(1,0,"欢迎进入")

lcd_xieping(2,0," 密码锁系统! ")

lcd_xieping(3,0,"****************")

delay_ms(4000)

lcd_xieping(0,0," 系统初始化中 ")

lcd_xieping(1,0,"请稍后…")

lcd_xieping(2,0,"————————")

lcd_xieping(3,0," ")

for(j=3j>0j--)

{

for(i=0i<8i++)

{

lcd_xieping(3,i,"*")

delay_ms(250)

}

lcd_xieping(3,0,"")

}

}

//********************************************************

//12864显示

//********************************************************

void write_cmd(uchar cmd)

{

LCD_RS = 0

LCD_RW = 0

LCD_EN = 0

P0 = cmd

delay_ms(5)

LCD_EN = 1

delay_ms(5)

LCD_EN = 0

}

void write_dat(uchar dat)

{

LCD_RS = 1

LCD_RW = 0

LCD_EN = 0

P0 = dat

delay_ms(5)

LCD_EN = 1

delay_ms(5)

LCD_EN = 0

}

void lcd_xieping0(uchar x,uchar y,uchar date)

{

switch(x)

{

case 0: write_cmd(0x80+y)break

case 1: write_cmd(0x90+y)break

case 2: write_cmd(0x88+y)break

case 3: write_cmd(0x98+y)break

}

write_dat(date)

}

void lcd_xieping(uchar x,uchar y,uchar *str)

{

switch(x)

{

case 0: write_cmd(0x80+y)break

case 1: write_cmd(0x90+y)break

case 2: write_cmd(0x88+y)break

case 3: write_cmd(0x98+y)break

}

while (*str)

{

write_dat(*str)

str++

}

}

void lcd_init()

{

LCD_PSB = 1//并口方式

write_cmd(0x30) //基本指令 *** 作

delay_ms(5)

write_cmd(0x0C) //显示开,关光标

delay_ms(5)

write_cmd(0x01) //清除LCD的显示内容

delay_ms(5)

}

//**************************************************************

// 键盘扫描函数

//**************************************************************

uchar keyscan1()//矩阵键盘扫描函数

{

uchar temp

while(!num)

{P1=0xfe//赋值

temp=P1//读回数据

temp=temp&0xf0 //与运算

if(temp!=0xf0) //判断

{

delay_ms(2) //延时消抖

temp=P1//读回数据

temp=temp&0xf0

if(temp!=0xf0)

{

switch(temp) //多分支选择

{

case 0x70:num=1break //跳出

case 0xb0:num=2break

case 0xd0:num=3break

case 0xe0:num=4break

}

while(temp!=0xf0)

{

temp=P1

temp=temp&0xf0

}//等待按键释放

}

}

P1=0xfd//赋值

temp=P1//读回数据

temp=temp&0xf0 //与运算

if(temp!=0xf0) //判断

{

delay_ms(2) //延时消抖

temp=P1//读回数据

temp=temp&0xf0

if(temp!=0xf0)

{

switch(temp) //多分支选择

{

case 0x70:num=5break //跳出

case 0xb0:num=6break

case 0xd0:num=7break

case 0xe0:num=8break

}

while(temp!=0xf0)

{

temp=P1

temp=temp&0xf0

}//等待按键释放

}

}

P1=0xfb//赋值

temp=P1//读回数据

temp=temp&0xf0 //与运算

if(temp!=0xf0) //判断

{

delay_ms(2) //延时消抖

temp=P1//读回数据

temp=temp&0xf0

if(temp!=0xf0)

{

switch(temp) //多分支选择

{

case 0x70:num=9break //跳出

case 0xb0:num=10break

case 0xd0:num=11break

case 0xe0:num=12break

}

while(temp!=0xf0)

{

temp=P1

temp=temp&0xf0

}//等待按键释放

}

}

}

return(num)//返回值

}

uchar keyscan2()

{

uchar temp

while(!num1)

{P1=0xf7//赋值

temp=P1//读回数据

temp=temp&0xf0 //与运算

if(temp!=0xf0) //判断

{

delay_ms(2) //延时消抖

temp=P1//读回数据

temp=temp&0xf0

if(temp!=0xf0)

{

switch(temp) //多分支选择

{

case 0x70:num1=1break //跳出

case 0xb0:num1=2break

case 0xd0:num1=3break

case 0xe0:num1=4break

}

while(temp!=0xf0)

{

temp=P1

temp=temp&0xf0

}//等待按键释放

}

}

}

return(num1)

}

//**************************************************************

//直流电机

//**************************************************************

void dianjiZZ()

{

EN=1

ZZ=1

FZ=0

}

void dianjiFZ()

{

EN=1

ZZ=0

FZ=1

}

void dianji_stop()

{

EN=0

}

//**************************************************************

//EPPROM

//**************************************************************

void start() //启动信号

{

SDA=1

delay()

SCL=1

delay()

SDA=0

delay()

}

void stop()//停止信号

{

SDA=0

delay()

SCL=1

delay()

SDA=1

delay()

}

void respons() //响应信号

{

uchar i

SCL=1

delay()

while((SDA==1)&&(i<250))

i++

SCL=0

delay()

}

void writebyte(uchar date) //写一个字节

{

uchar i,temp

temp=date

for(i=0i<8i++)

{

temp=temp<<1

SCL=0

delay()

SDA=CY

delay()

SCL=1

delay()

}

SCL=0

delay()

SDA=1//释放总线

delay()

}

uchar readbyte() //读一个字节

{

uchar i,k

SCL=0

delay()

SDA=1

for(i=0i<8i++)

{

SCL=1

delay()

k=(k<<1)|SDA

SCL=0

delay()

}

delay()

return(k)

}

void write(uchar add,uchar date) //在一个地址写一个字节

{

start()

writebyte(0xa0)

respons()

writebyte(add)

respons()

writebyte(date)

respons()

stop()

}

uchar read(uchar add) //在一个地址读一个字节

{

start()

writebyte(0xa0)

respons()

writebyte(add)

respons()

start()

writebyte(0xa1)

respons()

b=readbyte()

respons()

stop()

return(b)

}

//**************************************************************

//时间日期函数

//**************************************************************

void v_WTInputByte(uchar ucDa)

{

uchar i

ACC= ucDa

for(i=8i>0i--)

{

T_IO = ACC0//*相当于汇编中的 RRC

T_CLK = 1

T_CLK = 0

ACC =ACC>>1

}

}

uchar uc_RTOutputByte(void)

{

uchar i

for(i=8i>0i--)

{

ACC = ACC>>1//*相当于汇编中的 RRC

ACC7 = T_IO

T_CLK = 1

T_CLK = 0

}

return(ACC)

}

void v_W1302(uchar ucAddr, uchar ucDa)

{

T_RST = 0

T_CLK = 0

T_RST = 1

v_WTInputByte(ucAddr)/* 地址,命令 */

v_WTInputByte(ucDa)/* 写1Byte数据*/

T_CLK = 1

T_RST =0

}

uchar uc_R1302(uchar ucAddr)

{

uchar ucDa

T_RST = 0

T_CLK = 0

T_RST = 1

v_WTInputByte(ucAddr)// 地址,命令 //

ucDa = uc_RTOutputByte()// 读1Byte数据 //

T_CLK = 1

T_RST =0

return(ucDa)

}

void Init1302(void)

{

v_W1302(0x8e,0x00) //控制写入WP=0

v_W1302(0x80,0x80)

v_W1302(0x90,0xa9)

v_W1302(0x80,0x00) //秒

v_W1302(0x82,0x24) //分

v_W1302(0x84,0x12) //时

v_W1302(0x86,0x29) //日

v_W1302(0x88,0x10) //月

v_W1302(0x8a,0x05) //星期

v_W1302(0x8c,0x10) //年 //

v_W1302(0x8e,0x80)

}

void donetime(void)

{

uchar d

d=uc_R1302(0x87)

day[10]=(d&0x0f)+48

day[9]=((d>>4)&0x03)+48

d=uc_R1302(0x89)

day[7]=(d&0x0f)+48

day[6]=((d>>4)&0x01)+48

d=uc_R1302(0x8b)

day[13]=(d&0x07)+48

d=uc_R1302(0x8d)

day[4]=(d&0x0f)+48

day[3]=(d>>4)+48

d=uc_R1302(0x81)

time[15]=(d&0x0f)+48

time[14]=(d>>4)+48

d=uc_R1302(0x83)

time[12]=(d&0x0f)+48

time[11]=(d>>4)+48

d=uc_R1302(0x85)

time[9]=(d&0x0f)+48

time[8]=(d>>4)+48

}

//**************************************************************

//温度检测函数

//**************************************************************

void dsreset(void)//18B20复位,初始化函数

{

uint i

ds=0

i=103

while(i>0)i--

ds=1

i=4

while(i>0)i--

}

bit tempreadbit(void) //读1位函数

{

uint i

bit dat

ds=0i++ //i++ 起延时作用

ds=1i++i++

dat=ds//读数据

i=8while(i>0)i--

return (dat)

}

uchar tempread(void) //读1个字节

{

uchar i,j,dat

dat=0

for(i=1i<=8i++)

{

j=tempreadbit()

dat=(j<<7)|(dat>>1) //读出的数据最低位在最前面,这样刚好一个字节在DAT里

}

return(dat)

}

void tempwritebyte(uchar dat) //向18B20写一个字节数据

{

uint i

uchar j

bit testb

for(j=1j<=8j++)

{

testb=dat&0x01 //判断最后一位是1还是0

dat=dat>>1

if(testb) //写 1

{

ds=0

i++i++

ds=1

i=8while(i>0)i--

}

else

{

ds=0 //写 0

i=8while(i>0)i--

ds=1

i++i++

}

}

}

void tempchange(void) //DS18B20 开始获取温度并转换

{

dsreset()//初始化,每次对18B20的 *** 作都首先要初始化

delay_ms(1)

tempwritebyte(0xcc) // 写跳过读ROM指令

tempwritebyte(0x44) // 写温度转换指令

}

void get_temp() //读取寄存器中存储的温度数据

{

uchar a,b

dsreset() //初始化

delay_ms(1)

tempwritebyte(0xcc) // 写跳过读ROM指令

tempwritebyte(0xbe) //写读指令

a=tempread()//读低8位

b=tempread()//读高8位

temp1=b

temp1<<=8 //两个字节组合为1个字

temp1=temp1|a

f_temp=temp1*0.0625 //温度在寄存器中为12位 分辨率位0.0625°

}

//**************************************************************

//解密函数

//**************************************************************

void unlock()

{

uchar in,i

if(num==0)

{

lcd_xieping(0,0,"**密码锁系统**")

lcd_xieping(1,0,"—————————")

lcd_xieping(2,0," 请输入密码: ")

lcd_xieping(3,0,"")

for(i=0i<4i++)

{

t1[i]=keyscan1()

lcd_xieping(3,i,"*")

num=0

}//输密码

}

in=keyscan1()

if(in==12)//in-确定键标志位

{

in=0

num=0

if((t1[0]==t[0])&&(t1[1]==t[1])&&(t1[2]==t[2])&&(t1[3]==t[3]))

{

flag=1//解密成功与否标志位

//unlock_i=1

a=0//功能键标志

lcd_xieping(0,0,"**密码锁系统** ")

lcd_xieping(1,0,"——————————")

lcd_xieping(2,0,"密码正确!")

lcd_xieping(3,0," 您的身份已确认")

delay_ms(1500)

lcd_xieping(1,0,"————————")

lcd_xieping(2,0,"功能 I 开锁")

lcd_xieping(3,0," II修改密码")

}

else

{

flag=0

count++

if(count==3)

{

count=0

num=1

lcd_xieping(1,0,"——————————")

lcd_xieping(2,0,"您的机会已用完 ")

lcd_xieping(3,0,"对不起**无法进入")

FM=0

delay_ms(1000)

FM=1

}

}

}

}

//**************************************************************

// 修改密码函数

//**************************************************************

void xiugaimima()

{ uchar i,j,l,im,ib

uchar t2[4]

uchar t3[4]

num=0

lcd_xieping(1,0,"————————")

lcd_xieping(2,0,"请输入新密码: ")

lcd_xieping(3,0,"")

for(i=0i<4i++)

{

t2[i]=keyscan1()

lcd_xieping0(3,i,mima[num])

num=0

}

im=keyscan1()

if(im==12)//im,in,ib,同为确定键标志位

{

im=0

num=0

lcd_xieping(1,0,"————————")

lcd_xieping(2,0,"请再次输入新密码")

lcd_xieping(3,0,"")

for(i=0i<4i++)

{

t3[i]=keyscan1()

lcd_xieping0(3,i,mima[num])

num=0

}

}

ib=keyscan1()

if(ib==12)

{

ib=0

num=0

if(t2[0]==t3[0]&&t2[1]==t3[1]&&t2[2]==t3[2]&&t2[3]==t3[3])

{

t[0]=t3[0]

t[1]=t3[1]

t[2]=t3[2]

t[3]=t3[3]

lcd_xieping(1,0,"————————")

lcd_xieping(2,0,"祝贺您!")

lcd_xieping(3,0," 密码修改成功 ")

flag=0

for(j=0j<4j++)

{

l=j+1

write(l,t[j])

delay_ms(10)

}//24C08写数据

delay_ms(1000)

}

else

{

lcd_xieping(2,0,"两次输入密码不同")

lcd_xieping(3,0," 密码修改失败 ")

flag=1

delay_ms(500)

}

}

}

//**************************************************************

//显示函数

//**************************************************************

void xianshi()

{

donetime()

tempchange()

get_temp()

s_temp=f_temp*100

temp0[7]=(s_temp/1000)+48

temp0[8]=(s_temp%1000/100)+48

temp0[10]=(s_temp%100/10)+48

temp0[11]=(s_temp%10)+48

lcd_xieping(0,0,"**密码锁系统** ")

lcd_xieping(1,0,temp0)

lcd_xieping(2,0,day)

lcd_xieping(3,0,time)

num=0

}

//**************************************************************

//开锁函数

//**************************************************************

void kaisuo()

{

uchar i

lcd_xieping(2,0,"开锁中…… ")

lcd_xieping(3,0,"——耐心等待——")

for(i=3i>0i--)

{

FM=0

delay_ms(100)

FM=1

delay_ms(100)

flag=0

}

dianjiZZ()

delay_ms(10000)

dianji_stop()

lcd_xieping(2,0,"—开锁过程结束—")

lcd_xieping(3,0,"请开门")

delay_ms(5000)

dianjiFZ()

delay_ms(10000)

dianji_stop()

flag=0

}

//**************************************************************

//主函数

//**************************************************************

void main()

{

uchar m

unlock_i=1

lcd_init() //液晶初始化

//Init1302()

kjxs() //开机显示

for(m=0m<4m++)

{

t[m]=read(m+1)

delay_ms(10)

}//24C08读数据

while(1)

{

reshi()

if(!unlock_i)

{

unlock()//解密函数

}

else

{

xianshi()//时间、日期、温度显示函数

}

if(flag==1)

{

num1=0

a=keyscan2()

if(a==1)

{

kaisuo()//开锁函数

}

if(a==2)

{

xiugaimima()//修改密码函数

}

}

}

}

include<reg52.h>//包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义

#define DataPort P0 //定义数据端口 程序中遇到DataPort 则用P0 替换

#define KeyPort P1

sbit LATCH1=P2^2//定义锁存使能端口 段锁存

sbit LATCH2=P2^3// 位锁存

unsigned char code dofly_DuanMa[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,

0x77,0x7c,0x39,0x5e,0x79,0x71}// 显示段码值0~F

unsigned char code dofly_WeiMa[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}//分别对应相应的数码管点亮,即位码

unsigned char TempData[10]//存储显示值的全局变量

unsigned char code password[8]={1,2,3,4,5,6,7,8}

//可以更改此密码做多组测试

void DelayUs2x(unsigned char t)//us级延时函数声明

void DelayMs(unsigned char t)//ms级延时

void Display(unsigned char FirstBit,unsigned char Num)//数码管显示函数

unsigned char KeyScan(void)//键盘扫描

unsigned char KeyPro(void)

void Init_Timer0(void)//定时器初始化

/*------------------------------------------------

主函数

------------------------------------------------*/

void main (void)

{

unsigned char num,i,j

unsigned char temp[8]

bit Flag

Init_Timer0()

while (1) //主循环

{

num=KeyPro()

if(num!=0xff)

{

if(i==0)

{

for(j=0j<8j++)//清屏

TempData[j]=0

}

if(i<8)

{

temp[i]=dofly_DuanMa[num]//把按键值输入到临时数组中

for(j=0j<=ij++) //通过一定顺序把临时数组中

//的值赋值到显示缓冲区,从右往左输入

TempData[7-i+j]=temp[j]

}

i++ //输入数值累加

if(i==9)//正常等于8即可,由于我们需要空一个用于清屏,

//清屏时的按键不做输入值

{

i=0

Flag=1//先把比较位置1

for(j=0j<8j++)//循环比较8个数值,

//如果有一个不等 则最终Flag值为0

Flag=Flag&&(temp[j]==dofly_DuanMa[password[j]])

//比较输入值和已有密码

for(j=0j<8j++)//清屏

TempData[j]=0

if(Flag)//如果比较全部相同,标志位置1

{

TempData[0]=0x3f// "o"

TempData[1]=0x73// "p"

TempData[2]=0x79// "E"

TempData[3]=0x54// "n"

//说明密码正确,输入对应 *** 作 显示"open"

}

else

{

TempData[0]=0x79// "E"

TempData[1]=0x50// "r"

TempData[2]=0x50// "r"

//否则显示"Err"

}

}

}

}

}

/*------------------------------------------------

uS延时函数,含有输入参数 unsigned char t,无返回值

unsigned char 是定义无符号字符变量,其值的范围是

0~255 这里使用晶振12M,精确延时请使用汇编,大致延时

长度如下 T=tx2+5 uS

------------------------------------------------*/

void DelayUs2x(unsigned char t)

{

while(--t)

}

/*------------------------------------------------

mS延时函数,含有输入参数 unsigned char t,无返回值

unsigned char 是定义无符号字符变量,其值的范围是

0~255 这里使用晶振12M,精确延时请使用汇编

------------------------------------------------*/

void DelayMs(unsigned char t)

{

while(t--)

{

//大致延时1mS

DelayUs2x(245)

DelayUs2x(245)

}

}

/*------------------------------------------------

显示函数,用于动态扫描数码管

输入参数 FirstBit 表示需要显示的第一位,如赋值2表示从第三个数码管开始显示

如输入0表示从第一个显示。

Num表示需要显示的位数,如需要显示99两位数值则该值输入2

------------------------------------------------*/

void Display(unsigned char FirstBit,unsigned char Num)

{

static unsigned char i=0

DataPort=0 //清空数据,防止有交替重影

LATCH1=1//段锁存

LATCH1=0

DataPort=dofly_WeiMa[i+FirstBit]//取位码

LATCH2=1//位锁存

LATCH2=0

DataPort=TempData[i]//取显示数据,段码

LATCH1=1//段锁存

LATCH1=0

i++

if(i==Num)

i=0

}

/*------------------------------------------------

定时器初始化子程序

------------------------------------------------*/

void Init_Timer0(void)

{

TMOD |= 0x01 //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响

//TH0=0x00 //给定初值

//TL0=0x00

EA=1 //总中断打开

ET0=1 //定时器中断打开

TR0=1 //定时器开关打开

}

/*------------------------------------------------

定时器中断子程序

------------------------------------------------*/

void Timer0_isr(void) interrupt 1

{

TH0=(65536-2000)/256 //重新赋值 2ms

TL0=(65536-2000)%256

Display(0,8) // 调用数码管扫描

}

/*------------------------------------------------

按键扫描函数,返回扫描键值

------------------------------------------------*/

unsigned char KeyScan(void) //键盘扫描函数,使用行列反转扫描法

{

unsigned char cord_h,cord_l//行列值中间变量

KeyPort=0x0f //行线输出全为0

cord_h=KeyPort&0x0f//读入列线值

if(cord_h!=0x0f)//先检测有无按键按下

{

DelayMs(10) //去抖

if((KeyPort&0x0f)!=0x0f)

{

cord_h=KeyPort&0x0f //读入列线值

KeyPort=cord_h|0xf0 //输出当前列线值

cord_l=KeyPort&0xf0 //读入行线值

while((KeyPort&0xf0)!=0xf0)//等待松开并输出

return(cord_h+cord_l)//键盘最后组合码值

}

}return(0xff)//返回该值

}

/*------------------------------------------------

按键值处理函数,返回扫键值

------------------------------------------------*/

unsigned char KeyPro(void)

{

switch(KeyScan())

{

case 0x7e:return 0break//0 按下相应的键显示相对应的码值

case 0x7d:return 1break//1

case 0x7b:return 2break//2

case 0x77:return 3break//3

case 0xbe:return 4break//4

case 0xbd:return 5break//5

case 0xbb:return 6break//6

case 0xb7:return 7break//7

case 0xde:return 8break//8

case 0xdd:return 9break//9

case 0xdb:return 10break//a

case 0xd7:return 11break//b

case 0xee:return 12break//c

case 0xed:return 13break//d

case 0xeb:return 14break//e

case 0xe7:return 15break//f

default:return 0xffbreak

}

}

TLOWEQU 00H

THIGH EQU 0EEH

COUN0 EQU 200 5ms * 200 = 1S

COUN1 EQU 3FH 密码的位数

D_TIME EQU 3EH 设置密码时的时间限制

SEC EQU 3DH 秒单元

PASS_OLD EQU 30H

PASS_NEW EQU 40H

SDA EQU P3.424C01的串行数据线

SCLK24 EQU P3.324C01的串行时钟线

20H.0 为时间限制标记

20H.1 为按错键标记

20H.2 为比较对错标记

20H.3 为3秒时间标记

RSEQU P2.0

RWEQU P2.1

ENEQU P2.2

X EQU 2FH LCD 地址变量

BEEP EQU P3.7

--------------------------------------------------

ORG 0000H

JMP MAIN

ORG 0BH

JMP T0_INT

--------------------------------------------------

MAIN:

MOV SP,#60H

MOV A,#00H

MOV D_TIME,A

MOV SEC,A

MOV COUN1,A

MOV R5,#06H

MOV R0,#PASS_OLD

CLR_01:MOV @R0,A

INC R0

DJNZ R5,CLR_01

CLR EN

CLR 20H.0

CLR 20H.1

CLR 20H.2

CLR 20H.3

CALL INIT_TIMER

CALL SET_LCD

CALL MENU1

CALL PASS_READ 读出预定密码。

LOOP1:

CALL SCAN_KEY

CALL DELAY2

CJNE A,#0fH,LOOP1 按F键启动进入输入密码程序

SETB TR0

LP0: CALL SCAN_KEY 按住F键3秒以上蜂鸣器不响为止

CALL DELAY2

CJNE A,#0fH,LP3

JB 20H.3,LP1 3秒到,20H.3置1。

JMP LP0

LP1: CLR TR0停止中断

MOV SEC,#00H 秒单元清零

CLR 20H.3 清3秒标记位

LP2: MOV P1,#0F0H 等待键释放

MOV A,P1

CJNE A,#0F0H,LP2

CALL PASS_IN

JB 20H.2,LOOP2 密码正确后方可转下一步

JMP LP4

LP3: CLR TR0停止中断

CLR 20H.3 清3秒标记位

MOV SEC,#00H 秒单元清零

LP4: CALL SYS_RST 系统复位

RET

NOP

NOP

NOP

LJMP MAIN

LOOP2: MOV R4,#06H 模拟输出蜂鸣器响六声

LOOP3: CALL BZ

DJNZ R4,LOOP3

MOV A,#00H

LOOP4: CALL SCAN_KEY

CJNE A,#0AH,LOOP6按A键进入PASS_LOOK

CALL PASS_LOOK

LOOP5: CALL SCAN_KEY

CJNE A,#0EH,LOOP5按E键退出PASS_LOOK

CALL MENU1

CALL MENU2

LOOP6: CJNE A,#0BH,LOOP8按B键进入PASS_SET

CALL PASS_SET

LOOP7: CALL SCAN_KEY

CJNE A,#0EH,LOOP7按E键退出PASS_SET

CALL MENU1

CALL MENU2

LOOP8: CJNE A,#0DH,LOOP4按D键退出

ACALL MAIN PASS_LOOK和PASS_SET状态

JMP LOOP4

SYS_RST:

RST1: CALL SCAN_KEY

CJNE A,#0DH,RST1 "D" 键

LJMP MAIN

RET

NOP

NOP

NOP

LJMP MAIN

PASS_IN:

MOV 21H,#03H 允许输入三次密码

P_IN1: MOV DPTR,#IN_PASS0

MOV A,#1

CALL LCD_SHOW

MOV DPTR,#INFO2 指针指到显示信息2

MOV A,#2 显示在第二行

CALL LCD_SHOW

MOV R0,#PASS_OLD

MOV R3,#00H

MOV R2,#09H 设置LCD地址

MOV COUN1,#06H 6位密码

SETB TR0

P_IN2: JB 20H.0,P_IN4

MOV P1,#0F0H

MOV A,P1

CJNE A,#0F0H,P_IN3

jmp P_IN2

P_IN3:

CALL KEY_IN

CALL SETDATA0

CALL BZ

JB 20H.1,P_IN4

DJNZ COUN1,P_IN2

CLR TR0

CLR 20H.0

CALL PASS_COMP

JNB 20H.2,P_IN4 比较对错标记

RET

P_IN4: CLR TR0

CLR 20H.0

CLR 20H.1

CALL INPUT_ERR

DJNZ 21H,P_IN1

RET

IN_PASS0:

DB " INPUT PASSWORD ",0

PASS_COMP:

MOV COUN1,#06H比较6位数值

MOV R0,#PASS_OLD

MOV R1,#PASS_NEW

P_COMP0: MOV A,@R0

MOV B,@R1

CJNE A,B,P_COMP1

INC R0

INC R1

DJNZ COUN1,P_COMP0

CALL INPUT_OK

SETB 20H.2

RET

P_COMP1: CLR 20H.2

CALL INPUT_ERR

RET

PASS_SET:

MOV 21H,#05H 允许输入三次密码

P_SET1:MOV DPTR,#SET_PASS0

MOV A,#1

CALL LCD_SHOW

MOV DPTR,#INFO2 指针指到显示信息2

MOV A,#2 显示在第二行

CALL LCD_SHOW

MOV R3,#00H

MOV R2,#09H 设置LCD地址

MOV R1,#PASS_NEW

MOV COUN1,#06H 6位密码

SETB TR0

P_SET2:JB 20H.0,P_SET4

MOV P1,#0F0H

MOV A,P1

CJNE A,#0F0H,P_SET3

jmp P_SET2

P_SET3:

CALL KEY_IN

CALL SETDATA1

CALL BZ

JB 20H.1,P_SET4

MOV R5,#04H

CALL DELAY

DJNZ COUN1,P_SET2

CLR TR0

CLR 20H.0

CALL RESET_OK

CALL EEPW

RET

P_SET4:CLR TR0

CLR 20H.0

CLR 20H.1

CALL RESET_ERR

DJNZ 21H,P_SET1

RET

SET_PASS0:

DB " RESET PASSWORD ",0

MENU1: LCD 显示工作菜单信息

MOV DPTR,#MENU01

MOV A,#1 在第一行显示信息

CALL LCD_SHOW

RET

MENU01: DB "PASSWORD CONTROL",0

MENU2: LCD 显示工作菜单信息

MOV DPTR,#MENU02

MOV A,#2 在第一行显示信息

CALL LCD_SHOW

RET

SETDATA1:

MOV A,R3

ANL A,#0FH取出低四位二进制数

PUSH ACC

CLR C

SUBB A,#0AH减10

POP ACC

JCASCII3该数小于10,转ASCII0

SETB 20H.1

RET

ADD A,#07H大于10的数加上37H

ASCII3: ADD A,#30H小于10的数加上30H

MOV @R1,A

MOV B,R2

CALL LCDP2

INC R2

INC R1

RET

SETDATA0:

MOV A,R3

ANL A,#0FH取出低四位二进制数

PUSH ACC

CLR C

SUBB A,#0AH减10

POP ACC

JCASCII4该数小于10,转ASCII4

SETB 20H.1

RET

ASCII4: ADD A,#30H小于10的数加上30H

MOV @R0,A 保存密码值

MOV A,#2AH 显示" * "

MOV B,R2

CALL LCDP2

INC R2

INC R0

RET

PASS_LOOK:

MOV DPTR,#LOOK1 指针指到显示信息1

MOV A,#1 显示在第一行

CALL LCD_SHOW

MOV DPTR,#LOOK2 指针指到显示信息2

MOV A,#2 显示在第二行

CALL LCD_SHOW

MOV R1,#PASS_NEW

MOV R2,#09

MOV COUN1,#06

LOOK0:MOV A,@R1

MOV B,R2

CALL LCDP2

INC R2

INC R1

DJNZ COUN1,LOOK0

RET

LOOK1: DB " LOOK PASSWORD ",0 LCD 第一行显示信息

LOOK2: DB "PASSWORD ------ ",0 LCD 第二行显示信息

SCAN_KEY:

SCAN_K:MOV P1,#0F0H

MOV A,P1

CJNE A,#0F0H,KEY_NUM0 有键按下转

JMP KEY_END

KEY_NUM0: CALL KEY_IN

CALL BZ

MOV R5,#04H

CALL DELAY

KEY_END: RET

KEY_IN:MOV P1,#0F0H 置列线为0,行线为1

MOV A,P1

ANL A,#0F0H

MOV B,A

MOV P1,#0FH置列线为1,行线为0

MOV A,P1

ANL A,#0FH

ORL A,B高四位与低四位重新组合

CJNE A,#0FFH,KEY_IN1 0FFH为末按键

RET

KEY_IN1: MOV B,A

MOV DPTR,#KEYTABLE

MOV R3,#0FFH

KEY_IN2: INC R3

MOV A,R3

MOVC A,@A+DPTR

CJNE A,B,KEY_IN3

MOV A,R3 找到,取顺序码

RET

KEY_IN3: CJNE A,#0FFH,KEY_IN2 末完,继续查

RET0FFH为结束码

SET_LCD:

CLR EN

CALL INIT_LCD 初始化 LCD

CALL DELAY1

MOV DPTR,#INFO1 指针指到显示信息1

MOV A,#1 显示在第一行

CALL LCD_SHOW

MOV DPTR,#INFO2 指针指到显示信息2

MOV A,#2 显示在第二行

CALL LCD_SHOW

RET

INFO1: DB "",0 LCD 第一行显示信息

INFO2: DB "PASSWORD ------ ",0 LCD 第二行显示信息INIT_LCD: 8位I/O控制 LCD 接口初始化

MOV A,#38H 双列显示,字形5*7点阵

CALL WCOM

CALL DELAY1

MOV A,#38H 双列显示,字形5*7点阵

CALL WCOM

CALL DELAY1

MOV A,#38H 双列显示,字形5*7点阵

CALL WCOM

CALL DELAY1

MOV A,#0CH 开显示,关光标,

CALL WCOM

CALL DELAY1

MOV A,#01H 清除 LCD 显示屏

CALL WCOM

CALL DELAY1

RET

LCD_SHOW: 在LCD的第一行或第二行显示信息字符

CJNE A,#1,LINE2 判断是否为第一行

LINE1: MOV A,#80H 设置 LCD 的第一行地址

CALL WCOM写入命令

CALL CLR_LINE清除该行字符数据

MOV A,#80H 设置 LCD 的第一行地址

CALL WCOM写入命令

JMP FILL

LINE2: MOV A,#0C0H 设置 LCD 的第二行地址

CALL WCOM写入命令

CALL CLR_LINE清除该行字符数据

MOV A,#0C0H 设置 LCD 的第二行地址

CALL WCOM

FILL: CLR A填入字符

MOVC A,@A+DPTR 由消息区取出字符

CJNE A,#0,LC1判断是否为结束码

RET

LC1:CALL WDATA 写入数据

INC DPTR 指针加1

JMP FILL 继续填入字符

RET

CLR_LINE: 清除该行 LCD 的字符

MOV R0,#24

CL1: MOV A,#' '

CALL WDATA

DJNZ R0,CL1

RET

ENABLE:写指令使能

CLR RS RS=L,RW=L,D0-D7=指令码,E=高脉冲

CLR RW

SETB EN

CALL DELAY0

CLR EN

RET

ENABLE1: 写数据使能

SETB RSRS=H,RW=L,D0-D7=数据,E=高脉冲

CLR RW

SETB EN

CALL DELAY0

CLR EN

RET

DELAY0: MOV R7,#250 延时500微秒

DJNZ R7,$

RET

WCOM: 以8位控制方式将命令写至LCD

MOV P0,A 写入命令

CALL ENABLE

RET

WDATA:以8位控制方式将数据写至LCD

MOV P0,A 写入数据

CALL ENABLE1

RET

LCDP2:在LCD的第二行显示字符

PUSH ACC

MOV A,B 设置显示地址

ADD A,#0C0H 设置LCD的第二行地址

CALL WCOM 写入命令

POP ACC 由堆栈取出A

CALL WDATA 写入数据

RET

CONV:

MOV X,#9设置显示起始位置

MOV A,R3

ANL A,#0F0H 取出高四位二进制数

SWAP A高四位与低四位互换

PUSH ACC 压入堆栈

CLR CC=0

SUBB A,#0AH 减10

POP ACC d出堆栈

JCASCII0 该数小于10,转ASCII0

ADD A,#07H 大于10的数加上37H

ASCII0: ADD A,#30H 小于10的数加上30H

MOV B,X

CALL LCDP2

MOV A,R3

ANL A,#0FH取出低四位二进制数

PUSH ACC

CLR C

SUBB A,#0AH减10

POP ACC

JCASCII1该数小于10,转ASCII1

ADD A,#07H大于10的数加上37H

ASCII1: ADD A,#30H小于10的数加上30H

INC X

MOV B,X

CALL LCDP2

RET

DELAY2: MOV R5,#15H

DELAY:延时R5×10MS

MOV R6,#50

D1:MOV R7,#100

DJNZ R7,$

DJNZ R6,D1

DJNZ R5,DELAY

RET

DELAY1:延时5MS

MOV R6,#25

D2:MOV R7,#100

DJNZ R7,$

DJNZ R6,D2

RET

KEYTABLE:

DB 0EEH,0EDH,0EBH,0E7H,0DEH 0,1,2,3,4, 顺序码

DB 0DDH,0DBH,0D7H,0BEH,0BDH 5,6,7,8,9,

DB 0BBH,0B7H,07EH,07DH,07BH A,B,C,D,E,

DB 077H,0FFH F 0FF为结束码

DB 0DBH,0EEH,0DEH,0BEH,07EH 0,1,2,3,4, 顺序码

DB 0EDH,0DDH,0BDH,07DH,0EBH 5,6,7,8,9,

DB 0BBH,07BH,0E7H,0D7H,0B7H A,B,C,D,E,

DB 077H,067H,066H,065H,0FFH F,C+F,1+F,8+C

INIT_TIMER:初始化定时器

MOV TMOD,#01H设置定时器0 工作模式为模式1

MOV IE, #82H启用定时器0 中断产生

MOV TL0,#TLOW

MOV TH0,#THIGH

RET

T0_INT:

PUSH ACC

MOV TL0,#TLOW

MOV TH0,#THIGH

INC D_TIME

MOV A,D_TIME5ms 计数值加1

CJNE A,#COUN0,T0_T

MOV D_TIME,#0

INC SEC 秒加1

MOV A,SEC

CJNE A,#03H,TO_INT0

SETB 20H.3

TO_INT0: CJNE A,#8,T0_T 是否到8秒?

MOV SEC,#0 秒单元清0

SETB 20H.0

T0_T: POP ACC

RETI

PASS_READ:

CALL EEPR

RET

MOV COUN1,#06H6位密码数值

MOV R7,#00H

MOV R1,#PASS_NEW

P_READ: MOV A,R7

MOV DPTR,#R_TABLE

MOVC A,@A+DPTR

MOV @R1,A

INC R1

INC R7

DJNZ COUN1,P_READ

RET

R_TABLE:

DB 35H,36H,34H,34H,38H,38H

COMP_ERR:

MOV DPTR,#COMP_ERR1

MOVA,#2

CALL LCD_SHOW

RET

COMP_ERR1:

DB " PASSWORD ERROR ",0

COMP_OK:

MOV DPTR,#COMP_OK0

MOVA,#2

CALL LCD_SHOW

RET

COMP_OK0:

DB " PASSWORD RIGHT ",0

RESET_ERR:

MOV DPTR,#RESET_ERR0

MOVA,#2

CALL LCD_SHOW

RET

RESET_ERR0:

DB " RESET ERROR ",0

RESET_OK:

MOV DPTR,#RESET_OK0

MOVA,#2

CALL LCD_SHOW

RET

RESET_OK0:

DB " RESET RIGHT ",0

INPUT_ERR:

MOV DPTR,#INPUT_ERR0

MOVA,#2

CALL LCD_SHOW

RET

INPUT_ERR0:

DB " INPUT ERROR ",0

INPUT_OK:

MOV DPTR,#INPUT_OK0

MOVA,#2

CALL LCD_SHOW

RET

INPUT_OK0:

DB " INPUT RIGHT ",0

EEPW:PUSH ACC

PUSH PSW

CLR PSW.3

SETB PSW.4

MOV R1,#PASS_NEW

MOV R7,#06H 连续写8个字节

LCALL START

MOV A,#0A0H 送器件地址

ACALL SUBS

MOV A,#00H送片内字节地址

ACALL SUBS

AGAIN1:

MOV A,@R1

ACALL SUBS 调发送单字节子程序

INC R1

DJNZ R7,AGAIN1连续写8个字节

LCALL STOP 发停止信号

POP PSW

POP ACC

RET

SUBS:

MOV R0,#08H 发送单字节子程序

LOOP:CLR SCLK24

RLC A

MOV SDA,C

NOP

SETB SCLK24

ACALL DELAY24

DJNZ R0,LOOP 循环8次送8个bit

CLR SCLK24

ACALL DELAY24

SETB SCLK24

REP: MOV C,SDA

JC REP 判应答到否,未到则等待

CLR SCLK24

RET

DELAY24:

NOP

NOP

RET

EEPR:PUSH ACC

PUSH PSW

CLR PSW.3

SETB PSW.4

MOV R7,#06H

MOV R1,#PASS_NEW

LCALL START发开始信号

MOV A,#0A0H 送器件地址

ACALL SUBS 调发送单字节子程序

MOV A,#00H 送片内字节地址

ACALL SUBS

LCALL START再发开始信号

MOV A,#0A1H

ACALL SUBS

MORE:ACALL SUBR

MOV @R1,A

MOV A,#00H

INC R1

DJNZ R7,MORE

LCALL STOP 送停止信号

POP PSW

POP ACC

RET

SUBR:MOV R0,#08H 接受单字节子程序

SUBR2: SETB SCLK24

ACALL DELAY24

MOV C,SDA

RLC A

CLR SCLK24

ACALL DELAY24

DJNZ R0,SUBR2

CJNE R7,#01H,ALOW

SETB SDA 若是最后一个字节置SDA=1

AJMP SETOK

ALOW:

CLR SDA 否则置SDA=0

SETOK: ACALL DELAY24

SETB SCLK24

ACALL DELAY24

CLR SCLK24

ACALL DELAY24

SETB SDA 应答毕,SDA置1

RET

START:

CLR SDA

ACALL DELAY24

SETB SDA

SETB SCLK24

ACALL DELAY24

CLR SDA

SETB SCLK24

RET

STOP:

CLR SDA

SETB SCLK24

ACALL DELAY24

SETB SDA

ACALL DELAY24

RET

BZ:蜂鸣器

MOV R6,#100

B1:CALL DEX

CPL BEEP

DJNZ R6,B1

MOV R5,#20

CALL DELAY

RET

DEX:MOV R7,#180

DE1:NOP

DJNZ R7,DE1

RET

END


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/11137341.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-13
下一篇 2023-05-13

发表评论

登录后才能评论

评论列表(0条)

保存