I2C总线协议(转载)

SDA线上的数据在时钟期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的状态才可以改变。输出到SDA线上的每个字 节必须是8位,每次传输的字节不受限制,每个字节必须有一个应答为ACK

 

 数据传送具有应答是必须的。与应答对应的时钟脉冲由主控器产生,发送器在应答期间必须下拉SDA线。当寻址的被控器件不能应答时,数据保持为高,接着主控 器产生停止条件终止传输。

ACK:   SDA=0;SCL=1;udelay();SCL=0;

NOACK: SDA=1;SCL=1;udelay();SCL=0;

 

(改自周立功软件包)

#include <reg51.h>
#include <intrins.h>

#define   uchar unsigned char   /*宏定义*/
#define   uint   unsigned int
extern void Delay1us(unsigned char );

sbit SDA=P1^6;              /*模拟I2C数据传送位*/
sbit SCL=P1^7;              /*
模拟I2C时钟控制位*/

bit ack;            /*应答标志位*/


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

起动总线函数              
函数原型
: void   Start_I2c(); 
功能:      启动I2C总线,即发送I2C起始条件

********************************************************************/
void Start_I2c()
{
   SDA=1;     /*
发送起始条件的数据信号
*/
   Delay1us(1);
   SCL=1;
   Delay1us(5);      /*
起始条件建立时间大于4.7us,延时
*/
   
   SDA=0;     /*
发送起始信号
*/
   Delay1us(5);      /*
起始条件锁定时间大于
4μs*/
       
   SCL=0;     /*
钳住I2C总线,准备发送或接收数据
*/
   Delay1us(2);
}

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

结束总线函数              
函数原型
: void   Stop_I2c(); 
功能:      结束I2C总线,即发送I2C结束条件

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

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

字节数据发送函数              
函数原型
: void   SendByte(uchar c);
功能:      将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对

          
此状态位进行操作.(不应答或非应答都使ack=0)    
        
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。

********************************************************************/
void   SendByte(uchar c)
{
uchar BitCnt;

for(BitCnt=0;BitCnt<8;BitCnt++)   /*要传送的数据长度为8*/
     {
      if((c<<BitCnt)&0x80)SDA=1;    /*
判断发送位
*/
        else   SDA=0;               
      Delay1us(1);
      SCL=1;                /*
置时钟线为高,通知被控器开始接收数据位
*/
     
      Delay1us(5);              /*
保证时钟高电平周期大于
4μs*/
            
      SCL=0;
     }
   
     Delay1us(2);
     SDA=1;                 /*8
位发送完后释放数据线,准备接收应答位
*/
     Delay1us(2);  
     SCL=1;
     Delay1us(3);
     if(SDA==1)ack=0;    
        else ack=1;         /*
判断是否接收到应答信号,由从机返回
*/
     SCL=0;
     Delay1us(2);
}

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

字节数据接收函数              
函数原型
: uchar   RcvByte();
功能:      用来接收从器件传来的数据,并判断总线错误(不发应答信号)

          
发完后请用应答函数应答从机。 
********************************************************************/
uchar   RcvByte()
{
   uchar retc;
   uchar BitCnt;
 
   retc=0;
   SDA=1;                /*
置数据线为输入方式
*/
   for(BitCnt=0;BitCnt<8;BitCnt++)
       {
         Delay1us(1);  
         SCL=0;                   /*
置时钟线为低,准备接收数据位
*/
         Delay1us(5);                  /*
时钟低电平周期大于
4.7μs*/
         SCL=1;                   /*
置时钟线为高使数据线上数据有效
*/
         Delay1us(3);
         retc=retc<<1;
         if(SDA==1)retc=retc+1;   /*
读数据位,接收的数据位放入retc
*/
         Delay1us(2);
       }
   SCL=0;   
   Delay1us(2);
   return(retc);
}

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

应答子函数 (用于主机读取数据后,应答从机)
函数原型
:   void Ack_I2c(bit a);
功能:       主控器进行应答信号(可以是应答或非应答信号,由位参数a决定
)
********************************************************************/
void Ack_I2c(bit a)
{
 
   if(a==0)SDA=0;            /*
在此发出应答或非应答信号
*/
         else SDA=1;
   Delay1us(3);     
   SCL=1;
   Delay1us(5);                     /*
时钟低电平周期大于
4μs*/
   SCL=0;                      /*
清时钟线,钳住I2C总线以便继续接收
*/
   Delay1us(2);   
}


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

向有子地址器件发送多字节数据函数              
函数原型
: bit   ISendStr(uchar sla,uchar suba,ucahr *s,uchar no); 
功能:      从启动总线到发送地址,子地址,数据,结束总线的全过程,从器件

          
地址sla,子地址suba,发送内容是s指向的内容,发送no个字节。
          
如果返回1表示操作成功,否则操作有误。
注意:     使用前必须已结束总线。
********************************************************************/
bit ISendStr(uchar sla,uchar suba,uchar *s,uchar no)
{
    uchar i;

    Start_I2c();                /*启动总线*/
    SendByte(sla);              /*
发送器件地址
*/
    if(ack==0)return(0);
    SendByte(suba);             /*
发送器件子地址
*/
    if(ack==0)return(0);
    for(i=0;i<no;i++)
    {  
      SendByte(*s);             /*
发送数据
*/
      if(ack==0)return(0);       // 
检测从机发送过来的应答位,acksendbyte函数返回

      s++;
    }
    Stop_I2c();                 /*
结束总线*/

    return(1);
}

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

向有子地址器件读取多字节数据函数              
函数原型
: bit   ISendStr(uchar sla,uchar suba,ucahr *s,uchar no); 
功能:      从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件

          
地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
           
如果返回1表示操作成功,否则操作有误。
注意:     使用前必须已结束总线。
********************************************************************/
bit IRcvStr(uchar sla,uchar suba,uchar *s,uchar no)
{
    uchar i;

    Start_I2c();                   /*启动总线*/
    SendByte(sla);                 /*
发送器件地址
*/
    if(ack==0)return(0);
    SendByte(suba);                /*
发送器件子地址
*/
    if(ack==0)return(0);
    Start_I2c();     /*
重新启动总线
*/
    SendByte(sla+1);
    if(ack==0)return(0);
    for(i=0;i<no-1;i++)
    {  
      *s=RcvByte();                /*
接收数据
*/
       Ack_I2c(0);                 /*
读完一个数据,就发送一个应答位
*/ 
      s++;
    }
    *s=RcvByte();
    Ack_I2c(1);                    /*
全部读完,发送非应位
*/
    Stop_I2c();                    /*结束总线
*/
    return(1);
}

说明:这个软件删减了一部分,主要是改写了延时子程序部分,使之适合STC12C2052/5410系列的单片机时序!

改动后可以很好的应用在STC单片机上,应用时主要使用

bit IRcvStr(uchar sla,uchar suba,uchar *s,uchar no)

bit ISendStr(uchar sla,uchar suba,uchar *s,uchar no)
这两个函数,就可以实现对有子地址的器件的读写!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值