零、设计要求
1.时钟可以显示小时、分钟以及秒
2.,四个按键可以实现时间的手动调节
3.加入AT24C02,存储关机之前的时间,并且下次开机后可以通过某一按键将存储的时间读取出来
一、硬件仿真
二、程序设计
二、程序部分
1.IIC
#include <I2C.h>
/*
开始信号
*/
void I2C_Star()
{
SDA = 1;
SCL = 1;
delay();
SDA = 0;
delay();
SCL = 0;
}
/*
停止信号
*/
void I2C_Stop()
{
SCL = 0;
SDA = 0;
delay();
SCL = 1;
delay();
SDA = 1;
delay();
}
/*
写操作
*/
bit I2C_Write(uchar date)
{
bit ack;//应答位
uchar mask;
for(mask=0x80;mask!=0;mask>>=1)
{
if((date&mask)==0)
SDA = 0;
else
SDA = 1;
delay();
SCL = 1;//拉高让从机读信号
delay();
SCL = 0;//拉低准备写下一位
}
SDA = 1;//释放数据线,以检测从机应答
delay();
SCL = 1;
ack = SDA;
delay();
SCL = 0;
return (~ack);//返回1--应答 返回0--非应答
}
/*
读操作 + 应答
*/
uchar I2C_Read_Ack()
{
uchar date;//读到的一个字节
uchar mask;
SDA = 1;//确保数据线释放
for(mask=0x80;mask!=0;mask>>=1)
{
delay();
SCL = 1;//拉高时钟线准备读
if(SDA==1)
date |= mask;
else
date &= ~mask;
delay();
SCL = 0;
}
//读完给从机发送应答
SDA = 0;
delay();
SCL = 1;//从机读走应答位
delay();
SCL = 0;
return date;//返回读到的一字节
}
/*
读操作 + 非应答
*/
uchar I2C_Read_NAck()
{
uchar date;//读到的一个字节
uchar mask;
SDA = 1;//确保数据线释放
for(mask=0x80;mask!=0;mask>>=1)
{
delay();
SCL = 1;//拉高时钟线准备读
if(SDA==1)
date |= mask;
else
date &= ~mask;
delay();
SCL = 0;
}
//读完给从机发送非应答
SDA = 1;
delay();
SCL = 1;//从机读走非应答位
delay();
SCL = 0;
return date;//返回读到的一字节
}
2. 按键
#include "key.h"
/*
毫秒级延时
*/
void delay_ms(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=125;y>0;y--);
}
/*
按键扫描
*/
uint key_scan()
{
if(sec_up==0)
{
delay_ms(10);
while(!sec_up);
return 1;
}
if(sec_down==0)
{
delay_ms(10);
while(!sec_down);
return 2;
}
if(min_up==0)
{
delay_ms(10);
while(!min_up);
return 3;
}
if(min_down==0)
{
delay_ms(10);
while(!min_down);
return 4;
}
if(read_e2==0)
{
delay_ms(10);
while(!read_e2);
return 5;
}
return 0;
}
3.液晶显示
#include "LCD1602.h"
/*
微秒级延时
*/
void delay_us(uint x)
{
for(;x>0;x--)
_nop_();
}
/*
写命令
*/
void write_com(uchar com)
{
P0 = com;
rs = 0;
lcd_en = 0;
delay_us(100);
lcd_en = 1;
delay_us(100);
lcd_en = 0;
}
/*
写数据
*/
void write_date(uchar date)
{
P0 = date;
rs = 1;
lcd_en = 0;
delay_us(100);
lcd_en = 1;
delay_us(100);
lcd_en = 0;
}
/*
初始化
*/
void Init_lcd()
{
uchar i=0;
rw = 0;//写入数据或指令
write_com(0x38);//显示模式16X2
delay_us(100);
write_com(0x0c);//开显示,不显示光标
delay_us(100);
write_com(0x06); //写入新数据光标右移
delay_us(100);
write_com(0x01);//清屏
delay_us(100);
}
/*
显示位置设置
*/
void lcd_location(uchar location)
{
write_com(0x80 | location);
}
4.主函数
#include<reg52.h>
#include "LCD1602.h"
#include "key.h"
#include "I2C.h"
/**接口定义**/
sbit START = P3^5;
/**变量定义**/
uchar sec,min,hour;
uchar count = 0;
uchar KeyCount = 0;
/**数组定义**/
uchar code lcd_date_1[]={" Design-By-WHH "};
uchar code lcd_date_2[]={" 00:00:00 "};
uchar display[6];
uchar buf[3];//24C02缓存
/**函数声明**/
void display_lcd();
void Display_Init();
void Timer0_Config();
void key_action();
void E2PROM_Write(uchar *buf,uchar address,uchar len);
void E2PROM_Read(uchar *buf,uchar address,uchar len);
void Init_E2Data_Write();
void Init_E2Data_Read();
/*****/
/*
主函数
*/
void main()
{
Timer0_Config();
Init_lcd();
Display_Init();
while(1)
{
if(START==0)
{
if(!START)
{
KeyCount++;
switch(KeyCount%2)
{
case 1:
EA = 1;
break;
case 0:
EA = 0;
break;
}
}
}
key_action();
display_lcd();
}
}
/*
掉电写数据包
*/
void Init_E2Data_Write()
{
buf[0] = hour;
buf[1] = min;
buf[2] = sec;
}
/*
上电读数据包
*/
void Init_E2Data_Read()
{
hour = buf[0];
min = buf[1];
sec = buf[2];
}
/*
显示函数
*/
void display_lcd()
{
display[0] = hour/10+0x30;
display[1] = hour%10+0x30;
display[2] = min/10+0x30;
display[3] = min%10+0x30;
display[4] = sec/10+0x30;
display[5] = sec%10+0x30;
lcd_location(0x44);//第二行第五个位置
write_date(display[0]);
write_date(display[1]);
write_date(0x3a);//显示':'
write_date(display[2]);
write_date(display[3]);
write_date(0x3a);//显示':'
write_date(display[4]);
write_date(display[5]);
}
/*
显示初始化
*/
void Display_Init()
{
uchar i;
lcd_location(0x00);
for(i=0;i<16;i++)
{
write_date(lcd_date_1[i]);
}
lcd_location(0x40);//第二行初始位置
for(i=0;i<16;i++)
{
write_date(lcd_date_2[i]);
}
}
/*
定时器配置
*/
void Timer0_Config()
{
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = 0x4c;//定时50ms
TL0 = 0X00;
ET0 = 1;
TR0 = 1;
}
/*
按键动作操作
*/
void key_action()
{
uint key_value = 0;
key_value = key_scan();
if(key_value==1)
{
min++;
}
if(key_value==2)
{
min--;
}
if(key_value==3)
{
sec++;
}
if(key_value==4)
{
sec--;
}
if(key_value==5)
{
Init_E2Data_Read();
E2PROM_Read(buf,0x3a,sizeof(buf));
}
}
/*
E2PROM写多个字节
*/
void E2PROM_Write(uchar *buf,uchar address,uchar len)
{
while(len--)
{
do
{
I2C_Star();
if(I2C_Write(0xa0))
break;//如果允许写入则退出检测循环
I2C_Stop();
}while(1);
I2C_Write(address++);
I2C_Write(*buf++);
I2C_Stop();
}
}
/*
E2PROM读多个字节
*/
void E2PROM_Read(uchar *buf,uchar address,uchar len)
{
do
{
I2C_Star();
if(I2C_Write(0xa0))
break;//如果允许写入则退出检测循环
I2C_Stop();
}while(1);
I2C_Write(address);//写入要读取的地址
I2C_Star();
I2C_Write(0xa1);//选择读
while(len>1)//读取字节数-1
{
*buf++ = I2C_Read_Ack();//给应答以继续读
len--;
}
*buf = I2C_Read_NAck();//给非应答不继续读了
I2C_Stop();
}
/*
定时器0中断服务函数
*/
void Time0() interrupt 1
{
TH0 = 0x4c;//定时50ms
TL0 = 0X00;
count++;
if(count==20)//1s
{
Init_E2Data_Write();
E2PROM_Write(buf,0x3a,sizeof(buf));//1s时间写一次数据到E2
count=0;
sec++;
}
if(sec==60)//1min
{
sec = 0;
min++;
}
if(min==60)//1h
{
min=0;
hour++;
}
}