零、設計要求
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++;
}
}