I2C 协议之软件模拟时序

1.1 I2C 简介 

                   I2C 总线时 PHILIPS 公司推出的一种串行总线,具备多主机系统所需的包括总线仲裁和高低速器件同步功能的高性能串行总线。它只需要两跟双向的信号线,一根数据线 SDA,一个是时钟线 SCL。在 I2C 总线上面,每个器件都有自己相应的 I2C 地址,所以在两个器件之间进行通信的时候,都要首先呼叫你想要通信的器件地址,然后等待相应的从器件进行应答之后才开始通信。首先我们来看一下,一个 I2C 信号传输的一个过程,如图: 

从图上我们可以看出在 I2C 上面一个完整信号的传输过程,一定要有一个始信号,还有一
个结束信号,在每个字节传输结束的时候,从机还要提供一个应答信号。一个完整的信号传
输就是这样子。接下来我们来看一下,I2C 总线上面对起始信号、应答信号、结束信号、还
有高低电平的协定是怎么样的呢?这里有两个要注意的要点: 
1、在总线空闲的时候,SDA 和 SCL 都是高电平的。 
2、在 SCL 为高电平期间,SDA 必须保持稳定。所以 SDA 改变状态最好在 SCL 为低电
平的时候改变,如果在高电平改变的话回被认为是一种有效信号(如:起始信号或者结束信
号) 

1. 起始信号 
起始信号简介 SCL 线为高电平期间,SDA 线由高电平向低电平的变化表示起始信号, 
信号时序如图: 

这里要注意的就是,在 I2C 总线上面,当总线空闲的时候,SCL 和 SDA 都是高电平
的。起始信号,它是需要有一定的保持时间的,在 SDA 从高电平向低电平跳变的时候,两
个先必须至少保持 4.7us 的时间,而跳变之后,也要保持 SCL 高电平和 SDA 低电平要至
少保持 4us 的时间(从这里我们看出 I2C 总高速率已经决定了) 。 

 

//IO 口模拟起始信号 
//产生起始信号 
void I2C_Start(void) 
{ 
    I2C_SDA_OUT(); 
  
 I2C_SDA_H; 
 I2C_SCL_H; 
 delay_us(5); 
 I2C_SDA_L; 
 delay_us(6); 
 I2C_SCL_L; 
} 

2. 结束信号 
结束信号简介 SCL 线为高电平期间,SDA 线由低电平向高电平的变化表示终止信号。 
信号时序如图: 
 
注意的就是这里保持时间也是有一定限制的。 

 

 

 

 

//IO 口模拟结束信号 
//产生停止信号 
void I2C_Stop(void) 
{ 
   I2C_SDA_OUT(); 
 
   I2C_SCL_L; 
   I2C_SDA_L; 
   I2C_SCL_H; 
   delay_us(6); 
   I2C_SDA_H; 
   delay_us(6); 
}

3. 应答信号 
应答信号 
应答,也叫响应。数据的传输必须要带应答。在响应的时钟脉冲期间(也就是 SCL 在高电
平的时候) ,发送器释放 SDA 线(释放 SDA 意思就是将 SDA 拉为高电平,这里要注
意的是,不能在 SCL 为高电平的时候讲 SDA 从低电平拉到高电平,可以在在 SCL 在低
电平的时候,将 SDA 拉为高电平等待),然后等待应答,在应答时钟脉冲器件,接收器 
必须将 SDA 拉低,使它在这个时钟脉冲的高电平期间保持稳定的低电平。而一个字节传输
完毕之后,接收器没有应答则表示接收完毕。还有一种情况是,当主机作为接收器的时候,
接收完最后一个字节之后,必须向从机发出一个结束传送的信号。这个信号是由对从机“非 
应答”来实现的。(从上面的规则我们知道,当主机作为接收器的时候,如果是进行应答,
那么在接收完一个字节的最后一位之后产生一个低电平的时钟,进行应答。而非应答呢,就
是产生一个高电平的时钟,进行应答) 。如果大家不是很理解呢,大家可以参考我们例程
里面 I2C 的 IO 模拟信号里面接收数据的函数,最后的应答和非应答。 

 

//IO 口模拟应答信号 
//主机产生应答信号 ACK 
void I2C_Ack(void) 
{ 
   I2C_SCL_L; 
   I2C_SDA_OUT(); 
   I2C_SDA_L; 
   delay_us(2); 
   I2C_SCL_H; 
   delay_us(5); 
   I2C_SCL_L; 
} 
//主机不产生应答信号 NACK 
void I2C_NAck(void) 
{ 
   I2C_SCL_L; 
   I2C_SDA_OUT(); 
   I2C_SDA_H; 
   delay_us(2); 
   I2C_SCL_H; 
   delay_us(5); 
   I2C_SCL_L; 
} 
//等待从机应答信号 
//返回值:1 接收应答失败 
//    0 接收应答成功 
u8 I2C_Wait_Ack(void) 
{ 
 u8 tempTime=0; 
 
 I2C_SDA_IN(); 
 
 I2C_SDA_H; 
 delay_us(1); 
 I2C_SCL_H; 
 delay_us(1); 
 
 while(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA)) 
 { 
  tempTime++; 
  if(tempTime>250) 
  { 
   I2C_Stop(); 
   return 1; 
  }   
 } 
 
 I2C_SCL_L; 
 return 0; 
} 

4. 逻辑“1”的表示 
要传输数据,那么肯定要分传输“1”和“0” ,而在 I2C 上面是怎么表示这两个逻辑变量
的呢?如图: 

 

 

 

一般 I2C 读取的时候,都是在 SCL 的为高电平的时候进行读取,所以在 SCL 为高电平
的时候,需要保持 SDA 稳定。而且注意的还有就是他们的保持时间要大于 4us。 

5. 逻辑“0”的表示 
逻辑“0”和逻辑“1”的表示其实差不多,只是 SDA 正好相反。如图: 

 

注意的事项跟逻辑“1”的表示差不多。 

6. IO 口模拟发送一个字节数据 

 

//I2C 发送一个字节 
void I2C_Send_Byte(u8 txd) 
{ 
 u8 i=0; 
 
 I2C_SDA_OUT(); 
 I2C_SCL_L;//拉低时钟开始数据传输 

 
 for(i=0;i<8;i++) 
 { 
  if((txd&0x80)>0) //0x80  1000 0000 
   I2C_SDA_H; 
  else 
   I2C_SDA_L; 
 
  txd<<=1; 
  I2C_SCL_H; 
  delay_us(2); //发送数据 
  I2C_SCL_L; 
  delay_us(2); 
 } 
} 


7. IO 口模拟接收一个字节数据 

 

 

 

 

//I2C 读取一个字节 
 
u8 I2C_Read_Byte(u8 ack) 
{ 
   u8 i=0,receive=0; 
 
   I2C_SDA_IN(); 
   for(i=0;i<8;i++) 
   { 
     I2C_SCL_L; 
  delay_us(2); 
  I2C_SCL_H; 
  receive<<=1; 
  if(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA)) 
     receive++; 
  delay_us(1);  
   } 
 
    if(ack==0) 
     I2C_NAck(); 
 else 
  I2C_Ack(); 
 
 return receive; 
} 

 

 

 

 

 

 

 

  • 12
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值