博主将 I2C spec 文章总结为一篇,目录如下
I2C Introduction
I2C Architecture
I2C Transfer
I2C Synchronization And Arbitration
I2C Hs-mode
通信速度如下:
• Bidirectional bus:
• Unidirectional bus:
速度由 SCL 决定,不同模式对上升沿的要求不一样,上升沿斜率受上拉电阻和等效电容影响。
7-bit address :2 的 7 次方,能挂 128 个设备。
10-bit address :2 的 10 次方,能挂 1024 个设备。
但是 I2C 协议规定,总线上的电容不可以超过 400pF。管脚都是有输入电容的,PCB上也会有寄生电容,所以会有一个限制。实际设计中经验值大概是不超过 8 个器件。
总线之所以规定电容大小,是因为 I2C 使用的 GPIO 一般为开漏结构,要求外部有电阻上拉,电阻和总线电容产生了一个 RC 延时效应,电容越大信号的边沿就越缓,有可能带来信号质量风险(方波变三角波)。传输速度越快,信号的窗口就越小,上升沿下降沿时间要求更短更陡峭,所以 RC 乘积必须更小。(可以理解为输出高电平就是给电容充电,电容越大,充电越慢)
注意,要把 spec 规定的预留设备地址去除,保留地址如下:
note:写的是 two groups,而不仅仅是八个,0000 XXX 和 1111 XXX 系列地址都是保留的。
note:注意 1111 1XXX 是 Hs-mode master code,1111 0XXX 是 10-bit slave addressing,博主后面会讲。
I2C 采用的 GPIO 一般为开漏模式,支持线与功能,但是开漏模式无法输出高电平,所以需要外部上拉。Vdd 可以采用 5V、3.3V、1.8V 等,电源电压不同,上拉电阻阻值也不同。
一般认为 I2C 总线上,低于 0.3Vdd 为低电平,高于 0.7Vdd 为高电平。
I2C 协议中每个挂到总线上的设备都有独一无二的静态设备地址。
空闲时,I2C 总线上两根线都是高电平,因为有上拉电阻。
1、推挽结构:使用两个三极管或 MOSFET,以推挽方式存在于电路中。电路工作时,两只对称的开关管每次只有一个导通,所以导通损耗小、效率高。既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。
图中上面是 NPN 型三极管,下面是 PNP 型三极管。分别有以下两种情况:
输出高电平:向负载灌电流。
输出低电平:从负载拉电流。
2、开漏结构(OD):对比推挽结构,开漏结构只有一个三极管或者MOS管。
之所以叫开漏,是因为 MOS 管分为三极:源极、栅极、漏极。漏极开路输出,所以叫开漏;如果是三极管:基极、集电极、发射极,集电极开路,所以叫开集输出(OC)。
开集输出,NPN 三极管:
Vin 高电平,三极管导通,对外输出低电平,外部被直接拉到低。
Vin 低电平,集电极(C)开路,输出电平状态由外部决定。
以上分析均采用三极管,MOS 管类似。
因此,推挽结构可以输出高低电平。开漏输出只能输出低电平,高电平由外部电路决定。
线与:所有 GPIO 输出高就是高,只要有一个输出低,整条线上面的都是低,这就是“与”的意思。
1、推挽结构下,两个 GPIO 口连接到一根线上,假如左边的 PMOS 导通,右边的 NMOS 导通,Vdd 就会通过两个 MOS 管直接接地,由于 MOS 管导通电阻不大,会导致电流很大,直接损坏这两个 GPIO口,因此,推挽输出不支持线与。
note:实际上并不一定是上面 NMOS 下面 PMOS,只要上下两个管子采用不同类型,即可保证同一时刻只有一个管子导通,即可分别输出高低电平。
2、开漏结构:假如很多 GPIO 是开漏结构,接到了一根线,如下图。开漏结构输出的高电平靠外部上拉,假如有一个 GPIO 接地,那么电流会通过上拉电阻流进地,因为有上拉电阻的存在,所以电流不大,不会损坏 GPIO 口。
线与,是 I2C 协议的基础!
小节
mode | open-drain | push-pull |
---|---|---|
speed | slower | fsater |
power | higher | lower |
slave clock stretching | yes | not supported |
power 功耗上,开漏因为上拉电阻的存在,每次高低电平变换都会消耗能量,因此功耗高。
clock stretching 时钟延展方面,开漏支持时钟延展,推挽结构不支持时钟延展。原因和上面的推挽不支持线与一样的。有的人会有疑问,时钟延展不是在 SCL 低电平时,从设备去拉 SCL 线吗?不应该有问题呀。但如果是推挽 GPIO,此时主控会尝试拉高 SCL ,才会发现 SCL 被从设备拉低,这时候就会短路。
再次提醒,线与:当总线上只要有一个设备输出低电平,整条总线便处于低电平状态,这时候总线被称为占用状态。
1、上拉电阻过小,电流大,端口低电平 level 增大,会发现总线上电平拉不到 0V。
2、上拉电阻过大,上升沿时间增大,方波可能会变成三角波。
因此计算出一个精确的上拉电阻阻值是非常重要的。计算上拉电阻的阻值,有明确计算公式:
最大电阻和上升沿时间 tr 、总线电容 Cb 、标准上升沿时间 0.8473 有关。
最小电阻和电源 Vdd 电压、GPIO 最大输出电压 Vol、 GPIO 最大电流 Vol 有关。
查《I2C-bus specification and user manual.pdf》7.1节:
查《I2C-bus specification and user manual.pdf》表10:
从上图可以得到最大电阻和最小电阻计算公式以及如下数据:
1、标准模式:0~100KHz,上升沿时间要求 tr = 1us
2、快速模式:100~400KHz,上升沿时间要求 tr = 0.3us
3、高速模式:up to 3.4MHz,上升沿时间要求 tr = 0.12us
note:该上升沿时间 tr 是 0.3Vdd 到 0.7Vdd 的时间要求。
假设:Vdd 是 1.8V,Cb 总线电容 200pF(虽然协议规定负载电容最大 400pF,实际上超过 200pF波形就很不好,我们以 200pF 来计算,实际大家使用时建议以 100pF 计算)
标准模式 :
快速模式:
高速模式:
最小电阻(Vdd越大,上拉电阻就要越大):
注意,高速模式下,电源电压一般采用 1.8 V,不会采用 3.3V,因为如果用 3.3V 计算你会发现最小电阻比最大电阻大。
采用合适的电源电压和合适的上拉电阻,才会让你的 I2C 传输信号最优。
大家在不同速率采用的电阻一般有以下几种:1.5K、2.2K、4.7K。
上拉电阻关系图
想要深入探讨 I2C 协议,必须深刻理解各种时间的定义,如下为 F/S-mode
标识符 | 定义 |
---|---|
tf | 信号下降时间 |
tr | 信号上升时间 |
tLOW | 信号低电平时间 |
tHIGH | 信号高电平时间 |
tHD;DAT | 数据保持时间 |
tSU;DAT | 数据建立时间 |
tSP | 输入滤波器必须抑制的毛刺脉宽 |
tBUF | 启动和停止条件的空闲时间 |
tHD;STA | 重复起始条件的保持时间 |
tSU;STA | 重复起始条件的建立时间 |
tSU;STO | 停止条件建立时间 |
Sr 重新启动,S 启动,P 停止。
如上参数在 spec 中有严格规定,可查表,一般 standard mode 和 Fast mode 在一起,Hs mode 单独列,表4、表5、表6、表7:
Master
slave
在 SCL 高电平期间,SDA 必须稳定,所以一般情况下,SCL 高电平宽度小,SDA 高电平宽度大,用示波器看也是这样的。
起始条件:SCL 高电平时,SDA 由高变低。
停止条件:SCL 高电平时,SDA 由低变高。
note:因为 SCL 和 SDA 两根线有上拉电阻,因此空闲时两根线为高电平。因此,START 条件一定是某条线拉低,spec 规定是 SDA 线拉低为开始条件。这也是开始条件和停止条件不能互换的原因。(至于为什么不是 SCL 线拉低为开始条件,大家看到后面会理解)
byte format
在 ACK 后,从设备可以拉低 SCL 线进行时钟延展(比如从设备需要准备数据等)
note:SCL 高电平的时候,SDA 开始采样,SDA 是高就是 1,是低就是 0。SCL 低电平期间,SDA 变换数据。不可以在 SCL 高电平期间变换数据,否则会认为是 起始和停止条件。
每次传输完一个字节以后,从设备要进行一个回应,回应 ACK 或者 NACK。
ACK :在传输 8 bit 以后,在第九个 bit ,SCL 高电平,如果 SDA 是低电平,说明回应了 ACK。
NACK:在传输 8 bit 以后,在第九个 bit ,SCL 高电平,如果 SDA 是高电平,说明回应了 NACK。
spec 规定以下五种情况会出现 NACK
主机向从机写数据,在通信结束的最后一个字节,正常从机都会回应一个 ACK ,告诉主机最后一个字节写成功,这时候主机会产生 STOP 信号。
如果最后一个字节从机回应一个 NACK ,主机也会产生一个 STOP 信号,并且这时候主机会向上层上报一个 ACK error 。
上层如何处理,是上层的事情,芯片设计时 I2C 外设控制器一定会在这个时候产生一个 ACK error。如果用的是 Linux 操作系统,可以配置在上层忽略最后的这个 ACK error 。
主机从从机读数据,在最后一个字节后,主机会给从机一个 NACK ,告诉从机不再读数据了,然后主机产生一个 STOP 信号。这是唯一一个在正常传输过程中的 NACK
在重复开始信号 Sr 前后,两个 slave address 可以不同。也就是说,一个 I2C 主机可以不产生 STOP 信号,直接产生一个重复开始信号去访问另外一个从机。(如果 I2C 总线上有多个主机,则不用再一次仲裁,节省时间)
另外,在 Linux 系统中,由于 i2c_msg 结构体的规定,单笔 I2C 传输最大 64KB,超过 64KB 也要再来一次 STOP 信号或者 重复开始信号。
10 位从机地址规定如下,其中 11110 为 10 位地址的指示信号,A9-A0 表示 10bits 地址:
主机向从机写数据(需要 2 bytes)
主机从从机读数据(需要 3 bytes)
上图中,大家会在 SDA 线上发现有三个很细的毛刺,每次都是出现在从机回应了 ACK 以后。这是由于从机拉低 SDA 线回应 ACK 后,释放了 SDA 线,因为有上拉电阻的存在,SDA 线被拉高,然后主机又立刻接管了 SDA 线,把 SDA 线拉低。即该毛刺是由于 slave 和 master 换手有时差导致的。
因为该毛刺是出现在 SCL 低电平期间,而 SCL 低电平期间,SDA 本来就可以变换数据,所以不会对 I2C 通信产生负面影响,该毛刺一般不用关注。
如果觉得波形不美观,可以找芯片原厂,看能否调整 master 控线的 setup time 和 hold time ,来减小该毛刺的幅值。
I2C 不支持从设备在 SCL 和 SDA 总线上发起一个中断,通知主设备来读数据。有中断需求的从设备需要额外接一根中断线,通知主控数据已经准备好,让主控发起读数据的操作。
这无疑增加了系统复杂性,多占用了 pin 脚。I3C 则不存在这种问题,I3C 允许从设备在 SCL 和SDA 上发起中断,叫“带内中断”,I3C 后面会讲。
三个概念:时钟延展、同步、仲裁
byte level
时钟延展导致需要更多时间来存储接收到的字节或准备另一个要传输的字节
bit level
通过延长每个时钟低电平周期来降低总线时钟。任何主机的速度都与该设备的内部运行速度相适应。
在 Hs mode,只能使用 byte level,也就是只能在传输完一个字节(8bits)后拉低 SCL 进行时钟延展。在 Standard-mode 和 Fast-mode,既可以 byte level 也可以 bit level,bit level 意思是哪怕你之传输了 2 bits ,从机也可以拉低 SCL 线进行时钟延展,临时暂停传输。
时钟延展通俗解释
I2C 主设备始终控制着时钟线 SCL,不论是往设备写还是从设备读。一般情况下,如果操作对象是 EEPROM 或者其他简单设备而言,无所谓,但是,如果从设备是处理器,在接到主机命令后要去处理一些运算然后得出结果返回给主机。这个时候可能造成来不及处理。怎么办?这时,从设备会主动控制时钟线把它拉低!直到数据准备好之后再释放时钟线,把控制权交还给 MASTER。这也是 I2C 通信系统中,从机唯一能控制总线的时候!
关键是很多 I2C 主机不支持 clock stretching 功能,所以,无法和带有 clock stretching 功能的从机通信!所以,各位在选择主机器件之前,必须要注意这一点,不然整个设计方案可能报废,影响很大。
I2C 是多主从架构,也就是一条总线上可以同时挂多个 I2C 主机和多个 I2C 从机。
但是如果有两个或两个以上的主机同时向总线上发送启动信号并开始传送数据,这样就形成了冲突。要解决这种冲突,就要进行仲裁的判决,这就是 I2C 总线上的仲裁。
I2C 总线上的仲裁分两部分:SCL 线的同步和 SDA 线的仲裁,这两部分没有先后关系,是在同时进行。
所有主机都在 SCL 线上输出自己的时钟,因此同步过程需要定义自己的时钟。
SCL 同步是由于总线具有线“与”的逻辑功能,即只要有一个节点发送低电平时,总线上就表现为低电平。当所有的节点都发送高电平时,总线才能表现为高电平。正是由于线“与”逻辑功能的原理,当多个节点同时发送时钟信号时,在总线上表现的是统一的时钟信号。这就是 SCL 的同步原理。
同步过程如下图:
主机 1 产生 CLK1,主机 2 产生 CLK2,同时向 SCL 线上输出自己的时钟,由于 CLK2 的低电平更长,因此 SCL 线上出现的电平和 CLK2 保持一致。因此在第一个周期中,CLK1 后期进入了高电平等待状态。后面 SCL 上的电平以 CLK2 为准。
SDA 线的仲裁也是建立在总线具有线“与”逻辑功能的原理上的。节点在发送1位数据后,比较总线上所呈现的数据与自己发送的是否一致。是,继续发送;否则,退出竞争。
SDA 线的仲裁可以保证 I2C 总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失。总线系统通过仲裁只允许一个主节点可以继续占据总线。
DATA1 和 DATA2 分别是两个主机向总线所发送的数据信号,SDA 为总线上所呈现的数据信号,SCL 是总线上所呈现的时钟信号。
主机 1、2 同时发送起始信号,在 clock1 ,两个主机都发送了高电平信号。这时总线上呈现的信号为高电平,两个主节点都检测到总线上的信号与自己发送的信号相同,继续发送数据。
第2个时钟周期,2个主节点都发送低电平信号,在总线上呈现的信号为低电平,仍继续发送数据。
在第3个时钟周期,主节点1发送高电平信号,而主节点2发送低电平信号。根据总线的线“与”的逻辑功能,总线上的信号为低电平,这时主节点1检测到总线上的数据和自己所发送的数据不一样,就断开数据的输出级,转为从机接收状态。这样主节点2就赢得了总线,而且数据没有丢失,即总线的数据与主节点2所发送的数据一样,而主节点1在转为从节点后继续接收数据,同样也没有丢掉 SDA 线上的数据。因此在仲裁过程中数据没有丢失。
HS mode 为什么单独讲解?因为高速模式和其他模式有很多不一样的地方。
Master device
Slave device
只有 Hs 模式器件的系统的物理 I2C 总线配置
(可选)串联电阻器 Rs 保护 I2C 总线设备的 I/O 免受总线上的高压尖峰影响,并将振铃和干扰降至最低。
右下角两个设备,不光是从设备,也可以当主设备。这种器件有一个 MCS 电流源。如果总线上器件较多,会导致总线电容较大,拉升总线电压相当于给电容充电,这需要时间,这会导致波形上升沿过缓,所以加了电流源可以使上升沿很快。
1、data transfer format in Hs-mode
2、在 Hs 模式下启用电流源上拉电路
3、在下一次重复启动条件后,依旧在 Hs-mode
由上图可以看出,在快速模式(FS mode)下发送一个 Master code,然后切换到高速模式(HS mode),发送从设备地址。
在第一阶段 FS mode 时候,发送主机码(0x0000 1xxx),这时候会进行仲裁。因此 Hs mode 阶段没有时钟同步和仲裁。
在中间的 HS mode 传输结束后,如果是一个 STOP 信号,则立刻回到 F/S mode,如果是 Sr 重复开始i信号,则依旧留在 Hs mode(右下角有说明)
上图为 Hs mode 完整通信波形示意图。先在快速模式下发送主机码,不需要从机回复。然后切换到高速模式,会发送一个 reSTART,然后进行数据传输。
需要注意如下几点:
博主将 I2C spec 单独总结出来,意思是说,不管你是单片机平台,还是 FreeRTOS 平台,还是 Linux 平台,I2C spec 都是一样的,所以总结为一文方便大家查看。
本文分享自 嵌入式Linux系统开发 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!