转)8051模拟i2C总线,24c02读写源代码C语言(转)

51系列单片机在使用时,有时需要模拟I2C总线_nbsp;                     */ 
/*  这里举出一个实例(读写串行EEPROM芯片at24C02_nbsp;                     */
/************************************************************************/
/* Name:AT24C02存储器的读写程序,用到I2C总线,含相对独立的I2C总线读写函数 */
/* Language: C51单片机编程语言                                          */
/* Platform: Win98,Intel Celeron 433 Processor,伟福仿真器,仿真8751    */
/* Author:  StephenZhu    javasdk@163.com                              */
/* Date: 2003广朿1旿5朿2旿5朿9旿nbsp;                                 */
/* Version: 1.1.1                            */
/* Others: None                                                        */
/************************************************************************/

    #i nclude <string.h>
    #i nclude <reg52.h>
    #i nclude <intrins.h>
    #define DELAY_TIME 60 /*经实验,不要小于50!否则可能造成时序混乱*/
    #define TRUE 1
    #define FALSE 0

    sbit SCL=P1^7;/*假设由P1.7和P1.6控制*/
    sbit SDA=P1^6;

/********** Definition 函数定义 ************/

    void DELAY(unsigned int t) /*延时函数*/
    {
        while(t!=0)
            t--;
    }

    void I2C_Start(void)
    {
        /*启动I2C总线的函数,当SCL为高电平时使SDA产生一个负跳变*/
        SDA=1;
        SCL=1;
        DELAY(DELAY_TIME);
        SDA=0;
        DELAY(DELAY_TIME);
        SCL=0;
        DELAY(DELAY_TIME);
    }

    void I2C_Stop(void)
    {
        /*终止I2C总线,当SCL为高电平时使SDA产生一个正跳变*/
        SDA=0;
        SCL=1;
        DELAY(DELAY_TIME);
        SDA=1;
        DELAY(DELAY_TIME);
        SCL=0;
        DELAY(DELAY_TIME);
    }

    void SEND_0(void)  /* SEND ACK */
    {
        /*发逿,在SCL为高电平时使SDA信号为低*/
        SDA=0;
        SCL=1;
        DELAY(DELAY_TIME);
        SCL=0;
        DELAY(DELAY_TIME);
    }

    void SEND_1(void)
    {
        /*发逿,在SCL为高电平时使SDA信号为高*/
        SDA=1;
        SCL=1;
        DELAY(DELAY_TIME);
        SCL=0;
        DELAY(DELAY_TIME);
    }

    bit Check_Acknowledge(void)
    {
        /*发送完一个字节后检验设备的应答信号*/
        SDA=1;
        SCL=1;
        DELAY(DELAY_TIME/2);
        F0=SDA;
        DELAY(DELAY_TIME/2);
        SCL=0;
        DELAY(DELAY_TIME);
        if(F0==1)
            return FALSE;
        return TRUE;
    }

    void WriteI2CByte(char b)reentrant
    {
        /*向I2C总线写一个字芿/
        char i;
        for(i=0;i <8;i++)
            if((b < <i)&0x80)
                SEND_1();
            else
                SEND_0();
    }



    char ReadI2CByte(void)reentrant
    {
        /*从I2C总线读一个字芿/
        char b=0,i;
        for(i=0;i <8;i++)
        {
            SDA=1;    /*释放总线*/
            SCL=1;    /*接受数据*/
            DELAY(10);
            F0=SDA;
            DELAY(10);
            SCL=0;
            if(F0==1)
                {
                    b=b < <1;
                    b=b|0x01;
                }
            else
                b=b < <1;
        }
        return b;
    }


/**********以下为读冿4c02的函敿*********/
void Write_One_Byte(char addr,char thedata)
{
          bit acktemp=1;
          /*write a byte to mem*/
          I2C_Start();
          WriteI2CByte(0xa0);
          acktemp=Check_Acknowledge();
          WriteI2CByte(addr);/*address*/
          acktemp=Check_Acknowledge();
          WriteI2CByte(thedata);/*thedata*/
          acktemp=Check_Acknowledge();
          I2C_Stop();

}

