- 设计采用AT89S52单片机作为核心控制单元,配以相应的硬件电路,完成开锁密码的设置、存储、校对、提醒和报警功能。
- 硬件结构
- 整体电路
- 软件结构
- 液晶驱动12864.c
1 /* **************************************************** 2 程序参考地址 3 http://blog.chinaunix.net/uid-21658993-id-1819849.html 4 图片显示 5 http://www.51hei.com/mcu/4161.html 6 ********************************************************/ 7 #ifndef _12864_H_ 8 #define _12864_H_ 9 #define LCD_DATA P0 10 sbit RS = P2^4; //并行的指令/数据选择信号: 1数据, 0命令 11 sbit RW = P2^5; //并行读写选择信号:1读, 0写 12 sbit E = P2^6; //并行使能端:1有效 13 sbit PSB = P2^1; //并/串接口选择:1并,0串 14 sbit RET = P2^3; //复位:0有效 15 16 bit checkBusy() 17 { bit busy; 18 RS = 0; 19 RW = 1; 20 E = 1; 21 delayUs(); 22 busy = (bit)(LCD_DATA&0x80); 23 E = 0; 24 return busy; 25 } 26 void wait() 27 { 28 while(checkBusy()); 29 } 30 void writeCmd(uchar cmd) 31 { 32 wait(); 33 RS = 0; 34 RW = 0; 35 E = 0; 36 delayUs(); 37 LCD_DATA = cmd; 38 delayUs(); 39 E = 1; 40 delayUs(); 41 E = 0; 42 } 43 void writeData(uchar dat) 44 { 45 wait(); 46 RS = 1; 47 RW = 0; 48 E = 0; 49 delayUs(); 50 LCD_DATA = dat; 51 delayUs(); 52 E = 1; 53 delayUs(); 54 E = 0; 55 } 56 57 void setPosition(uchar x, uchar y) //4*8 58 { uchar p; 59 switch(x%4) 60 { 61 case 0: p = 0x80; break; //第0行 62 case 1: p = 0x90; break; //第1行 63 case 2: p = 0x88; break; //第2行 64 case 3: p = 0x98; break; //第3行 65 } 66 p += y; 67 writeCmd(p); 68 } 69 void lcd_mesg(uchar * str) 70 { uchar n = 0; 71 while(str[n] != ‘\0‘) 72 { 73 writeData(str[n++]); 74 } 75 } 76 void lcd_draw(unsigned char code *pic) 77 { 78 unsigned i,j,k; 79 writeCmd(0x34);//扩充指令集 80 for(i=0;i<2;i++)//上半屏和下半屏 81 { 82 for(j=0;j<32;j++)//上下半屏各32行 83 { 84 writeCmd(0x80+j);//写行地址(y地址) 85 if(i==0) 86 { 87 writeCmd(0x80);//写列地址(x地址),上半屏列地址为0x80,下半屏列地址为0x88 88 } 89 else 90 { 91 writeCmd(0x88); 92 } 93 for(k=0;k<16;k++)//写入列数据 94 { 95 writeData(*pic++); 96 } 97 } 98 } 99 writeCmd(0x36);//显示图形 100 writeCmd(0x30);//基本指令集 101 } 102 void init_lcd() 103 { 104 PSB = 1; //并口方式 105 writeCmd(0x30); //基本指令, 扩充指令为34H 106 delayMs(10); 107 writeCmd(0x0c); //显示开, 关光标 108 delayMs(10); 109 writeCmd(0x01); //清屏 110 delayMs(10); 111 } 112 113 #endif
- 存储驱动24c02.c
#ifndef _24C02_H_ #define _24C02_H_ uchar flag_lock; //键盘锁定标识,1有效 uchar userpsw[6]; uchar adminpsw[6]; /*24C02读写驱程序*/ sbit scl=P1^1; sbit sda=P1^2; sbit wp=P1^0; //写保护 //应答函数:在SCL保持高电平期间,拉低SDA,且保持4us以上,再拉高 void iic_ack() { scl=0; //默认初值为0 scl=1; //SCL保持高电平 delay_5us(); sda=1; delay_5us(); sda=0; delay_5us(); scl=0; //SCL结束保持高 sda=1; //SDA返回原值 } void iic_start() //开始信号,上升沿 { sda=1; //发送起始条件的数据信号 scl=1; //发送起始条件的时钟信号 delay_5us(); sda=0; //发送起始信号 delay_5us(); scl=0; //钳住IIC总线,准备发送或接收数据 delay_5us(); } void iic_stop() //结束信号,下降沿 { sda=0; //发送结束条件的数据信号 scl=1; //发送结束条件的时钟信号 delay_5us(); sda=1; //发送结束信号 delay_5us(); } void writex(unsigned char x) //写一个字节 { unsigned char i; for(i=0;i<8;i++) //循环8次,由高到低赋给SDA { scl=0; //允许更改SDA delay_5us(); //存在边沿时间,需等5us sda=x&0x80; //SDA = 数据和10000000按位与; 串口SDA只送最高位 x=x<<1; delay_5us(); scl=1; delay_5us(); scl=0; } } unsigned char readx() //读一个字节 { unsigned char i,value; for(i=0;i<8;i++) //8次读取1个字节 { scl=0; delay_5us(); scl=1; //保持着SDA的值,以便读出从机数据 delay_5us(); if(sda==1) { value=(value<<1)|1; } if(sda==0) { value=(value<<1)|0; } delay_5us(); } return value; } //向24C02的地址address中写入一个字节的数据info void write_24c02(unsigned char address,unsigned char info) { iic_start(); writex(0xae); //24c02芯片地址为111,写方向位0:1010 1110 iic_ack(); writex(address); iic_ack(); writex(info); iic_ack(); iic_stop(); } //从24C02的地址address中读取一个字节的数据 unsigned char read_24c02(unsigned char address) { unsigned char value; iic_start(); writex(0xae); iic_ack(); writex(address); iic_ack(); iic_start(); writex(0xaf); //24c02芯片地址为111,读方向位1:1010 1111 iic_ack(); value=readx(); iic_stop(); return(value); } void initset_read() { uchar tmp; flag_lock=read_24c02(20); //读出键盘锁定标识并显示,地址20,出厂状态为0 delay_ms(50); for(tmp=0;tmp<6;tmp++) { adminpsw[tmp]=read_24c02(10+tmp); } delay_ms(50); for(tmp=0;tmp<6;tmp++) { userpsw[tmp]=read_24c02(tmp); } delay_ms(50); } void init_24c02() //总线初始化,拉高释放总线,并读出错误计数和密码 { wp=1; scl=1; delay_5us(); sda=1; delay_5us(); initset_read(); //将外部存储器中的密码读入缓存区 } #endif
- 日历/时钟驱动ds1302.c
/* **************************************************** 程序参考地址 http://www.51hei.com/mcu/1730.html ********************************************************/ #ifndef _DS1302_H_ #define _DS1302_H_ sbit CLK=P3^6; //DS1302引脚定义 sbit IO=P3^4; sbit CE=P3^5; //RST sbit ACC0=ACC^0; sbit ACC7=ACC^7; extern uchar timer_cnt; void Input_1byte(uchar TD) //DS1302输入一字节数据 { uchar i; ACC=TD; for(i=8;i>0;i--) { IO=ACC0; CLK=1; CLK=0; ACC=ACC>>1; } } uchar Output_1byte(void) //DS1302输出一字节数据 { uchar i; for(i=8;i>0;i--) { ACC=ACC>>1; ACC7=IO; CLK=1; CLK=0; } return(ACC); } void Write_DS1302(uchar add,uchar dat)//向DS1302写 { CE=0; CLK=0; CE=1; Input_1byte(add); Input_1byte(dat); CE=0; } uchar Read_DS1302(uchar add) //从DS1302读 { uchar inf; //信息临时存储变量 CE=0; CLK=0; CE=1; Input_1byte(add); inf=Output_1byte(); CE=0; return(inf); } /**********************DS1302初始化*****************************/ void init_1302() { if(Read_DS1302(0xd1)==0x55) //判断内存单元的内容,是否进行初始化 return; else { Write_DS1302(0x8e,0x00); //关闭写保护 Write_DS1302(0x90,0x00); //电池充电设置 Write_DS1302(0x80,0x00); //秒 Write_DS1302(0x82,0x51); //分 Write_DS1302(0x84,0x17); //时 Write_DS1302(0x86,0x17); //日 Write_DS1302(0x88,0x04); //月 Write_DS1302(0x8c,0x16); //年 Write_DS1302(0xd0,0x55); //写RAM Write_DS1302(0x8e,0x80); //打开写保护 } } /********************************************************* * DS1302显示 * **********************************************************/ void disp_ds1302() { uchar min,min1,min2; uchar hour,hour1,hour2; uchar date,date1,date2; uchar mon,mon1,mon2; uchar year,year1,year2; //******读出1302的日期******/ min=Read_DS1302(0x83); //读分:min1=十位,min2=个位 min2=min&0x0f; min1=min>>4; hour=Read_DS1302(0x85); //读时:hour1=十位,hour2=个位 hour2=hour&0x0f; hour1=hour>>4; date=Read_DS1302(0x87); //读日期:date1=十位,date2=个位 date2=date&0x0f; date1=date>>4; mon=Read_DS1302(0x89); //读月份:mon1=十位,mon2=个位 mon2=mon&0x0f; mon1=mon>>4; year=Read_DS1302(0x8d); //读年份:year1=十位,year2=个位 year2=year&0x0f; year1=year>>4; //******显示1302的日期******/ setPosition(0,0); writeData(2+0x30); writeData(0+0x30); writeData(year1+0x30); //年 writeData(year2+0x30); writeData(‘-‘); //- writeData(mon1+0x30); //月 writeData(mon2+0x30); writeData(‘-‘); //- writeData(date1+0x30); //日 writeData(date2+0x30); writeData(‘ ‘); //空格 writeData(hour1+0x30); //时 writeData(hour2+0x30); if(timer_cnt<18) writeData(‘:‘); else writeData(‘ ‘); writeData(min1+0x30); //分 writeData(min2+0x30); } #endif
- 键盘扫描keyscan.c
#ifndef _KEYSCAN_H_ #define _KEYSCAN_H_ #define keyport P3 uchar key=0; //按键值 /*------------------------------------------------ 按键扫描函数,返回扫描键值 ------------------------------------------------*/ unsigned char key_scan(void)//键盘扫描函数,使用行列反转扫描法 { uchar cord_h,cord_l; //行列值中间变量 keyport=0xf0; //行线输出全为0,P3=1111 0000 cord_h=keyport&0xf0; //读入列线值 if(cord_h!=0xf0) //有键按下 { delay_ms(5); //去抖 if(cord_h!=0xf0) { cord_h=keyport&0xf0; //读入列线值 keyport=cord_h|0x0f; //输出当前列线值 delay_ms(5); cord_l=keyport&0x0f; //读入行线值 while((keyport&0x0f)!=0x0f);//等待松开并输出 return(cord_h+cord_l); //键盘最后组合码值 } } return(0xff); //返回"无键按下",键盘不起作用 } /*------------------------------------------------ 键值处理函数,返回扫键值 ------------------------------------------------*/ unsigned char key_value(void) //按下相应的键显示相对应的码值 { switch(key_scan()) { case 0xee:return 0;break; //0 1110 1110 case 0xde:return 1;break; //1 case 0xbe:return 2;break; //2 case 0x7e:return 3;break; //3 case 0xed:return 4;break; //4 case 0xdd:return 5;break; //5 case 0xbd:return 6;break; //6 case 0x7d:return 7;break; //7 case 0xeb:return 8;break; //8 case 0xdb:return 9;break; //9 case 0xbb:return 10;break; //a * 键 case 0x7b:return 11;break; //b # 键 case 0xe7:return 12;break; //c 管理/退出键 case 0xd7:return 13;break; //d 删除键 case 0xb7:return 14;break; //e 清空键 case 0x77:return 15;break; //f 确认键 default:return 0xff;break; } } #endif
- 软件仿真
时间: 2024-10-29 17:58:43