通信协议——I2C总线

来源:互联网 发布:好的职业技术学校知乎 编辑:程序博客网 时间:2024/06/07 20:03

    I2C总线是一种简单、双向二线制同步串行总线,由SCL和SDA两根线即可连接于总线上的器件之间传送信息。

其中SCL为时钟线,SDA为数据线,在时钟线SCL控制的时钟信号下,SDA进行数据的传送,而SDA上传送的每个字节必须为8位,每个字节后跟一个响应位,而传输是一位位进行的,其首先传输的是最高位。

一、I2C时序图及程序

1、起始信号和终止信号


    起始信号从时序图可看出,先拉高SDA和SCL,然后拉低SDA,一般START信号到这里就结束了,但为了后面的发送字节、读字节等的方便,所以一般在拉低SDA并延时一小段时间后再拉低SCL。

终止信号,拉低SDA然后拉高SCL,最后再拉高SDA就完成了终止信号的发送。

具体程序可参考如下:

[plain] view plain copy
  1. /*******************************************************************************  
  2. * 函 数 名         : I2C_Start()  
  3. * 函数功能         : 起始信号:在I2C_SCL时钟信号在高电平期间I2C_SDA信号产生一个下降沿  
  4. * 输    入         : 无  
  5. * 输    出         : 无  
  6. * 备    注         : 起始之后I2C_SDA和I2C_SCL都为0  
  7. *******************************************************************************/  
  8. void I2C_Start()  
  9. {  
  10.     I2C_SDA = 1;  
  11.     I2C_SCL = 1;  
  12.     I2C_Delay_us(5);//建立时间是I2C_SDA保持时间>4.7us  
  13.     I2C_SDA = 0;  
  14.     I2C_Delay_us(4);//保持时间是>4us  
  15.     I2C_SCL = 0;                  
  16. }  
  17.   
  18. /*******************************************************************************  
  19. * 函 数 名           : I2C_Stop()  
  20. * 函数功能           : 终止信号:在I2C_SCL时钟信号高电平期间I2C_SDA信号产生一个上升沿  
  21. * 输    入           : 无  
  22. * 输    出             : 无  
  23. * 备    注           : 结束之后保持I2C_SDA和I2C_SCL都为1;表示总线空闲  
  24. *******************************************************************************/  
  25. void I2C_Stop()  
  26. {  
  27.     I2C_SDA = 0;  
  28.     I2C_Delay_us(4);  
  29.     I2C_SCL = 1;  
  30.     I2C_SDA = 1;          
  31. }  
2.I2C发送字节和读字节

    如时序图所示,在SCL为低电平的时候,SDA处于数据转换的状态,此时数据状态是不稳定的也是不确定的,当SCL处于高电平后,此时在SDA上传输的数据稳定,而传输的可能是高电平,也可能是低电平。

对于发送字节,先拉低SCL,根据要发送的字节的当前正在处理的某一位的高低来转换给SDA,在转换完后,拉高SCL,进行数据传输。

对于读字节,因为读字节时读SDA线上是高电平还是低电平,因此应该要先拉高SCL,然后判断SDA上的数据是高还是低并赋值给相应的变量,然后再拉低SCL。

具体程序可参考如下:

[plain] view plain copy
  1. /*******************************************************************************  
  2. * 函 数 名           : I2C_SendByte(unsigned char byt)  
  3. * 函数功能           : 发送一个字节  
  4. * 输    入           : byt  
  5. * 输    出             : 无  
  6. * 备    注           : 无  
  7. *******************************************************************************/  
  8. void I2C_SendByte(unsigned char byt)  
  9. {  
  10.     unsigned char i;  
  11.     I2C_SCL = 0;  
  12.     for(i = 0;i < 8;i++)  
  13.     {          
  14.         if(byt&0x80)              //从最高位开始判断发送的字节byt是0还是1来判断I2C_SDA是0还是1  
  15.         {  
  16.             I2C_SDA = 1;  
  17.         }  
  18.         else  
  19.         {  
  20.             I2C_SDA = 0;  
  21.         }             
  22.         byt <<= 1;              //判断完一位后,那么要左移一位,让下一位继续判断,一个字节8位,所以一共是要判断8次  
  23.         delay_us(2);  
  24.         I2C_SCL = 1;          
  25.         delay_us(2);  
  26.         I2C_SCL = 0;  
  27.     }  
  28.     I2C_SCL = 0;  
  29. }  
  30.   
  31. /*******************************************************************************  
  32. * 函 数 名           : unsigned char IIC_Read_Byte(unsigned char ack)  
  33. * 函数功能           : 读一个字节  
  34. * 输    入           : ack(0或1)  
  35. * 输    出             : 读到的一个字节dat  
  36. * 备    注           : 无  
  37. *******************************************************************************/  
  38. unsigned char IIC_Read_Byte(unsigned char ack)  
  39. {  
  40.     unsigned char i,dat = 0;  
  41.     I2C_SCL = 0;  
  42.     for(i = 0;i < 8;i++ )  
  43.     {  
  44.         IIC_SCL = 1;  
  45.         dat <<= 1;  
  46.         if(I2C_SDA)      //根据I2C_SDA的高低来一位一位赋值给dat  
  47.         {   
  48.             dat |= 0x01;   
  49.         }             
  50.         delay_us(1);   
  51.         I2C_SCL = 0;  
  52.     }                      
  53.     IIC_Ack(ack);     //如果只接收1个字节的数据,则发送非应答信号1,  
  54.                       //然后产生stop信号,告诉从机单片机停止接收数据,也就是不用再发了  
  55.                       //如果要接收多个字节数据,则接收完一个字节数据后要发送应答信号0,  
  56.                       //告诉从机要继续发给单片机  
  57.     return dat;  
  58. }  
