作者原地址,
https://www.cnblogs.com/alantu2018/p/8994719.html
本文,阐述i2c时序图,和C语言模拟 产生IIC时序,读写数据。
一、IIC 总线概述:
IIC 即Inter-Integrated Circuit(集成电路总线)
I2C总线是PHLIPS公司推出的一种串行总线, I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。
每个接到I2C总线上的器件都有唯一的地址。主机与其它器件间的数据传送可以是由主机发送数据到其它器件,这时主机即为发送器。由总线上接收数据的器件则为接收器。何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机
I2C 标准是一个具有冲突检测机制和仲裁机制的真正意义上的多主机总线,它能在多个主机同时请求控制总线时利用仲裁机制避免数据冲突并保护数据。作为嵌入式开发者,使用I2C总线通信的场景有很多,例如驱动FRAM、E2PROM、传感器等。
如下图,I2C 总线内部使用漏极开路输出驱动器,因此 SDA和 SCL 可以被拉低为低电平,但是不能被驱动为高电平,所以每条线上都要使用一个上拉电阻,默认情况下将其保持在高电平。
SDA 和SCL都接上拉电阻
开漏是指一种输出类型,它可以将总线拉低到电压(在大多数情况下为地),或者“释放”总线,让它被上拉电阻拉起。如果总线由主机或从机释放,线路上的上拉电阻 (RPU) 负责将总线电压拉至电源轨。由于没有设备可以强制拉高线路上的电压,这意味着总线永远不会遇到通信问题,即一个设备可能尝试发送高电平,而另一个设备发送低电平,导致短路(电源轨对地),仲裁。I2C 要求,如果多主机环境中的一个主机准备传输高电平,但看到线路电平为低电平(另一个设备正在将其拉低),说明另一个设备正在使用总线,因而将会停止通信。推挽式接口不允许这种类型的自由,这是 I2C 的优势。
如上一节所述,漏极开路设置只能将总线拉低,或“释放”总线,让电阻将其拉至高电平。下图显示了将总线拉低的电流。想要发送低电平的逻辑,将激活下拉式FET,这将提供接地短路,从而将线路拉低。
开漏释放总线:
当从机或主机希望发送逻辑高电平时,它只能通过关闭下拉式FET来释放总线。这使得总线悬空,上拉电阻将电压拉到电压轨,这将被解释为高电平。 下图显示了流经上拉电阻的电流,该电阻将总线拉至高电平
上面两张图,说明了SDA/SCL,内部是如何别拉高拉低,clk时序的,所以,外部必须要接上拉电阻,内部没有拉高的能力,只有拉低的能力,因为内部没有电压源。
IIC规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。 总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。SDA线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA状态的改变被用来表示起始和停止条件。
**
IIC总线的特点**
IIC总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此IIC总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。总线的长度可高达25英尺,并且能够以10Kbps的最大传输速率支持40个组件。
IIC总线的另一个优点是,它支持多主控(multimastering), 其中任何能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
IIC总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率100kbps。各种被控制电路均并联在这条总线上,**但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,**在信息的传输过程中,IIC总线上并接的每一模块电路既是主控器(或被控器),又是发送器(或接收器),这取决于它所要完成的功能。
最大主设备数:无限制;最大从机数:理论上,1008个从节点,寻址模式的最大节点数为2的7次方或2的10次方,但有16个地址保留用于特殊用途。I2C有16个保留I2C地址。这些地址对应于以下两种模式之一:0000 XXX或1111 XXX。下表显示了为特殊目的而保留的I2C地址。
I2C还有两个变体,分别专注于系统和电源应用,称为系统管理总线(SMBus)和电源管理总线(PMBus)。
二、IIC 总线通信协议:
要掌握IIC的通信协议,需要掌握以下6个通信信号:
1.起始信号
2.终止信号
3.写数据
4.读数据
5.应答信号
6.非应答信号
1、起始信号
SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;
在起始条件之后,必须是器件的地址,其中高四位为器件类型识别符(不同的芯片类型有不同的定义,EEPROM一般应为1010),接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作。
2、终止信号
SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
开始和停止条件
当总线上的主机都不驱动总线,总线进入空闲状态, SCL 和 SDA 都为高电平。总线空闲状态下总线上设备都可以通过发送开始条件启动通信。
当 SCL 线为高时,SDA 线上出现由高到低的信号,表明总线上产生了起始信号。 SDA 线上出现由低到高的信号,表明总线上产生了停止信号,如下图所示:
当两个起始信号之间没有停止信号时,即产生了重复起始信号。主机采用这种方法与另一个从机或相同的从机以不同传输方向进行通信(例如:从写入设备到从设备读出)而不释放总线。如下图所示:
3、应答信号
IIC 总线协议规定,每传送一个字节数据后,都要有一个应答信号以确定数据传送是否被对方收到。应答信号由接受设备产生,在SCL为高电平期间,接受设备将SDA拉低为低电平,表示数据传输正确,产生应答(ACK)
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
4、数据传送
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
地址传送
开始条件或者重新开始条件后面的帧是地址帧(一个字节),用于指定主机通信的对象地址,在发送停止条件之前,指定的从机一直有效。
I2C通讯支持:7 位寻址和10 位寻址两种模式。7 位寻址模式,地址帧(8bit)的高 7 位为从机地址,地址帧第 8 位来决定数据帧传送的方向:7 位从机地址 + 1位 读/写位,读/写位控制从机的数据传输方向(0:写; 1:读) 。帧格式如下所示:
10 位寻址模式,主机发送帧,第一帧 发送头序列(11110XX0,其中 XX 表示 10 位地址的高 两位),然后第二帧发送低八位从机地址。 主机接收帧 ,第一帧发送头序列(11110XX0,其中 XX 表示 10 位地址的高两位),然后第二帧发送低八位从机地址。接下来会发送一个重新开始条件,然后再发送一帧头序列(11110XX1 ,其中 XX 表示 10 位地址的高两位)帧格式如下所示:
数据传送与ACK
地址匹配一致后,总线上的主机根据 R/W 定义的方向一帧一帧的传送数据。 所有的地址帧后传送的数据都视为数据帧。10 位地址格式的低 8 位地址也视为数据帧。
数据帧的长度是 8 位。 SCL 的低电平 SDA 变化, SCL 的高电平 SDA 保持,每个时钟周期发送一位数据。数据帧后的第 9 个时钟是应答位,是接收方传送的握手信号。
从机接收数据时,第 9 个时钟周期不响应主机,从机必须发送 NACK;主机接收数据时,发送 NACK,从机接收到后停止发送数据;主机可以发送停止条件释放总线或重新开始条件;
如果从端设备无法接收或者发送另一个完整字节的数据。这时,可以先拉低SCL线,使得主设备进入等待状态,直到执行完毕其他功能。当从端设备准备好接受另一个字节的数据时,将其SCL线拉高,数据传输继续进行
在 I2C 总线上写入从机
要在 I2C 总线上写入,从机将在总线上发送一个起始条件,其中包含从机的地址,以及设置为0的最后一位 (R/W位) 这表示写入。在从机发送应答位后,主机将发送它希望写入的寄存器的寄存器地址。从机会再次应答,让主机知道它已经准备好了。在此之后,主机将开始将寄存器数据发送到从机,直到主机发送了它需要的所有数据(有时这只是一个字节),主机将以STOP条件终止传输。
从I2C 总线上的从机读取数据
从从机那里读取与写入非常相似,但有一些额外的步骤。为了从从机中读取,主机必须首先告诉从机希望读取的寄存器。这是由主机以与写入类似的方式开始传输来完成的,方法是发送 R/¯W 位等于0的地址(表示写入),然后是它希望读取的寄存器地址。一旦从机应答了此寄存器地址,主机将再次发送START条件,然后是 R/¯W 位设置为1(表示读取)的从地址。这一次,主机将应答读取请求,主机释放SDA总线,但将继续向从机提供时钟。在这一部分通讯中,主机将成为主-接收方,从机将成为从-发送器。
主机将继续发出时钟脉冲,但会释放SDA线,以便从机可以传输数据。在每个数据字节结束时,从机将向从机发送一个ACK,让从机知道它已经准备好处理更多数据。一旦主机收到它所期望的字节数,它将发送一个NACK,向从机发出STOP 条件以停止通信并释放总线。
总线仲裁
1)线与
I2C 支持多个主设备与多个从设备连接在同一根总线上,如果多个设备同时占用总线,怎么判断谁先占用总线呢?所以就需要一种仲裁机制。I2C 没有 Arbiter 直接的来处理仲裁,而是通过线与的逻辑实现仲裁。
2)遵循3个机制
“线与”机制。多主机时,总线具有“线与”的逻辑功能,即只要有一个节点发送低电平时,总线上就表现为低电平。 C语言 0 & 1 等于0。
SDA回读机制。总线被启动后,多个主机在每发送一个数据位时都要对自己的输出电平进行检测,只要检测的电平与自己发出的电平一致,就会继续占用总线。
低电平优先机制。由于线与的存在,当多主机发送时,谁先发送低电平谁就会掌握对总线的控制权。
3)仲裁过程
当主设备A准备占用I2C时,需要在SCL为高时,将SDA拉高,再拉低,满足一个启动条件。当主设备A将SDA拉高后,需要检查SDA的电平:
如果此时SDA电平为高,说明主设备可以占用总线,然后主设备A会将SDA拉低,一次满足启动条件,开始传输;
如果此时SDA电平为低,说明总线已经被其他设备占用,主设备A会退出。
为什么SDA为低,就是被其他设备占用了呢?
因为线与逻辑的存在。只有总线上有其他的设备将SDA置为0,线与后,SDA线的电平为0。主设备A检查SDA线的电平时,会发现为低电平。所以仲裁时,哪个设备更早地将SDA线拉低,谁就抢占了优先权。
4)仲裁过程实例
在图中不难看出DATA1发送的数据是 10111…
DATA2发送的数据是 100101…
在起始信号的被DATA1先行拉低;
在1、2周期的时候DATA1、2的数据位都是一样的,保持持续仲裁,当在第三周期时DATA1的数据位是1,DATA2的数据位是0,根据总线具有“线与”的逻辑功能(低电平优先),DATA2赢得仲裁,DATA1失去总线控制权。
二、一主多从,通信示意图
1、主机发送起始信号,所有从设备,都可以接收到,被激活。
2、主机发送,要找的地址,所有从设备,都会收到地址,与自己的地址进行对比。
3、回应ack,第三个从设备,对比地址后,与自己相同,就像打电话,打对方的号码,对方就可以接电话
4、开始接受数据,第三个才会接收数据,其他不会
5、回应ack,收到一个字节数据后,回应一个ack信号
6、发送停止信号,其实所有的芯片都会收到线上的电压变化,但是不会响应,因为没有被激活。只有第三个会接收解析停止信号。
四、多主多从示意图
这种需要非常注意竞争,多个人,打一个人电话时,只有一个可以打进去,当接电话后,有人打电话进来,会提醒是否挂断,i2c不知道是怎么机制,非常要注意,曾经我做项目时,单片机和soc都会与同一个外接i2c设备通信,结果发生竞争,概率性,导致死机。
五、普通gpio口,C语言模拟iic时序,
例子:根据AT24C02的芯片,可编写以下信号函数程序:
//1.起始信号
SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;
void IIC_Start(void)
{
SDA = 1;
SCL = 1;
delay_us(1); //15us >> 4.7us 必须有4.7us的延时
SDA = 0;
delay_us(1);
SCL = 0;
}
//2.终止信号
SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
void IIC_Stop(void)
{
SDA = 0;
SCL = 1;
delay_us(1); //15us >> 4.7us
SDA = 1;
delay_us(1);
SCL = 0;
}
//3.写数据
void IIC_SendByte(unsigned char dat)
{
unsigned char i;
//1、发送八位数据
for (i = 0; i < 8; i++)
{
if((dat<<i)&0x80)
{
SDA = 1;
}
else
{
SDA = 0;
}
SCL = 1; //开始让数据维持稳定
delay_us(1);
SCL = 0;
delay_us(1);
}
//2、等待回复ack
SDA = 1; //释放总线 , 发送完8位,主机置高电平
SCL = 1;
delay_us(1);
if (SDA) //SDA 低电平 从机回馈低电平
{
ack = 0; //0 == ack 代表无ack信号, 从机不应答,发送不成功
}
else
{
ack = 1; //从机应答,发送成功
}
//3、拉低clk
SCL = 0;
delay_us(5);
}
//4. 读数据
unsigned char IIC_RecvByte(void)
{
unsigned char i, temp;
SDA = 1; //保险 高的 与 上低的 是低的, 线与
for (i = 0; i < 8; i++)
{
SCL = 0; // 告诉 数据可以变化 SDA 脉冲线
//只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
delay_us(1);
SCL = 1; // 数据保持稳定, 开始读
delay_us(1);
temp<<=1;//移位,获取8位数据
if (SDA)
{
temp = temp + 1;
}
}
//3、拉低clk
SCL = 0;
delay_us(10);
return temp;
}
//5. 应答信号
void IIC_ACK(void)
{
SDA = 0;
SCL = 1;
delay_us(1);
SCL = 0;
}
//6. 非应答信号
void IIC_NOACK(void)
{
SDA = 1;
SCL = 1;
delay_us(1);
SCL = 0;
}
四、根据时序图,可编写24C02一个e2proom存储器的,的读写程序:
1、写数据
unsigned char AT24CXX_WriteStr(unsigned char devaddr, unsigned char romaddr, unsigned char *s, unsigned char num)
{
unsigned char i;
// 1、发送起始信号,器件地址,探测器件是否存在
IIC_Start();
IIC_SendByte(devaddr);
if (0 == ack)
{
return 0;
}
//2、发送要写入数据的地址
IIC_SendByte(romaddr);
if (0 == ack)
{
return 0;
}
//3、发送数据
for (i = 0; i < num; i++)
{
IIC_SendByte(*s);
if (0 == ack)
{
return 0;
}
s++;
}
//3、发送停止信号,写完了
IIC_Stop();
return 1;
}
2、读数据
unsigned char AT24CXX_ReadStr(unsigned char devaddr, unsigned char romaddr, unsigned char *s, unsigned char num)
{
unsigned char i;
// 1、发送起始信号,器件地址,探测器件是否存在
IIC_Start();
IIC_SendByte(devaddr);
if (0 == ack) //无应答 返回0 失败
{
return 0;
}
//2、发送要读数据的地址
IIC_SendByte(romaddr);
if (0 == ack)
{
return 0;
}
IIC_Start();
IIC_SendByte(devaddr + 1);
if (0 == ack)
{
return 0;
}
//3、读取num长度的数据,
for (i = 0; i < num - 1; i++)
{
*s = IIC_RecvByte();
IIC_ACK();
s++;
}
*s = IIC_RecvByte();
IIC_NOACK();
IIC_Stop();
return 1;
}
五、有关 IIC 总线常见面试题:(参考)
介绍一下你了解的I2C?
I2C总线是飞利浦(PHLIPS)公司推出的一种串行总线,用于连接微控制器及其外围设备, I2C串行总线有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。 它仅通过两根信号线就可以完成对所有挂载在I2C总线上的从器件进行操作。这样的好处是可以大大的节省我们微处理器的IO口资源。
I2C到底可以挂载多少个器件呢?
答:IIC协议规定,在启动总线后第1字节的高7位是从节点的寻址地址,其中高四位为器件类型识别符,接着三位为片选,最后一位为读写位,当为1时为读操作,为0时为写操作,所以具体挂载多少个器件由I2C地址决定,7位寻址地址减去1个广播地址0x00不用,所以有2^7=128 - 1 = 127,那就是127个地址, 所以理论上可以挂127个从器件。
I2C如何同时挂载多个同一种器件(地址相同的器件)?
答:理论上是不会这样设计的,如果一定要这样做的话,可以通过硬件上设计,控制器件是否挂载总线来实现(方法可用一个开关电路切断器件SDA或者SCL是否接入总线来实现)
I2C总线的主机与从机之间是如何通信的呢?
I2C总线的主机与从机之间的通信主要和I2C的时序有关。在通信开始的时候SCL与SDA都置为高电平,此时为总线空闲时间。当SCL为高电平期间SDA的电平被拉低,标志这总线的启动。当SCL为高电平期间SDA的电平被拉高,标志这总线的终止。在进行数据传送时,SCL为高电平期间,SDA上的数据必须保持稳定,只有在SCL的信号为低电平时,SDA上的高电平才允许变化。所以只要我们根据芯片手册正确的写好IIC的时序,按时序发送器件地址(不同的器件的地址不同)以及数据,就可以使主机与从机之间通信。
I2C总线的仲裁你知道吗?
总线上可能挂接有多个器件,有时会发生两个或多个主器件同时想占用总线的情况,这种情况叫做总线竞争。I2C总线具有多主控能力,可以对发生在SDA线上的总线竞争进行仲裁,其仲裁原则是这样的:当多个主器件同时想占用总线时,如果某个主器件发送高电平,而另一个主器件发送低电平,则发送电平与此时SDA总线电平不符的那个器件将自动关闭其输出级。总线竞争的仲裁是在两个层次上进行的。首先是地址位的比较,如果主器件寻址同一个从器件,则进入数据位的比较,从而确保了竞争仲裁的可靠性。由于是利用I2C总线上的信息进行仲裁,因此不会造成信息的丢失。
总线仲裁
主机只能在总线空闲的时候启动传输。两个或多个主机可能在起始条件的最小持续内产生一个起始条件,结果在总线上产生一个规定的起始条件。
当SCL线是高电平时,仲裁在SDA线发生:这样,在其他主机发送低电平时,发送高电平的主机将断开它的数据输出级,因为总线上的电平和它自己的电平不同。
仲裁可以持续多位。从地址位开始,同一个器件的话接着就是数据位(如果主机-发送器),或者比较相应位(如果主机-接收器)。IIC总线的地址和数据信息由赢得仲裁的主机决定,在这个过程中不会丢失信息。
仲裁不能在下面情况之间进行:
1)重复起始条件和数据位;
2)停止条件和数据位;
3)重复起始条件和停止条件。
I2C时钟信号(SCL)的同步问题
在I2C总线上传送信息时的时钟同步信号是由挂接在SCL线上的所有器件的逻辑“与”完成的。SCL线上由高电平到低电平的跳变将影响到这些器件,一旦某个器件的时钟信号下跳为低电平,将使SCL线一直保持低电平,使SCL线上的所有器件开始低电平期。此时,低电平周期短的器件的时钟由低至高的跳变并不能影响SCL线的状态,于是这些器件将进入高电平等待的状态。当所有器件的时钟信号都上跳为高电平时,低电平期结束,SCL线被释放返回高电平,即所有的器件都同时开始它们的高电平期。其后,第一个结束高电平期的器件又将SCL线拉成低电平。这样就在SCL线上产生一个同步时钟。可见,时钟低电平时间由时钟低电平期最长的器件确定,而时钟高电平时间由时钟高电平期最短的器件确定。
I2C总线的其他注意点
1、进行数据传送时,在SCL为高电平期间,SDA线上电平必须保持稳定,只有SCL为低时,才允许SDA线上电平改变状态。并且每个字节传送时都是高位在前。
2、对于应答信号,ACK=0时为有效应答位,说明从机已经成功接收到该字节,若为1则说明接受不成功。
3、如果从机需要延迟下一个数据字节开始传送的时间,可以通过把SCL电平拉低并保持来强制主机进入等待状态。
4、主机完成一次通信后还想继续占用总线在进行一次通信,而又不释放总线,就要利用重启动信号Sr。它既作为前一次数据传输的结束,又作为后一次传输的开始。
5、总线冲突时,按“低电平优先”的仲裁原则,把总线判给在数据线上先发送低电平的主器件。
6、在特殊情况下,若需禁止所有发生在I2C总线上的通信,可采用封锁或关闭总线,具体操作为在总线上的任一器件将SCL锁定在低电平即可。
7、SDA仲裁和SCL时钟同步处理过程没有先后关系,而是同时进行的。
4、特性总结, IIC肯定是2线的(不算地线)IIC协议确实很科学,比3/4线的SPI要好,当然线多通讯速率相对就快了