万变不离其宗之I2C要点总结

[导读] 前文大致总结了单片机串口的一些值得注意的要点,本文来梳理一下I2C总线的一些应用要点。这个题目有点大,对于I2C其实很多地方也没讲清楚,只为了与前文形成系列,如果大家有补充欢迎留言。说了些闲话,进入正题吧。

I2C之前世今生

\(I^2C\)(Inter-Integrated Circuit),是一种同步、多主、多从、分组交换、单端、串行计算机总线,由飞利浦半导体(现在的NXP半导体)在1982年发明。它广泛用于在短距离、板内通信中将低速外设集成电路附加到处理器和微控制器上。\(I^2C\)也可以写成I2C或IIC。

自2006年10月10日起,实施I2C协议不需要任何许可费用。 但是,获得恩智浦分配的I2C从设备地址需要付费。一些竞争者,如西门子(后来的英飞凌技术,现在的英特尔移动通信)、NEC、德州仪器TI、意法半导体(以前的SGS-Thomson)、摩托罗拉(后来的飞思卡尔,现在与NXP合并)、Nordic半导体和Intersil,自20世纪90年代中期以来已经将发布了很多兼容的I2C标准的芯片。

模式

自Version 4之后 I2C支持下面几种模式:

  • 双向总线
    • standard-mode(Sm): ≤100 Kbit/S
    • Fast-Mode(Fm):≤400 Kbit/S
    • Fast-mode Plus(Fm+):≤1Mbit/S
    • High-speed mode (Hs-mode): ≤ 3.4 Mbit/s
  • 单向总线:
    • Ultra Fast-mode (UFm): ≤ 5 Mbit/s

好处

I2C标准能带来些啥好处呢?

  • 简单的2线串行I2C总线最小化互连,节省PCB布板走线空间;
  • 完全集成的I2C总线协议消除了地址解码器。
  • I2C总线的多主控能力允许终端用户设备通过外部连接到装配线进行快速测试和校准。
  • 标准支持广泛,大量无铅封装I2C总线兼容集成芯片进一步降低了空间需求。

其他子集

系统管理总线(SMBus),由Intel在1995年定义,是I2C的一个子集,定义了更严格的用法。SMBus的一个目的是促进健壮性和互操作性。因此,现代I2C系统合并了来自SMBus的一些策略和规则,有时同时支持I2C和SMBus,只需要通过命令或输出引脚使用最小限度的重新配置。

TWI(双线接口)或TWSI(双线串行接口)本质上是在Atmel和其他供应商的各种系统芯片处理器上实现的同一总线。

I2C拓扑结构

从概念上,I2C总线有两根线SDA/SCL就可以连一堆芯片,实现很多的应用。连接拓扑极简!

比如这样一个系统:

  • LCD显示
  • ADC采样
  • EEPROM/FRAM 非易失存储
  • 温度采集
  • .....

接下来看看各模式下,连接拓扑图:

标准速度/快速模式

高速模式拓扑

混速模式拓扑

工作原理

如果使用IO口模拟I2C总线,或者使用FPGA实现I2C接口,深刻理解I2C时序波形无疑是重点中的重点!即使使用内置的I2C控制器外设实现一个I2C总线编程,在调试底层时或者踩坑过程中,深入理解时序波形原理,也是非常必要的!

时序图

I2C的时序图如下:

  • START事件:可以联想一下UART的起始位,这个用于通知I2C通信的发起。用一句话描述就是在SCL常高时,采集到SDA高到低跳变,这就是启动事件。

  • 数据有效性SDA线上的数据必须在时钟的高周期保持稳定。数据线的高或低状态只能在SCL线上的时钟信号低时改变。每个传输的数据位产生一个时钟脉冲。

  • ACK:确认信号ACK的定义如下:发送器在ACK时钟脉冲期间释放SDA线,因此接收器可以将SDA线拉低,并在此时钟脉冲的高电平期间保持稳定的低电平(见上图)。须严格遵循电气的建立保持时间,使用时需要用示波器去严格测试信号是否能满足这些参数。

  • NACK:当在第九个时钟脉冲期间SDA保持高电平时,这被定义为“NACK”信号。 之后主机可以产生停止条件以中止传输,或产生重复的开始条件以开始新的传输。 导致NACK产生的条件有五个:

    1. 总线上没有报文中所包含地址的接收器,因此没有设备响应应答。
    2. 接收器无法执行接收或发送操作,比如它正在执行某些实时功能,并且尚未准备好与主机进行通信。
    3. 在传输过程中,接收器收到应用协议不理解的数据或命令。
    4. 在传输期间,接收器无法再接收更多有效数据字节。比如程序或者芯片内置缓冲区已经满了
    5. 主接收器用NACK通知从发送器结束传输。这是何意呢?比如主设备已经接受到足够多的数据,不希望从设备发送更多的数据时,就可以NACK从设备,这样从设备就会停止发送