3.I2C主机等待应答和产生应答


    等待应答和产生应答类似于读字节和写字节,不同的是读和写字节是一个字节8位,因此函数内部进行了8次循环,而等待应答和产生应答只需处理一位数据,所以不需要循环,其本质是差不多的,在这里就不再讲述了,具体可参考下面的程序去理解:

[plain] view plain copy
  1. /*******************************************************************************  
  2. * 函 数 名           : I2C_Wait_Ack()  
  3. * 函数功能           : 等待应答:即等待从设备把I2C_SDA拉低  
  4. * 输    入           : 无  
  5. * 输    出             : 0或1  
  6. * 备    注           : 0表示应答发送失败或非应答,1表示接收到应答  
  7. *******************************************************************************/  
  8. unsigned char I2C_Wait_Ack(void)  
  9. {  
  10.     unsigned char acktime;  
  11.     I2C_SCL = 1;  
  12.     delay_us(1);  
  13.     while(I2C_SDA)                 //等待应答,即等待从设备把I2C_SDA拉低  
  14.     {  
  15.         acktime++;  
  16.         if(acktime>200)            //如果超过200us没有应答,则发送失败,或者为非应答,表示接受结束  
  17.         {  
  18.             I2C_SCL = 0;  
  19.             delay_us(1);  
  20.             return 0;  
  21.         }         
  22.     }  
  23.     delay_us(1);  
  24.     I2C_SCL = 0;  
  25.     return 1;  
  26. }  
  27.   
  28. /*******************************************************************************  
  29. * 函 数 名           : IIC_Ack(unsigned char ackbit)  
  30. * 函数功能           : 产生应答  
  31. * 输    入           : 0或1  
  32. * 输    出             : 无  
  33. * 备    注           : 0表示产生应答,1表示不产生应答  
  34. *******************************************************************************/  
  35. void IIC_Ack(unsigned char ackbit)  
  36. {  
  37.     I2C_SCL = 0;  
  38.     I2C_SDA = ackbit;  
  39.     delay_us(2);  
  40.     I2C_SCL = 1;  
  41. }  

  42. 二、主从机I2C通信过程:

  43. 1.主机发送过程

    (1)主机在检测到总线为“空闲状态”(即 SDA、SCL 线均为高电平)时,发送一个启动信号“S”,开始一次通信的开始

    (2)主机接着发送一个命令字节。该字节由 7 位的外围器件地址和 1 位读写控制位 R/W组成(此时 R/W=0)

    (3)相对应的从机收到命令字节后向主机回馈应答信号 ACK(ACK=0)

    (4)主机收到从机的应答信号后开始发送第一个字节的数据

    (5)从机收到数据后返回一个应答信号 ACK

    (6)主机收到应答信号后再发送下一个数据字节

    (7)当主机发送最后一个数据字节并收到从机的 ACK 后,通过向从机发送一个停止信号P结束本次通信并释放总线。从机收到P信号后也退出与主机之间的通信

    2.主机接收过程

    (1)主机发送启动信号后,接着发送命令字节(其中 R/W=1)

    (2)对应的从机收到地址字节后,返回一个应答信号并向主机发送数据

    (3)主机收到数据后向从机反馈一个应答信号

    (4)从机收到应答信号后再向主机发送下一个数据 

    (5)当主机完成接收数据后,向从机发送一个“非应答信号(ACK=1)”,从机收到ASK=1 的非应答信号后便停止发送

    (6)主机发送非应答信号后,再发送一个停止信号,释放总线结束通信


  44. 三、以51单片机为主机,AT24C02为从机的I2C主从机通信程序

  45. /******************************************************************************** 函 数 名           : write_eeprom(unsigned char add,unsigned char val)* 函数功能         : 向eeprom写一个字节* 输    入           : eeprom中某个地址和要写入的数据字节* 输    出          : 无* 备    注           : 无*******************************************************************************/void write_eeprom(unsigned char add,unsigned char val){    I2C_Start();    I2C_SendByte(0xa0);    I2C_Wait_Ack();    I2C_SendByte(add);    I2C_Wait_Ack();    I2C_SendByte(val);    I2C_Wait_Ack();    I2C_Stop();}/******************************************************************************** 函 数 名           : unsigned char read_eeprom(unsigned char add)* 函数功能         : 从eeprom读一个字节* 输    入           : eeprom中的某个地址* 输    出          : 选中地址里面存储的数据* 备    注           : 无*******************************************************************************/unsigned char read_eeprom(unsigned char add){unsigned char da;  I2C_Start();I2C_SendByte(0xa0);I2C_Wait_Ack();I2C_SendByte(add);I2C_Wait_Ack();I2C_Start();I2C_SendByte(0xa1);I2C_Wait_Ack();da = IIC_Read_Byte(); I2C_Stop();return da;}


原创粉丝点击