//************************************************************
// 实时时钟SD30XX读写C51演示程序
// AT89S52 11.0592MHz
// E-mail: FAE@whwave.com.cn
// TEL: 0755-83114387
// Last update: 2014/11/14
//************************************************************
#pragma code
#include <reg51.h>
#include <intrins.h>
#define RTC_Address 0x64 //RTC器件地址
#define IDcode 0x72 //8字节ID号起始地址
#define Bat_bit8 0x1A //电量最高位寄存器地址
#define Bat_Low8 0x1B //电量低八位寄存器地址
#define true 1
#define false 0
//*********变量及IO口定义*********
typedef unsigned char uchar;
typedef unsigned int uint;
sbit SDA=P3^4;
sbit SCL=P3^5;
uchar Batbit8,Batlow7; //电池电量寄存器值
uint Bat; //电池电量值
uchar Sram[8]; //通用数据缓存器
uchar data1[8]; //通用数据缓存器
typedef struct
{
uchar second;
uchar minute;
uchar hour;
uchar week;
uchar day;
uchar month;
uchar year;
}S_Time;
S_Time RTC={0x55,0x59,0x14,0x01,0x12,0x11,0x14}; //初始化时间结构体变量(设置时间:2014年11月12日 14:59:55 星期一)
// 55秒 59分 14时 周一 10日 11月 14年
/********SD30XX函数名********/
uchar I2CReadOneByte(uchar DeviceAddress,uchar add);//读一字节
bit I2CWriteOneByte(uchar DeviceAddress,uchar add, uchar date);//写一字节
uchar I2CReadSerial(uchar DeviceAddress, uchar Address, uchar length,uchar *ps);//连续读
uchar I2CWriteSerial(uchar DeviceAddress, uchar Address, uchar length,uchar *ps);//连续写
bit I2CWriteDate(S_Time SetRTC);//写时间
bit I2CReadDate(S_Time *psRTC);//读时间
bit WriteTimeOn(void);//写允许
bit WriteTimeOff(void);//写禁止
void Delay(uint nn);
/*********I2C延时4us***********/
void I2CWait(void)//4us
{
_nop_();_nop_();_nop_();_nop_();//AT89S52一个nop是1us
}
/********开启SD30XX的I2C总线********/
bit I2CStart(void)
{
SDA=1;
SCL=1;
I2CWait();
if(!SDA)return false; //SDA线为低电平则总线忙,退出
SDA=0;
I2CWait();
while(SDA)return false; //SDA线为高电平则总线出错,退出
SCL=0;
I2CWait();
return true;
}
/********关闭SD30XX的I2C总线*******/
void I2CStop(void)
{
SDA=0;
SCL=0;
I2CWait();
SCL=1;
I2CWait();
SDA=1;
}
/*********发送 ACK*********/
void I2CAck(void)
{
SDA=0;
SCL=0;
I2CWait();
SCL=1;
I2CWait();
SCL=0;
}
/*********发送NO ACK*********/
void I2CNoAck(void)
{
SDA=1;
SCL=0;
I2CWait();
SCL=1;
I2CWait();
SCL=0;
}
/*********读取ACK信号*********/
bit I2CWaitAck(void) //返回为:1=有ACK,0=无ACK
{
SCL=0;
SDA=1; //设置SDA为输入(其它类型的单片机需要配置IO输入输出寄存器)
I2CWait();
SCL=1;
I2CWait();
while(SDA)
{
SCL=0;
return false;
}
SCL=0;
return true;
}
/************MCU向SD30XX发送一个字节*************/
void I2CSendByte(uchar demand) //数据从高位到低位//
{
uchar i=8;
while(i--)
{
SCL=0;
_nop_();
SDA=(bit)(demand&0x80);
demand<<=1;
I2CWait();
SCL=1;
I2CWait();
}
SCL=0;
}
/*********MCU从SD30XX读入一字节*********/
uchar I2CReceiveByte(void) //数据从高位到低位//
{
uchar i=8;
uchar ddata=0;
SDA=1; //设置SDA为输入(其它类型的单片机需要配置IO输入输出寄存器)
while(i--)
{
ddata<<=1; //数据从高位开始读取
SCL=0;
I2CWait();
SCL=1;
I2CWait(); //从高位开始 ddata|=SDA;ddata<<=1
if(SDA)
{
ddata|=0x01;
}
}
SCL=0;
return ddata;
}
/******I2C写一个字节******/
bit I2CWriteOneByte(uchar DeviceAddress,uchar add, uchar date)
{
if(!I2CStart())return false;
I2CSendByte(DeviceAddress);
I2CWaitAck();
I2CSendByte(add); //设置写地址
I2CWaitAck();
I2CSendByte(date); //写数据
I2CWaitAck();
I2CStop();
return true;
}
/******I2C读一个字节程序******/
uchar I2CReadOneByte(uchar DeviceAddress,uchar add)
{
uchar dat;
if(!I2CStart())return false;
I2CSendByte(DeviceAddress);
if(!I2CWaitAck()){I2CStop(); return false;}
I2CSendByte(add); //设置要读的地址
I2CWaitAck();
I2CStart();
I2CSendByte(DeviceAddress+1);
I2CWaitAck();
dat=I2CReceiveByte(); //读数据
I2CNoAck();
I2CStop();
return dat;
}
/******写SD30XX允许程序******/
bit WriteTimeOn(void)
{
if(!I2CWriteOneByte(RTC_Address,0x10,0x80))return false;
I2CWriteOneByte(RTC_Address,0x0f,0xff);
return true;
}
/******写SD30XX禁止程序******/
bit WriteTimeOff(void)
{
if(!I2CWriteOneByte(RTC_Address,0x0f,0x7b))return false;
I2CWriteOneByte(RTC_Address,0x10,0);
return true;
}
/******读SD30XX实时数据寄存器******/
bit I2CReadDate(S_Time *psRTC)
{
if(!I2CStart())return false;
I2CSendByte(RTC_Address+1);
if(!I2CWaitAck()){I2CStop(); return false;}
psRTC->second=I2CReceiveByte();
I2CAck();
psRTC->minute=I2CReceiveByte();
I2CAck();
psRTC->hour=I2CReceiveByte();
I2CAck();
psRTC->week=I2CReceiveByte();
I2CAck();
psRTC->day=I2CReceiveByte();
I2CAck();
psRTC->month=I2CReceiveByte();
I2CAck();
psRTC->year=I2CReceiveByte();
I2CNoAck(); //读时间完成,发送NoAck
I2CStop();
return true;
}
/******写SD30XX实时数据寄存器******/
bit I2CWriteDate(S_Time SetRTC) //写时间操作要求一次对实时时间寄存器(00H~06H)依次写入,
{ //不可以单独对7个时间数据中的某一位进行写操作,否则可能会引起时间数据的错误进位.
//要修改其中某一个数据 , 应一次性写入全部 7 个实时时钟数据.
S_Time *psRTC;
psRTC=&SetRTC;
WriteTimeOn(); //使能,开锁
if(!I2CStart())return false;
I2CSendByte(RTC_Address);
if(!I2CWaitAck()){I2CStop(); return false;}
I2CSendByte(0x00); //设置写起始地址
I2CWaitAck();
I2CSendByte(psRTC->second); //second
I2CWaitAck();
I2CSendByte(psRTC->minute); //minute
I2CWaitAck();
I2CSendByte(psRTC->hour|0x80); //hour ,同时设置小时寄存器最高位(0:为12小时制,1:为24小时制)
I2CWaitAck();
I2CSendByte(psRTC->week); //week
I2CWaitAck();
I2CSendByte(psRTC->day); //day
I2CWaitAck();
I2CSendByte(psRTC->month); //month
I2CWaitAck();
I2CSendByte(psRTC->year); //year
I2CWaitAck();
I2CStop();
WriteTimeOff(); //使能,关锁
return true;
}
/******设置SD30XX报警中断演示程序演示******/
void WriteALARM(void) //设置报警时间:2015年2月14日 8:00
{ //只有设置未来的时间才有效
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x09,0x08); //8时
I2CWriteOneByte(RTC_Address,0x0b,0x14); //14日
I2CWriteOneByte(RTC_Address,0x0c,0x02); //02月
I2CWriteOneByte(RTC_Address,0x0d,0x15); //15年
I2CWriteOneByte(RTC_Address,0x0e,0x74); //设置报警允许(使能年、月、日、小时报警)
I2CWriteOneByte(RTC_Address,0x10,0x92); //设置INT中断选通(INTS1,INTS0),及报警中断总允许位(INTAE)
WriteTimeOff();
}
/******关闭SD30XX报警中断程序******/
void ClrALARM(void) //关闭报警中断
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0x90);
WriteTimeOff();
}
/******设置SD30XX倒计时中断演示******/
void SetDjs(void) //设置倒计时中断
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0x0f);//先清倒计时中断总允许位(INTDE)
I2CWriteOneByte(RTC_Address,0x10,0xf4);//设置周期性中断(IM=1)INT中断选通(INTS1,INTS0),配置倒计时中断总允许位(INTDE)
I2CWriteOneByte(RTC_Address,0x11,0x30);//选择定时器频率源(TDS1、TDS0)为1/60HZ
I2CWriteOneByte(RTC_Address,0x13,0x05);//倒计时初值寄存器,设置8位倒计时计数初值(5min)
WriteTimeOff();
}
/******关闭SD30XX倒计时中断程序******/
void ClrDjs(void)
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0xf0);
WriteTimeOff();
}
/******设置SD30XX频率中断演示******/
void SetFrq(void)
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0xa1); //选通频率中断(INTS1,INTS0),设置频率中断总允许位(INTFE)
I2CWriteOneByte(RTC_Address,0x11,0x09); //设置2Hz频率中断
WriteTimeOff();
}
/******关闭SD30XX频率中断******/
void ClrFrq(void)
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0xa0);
WriteTimeOff();
}
//|************I2C连续读多个字节************|
uchar I2CReadSerial(uchar DeviceAddress, uchar Address, uchar length,uchar *ps)
{
uchar i;
if(!I2CStart())return false;
I2CSendByte(DeviceAddress);
if(!I2CWaitAck()){I2CStop(); return false;}
I2CSendByte(Address); //设置要读的地址
I2CWaitAck();
I2CStart();
I2CSendByte(DeviceAddress+1);
I2CWaitAck();
for(i=0;i<length-1;i++,ps++)
{
*ps=I2CReceiveByte(); //读数据
I2CAck();
}
*ps=I2CReceiveByte();
I2CNoAck();
I2CStop();
return true;
}
//|******************I2C连续写多个字节******************|
uchar I2CWriteSerial(uchar DeviceAddress, uchar Address, uchar length,uchar *ps)
{
uchar i;
if(!WriteTimeOn())return false;
if(!I2CStart())return false;
I2CSendByte(DeviceAddress); //器件地址
if(!I2CWaitAck()){I2CStop(); return false;}
I2CSendByte(Address); //设置起始地址
I2CWaitAck();
for(i=0;i<length;i++)
{
I2CSendByte(*(ps++));
I2CAck();
}
I2CStop();
WriteTimeOff();
return true;
}
/*********延时子程序*********/
void Delay2_5ms() //延时2.5ms
{
uchar i=255;
while(i--);
}
void Delayms(uchar n)
{
while(n--)
Delay2_5ms();
}
//////*****主程序演示*****//////
void main()
{
I2CWriteDate(RTC); //设置时间演示
// WriteALARM(); //设置报警中断时间演示
// SetDjs(); //设置倒计时中断演示
SetFrq(); //设置频率中断演示
I2CReadSerial(RTC_Address,IDcode,8,Sram); //读内部8字节ID号
I2CWriteSerial(RTC_Address,0x30,8,Sram); //把内部8字节的ID号写入用户通用存储器的0x30-0x37地址
I2CReadSerial(RTC_Address,0x30,8,data1); //读从用户RAM寄存器把先前存入的ID号读出来(0x30-0x37地址)
Batbit8=I2CReadOneByte(RTC_Address,Bat_bit8); //读SD30XX的电池电量最高位
Batlow7=I2CReadOneByte(RTC_Address,Bat_Low8); //读SD30XX的电池电量低八位
Bat=(Batbit8>>7)*255+Batlow7; //计算电池电量值演示。如Bat=285则表示2.85V
while(1)
{
I2CReadDate(&RTC); //读时间演示
Delayms(100); //延时250ms,1s读4次
}
}
//特别提醒:当写实时时间数据时 (00H~06H), 不可以单独 对 7 个时间数据中的某一位进行写操作 ,
//否则可能会引起时间数据的错误进位 , 所以要修改其中某一个数据 , 应一次性写入全部 7 个实时时钟数据 .