时钟同步与仲裁

  • 时钟同步:两个主机可以同时开始在空闲总线上进行传输,并且必须有一种方法来确定控制总线并完成其传输的方法。 这是通过时钟同步和仲裁完成的。 在单主机系统中,不需要时钟同步和仲裁。

    时钟同步是通过I2C接口中SCL线的线与实现的。啥意思呢?

    • 当SCL从高到低的过渡时,总线上的主机开始计数其低电平时间,且一旦主机时钟变为低,它就会将SCL保持在该状态,直到变为高状态为止。
    • 但是,如果另一个主机时钟仍在其低周期内,则此时钟从低到高的转变不会改变SCL线的状态。 所以,SCL线由主机以最长的低电平周期保持为低电平低电平周期较短的主机在此期间进入高电平等待状态
    • 上面的话不好理解?看看线与的本质是,啥叫与呢? 比如C=A&B,只要其中一个变量A/B为低,那么C就必然为0,比如下图中,即便CLK1(为其中一个主机)为高了,但奈何另一主机的CLK2任然为低啊?所以SCL线上测出来就是低。
    • 当所有相关的主机都计数完低电平周期后,时钟线被释放并变为高电平。 这样,主时钟和SCL线的状态之间就没有区别,所有主时钟都开始计数其高电平周期。 第一个完成其高电平周期的主机将SCL线再次拉低。

    这里的几句话需要划重点去理解,这就是I2C总线的核心之核心工作原理:线与!

  • 仲裁:仲裁与同步类似,仅在系统中使用多个主机时才会涉及到,从站不参与仲裁过程。首先要理解一下仲裁是干啥的?所谓仲裁就是在多主机模式下,哪一个主机能获取介质的访问权限,获得权限的主机才可以传输I2C通信报文。 只有在总线空闲时,主机才可以开始传输。 两个主机可以在START的最小保持时间内产生START条件这种情况会导致总线上出现有效的START条件。 然后需要仲裁以确定哪个主机将完成其传输。

    仲裁是一位一位地进行。 节点发送1个位后,回读比较总线上所呈现的数据与自己发送的是否一致。是,继续发送;否则,退出竞争。SDA线的仲裁可以保证I2C总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失。总线系统通过仲裁只允许一个主节点可以继续占据总线

上图显示了两个主机的仲裁程序。 实际使用中连接到总线的主机数量可能会更多。 当主机产生的DATA1的内部数据电平与SDA线上的实际电平之间存在差异时,DATA1输出将关闭。 从而主机1退出竞争,没有获得总线的控制权。

  • 时钟延长:时钟延长通过将SCL线保持为低电平来暂停事务。 直到再次释放高电平,事务才能继续。 时钟延长是可选的,实际上,大多数从设备不包括SCL驱动能力,因此它们无法延长时钟。

    为啥要设计这样一个机制呢?个人理解是为了增强系统的健壮性而设计的:

    • 在字节传输级别,设备可能能够以快速速率接收数据字节,但需要更多时间来存储接收到的字节或准备另一个要发送的字节。此时,从机可以在接收和确认字节后将SCL线保持为LOW,以强制主机进入等待状态,直到从机为握手过程中的下一个字节传输做好准备。

    • 在位级别上,诸如微控制器之类的设备可以通过延长每个时钟的LOW周期来减慢总线时钟。 任何主机的速度都将根据该设备的内部工作速率进行调整。

    地址及R/W位:

    地址及R/W

    • 7位地址:分读写两种情况

  • 10位地址:分读写两种情况

  • 保留地址

从设备地址 读写位 描述
0000 000 0 广播地址
0000 000 1 启动字节
0000 001 X CBUS地址
0000 010 X 预留给不同的总线格式
0000 011 X 预留未来扩展使用
0000 1XX 1 Hs-mode 主代码
1111 1XX 1 设备ID
1111 0XX X 10位从地址

