//-------------------------------------------------------------------------
// SD24XX读写演示程序,此程序仅供参考
// e-mail: FAE@whwave.com.cn
// TEL:0755-83114387
// 2014/07/01
//-------------------------------------------------------------------------
#include <iom64v.h>
#include <macros.h>
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
//数据sda的位设置
#define sda_in DDRC &= ~(1 << PC1) //数据设置输入
#define sda_out DDRC |= (1 << PC1) //数据设置输出
#define sda_setb PORTC |= (1 << PC1) //数据拉高
#define sda_clr PORTC &= ~(1 << PC1) //数据拉低
#define sda_r PINC & (1 << PC1) //数据读取
//时钟scl的位设置
#define scl_in DDRC &= ~(1 << PC0) //时钟设置输入
#define scl_out DDRC |= (1 << PC0) //时钟设置输出
#define scl_setb PORTC |= (1 << PC0) //时钟拉高
#define scl_clr PORTC &= ~(1 << PC0) //时钟拉低
#define scl_r PINC & (1 << PC0) //时钟读取
//===============
#define true 1
#define false 0
//===============
#define RTC_Address 0x64//RTC器件地址
//EEPROM/NVSRAM地址定义。尾缀为A的内部不带EEPROM存储器
#define EE_FirstPage_Address 0xA0//第一页(尾缀B、C、D、E、F型有)
#define EE_SecondPage_Address 0xA2//第二页(尾缀B、C型有)
#define EE_ThirdPage_Address 0xA4//第三页(尾缀C型)
#define EE_FourthPage_Address 0xA6//第四页(尾缀C型)
#define EE_FifthPage_Address 0xA8//第五页(尾缀C型)
#define EE_SixthPage_Address 0xAA//第六页(尾缀C型)
#define EE_SeventhPage_Address 0xAC//第七页(尾缀C型)
#define EE_EighthPage_Address 0xAE//第八页(尾缀C型)
uchar data1[8]; // EEPROM数据
uchar Sram[8]={"01234567"}; //通用数据缓存器
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年
/********SD24XX函数名********/
uchar I2CReadOneByte(uchar DeviceAddress,uchar add);//读一字节
char 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);//连续写
char I2CWriteDate(S_Time SetRTC);//写时间
char I2CReadDate(S_Time *psRTC);//读时间
char WriteTimeOn(void);//写允许
char WriteTimeOff(void);//写禁止
void Delay(uint nn);
//====================================================================
/*延时子程序*/
void delay_ms(uint time) //11.0592M,time=1表示延时1ms
{
uint i;
for(;time>0;time--)
for(i=0;i<1571;i++);
asm("nop");
asm("nop");
asm("nop");
}
void delay_1us(void) //11.0592M, 1us延时函数
{
asm("nop");
}
void delay_us(uint time) //11.0592M, Nus延时函数
{
uint i;
for (i=0;i<time;i++)
asm("nop");
}
//=====================================================================
/********开启SD24XX的I2C总线********/
uchar I2CStart(void)
{
sda_out;
scl_out;
delay_1us();
sda_setb;
scl_setb;
delay_us(5);
sda_in;
delay_us(5);
if(!sda_r)return false; //SDA线为低电平则总线忙,退出
sda_out;
delay_1us();
sda_clr;
delay_us(10);
sda_in;
delay_us(10);
while(sda_r)return false; //SDA线为高电平则总线出错,退出
scl_clr;
delay_us(5);
return true;
}
//********关闭SD24XX的I2C总线****//
void I2CStop(void)
{
scl_out;
sda_out;
delay_1us();
sda_clr;
delay_1us();
scl_clr;
delay_us(5);
scl_setb;
delay_us(2);
sda_setb;
}
//*********发送 ACK****//
void I2CAck(void)
{
scl_out;
sda_out;
delay_1us();
sda_clr;
delay_1us();
scl_clr;
delay_us(5);
scl_setb;
delay_us(5);
scl_clr;
}
/*********发送NO ACK*********/
void I2CNoAck(void)
{
scl_out;
sda_out;
delay_1us();
sda_setb;
delay_1us();
scl_clr;
delay_us(5);
scl_setb;
delay_us(5);
scl_clr;
}
//*********读取ACK信号*********
uchar I2CWaitAck(void) //返回为:1=有ACK,0=无ACK
{
uchar errtime=255;
scl_out;
sda_in;
delay_1us();
scl_clr;
delay_us(5);
scl_setb;
delay_1us();
sda_in;
delay_1us();
while(sda_r)
{
errtime--;
delay_1us();
if(!errtime)
{delay_us(10);
scl_clr;
return false;
}
}
scl_clr;
return true;
}
/************MCU向SD24XX发送一个字节*************/
void I2CSendByte(uchar demand) //数据从高位到低位
{
uchar bd=8;
sda_out;
scl_out;
while(bd--)
{
scl_clr;
delay_1us;
if(demand&0x80)
{
sda_setb;
}
else
{
sda_clr;
}
demand<<=1;
delay_us(3);
scl_setb;
delay_us(3);
}
scl_clr;
}
/*********MCU从SD24XX读入一字节*********/
uchar I2CReceiveByte(void) //数据从高位到低位//
{
uchar bl=8;
uchar ddata=0;
scl_out;
delay_1us();
delay_1us();
sda_in; //端口切换为输入状态!!
sda_setb; //有的单片机需要加这句,如果不需要可以删掉
delay_1us();
while(bl--)
{
ddata<<=1; //数据从高位开始读取
delay_1us();
scl_clr;
delay_us(5); //从高位开始 ddata|=SDA;ddata<<=1
scl_setb;
delay_us(5);
if(sda_r)
{
ddata|=0x01;
}
}
scl_clr;
return ddata;
}
/******I2C写一个字节******/
uchar 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;
}
/******写SD24XX允许程序******/
uchar WriteTimeOn(void)
{
if(!I2CWriteOneByte(RTC_Address,0x10,0x80))return false;
I2CWriteOneByte(RTC_Address,0x0f,0x84);
return true;
}
/******写SD24XX禁止程序******/
uchar WriteTimeOff(void)
{
if(!I2CWriteOneByte(RTC_Address,0x0f,0))return false;
I2CWriteOneByte(RTC_Address,0x10,0);
return true;
}
/******读SD24XX实时数据寄存器******/
uchar 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;
}
/******写SD24XX实时数据寄存器******/
uchar 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;
}
/******设置SD24XX报警中断演示程序演示******/
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();
}
/******关闭SD24XX报警中断程序******/
void ClrALARM(void) //关闭报警中断
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0x90);
WriteTimeOff();
}
/******设置SD24XX倒计时中断演示******/
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();
}
/******关闭SD24XX倒计时中断程序******/
void ClrDjs(void)
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0xf0);
WriteTimeOff();
}
/******设置SD24XX频率中断演示******/
void SetFrq(void)
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0xa1); //选通频率中断(INTS1,INTS0),设置频率中断总允许位(INTFE)
I2CWriteOneByte(RTC_Address,0x11,0x09); //设置2Hz频率中断
WriteTimeOff();
}
/******关闭SD24XX频率中断******/
void ClrFrq(void)
{
WriteTimeOn();
I2CWriteOneByte(RTC_Address,0x10,0xa0);
WriteTimeOff();
}
//|************I2C连续读多个字节************|
//|***尾缀B、C、F型读EEPROM程序(连续读)***|
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连续写多个字节******************|
//|******尾缀B、C型和F型写EEPROM程序(连续写)演示******|
uchar I2CWriteSerial(uchar DeviceAddress, uchar Address, uchar length,uchar *ps)
{
uchar i;
if(!I2CStart())return false;
I2CSendByte(DeviceAddress); //器件地址(或EEPROM页地址)
if(!I2CWaitAck()){I2CStop(); return false;}
I2CSendByte(Address); //设置起始地址
I2CWaitAck();
for(i=0;i<length;i++)
{
I2CSendByte(*(ps++)); //B、C型最多连续发送16个字节数据,F型最多连续发送8个字节数据,若使
I2CAck(); //用的是NVSRAM则无此限制,可连续写任意多个数据直到存储器的末地址。
}
I2CStop();
return true;
}
/******尾缀D、E型写EEPROM程序(连续写)******/
void WriteEE_DE(uchar sramadd_hi,uchar sramadd_lo,uchar sramdata)
{
uchar n;
I2CStart();
I2CSendByte(EE_FirstPage_Address); //发送页地址
I2CWaitAck();
I2CSendByte(sramadd_hi); //先发送高8位地址
I2CWaitAck();
I2CSendByte(sramadd_lo); //再发送低8位地址
I2CWaitAck();
for(n=0;n<8;n++)
{
I2CSendByte(sramdata++); //D型最多连续发送32个字节数据,E型最多连续发送64个字节数据,若使用
I2CWaitAck(); //的是NVSRAM则无此限制,可连续写任意多个数据直到存储器的末地址。
}
I2CStop();
}
/******D、E型读EEPROM程序******/
void ReadEE_DE(uchar sramadd_hi,uchar sramadd_lo,uchar m)
{
uchar n;
I2CStart();
I2CSendByte(EE_FirstPage_Address); //发送页地址
I2CWaitAck();
I2CSendByte(sramadd_hi); //先发送高8位地址
I2CWaitAck();
I2CSendByte(sramadd_lo); //再发送低8位地址
I2CWaitAck();
I2CStart(); //重发起始信号
I2CSendByte(EE_FirstPage_Address+1);//发送读EEPROM命令
I2CWaitAck();
for(n=0;n<m;n++) //可读任意多个数据,直到存储器的末地址
{
data1[n]=I2CReceiveByte();
if (n!=m-1) //最后一个数据不应答
{
I2CAck();
}
}
I2CNoAck();
I2CStop();
}
//*****主程序演示*****//
void main()
{
I2CWriteDate(RTC); //设置时间演示
// WriteALARM(); //设置报警中断时间演示
// SetDjs(); //设置倒计时中断演示
SetFrq(); //设置频率中断演示
I2CWriteSerial(EE_FirstPage_Address,0,8,Sram); //Sram1数据写入EEPROM的00-07地址
Delayms(4); //写完EEPROM以后需要延时10ms
I2CReadSerial(EE_FirstPage_Address,0,8,data1); //读从EEPROM第一页的00地址开始读8字节数据(00-07地址)演示
while(1)
{
I2CReadDate(&RTC); //读时间演示
delay_ms(100);
}
}
//特别提醒:当写实时时间数据时 (00H~06H), 不可以单独 对 7 个时间数据中的某一位进行写操作 ,
//否则可能会引起时间数据的错误进位 , 所以要修改其中某一个数据 , 应一次性写入全部 7 个实时时钟数据 .