[资料工具] 单片机I2C总线介绍

[复制链接]
 楼主| HuangHongLun 发表于 2019-7-30 11:20 | 显示全部楼层 |阅读模式
I2C总线是飞利浦公司推出的一种串行总线,所有器件共用两根信号线,实现数据的传输。
1751504-20190728144609709-124807176.jpg

总线接口接了上拉电阻,默认为高电平,所以就可以用“当低电平出现”来标记出一种起始信号。我个人把它想象成:许多人在一条走廊上的不同房间(器件)里,大家都把门打开,连出两根长长的听筒(小时候玩的那种),每个人都从两根大主线上各接一根到自己房间里。两根听筒平时都是安静的(1)。如果有某房间的人叫了一声(0),那剩下的人就知道,我们准备开始通话了。
为了保证秩序,大家选出一个人当领队,由他来主导通话的过程。这就是总线的主机,其他人就是从机。从机有多个,主机只有一个。
两根信号线,一根叫数据线SDA,一根叫时钟线SCL。顾名思义,数据线用来传输数据,时钟线用来管理顺序。

 楼主| HuangHongLun 发表于 2019-7-30 11:21 | 显示全部楼层
怎么样表示开始,怎么样表示结束,用下面的图表示。注意有严格的时间规定。
1751504-20190728145854230-582395044.jpg
首先,我们知道怎么样算是通话的开始和结束(起始信号和终止信号)。然后,规定怎样算是回答“可以”,怎么算是回答“不可以”(应答和非应答)。
接着,我们要知道谁向谁喊话,所以要给每个房间的人都赋予一个名字,也就是地址。再用一个0或1表示方向,从谁到谁。
因此,数据传输的过程,大体就是如此:领队喊出“开始”,说出一个房间名,同时,所有房间的人确认是不是自己的。领队表明目的,说出是向他传数据,还是从他那读数据;然后,确认是自己房间的队员给出应答,可以就开始传输数据。完成后,主机/从机给出应答,表明收到了没有。

 楼主| HuangHongLun 发表于 2019-7-30 11:21 | 显示全部楼层
下面就是SDA上传送的数据格式。
(a)主机向从机发送数据
1751504-20190728151001112-486498727.jpg
S表示起始信号。阴影表示主机发送。A表示应答,上加划线表示非应答。P表示停止。
(b)主机发送数据后,从从机读数据
1751504-20190728151123673-548823661.jpg
(c)传输过程中,想改变方向
1751504-20190728151211169-488315192.jpg
方法是,重复一次起始信号和从机地址,加一个方向位来改变方向。

 楼主| HuangHongLun 发表于 2019-7-30 11:22 | 显示全部楼层
SCL是用来管秩序的。只要SCL保持高电平状态,SDA正在传的数据就不能乱动,只有把它拉低以后,SDA才能变化。这样确保数据传输不会乱套,所以在实际的传输过程中,SCL会不断地翻转。
1751504-20190728151544845-1574420684.jpg

另外,如果存在许多一样的器件,怎么区分呢?方法是把前几位固定不能动,表示是同一种器件,后面的几位可以动(可编程)。假如后面空出3位,那么就是可编程8个,也就是允许有8个同种器件接到总线上。前面的地址叫“器件地址”,后面的地址叫“首地址”。所以,每次主从通信时,要先传器件地址,加方向位,等器件应答;再传首地址来寻找特定的器件,再加方向位,等待它应答。接着开始数据传输。
写入过程:
1751504-20190728152729662-1662774938.jpg
读出过程:
1751504-20190728152748092-2045452479.jpg


 楼主| HuangHongLun 发表于 2019-7-30 11:23 | 显示全部楼层