void Write_A_Page(char *buffer,char addr)
{
          bit acktemp=1;
          bit wrtmp;
          int i;
          /*write a page to at24c02*/
          I2C_Start();
          WriteI2CByte(0xa0);
          acktemp=Check_Acknowledge();
          WriteI2CByte(addr);/*address*/
          acktemp=Check_Acknowledge();
          for(i=0;i <7;i++)
          {
            WriteI2CByte(buffer[i]);
            if(!Check_Acknowledge())
                {
                    I2C_Stop();

                }
          }


          I2C_Stop();




}

char Read_One_Byte(char addr)
{        bit acktemp=1;
          char mydata;
          /*read a byte from mem*/
          I2C_Start();
          WriteI2CByte(0xa0);
          acktemp=Check_Acknowledge();
          WriteI2CByte(addr);/*address*/
          acktemp=Check_Acknowledge();
          I2C_Start();
          WriteI2CByte(0xa1);
          acktemp=Check_Acknowledge();


          mydata=ReadI2CByte();
          acktemp=Check_Acknowledge();

          return mydata;
          I2C_Stop();
}

void Read_N_Bytes(char *buffer,char n,char addr)
{
          bit acktemp=1;

          int i=0;
          /*read 8 bytes from mem*/
          I2C_Start();
          WriteI2CByte(0xa0);
          acktemp=Check_Acknowledge();
          WriteI2CByte(addr);/*address*/
          acktemp=Check_Acknowledge();
          I2C_Start();
          WriteI2CByte(0xa1);

          acktemp=Check_Acknowledge();




          for(i=0;i <n;i++)
          {
                buffer[i]=ReadI2CByte();
                if(i!=n-1)
                    SEND_0();    /*发送应筿/
                else
                    SEND_1();    /*发送非应答*/
          }

  I2C_Stop();

}


void main()
{
          int i;
          char mybyte;
          char myarray[8];
          char myarray2[8];
          char rdarray[16];
         
          for(i=0;i <8;i++)
          {
                myarray[i]=i;
                myarray2[i]=i+0x08;   

          }

          Write_One_Byte(0x20,0x28);

          Write_A_Page(myarray,0x10);
          Write_A_Page(myarray2,0x18);

          mybyte=Read_One_Byte(0x20);
          Read_N_Bytes(rdarray,16,0x10);

}

==================================================================================================

 

 实例2

/********************************** I2C总线驱动 *************************************

 模块名:I2C总线驱动   型号:I2C

 创建人:陈曦  日期_005-6-15
 修改人:陈曦  日期_005-6-19

 功能描述_/p>

 此模块包括发送数据及接收数据,应答位发送,并提供了几个直接面对器件的操作函数,能很
方便的与用户程序进行连接并扩展?br>    需要注意的是,函数是采用延时方法产甿SCL 脉冲,对高晶振频率要做一定的修改!!
    在写E2PROM的时候一定要延时!!_br>   
 说明_/p>

 1us机器周期,晶振频率要小于12MHz
 返回 1 则操作成功,返回 0 则操作失败?br> sla 为器件从地址,suba 为器件子地址?/p>

*************************************************************************************/

#i nclude "AT89X52.h"
#i nclude <intrins.h>

#define  _Nop() _nop_()   //定义空指仿/p>

sbit  SDA = P1^3;    //模拟I2C数据传输使br>sbit  SCL = P1^2;    //模拟I2C时钟控制使/p>

bit  bdata I2C_Ack;   //应答标志使/p>

/************************************ I2C_Start ************************************

 函数名:void I2C_Start()
 入口_br> 出口_/p>

 功能描述:启动I2C总线,即发送I2C初始条件
 调用函数_nbsp;
 全局变量_/p>

 创建者:陈曦   日期_005-6-15
 修改者:           日期_/p>

**********************************************************************************/