通用广播地址

通用广播地址用于同时寻址连接到I2C总线的所有设备。 但是,如果设备不需要处理广播数据,则可以通过不发出ACK来忽略该地址。 如果某设备需要来自通用广播地址的数据,它将发送ACK给该地址并充当从接收器。 主机实际上不知道有一个或多个设备响应时确认了广播数据(不确定有多少个ACK)。 每个能够处理此数据的从机接收器都会确认第二个字节和随后的字节。 无法处理这些字节的从站将不应答从而忽略。 同样,如果一个或多个从机应答,则主机不会看到未确认的消息。通用广播地址的含义总是在第二个字节中指定,如下图:

1.当B为0时,第2字节定义如下:

  • 0000 0110(06h):设备将复位以及设置地址的可编程部分。 接收到这个2字节命令后,所有支持响应通用广播地址的设备将复位,并将其地址的可编程部分改写保存。
    须采取预防措施以确保设备在施加电源电压后不会拉低SDA或SCL线,因为这些低电平会阻塞总线。

  • 0000 0100 (04h):收到该命令后设备将通过硬件设置地址的可编程部分。(Write programmable part of slave address by hardware).

  • 0000 0000 (00h): 不允许将此代码用作第二个字节.

对于06h/04h这两个命令,有些不太好理解。复位比较好理解。对于设置设备地址的可编程部分可能很多没有遇到过的朋友则不太好理解。这里来一个实际芯片的例子,以Microchip的MCP3423/MCP3424为例进行描述一下,MCP3423/MCP3424是一颗多通道ADC芯片,其芯片引脚如下:

当接收到通用广播访问且第2字节为06h命令后,芯片做两件事情:

  • 芯片复位如上电复位的行为一样
  • 同时锁住Adr1/Adr0的电平作为地址,这两位地址为芯片地址的可编程部分。

当然对于不同的芯片,具体如何实现通用广播地址的处理则各有不同,只需要认真阅读芯片的手册就能获取相应信息。这里仅仅就通用广播地址举个栗子,方便理解。老实说这个功能好像不太常见,具体有什么用?我反正没这么用过,感觉这功能有点蛋疼(直接用电阻配置好不更省事?)。如果有好的应用实例场景,欢迎留言交流。

2.当B为“ 1”时,则该2字节序列为“硬件通用呼叫”。 该报文由I2C主设备(例如键盘扫描器)发送,可以对其进行编程以发送所需的从地址。 由于I2C主设备事先不知道该消息必须传输到哪个从设备,故利用通用广播地址及通用呼叫命令并将自身的地址放在高7位,从而标识总线上发送通用硬件呼叫的设备ID。 该地址由连接到总线的智能设备识别(比如该智能设备是一个单片机系统),然后该智能设备从硬件主机接收信息。 如果硬件主机也可以充当从机,则从机地址与主机地址相同。

所以标准中定义这个功能,可以做些自适应应用,只需要制定出相应协议就可以完成比较灵活的多主通信应用协议。

软复位

如上面描述,当通用广播地址后面跟06h字节,就可以使从设备软复位。但这个功能并非所有芯片都支持,具体使用的时候需要仔细阅读芯片手册是否支持该功能。

须采取预防措施以确保设备在施加电源电压后不会拉低SDA或SCL线,因为这些低电平会阻塞总线。

起始START字节

单片机/DSP可以用两种方法连接到I2C总线:

  • 有的单片机/DSP具有片上I2C硬件外设,这就可以直接使用。
  • 如果没有或者被其他功能占用,则可以使用GPIO去模拟I2C总线时序。用这个方式去实现,则比较消耗CPU时间,

比如在一个多单片机用I2C总线连一起的系统,其中一个单片机I2C是用IO口模拟的,则快速的硬件设备与依赖软件轮询的相对较慢的单片机之间存在速度差异。这个不难想象,因为依靠轮询则不是硬实时,同时单片机肯定还有其他事物需要处理,那么检测START条件信号就有可能丢失,导致系统不健壮。那么I2C标准已然考虑这种需求了。

这就是起始字节需要解决的需求,前面介绍的就是起始字节设计的背景。那么起始字节究竟是怎样的呢?

  • START 事件(英文叫condition,我这样叫成一个事件有一点软件原语抽象的意思)
  • START字节0000 0001
  • ACK
  • 重复START事件

