AVR单片机SD24XX读写演示程序

℡╲_俬逩灬. 提交于 2020-01-19 05:16:32

//-------------------------------------------------------------------------
// 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 个实时时钟数据 .

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!