void I2C_Start()
{
 SDA = 1;     //发送起始条件的数据信号
 _Nop();
 SCL = 1;
 _Nop();      //起始条件建立时间大于4.7us,延时
 _Nop();
 _Nop();
 _Nop();
 _Nop();
 SDA = 0;     //发送起始信叿br> _Nop();      //起始条件建立时间大于4us,延时
 _Nop();
 _Nop();
 _Nop();
 _Nop();
 SCL = 0;     //钳住I2C总线准备发送或接收数据
 _Nop();
 _Nop();
}


/************************************ I2C_Stop ************************************

 函数名:void I2C_Stop()
 入口_br> 出口_/p>

 功能描述:结束I2C总线,即发送I2C结束条件
 调用函数_nbsp;
 全局变量_/p>

 创建者:陈曦  日期_005-6-15
 修改者:   日期_/p>

**********************************************************************************/


void I2C_Stop()
{
 SDA = 0;     //发送结束条件的数据信号
 _Nop();
 SCL = 1;     //发送结束条件的时钟信号
 _Nop();      //结束条件建立时间大于4us,延时
 _Nop();
 _Nop();
 _Nop();
 _Nop();
 SDA = 1;     //发送I2C总线结束信号
 _Nop();
 _Nop();
 _Nop();
 _Nop();
}

/************************************ I2C_CheckAck ************************************

 函数名:bit I2C_CheckAck(void)
 入口_br> 出口_(无应答),1(有应答_/p>

 功能描述_br> 检验I2C总线应答信号,有应答则返囿,否则返囿,超时值取255

 调用函数:void I2C_Stop()
 全局变量_/p>

 创建者:陈曦  日期_005-6-15
 修改者:          日期_/p>

**********************************************************************************/

bit I2C_CheckAck(void)
{
 uchar errtime = 255;    // 因故障接收方旿Ack,超时值为255
 SDA = 1;
 _Nop();
 _Nop();
 _Nop();
 SCL = 1;
 _Nop();        //时钟电平周期大于 4 us
 _Nop();
 _Nop();
 _Nop();
 _Nop();
 while(SDA)
 {
  errtime--;
  if(errtime==0)
  {
   I2C_Stop();
   return(0);
  }
 }
 SCL = 0;
 _Nop();
 return(1);
}

/************************************ I2C_SendB ************************************

 函数名:void I2C_SendB(uchar c)
 入口:uchar 型数捿br> 出口_/p>

 功能描述_br> 字节数据传送函数,将数捿c 发送出去,可以是地址,也可以是数据,发完后等待应答,并对
 此状态位进行操作

 调用函数:bit I2C_CheckAck()
 全局变量:I2C_Ack

 创建者:陈曦  日期_005-6-15
 修改者:          日期_/p>

**********************************************************************************/

void I2C_SendB(uchar c)
{
 uchar BitCnt;

 for (BitCnt=0; BitCnt<8; BitCnt++)  //要传送的数据长度丿使br> {
  if((c<<BitCnt)&0x80)    //判断发送位(从高位起发逿
  {
   SDA = 1;
  }
  else
  {
   SDA = 0;
  }

  _Nop();
  _Nop();
  SCL = 1;       //置时钟线为高通知被控器开始接收数据位
  _Nop();        //保证时钟高电平周期大亿4us
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  SCL = 0;
 }

 _Nop();
 _Nop();

 I2C_Ack = I2C_CheckAck();    //检验应答信叿br> _Nop();
 _Nop();
}


/************************************ I2C_RcvB ************************************

 函数名:uchar I2C_RcvB()
 入口_br> 出口:uchar型数捿/p>

 功能描述_br> 接收从器件传来的数据,并判断总线错误(不发应答信号),收完后需要调用应答函数?/p>

 调用函数_br> 全局变量_/p>

 创建者:陈曦  日期_005-6-15
 修改者:          日期_/p>

**********************************************************************************/