在需要访问总线的主机发送了START事件之后,发送START字节(0000 0001)。 另一个单片机/DSP可以以低采样率对SDA线进行采样,直到检测到START字节中的七个零之一为止。 在SDA线上检测到此LOW电平后,微控制器可以切换到更高的采样率,以找到重复的START事件,然后将其用于同步。

总线复位

在异常情况下,如果时钟SCL被拉为LOW了,则有哪些办法可以对总线复位呢?

  • 则优选的做法是如I2C设备具有硬件复位输入,则使用硬件复位信号来复位总线。
  • 如果I2C设备没有硬件复位输入信号,如果硬件设计可以考虑用MOSFET控制设备电源,重新通电以激活强制性的内部上电复位(POR)电路。
  • 还有一种做法是主机发送9个时钟SCL脉冲。 使总线保持低电平的设备应在这九个时钟内的某个时间释放它。这个具体怎么做呢?主设备初始化I2C总线时,可以冗余加9个SCL脉冲以复位I2C总线,或者检测到SDA长时间被拉低后,可以以控制IO高低翻转的方式控制SCL产生9个脉冲
//可能需要先关闭I2C控制器,如果是使用I2C控制器外设实现的
//I2C_SCL根据不同硬件进行移植,delay
#define I2C_SCL P10
void soft_rst_i2c(void)
{
    I2C_SCL = 1;
    for(int i=0;i<9;i++)
    {    	
    	I2C_SCL = 0;
    	delay(xx);
    	I2C_SCL = 1;
    	delay(xx);
    }
}

前面两种方法是更健壮的方案,如果硬件不支持,可以考虑后一种方法,但后一种方法的前提是拉死SCL的设备需要支持这种功能,如果两端都是自定义开发的则比较灵活了。

总线锁死,是I2C总线系统常踩的坑,有哪些原因会导致锁死呢?程序不健壮,I2C的波形不满足I2C规格书要求,或者在外加干扰情况下导致波形被干扰。有经验的同学可能会遇到设备平时工作的好好的,但是做EMC测试,常常设备会莫名死机,如果你的设备有I2C总线,请记得检查I2C是否被EMC干扰干死了!

设备ID

设备ID字段是一个可选的3字节只读(24位)字,提供以下信息:

  • 12位用于表示制造商名称,每个制造商唯一(例如,NXP)
  • 9位由制造商分配的芯片标识(例如,PCA9698)
  • 3位表示芯片版本,由制造商分配(例如RevX)

这个对于设计软件有什么可以利用的信息呢?比如一个系统可兼容不同厂家的基于I2C协议的传感器,利用这个字段就可以做设备信息管理。至于怎么读取,不同芯片或有不同。

接口电路简介

前面拓扑图中采用open-drain 开漏结构。I2C有的还用集电极开路输出结构,究其原因是内部是三极管的集电极开路。如下

Ultra Fast-mode

在Rev 4中还出现了Ultra Fast-mode,该模式使用push–pull推挽定义I2C内部硬件接口电路(我把它叫推拉),这个又长什么样呢?

这种推挽接口是用在Ultra Fast-mode(UFm)模式,为啥不继续采用集电极开路门/漏极开路门呢?因为这两种硬件已然无法满足如此高速的通讯波形要求了,推挽输出可以实现更为快速波形前沿特性以驱动总线电容负载。

对于Ultra Fast-mode模式其他如时序波形,报文定义基本一致,这里不做赘述了。需要提醒的是设备ID在该模式下不支持!

容性负载

为什么要特别讨论一下总线的容性负载特征呢?想象中的理想通信波形:

由于容性负载以及充放电常数特性,实际中却可能是这个鸟样:

如果实际总线中电阻选取过大,或者容性负载过大(设备节点过大或者布线不合理),也即RC常数过大,甚至可能是这个德行:

那么参数选取合适时,波形则可能是这样的:

所以就其本质而言,就是由于驱动接口电路的RC参数影响了波形的时序参数:

实际应用中,一方面电阻需要选取足够大以降低不必要的电流消耗,另一方面电阻又需要选择足够小以满足对应传输速度的波形时序要求。故需要在这一对矛盾体中寻求一个折中平衡!实际项目中先用示波器测测I2C波形非常必要,代码对了总线可未必如愿工作。做底层开发,尽量先硬后软~~
I2C总线从电气特性上需要特别注意的是其容性负载特征:

  • Fast-mode:连接到总线的外部上拉设备必须经过调整,以适应快速模式I2C总线较短的最大允许上升时间。 对于200 pF以内的等效总线负载,每条总线的上拉设备可以是一个电阻。 对于200 pF至400 pF之间的总线负载,上拉设备可以是电流源(最大3 mA)或开关电阻电路。

  • Fast-mode Plus (Fm+):该模式下设备中的驱动接口电路驱动能力比较强大,可以满足Fast-mode Plus时序规范,并具有与标准模式部件相同的400 pF负载。 为了与标准模式向后兼容,它们还可以承受标准模式设备的1μs上升时间。 在仅存在Fast-mode Plus部件的应用中,强驱动接口和对缓慢的上升和下降时间的容忍度允许使用较大的总线电容,只要软件设置好或硬件IC实现好,Fast-mode Plus的最小LOW时间和最小HIGH时间即可满足所有要求,并且下降时间和上升时间不超过标准模式的300ns上升沿时间和1μs 下降沿时间规格。 可以将总线速度与负载电容进行折衷,总线电容可增加大约十倍。

  • Hs-mode: 高速模式(Hs-mode)器件在I2C总线传输速度方面实现了飞跃。 高速模式设备可以实现高达3.4Mbit/s的比特率传输速度,但仍然向下兼容,与快速模式增强版、快速模式或标准模式(F/S)设备完全兼容以进行双向通信总线系统。 除了在Hs模式传输期间不执行仲裁和时钟同步外,与F/S模式系统保持相同的串行总线协议和数据格式。那么如此高速是如何做到的呢?这里将个人认为与应用相关的要点翻译总结下:

    • Hs模式主设备具有用于SDAH信号的漏极开路输出缓冲器,以及SCLH输出上的漏极开路下拉电路和电流源上拉电路的组合。 该电流源电路缩短了SCLH信号的上升时间。 任何时候仅在Hs模式下,仅启用一个主机的电流源。
    • 在多主机系统的Hs模式传输期间,不执行仲裁以及时钟同步以提高位处理能力。 仲裁过程始终在先前的F/S模式下的主代码传输之后完成。
    • Hs模式主设备生成串行时钟信号,其占空比为50%以减轻建立和保持时间的时序要求。这个项目中可利用示波器检查波形。
  • 具体设计时,可参考规格书电气特性参数规定以及所选芯片的手册。

编程策略

  • 硬件I2C控制器:要实现I2C总线,如果使用单片机/DSP/SOC内置了I2C控制器,就其本质就是抽象了I2C总线的各种事件以寄存器进行控制,最为常见的方式就是将总线事件抽象为异步中断事件。以STM32为例:

编程时,比较好的方式就是处理相应的中断事件。利用内置I2C控制器是优选方案。

  • IO模拟,如果系统中不存在I2C控制器,可利用IO口进行模拟,对于实现多设备以及高速模式系统则不推荐这样做。但在一些PCB尺寸受限或者成本受限、单片机引脚很少的系统中还是比较有实用价值的。其编程只需要对照I2C时序进行操作即可,难度较小。

在实际项目中,需要特别注意I2C的上升沿、下降沿波形时间参数是否满足设计速率要求,可通过配置寄存器以及调整驱动上拉电阻进行调整。对于高速模式则可能需要用电流源进行驱动。另外需要注意的是,I2C总线锁死情况处理。

总结一下

I2C总线是一个比较复杂的芯片间总线系统,你或许会用。但是如果不注意标准的很多细节,你可能无法用好!尤其总线上挂很多设备时,系统极可能不健壮!本文主要参考I2C version标准,I2C总线看似简单却极为复杂,本文总结了规格书中一些要点,也并不全面。在复杂应用场景中,还需要多多踩坑、填坑并加以总结。前文谈到了对于技术要点尽量总结、概括以及提炼,这里想提醒的是一些技术要点的标准往往是最为严谨、也最为全面的总结。具体使用时,可多多研读。

I2C VERSION6规格书可至www.i2c-bus.org下载,也可以至公众号后台回复I2C6,可直接领取。
文章出自微信公众号:嵌入式客栈,版权所有,严谨商用,更多内容,请关注本人公众号

posted @ 2020-07-08 21:37  逸珺  阅读(3482)  评论(0编辑  收藏  举报