下面是对于I2C总线模拟的一些关键函数的注释。
  1. //延时10微秒函数
  2. void Delay10us(void)
  3. {
  4.     unsigned char a,b;
  5.     for (b=1;b>0;b--)
  6.         for (a=2;a>0;a--)
  7.             ;
  8. }

  9. //I2C起始信号模拟
  10. void I2cStart()
  11. {
  12.     SDA = 1;
  13.     Delay10us();
  14.     SCL =1;
  15.     Delay10us();
  16.     SDA = 0;
  17.     Delay10us();
  18.     SCL =0;
  19.     Delay10us();

  20. }

  21. //I2C停止信号模拟
  22. void I2cStop()
  23. {
  24.     SDA = 0;
  25.     Delay10us();
  26.     SCL =1;
  27.     Delay10us();
  28.     SDA = 1;
  29.     Delay10us();
  30.                            
  31. }

  32. //I2C发送数据函数
  33. unsigned char I2cSendByte(unsigned char dat)
  34. {
  35.     unsigned char a = 0,b;
  36.     for(a=0;a<8;a++)    //一位一位传输数据
  37.     {
  38.         SDA = dat>>7;    //右移7位,最高位送给SDA
  39.         dat = dat<<1;    //左移一位,次高位变成最高位
  40.         Delay10us();
  41.         SCL = 1;
  42.         Delay10us();
  43.         SCL = 0;    //翻转SCL,SCL为低电平时传输的数据才能改变
  44.         Delay10us();
  45.     }
  46.     SDA = 1;
  47.     Delay10us();
  48.     SCL = 1;       //释放数据线和时钟线
  49.     while(SDA)     //等待从机应答,如应答则SDA拉低跳出循环
  50.     {
  51.         b++;       //一段时间没有应答就认定为失败
  52.         if(b>200)
  53.             {
  54.                 SCL = 0;
  55.                 Delay10us();
  56.                 return 0;   //数据发送失败
  57.             }
  58.     }
  59.     SCL = 0;
  60.     Delay10us();
  61.     return 1;    //数据发送成功

  62. }

  63. //I2C读取数据函数
  64. unsigned char I2cReadByte()
  65. {
  66.     unsigned char a = 0;
  67.     SDA = 1;    //拉高数据线,保持空闲等待数据
  68.     for(a=0;a<8;a++) //一位一位读取数据
  69.     {
  70.         SCL = 1;     //拉高时钟线,保持数据稳定,准备接收
  71.         Delay10us();
  72.         dat<<=1;      //左移一位,空出一位准备读数据
  73.         dat |= SDA;     //或运算,dat空出的位为0,如SDA也为0则为0,SDA为1就为1,相当于保存SDA数据
  74.         Delay10us();
  75.         SCL = 0;        //翻转时钟线,使下位数据能够改变
  76.         Delay10us();   
  77.     }
  78.     return dat;     //返回读取的数据
  79. }

  80. //向At24C02芯片写数据函数
  81. void At24c02Write(unsigned char addr,unsigned char dat)
  82. {
  83.     I2cStart();     //起始信号
  84.     I2cSendByte(0xa0);      //发送器件地址(固定)
  85.     I2cSendByte(addr);      //发送首地址(自定)
  86.     I2cSendByte(dat);       //发送数据
  87.     I2cStop();      //停止信号

  88. }

  89. //读数据函数
  90. unsigned char At24c02Read(unsigned char addr)
  91. {
  92.     unsigned char num;
  93.     I2cStart();     //起始信号
  94.     I2cSendByte(0xa0);      //发送器件地址(固定)
  95.     I2cSendByte(addr);      //发送首地址(自定)

  96.     I2cStart();             //加一个起始信号,用于改变数据传送方向
  97.     I2cSendByte(0xa1);      //读取器件地址,最后一位表示方向
  98.     num = I2cReadByte();    //保存读取的数据
  99.     I2cStop();      //停止信号

  100.     return num;
  101. }


zlmin 发表于 2019-10-3 20:11 来自手机 | 显示全部楼层
感谢分享!感谢分享!
mcu430 发表于 2019-10-10 15:14 | 显示全部楼层
分析到位
kkzz 发表于 2019-10-15 21:58 | 显示全部楼层
一般情况下,要进行I2C通讯  
hudi008 发表于 2019-10-15 21:59 | 显示全部楼层
哪些单片机具备I2C总线接口
lzmm 发表于 2019-10-15 21:59 | 显示全部楼层
很多单片机还是有IIC硬件模块的  
minzisc 发表于 2019-10-15 21:59 | 显示全部楼层

单片机IO上挂很多个I2C接口?
selongli 发表于 2019-10-15 21:59 | 显示全部楼层
片机可以和I2C EEPROM进行i2c 通讯  
fentianyou 发表于 2019-10-15 22:00 | 显示全部楼层
可以用proteus仿真里面有很多IIC器件  
xiaoyaodz 发表于 2019-10-15 22:00 | 显示全部楼层
单片机 实现i2c的引脚固定吗?  
febgxu 发表于 2019-10-15 22:00 | 显示全部楼层
I2C通信如何读一字节   
sdlls 发表于 2019-10-15 22:01 | 显示全部楼层
单片机I2C的硬件代码有吗   
pixhw 发表于 2019-10-15 22:01 | 显示全部楼层
怎样实现连接两个i2c  
lzmm 发表于 2019-10-15 22:01 | 显示全部楼层
  I2C走的是数据。  
kkzz 发表于 2019-10-15 22:01 | 显示全部楼层
芯片要本身就带有I2C通讯接口  
hudi008 发表于 2019-10-15 22:01 | 显示全部楼层
IIC一般用模拟的较多
您需要登录后才可以回帖 登录 | 注册

本版积分规则

50

主题

440

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部