uchar I2C_RcvB()
{
 uchar retc;
 uchar BitCnt;        //使/p>

 retc = 0;
 SDA = 1;         //置数据总线为输入方弿br> for(BitCnt=0;BitCnt<8;BitCnt++)
 {
  _Nop();
  SCL = 0;        //置时钟线为低准备接收数据使br>  _Nop();         //时钟低电平周期大亿.7us
  _Nop();
  _Nop();
  _Nop();
  _Nop();
  SCL = 1;        //置时钟线为高使数据有敿br>  _Nop();
  _Nop();
  retc = retc<<1;
  if(SDA==1)
  {
   retc = retc + 1;     //读数据位,接收的数据放入retc丿br>  }
  _Nop();
  _Nop();
  }

  SCL = 0;
  _Nop();
  _Nop();

  return(retc);
}

/************************************ I2C_Ackn ************************************

 函数名:void I2C_Ackn(bit a)
 入口_房
 出口_/p>

 功能描述:主控制器进行应答信号(可以是应答或非应答信号)
 调用函数_br> 全局变量_/p>

 创建者:陈曦  日期_005-6-15
 修改者:          日期_/p>

**********************************************************************************/


void I2C_Ackn(bit a)
{
 if(a==0)     //在此发送应答或非应答信叿br> {
  SDA = 0;
 }
 else
 {
  SDA = 1;
 }
 _Nop();
 _Nop();
 _Nop();
 SCL = 1;
 _Nop();      //时钟电平周期大于 4 us
 _Nop();
 _Nop();
 _Nop();
 _Nop();
 SCL = 0;     //清时钟线钳住I2C总线以便继续接收
 _Nop();
 _Nop();
}

/******************************** I2C_ISendB ************************************

 函数名:bit I2C_ISendB(uchar sla, uchar suba, uchar c)
 入口:从器件地址 sla,子地址 suba, 发送字芿c
 出口_(操作有误)_(操作成功)

 功能描述:从启动总线到发送地址、数据,结束总线的全过程_br>     如果返回1,表示操作成功,否则操作有误?/p>

 调用函数:I2C_Start(),I2C_SendB(uchar c),I2C_Stop()
 全局变量:I2C_Ack

 创建者:陈曦  日期_005-6-15
 修改者:          日期_/p>

**********************************************************************************/

bit I2C_ISendB(uchar sla, uchar suba, uchar c)
{
 I2C_Start();    //启动总线

 I2C_SendB(sla);    //发送器件地址
 if(!I2C_Ack)
 {
  return(0);
 }

 I2C_SendB(suba);   //发送器件子地址
 if(!I2C_Ack)
 {
  return(0);
 }

 I2C_SendB(c);    //发送数捿br> if(!I2C_Ack)
 {
  return(0);
 }

 I2C_Stop();     //结束总线
 return(1);
}

/********************************** I2C_IRcvB ************************************

 函数名:bit I2C_IRcvB(uchar sla, uchar suba, uchar *c)
 入口:从器件地址 sla, 子地址 suba, 收到的数据在 c
 出口_(操作成功)_(操作有误)

 功能描述:从启动总线到发送地址、读数据,结束总线的全过程?/p>

 调用函数_nbsp;I2CS_tart(),
    I2C_SendB(uchar c),
    I2C_RcvB(),
    I2C_Ackn(bit a),
    I2C_Stop()

 全局变量:I2C_Ack

 创建者:陈曦  日期_005-5-15
 修改者:          日期_/p>

**********************************************************************************/

bit I2C_IRcvB(uchar sla, uchar suba, uchar *c)
{
 I2C_Start();     //启动总线

 I2C_SendB(sla);
 if(!I2C_Ack)
 {
  return(0);
 }

 I2C_SendB(suba);    //发送器件子地址
 if(!I2C_Ack)
 {
  return(0);
 }

 I2C_Start();     //重复起始条件

 I2C_SendB(sla+1);    //发送读操作的地址
 if(!I2C_Ack)
 {
  return(0);
 }

 *c = I2C_RcvB();    //读取数据
 I2C_Ackn(1);     //发送非应答使br> I2C_Stop();      //结束总线
 return(1);
}

posted @ 2013-03-17 17:42  天涯海角路  阅读(1213)  评论(0)    收藏  举报