I2C的七宗罪

汽车电子嵌入式 2024-12-25 07:00

I2C第一罪


有些工程师聊起I2C的时候,会有不屑一顾的感觉, “切,不就是两根线吗,一个时钟,一个数据”!每每碰到这些人,就会有一种感觉,兄dei,你真是坑没有踩够啊!

下面是我按照本人工作时间先后顺序,列出碰到过的I2C的问题。有一点我要强调一下,我所列出的问题,不是从书上看来或者从哪里杜撰来的,每一个问题都是来自于不同的公司以及不同的项目,由本人亲历并且解决好的问题,希望对年轻的工程师有所帮助!

2005年, 在L公司(想想当年的L公司,那可是风光的很。)的光网络系统上有一块叫LKAXXX的线卡上出现了一个怪事,说到这里,肯定有人一看到MPC860, 就忍不住笑了, 一看就是上了年纪的大叔,哎,岁月不饶人呐 ^_^,我刚刚从学校里毕业的时候就是MPC860差不多诞生的时候。



先来描述一下问题:


1. 经常当有人按下Reset button后发现系统起不来了,UART console打印了一半就死在那里;

2. 再次按下复位后,症状一样, 不管怎么按复位按钮系统都死,UART打印一点点信息挂在那里;

3. 多次按复位按钮无效后,断电重启后OK;

4. 大部分情况,同一块板子按复位按钮后是OK的,但是少数情况是Fail的。


这时候很多就会说,这不是很简单,让软件用调试器加断点跟踪啊,说对了,我们firmware工程师还是很牛逼的,很快就告诉我们问题出在I2C上, 我们用示波器测量在死机情况下的I2C信号,发现SDA数据信号一直是低电平,怎么复位都没有用,一直是低,只有断电重启后,SDA才变高。那么原因初步定位了,正式因为I2C-SDA被强行拉低,才导致系统起不来,而断电后SDA被释放了,系统也就正常了。

我们知道I2C是open drain的,肯定是被什么芯片给拉了啊,这个很容易想到,不应该是CPU, 因为CPU已经被复位了啊, 那么怀疑的对象就到了和CPU相连的Device上, 乍一看下面的原理图, 我Kao,这是连连看么?还能有比这个更加简单的事情吗?


那么究竟是什么原因导致SDA被EEPROM拉低了呢?

我们再来看一张图:


我们看到这里对EEPROM的处理比较特殊, 在绝大部分的原理图里面,我从来都看不到,就是把EEPROM的电源加一个开关:

1. 当复位为低电平时,EEPROM的VDD连到低,断电;

2. 当复位为高电平时,EEPROM的VDD重新连到外部的VDD,恢复供电。


这时候聪明的小伙伴们已经悟出来, Kao!刚刚上面的LKAXXX的板子,如果我们每次按下复位按钮复位CPU的时候,通过按钮产生的复位信号High-Low-High由这里的开关电路把EEPROM的电源断开一会儿,是不是EEPROM就不会去把SDA拉低了啊,Bingo, 对了。

可是又有人说了,这不是增加成本嘛,还有啊,也没有看见有人这么干过啊,哈哈,对了,只有日本人才会这么用一根筋的设计方法,我们中国人永远找到更好的解决办法,我们继续往下走。

请看下面这张图,有没有很熟悉?


这是一个I2C的读操作,顺序如下:

1. Master发出start;

2. Master发出地址和读命令;

3. Slave给出ACK然后发出数据data7-0,这一共8拍由slave来驱动I2C SDA,(这里要记住SCL一直是由master来驱动的,当然后面也有特殊情况,这个我们留在后面的七宗罪里面详述),8拍的时间(假设100K的速率,周期为10us)是80us。


停停停停停, 搞笑的事情来了,如果在这80us的时间里面,有人按下了复位按钮, 会怎样 ?

举个“栗子”

假设四个人打麻将,我的上家把牌打出来后,就该轮到我出牌了,可是这时候来了个电话,我去接电话了, 等我回到牌桌后,我忘记了刚刚轮到我出牌,我以为上一把结束了,直接把麻将推倒洗牌了,这时候等着我的下家可就不干了,人家等着我出牌好胡呢。然后我这重来的举动惹恼了人家,道歉也好,赔不是也罢,人家不接受,不玩了!得嘞,这局麻将是玩不下去了。

类比上面I2C的情况,想象一下:

1. 80us的时候 EEPROM Slave正在配合CPU输出读的数据,人家玩得正high呢;

2. 这时候CPU收到一个复位指令,而且是强行复位,类似于接电话;

3. 等到CPU复位完了,完全忘记了刚刚EEPROM正在drive数据,甚至有可能EERPROM已经传完8bit数据,正在CPU的NAK(看上图),这事搁谁身上能受得了;

4. 这种情况CPU看起来也是没有办法,谁叫我们按下了复位按钮了呢。还有一种情况就是CPU自己不厚道,比如去执行优先级中断程序后,回来也忘记了别人(EEPROM)正在等待自己继续刚才的工作。

这里肯定会有人问,不是复位了吗?请仔细看上面的原理图, EEPROM是没有复位管脚的,也就是说, 我们按下复位按钮时, CPU复位了,但是EEPROM没有复位,它的状态机还在等待输出数据给CPU或者等待CPU的NAK指令以便结束当前的这笔操作,这和上面打麻将的例子是一回事。

那么请问刚刚的SDA被拉低是怎么回事呢?很简单, 上图的ACK就是低电平,或者Slave drive的data bit7-0其中有高有低啊。

那么除了上面给EEPROM的电源加开关的方式,我们还有上面办法来解决这个问题呢?

我们继续看图:


先来想想刚刚打麻将的事情,如果我打完麻将回来后,直接牌友每个人100块钱,别人肯定是乐意继续陪你玩的,很简单的办法解决了问题。如上图, 我们让软件工程师在代码里面做了下修改:

1. 当检测到SDA被拉低后;

2. 软件就持续发送9个时钟;

3. 在发9个clock的过程中SDA会变高变低;

4. 当Device发完所有数据后,SDA被释放;

5. 此时状态机到达NAK的phase时,SDA释放变高,产生了一个NAK;

6. 注意此时9个时钟不一定用完,EEPROM就把到达NAK phase把SDA释放了,但是CPU是不知道的,他会一直发完9个时钟;

7. 最后CPU再发送一个stop把整个读操作结束掉;


The 9 clock pulses make the hanging device’s state machine move to the next state after each clock pulse while the SDA released (not pulled down) which will cause a NACK when the state machine will move to the ACK phase. The NACK will force the device to go to idle mode(意思和我上面的步骤一样)。

1. Master tries to assert a logic 1 on the SDA line;

2. Master still see a logic 0 and then generate a clock pulse on SCL;

3. When device come to NAK phase, then master will generate SDA high which;is a NAK, but master does not know, until master send all the 9 clocks;

4. Master Generate a stop condition。


上面讲的是I2C读操作被中断导致死机的情况,下面聊聊I2C写操作被中断的情况,解决办法“简单粗暴”一些, 大家想想为什么。


这里一样还是只发9个时钟,在9个时钟的过程中,device就可能发出一个ack,CPU看到ACK后,再发一个stop结束本次操作:


1. Master tries to assert a logic 1 on the SDA line;

2. Master still see a logic 0 and then generate 9 clock pulse on SCL;

3. Generate a stop condition。


这里要注意,写操作被中断时,发出的9个clock一定是等到最后一个时钟发完, device才被释放。而读操作则不一定,有可能发了第一个时钟时device就被释放了只是CPU不知道,到第9个时钟时stop。


第二罪:I2C和电源的关系


提到电源大家的心理通常是紧张滴,不自觉地发问:是不是问题很严重?是的, 非常严重, 而且通过软件是没有办法解决的。我们先来看下面这张简图:

1.系统有风扇,12V供电;

2.Fan和CPU之间有转速检测TACH和转速控制PWM信号;

3.CPU左边有一个颗I2C EXPANDER,用来做GPIO扩展的;

4.DC-DC产生VCC-3.3V分别供给CPU和I2C Expander。


乍一看,也没啥问题啊!俗话说得好,平静的水面下暗流涌动啊,我先来给大家一点点提示,看看谁能先猜到问题所在,我们来看看CPU PWM/TACH内部结构。


这个也没有啥问题啊, 我们都知道IBIS buffer的样子如下,这个在网络上随便google或者度娘,到处都是啊。


这些看起来都没有什么问题,可是偏偏我们把这里的CPU(MCU)和风扇放在一起就出问题了,看看下面这张示意图,你也许会开始有feel:


我们从电源的上电顺序开始:

1.插入12V电源,Fan先开始运转,注意此时DC-DC还没有开始工作,也就是说VCC-3.3V还没有产生;

2.由于风扇的12V先上电,PWN/TACH信号线已经有电了,于是通过图中MCU的保护二极管(上管)对3.3V充电;

3.注意此时DC-DC仍然还没有工作,但是3.3V已经开始升压,但是来源不是DC-DC Buck,而且来自Fan的I/O管脚漏过来的;

4.此时DC-DC电源开始工作,由于同步整流的电源BUCK都需要自举,也就说我们通常所说的Bootstrap的电路,下管的MOSFET要先打开(关于电源部分我们后面再详细叙述),产生的后果是把原先I/O漏过来的电压拉到地;

5.Bootstrap电路开始工作后,DC-DC开始正式工作,从0开始产生VCC-3.3V。


于是我们就看了如上图的波形, 图中绿色的就是3.3V, 12V电源插入瞬间开始上升,然后在某一个时间,突然下降到0, 再重新上升到3.3V, 电源完成上电。

说到这, 有人开始问了,你不是讨论I2C吗?怎么和我们讨论起3.3V电源来了啊?别急别急, 事情都是慢慢明朗起来的。

如果此时你回去看最上面的第一张图,那么你就发现图中有一个I2C Expander,对了,问题就出自这里,通过我们从上面的讨论,我们已经知道, 3.3V的上电不是一帆风顺的,而是会有一个Spike,也就是一个倒钩。如下面所示:


我再来给一张清晰一点图,VCC每次上电都是先被12V-Fan I/O充电,然后跌落到地,在爬升会3.3V, 原因上面我已经给出详细的解释,那么又有人问了,这个会有什么问题吗?我先明确的回答,会出大问题。



我先来贴一段英文,我非常强烈建议大家平时工作中要养成读英文论文的习惯,我倒不是崇洋媚外,老外那些大牛的文章,真的让你觉得很精妙, 有时候自己翻译成中文总觉得怪怪的,生怕曲解了大牛的原意,所以还是老老实实去读原版文章,不要翻译成中文。

If by any chance, the TACH pin is pulled or driven high while the Expander VCC is not applied (which should not be allowed during normal operation), the Expander could be powered up from the TACH pin through the connected internal diode between the pin and VCC. Such false power-up events do not ensure the required power supply to Expander the POR would not be ensured, and a lockup may occur.

Point和上面的英文意思差不多,就是I2C Expander(我们这里是CPU)被别的电路 通过I/O管脚商店了。我们先来把这颗粒I2C expander的内部电路给画一下, 我们看到这颗芯片没有外部复位pin脚,所以一般内部会有一个简单设计的POR电路:


我们看到,当3.3V VCC上电有倒钩时,由于Fan通过TACH/PWM对I2C expander提前上电, 然后突然又倒回来, 在这个过程中有两件事是不确定的:

The POR brings the Expander to a known working state (the default condition) by initializing the internal storage elements (flip-flops) and recognizing its connections (such as identifying the status of pins A1 and A0 as high or low, which is how the Expander slave address can be decided). Without the POR feature, the Expander may start up in a random state and thus may cause a lockup.

1.I2C expander是不是已经上电成功了?我们不知道,因为3.3V上电到了半山腰又倒回来,此时芯片内部的POR是不是对芯片完成内部复位我们不清楚,不仅如此I2C expander有没有准确锁定外部的strap pin脚的状态,我们也不清楚;

2.由于不能确定是否完成内部存储单元(触发器)的初始化,I2X expander可能工作一个很random的状态,而不是我们需要的Idle状态。

关于这个解释,后来TI的工程师给出详细的解释, 英文我不就翻译成中文了,请读者仔细品味和理解:

The device has a 6 bit state machine (64 states) of which 49 valid state and 15 are invalid states. The above fix will work assuming the device comes up in a valid state. Typically on all our designs, an invalid state would lead to the idle state on the immediately next clock cycle. However, the PCF8575 is our very first I2C device and there are some invalid states that could latch the SDA line low indefinitely. The 9 clk cycle fix will not work if the device were to power up into an invalid state. The only way to bring the device out of this state is to provide a proper reset.

那我们说了那么多,究竟结果是怎么样呢?结果就是I2C expander产生了Lock up, I2C接口访问不了,有时候还会拉住SCL或者SDA.

总结下来就是一句话:芯片需要POR电路来进行复位,如果因为别的原因提前上电,导致POR工作不正常,那么就会有问题。

关于上面的现象还有另外一种解释,就是当VCC的倒钩时,此时I2C Expander的内部电路已经被搞乱了, 但是有内部POR的RC电路的平滑作用, 导致内部Reset被滤平滑了,内部Reset没有碰到门限电压,请仔细看上图红色的凹陷。


这样导致芯片也不能正常复位,当然也就不能正常工作了。请看大牛Howard Johnson的描述:

Howard Johnson : Power interruptions drive power-on-reset circuits crazy.. If a processor is involved, the dropout is long enough to make scrambled eggs of the processor’s internal state machines but not long enough to discharge the RC circuit. If the RC circuit doesn't discharge, ~RESET doesn't activate, and the processor spins out of control, powered on, but lost in space.

I2C第三罪

我们都知道I2C是open drain的,不是普通的push-pull,我们也知道open drain的接口信号需要加上拉电阻,那么为什么要用open drain呢?上拉电阻的阻值是否需要计算考量呢?我们先来说说为什么。


第一个问题, 为什么要用open drain?

请查看下方图表,I2C总线的布局通常是多个设备通过直线连接的方式串联在一起,最常见的配置包括一个主设备(通常是CPU)和若干从设备(例如EEPROM、温度传感器等)。这种连接方式简洁明了,易于掌握,但你知道吗?这其中也可能隐藏着问题。


让我们探讨一下为何在某些情况下不能使用传统的推挽(push-pull)输出,而必须采用开漏(open drain)输出。下图将展示两者之间的差异。所谓的推挽输出,实际上是由一个P沟道金属氧化物半导体(PMOS)晶体管和一个N沟道金属氧化物半导体(NMOS)晶体管组成的。

1.当输出高电平时:PMOS打开NMOS关闭;

2.当输出低电平时:PMOS关闭NMOS打开。


如上面的图左,此PMOS和NMOS交替打开和关闭,分别输出方波的高低电平,但是图右的open drain buffer则只有下面的NMOS:

1.当输出高电平时NMOS关闭,输出处于floating的状态;

2.当输出低电平时NMOS打开即可。

如此我们看出,open drain的输出高一定要借助外力,这就是为什么我们检查I2C的设计时,一定要确保外部有上拉电阻才行。那么为什么呢?如果我们用push-pull会产生什么样的情况?

上面第一张图已经解释了,在I2C总线中,由于多个设备通过线与方式连接,如果使用推挽(push-pull)输出缓冲器,可能会遇到以下问题:当一个设备试图输出高电平而另一个设备输出低电平时,即左侧设备的PMOS导通而右侧设备的NMOS导通,这会在电源VCC和地线GND之间造成短路。这种情况下,大电流可能会损坏设备,导致灾难性的后果。这种现象在英文中被称为“总线争用”(bus contention)。



为什么取了个这么奇怪的名字呢?

让我们回顾一下传统的公共总线设计。在I2C系统中,避免总线争用(bus contention)的问题相对简单,只需采用开漏(open drain)配置,毕竟它只有两条信号线。然而,对于早期产品中那些更宽的并行公共总线,比如32位或64位的总线,开漏配置就不适用了。如果在这样的总线上使用开漏,意味着需要添加大量的上拉电阻,这对于PCB设计工程师来说无疑是一项沉重的负担。

所以人们就发明了三态门,这又是怎么回事呢 ?我们知道64bit的数据总线是双向的,如下图所示:


当系统在复位或者紊乱时,总线的所有设备都自动把自己的data buffer设为三态,那么什么叫三态门呢?顾名思义就是:

1.输入、2.输出、3.三态

接着上图,绿色为输出buffer,红色为输入buffer,当两个buffer都被disable时,就是所谓的第三态输出。


因此,我们可以得出一个结论:对于那些多个设备直接相连并且能够双向通信的总线来说,它们通常要么采用开漏(open drain)设计,要么采用三态门(tri-state gate)设计。再回到前面提到的总线争用问题,这个术语实际上并不是源自于I2C这种低速且简单的总线,而是来自于那些高位数的并行总线。如下图所示,如果左侧的任意两个缓冲器同时处于输出状态,就会发生总线争用,其后果可能是灾难性的。



第二个问题,上拉电阻的阻值是否需要计算考量

现在我们明白了I2C为什么一定要用open drain加上拉电阻的方式,那再来说说上拉电阻的选择,曾经我在C公司的一块ASR路由器的板子上,有一颗I2C芯片在高温时,就访问不了,但是在常温和低温下就一切正常,其实现在我们根据结果来说这个问题,就没有啥意思了,因为说者有心,听着无意。

起初,我们并没有对上拉电阻产生怀疑,尝试了各种调试方法。比如,我们更换了不同的芯片,并使用协议分析仪来捕捉高温环境下失败时的读写波形。此外,在高温温箱中捕捉并定位这类问题本身就是一个挑战。最棘手的是,这种故障并不经常发生,使得问题难以重现。

这里我们先来思考一个问题,I2C是latch,不是flip-flop,再通俗一点说就是电平触发,不是边沿触发,所以通常情况下,我们是不关心上升沿和下降沿的,可是偏偏问题就出在这里,我们的芯片会对SCL和SDA的边沿提出要求,这又是为什么呢?


先把这和个问题留着,看下面这张图,当SDA输出高电平时,是蓝色的箭头,有VCC通过上拉电阻和电容充电,而当SDA输出低电平时,是红色的箭头


我们很容易就能看出,输出高比较慢,因为有电阻和电容的阻挡,需要通过RC进行指数充电,而输出低电平就比较快,因为从电容放电到地全程无遮挡啊,所以下面我们之研究上升沿。


这里有人会问,这里的电容是从哪里来的,问得好。我们知道复杂的大板子上面芯片之间的距离比较远,走线的杂散电容比较大,另外板子的I2C接的芯片数量比较多,每个芯片的输入pin的电容加起来,就形成了这里的一个总的电容,为了描述方便,我们就用一个电容代替。

现在我们来回答为什么I2C会Rise time提出要求呢?太慢了会有什么问题呢?我们先看下面的表格,I2C其实也有fast mode的形式,据说I2C的频率也会有到5Mhz的,图表中我只看到400Kbit/s,我们在表格的最右面已经看到对上升时间的要求随着频率(或者叫速率)的提高,也越来越苛刻。


我们来用一张图来表达,当上升时间太慢会产生什么样的问题,图中SCL信号如果Tr够快的话,就是红色的信号,而Tr不达标太慢的话,那么就会是蓝色的信号。


不难看出,蓝色线越过绿色的threshold的时间太短,导致接收端不能正确识别出高电平,当然接收端也就不能正确latch SDA的数据了。


明白了,我们现在了解到I2C信号的上升沿是受到规范的,虽然它仅用于锁存信号,但对上升沿仍有特定的要求,而下降沿由于速度较快,不必过于担心。接下来,让我们探讨一下,有哪些因素会导致I2C的传输速率(Tr)过慢并引发问题。可以参考上文提到的RC充电公式和下图,我们可以看到时间t等于RC,这个时间由两个主要因素决定:


1.电阻值、2.电容值

让我们先讨论电容的问题,这部分是我们无法控制的。正如之前提到的,由于主板面积较大、总线上连接的设备众多以及走线布局的复杂性,导致各个芯片的输入电容累积,再加上长而复杂的走线所带来的额外杂散电容,这些都是我们无法调整的。除非我们重新设计电路板,增加双向缓冲器,但作为工程师我们都知道,老板通常不会批准这样的改动。

真正能够调整的变量是电阻。参考下面的示意图,我们可以通过调整电阻值来实现一个合理的信号上升时间,只要满足接收芯片的要求,并留有一定的安全余量就足够了。



但是电阻值的改变是需要精心计算的,不能太小也不能太大,而是要合理,我个人建议分为两步走:

1.先确定最大值:取值太大的话,会导致两个后果,一是芯片接收端驱动能力不足,而是上升时间不够,下面的两个公式分别对应这两个现象,我们去其中更加小的值。


2.再确定最小值:电阻取值太小的话,会导致Vol太大,芯片低电平下不来,我们举个极端的例子,就是上拉电阻为0时,不能产生低电平,下面的两个公式都是一个意思,一个精确考虑了电流,一个精确考虑电压,思路都是一样的。

最终,我想分享一下在C公司遇到的难题的解决方案。经过一系列实验,我们确认了I2C设备对信号上升时间确实有特定的要求,尽管这种触发是基于电平变化的Latch机制。有时候,只有亲身经历并克服困难,才能真正深刻地记住和理解问题。因此,我建议工程师们在解决了技术难题之后,不要急于庆祝,而应该进行深入的总结和反思。这样,这些经历才能真正转化为你们宝贵的经验财富。


I2C第四罪


今天我们来探讨一个Facebook面试中的问题,我的一个朋友一直梦想着在美国硅谷的Facebook工作。面试中,面试官提出了一个关于I2C的直接问题,难度不大,但我朋友因为紧张而未能给出满意的答案。在阅读这篇文章时,你也可以测试一下自己是否能正确回答。

让我们先观察一张图,对I2C有所了解的人会认出这是I2C的电平转换电路,它在Philips的I2C标准中有所描述,其实并不复杂。



让我们先分析这张图,I2C的电平转换器是利用NMOS晶体管来实现的。可能会有人质疑为何不选择PMOS。通常情况下,我们只在外部电源输入端设计INRUSH电流的缓启动或防反接电路时才会采用PMOS。原因很直接,因为在电源插入的瞬间,系统尚未产生用于开启MOSFET的高电平信号,因此只能选用PMOS。相比之下,NMOS因其体积小、导通电阻低(RDSON低),在系统内部更受青睐。


我们来举个PMOS的用例:

回到主题,让我们继续探讨I2C的相关议题。我们必须明确一点,I2C采用开漏(Open-drain)配置,这意味着level shifter电路两端都配备了上拉电阻Rp。接下来,我将解释这个电路的工作原理。

以下是两张描述I2C基本操作时序的图表,展示了主设备(Master)对从设备(Slave)进行读取和写入的过程:



我们可以看到, SDA一定是双向的,既然是双向的,那么就有四种情况,我们来一一解释level shifter是怎么来cover它们的。(注意:这里面会含有一个Facebook的问题,不要忘记自测哦。)



01

第一步,Master往Slave发数据1



1) 左边MasterSDA_1为输出,驱H-3.3V,NMOS的VGS=0, 此时NMOS关闭;

2) 右边的Slave的SDA_2是输入,对外呈现高阻;

3) NMOS关断和SLAVE为输入,导致SDA_2悬空;

4) 最终SDA_2依靠RP2上拉到5V,完成3.3V到5V的转换。


02

第二步,Master往Slave发数据0



1) 左边MasterSDA_1为输出,驱Low=0,NMOS的VGS>0, 此时NMOS打开;

2) 右边的Slave的SDA_2是输入,对外呈现高阻;

3) NMOS打开和SLAVE为输入,导致SDA_2=SDA_1=0;

4) 最终SDA_2被SDA_1拉到0,完成低电平的转换。


03

第三步,Slave往Master发数据1



1) 右边Slave SDA_2为输出,驱H-5V;

2) 左边Master SDA_1为输入,对外高阻, 被RP1上拉到3.3V;

3) NMOS 因为VGS=0一直关闭;

4) NMOS关闭,Master SDA_1维持3.3V高电平,完成电平转换。


04

第三步,Slave往Master发数据0



1) 右边Slave SDA_2为输出,驱LOW;

2) 左边Master SDA_1为输入,对外高阻, 被RP1上拉到3.3V;

3) NMOS 因为VGS=0一直关闭;

4) NMOS关闭,Master SDA_1为3.3V高电平。

出现了一个问题,我们观察到右侧的SDA_2显示低电平,而左侧的SDA_1却是高电平,这似乎是个矛盾,不应该出现这种情况,毕竟电路是正常的。

让我们继续观察接下来的情况,实际上这个问题并不复杂,只要仔细观察这张图,就能较容易找到答案。

我们来揭晓答案:大家看看上图的NMOS下面多了一个二极管,我们把这个二极管叫做Body Diode,它会在这个时候发挥作用。


5) 由于左边SDA_1为高,右边SDA_2为LOW, Body Diode导通;

6) SDA_1被拉低,导致NMOS VGS>0 后打开;

7) NOMO打开后,SDA_1和SDA_2相当于短在一起;

8) 最终左边SDA_1被右边的SDA_2拉低变为LOW。

由于体二极管的影响,NMOS管被激活,使得右侧从设备输出的低电平(L=0)能够成功传输至左侧主设备的SDA_1输入端。

观察这张图时,如果按照图示逐步分析,可能不难找到答案。但在面试中,由于紧张和缺乏清晰的电路提示,有时可能难以充分发挥,你们认为呢?


I2C第五罪


每一笔I2C的访问都是随着设备互相之间的ACK后结束的,这就好像你和别人商量个事情,要等到别人答应了,这次对话才算结束;又或者你给别人寄一个包裹,一般要等到收件人收到东西后,给你回了电话,你才会认为东西送到了。


这里的“答应”“回电话”就等于I2C里面的ACK,这一点在前面都已经反复叙述了,这里不再展开说明(可以戳文段开头的链接回顾哦)


我们先来看看下面这张图:


做几点说明,让大家明白讨论内容


1.系统串口打印信息如下,表示I2C访问失败:

I2C slave device not found num1,addr 0xe2====i2c_send_command_to+scc_rommon send_status Failed.

I2C slave device not found num1,addr 0xe2====i2c_send_command_to+scc_rommon send_status Failed.

2.上图中我在ACK的位置坐了标注,细心的读者只要和下面这张正常的图一比较,就可以看出差别,在ACK的位置,SCL为High/SDA为Low,但是上图确刚刚好相反;


3.我们现在知道在第一个ACK的位置, Slave设备本来应该给出SDA=Low,为了更加清楚的分析问题,我们再来一张说明更多的图。

由前面的说明,我知道是Slave设备出了问题,那么到底出了什么问题呢?

我们先把最前面那张图的案例分析一下,可能有人已经注意到了图中有一个@-40C,对了,没错!就是我们的交换机放在温箱里面做高低温时,在零下-40C的时候发现的问题。

有人问-40度下,这波形怎么量到的啊?

这明显问到了关键点上,硬件工程师的辛苦就体现出来。首先要把I2C信号线和串口线分别从板子上焊接出来,再分别连到外面的示波器和电脑上,然后就苦逼地守着温箱,运气好的话很快就能复现问题,运气不好要折腾很久才能trigger到这个issue,真的是一把辛酸泪啊。

解决的问题过程更加复杂,而且并没有什么技术含量可言——说服Supply Chain的人认为是vendor的器件问题,让他们在芯片生产线做Screen的时候,从原来的泡-40/5分钟改为-45/20分钟,把没有margin的芯片筛选掉。这个和Supply chain以及vendor沟通的过程是痛苦的,大公司嘛,不说了,你们懂的……


现在我们知道了器件受环境影响会产生I2C不ACK的问题,下面这张图是另外一种情况,先故设迷局,一起看看下图中有什么问题?


我承认,这个真的不好发现,以前团队中的某工程师在调试一个Sony的IMX291 Sensor时,CPU通过I2C死活访问不了,最后把波形一点一点在示波器上触发下来看,还是看不出来,这就难怪,没有经验确实很难找。

我们先来看单次写操作:

1.Start

2.Master发device address

3.Master发 R/W为Write

4.Slave 回ACK

5.Master发寄存器地址,也叫Word Address

6.Slave 再回ACK

7.Master发要写的数据给Slave

8.Slave收到数据后回ACK

9.Master看到ACK后发Stop结束本次操作

我们再来看单次多操作:

1.Start

2.Master发device address

3.Master发 R/W为Write

4.Slave 回ACK

5.Master发寄存器地址,也叫Word Address

6.Slave收到后再回ACK

7.此时Slave内部的寄存器指针已经知道对应的寄存器地址

8.Master发start

9.Master再次发device address

10.Master 发R/W为Read

11.Slave发出ACK

12.Slave发数据给Slave

13.Master收到数据后发NAK给Slave

14.Master发stop结束本次读操作

所以上图的读操作一共有两个错误,为了防止读者烧脑,我直接标注出来吧:


不知道大家在前面阅读的时候有没有看出来呢?特别是第一个Start很容易漏掉,不是老司机一下子真心发现不了。

添加了这里漏掉的START和ACK后,我们的Sony Sensor终于可以访问了,所以时刻要注意ACK是否少了。

为了让大家清晰理解,这里给出一张完整的逻辑图,大家在理解时要注意和写操作做对比,然后一定要独立思考弄清楚为什么会有这样的差别?一直到自己彻底想明白了,以后独立解决问题的速度也就会变快。



最后我再给大家看一张图,请大家自行研究错在哪里?答案我们将在“第六宗罪”里面揭晓。

I2C第六罪


随着我们深入探讨后面的几个问题,难度会逐渐增加,请读者仔细思考,确保能够将所学应用于实践。

首先分享一个趣事:在大学时,我们总爱在图书馆抢占学习座位,尤其是那些靠近漂亮女生的座位,这样既能学习又能欣赏美女,^_^


当你需要短暂离开时,常用的方法是在椅子上放一本书占位。但往往回来后会发现书被移开,位置上换成了另一个男士正在和旁边的女士聊天,让人心生不悦。

你唯一能做的是等待那位男士和女士交谈完毕离开,然后重新占据座位。然而,当你失望地发现刚才的女士已经离开,取而代之的是另一位男士时,无疑是人生中的一大打击。

接下来展示一张图,请诸位检查是否存在异常。该图是通过I2C协议分析仪捕获的,初看之下似乎一切正常,所有必要的信号都在,包括停止位前的NAK信号,但实际情况真的完美无缺吗?

我们再来看一张图。


对比这两张图,我们得出:这是一个读数据的操作。

发现第一张图中的问题并不困难,只需细致对照即可。

以下是核对结果:我们注意到在第二个Start信号之前,意外地出现了一个Stop信号,使得错误变得显而易见。

说起来简单,软件工程师在编写代码构建时序时,往往会认为第一笔写操作完成后(即向从设备指针写入Word地址),在开始下一笔读操作之前应该发送一个Stop信号。



为了清洗的说明,我们列一个顺序如下:

1.Master把要读的数据(或者寄存器)的Address先写入Slave,这里要注意理解好,这里Word Address相当于是一笔数据;

2.此时要注意整个读操作刚刚进行了一半,千万不能加Stop;

3.Master在收到前面写操作的ACK后,发一个Start;

4.Master再发一次Device Address,然后开始接收读的数据;

5.Master收到数据发一个NAK, 然后再发一个Stop结束整笔操作。

有人问,既然我们在第二个Start前多加了一个Stop,也没有见系统报错,一切正常啊,有时候访问还是成功的,这到底是为什么啊?

这里有个原因很重要:因为读操作最后是有Master发出的NAK + Stop来结束掉的,而NAK是SDA-HIGH,所以即便有时候操作不正常,只要不操作SDA(SDA默认的电平时HIGH),也能得到NAK误导对方。

我们继续来说一下,如果第二Start前面多了一个Stop会产生什么样的现象?

这是发生在ONU光猫上的Issue,现象是:从光模块SFP读回来的数据值总是不对,反复试验发现偶尔也能读对,但是写操作都是准确的。

这里一定会有人问,你读操作不正常,怎么知道写的是对的啊?好问题, 我们通过设置环回和打开/关闭光模块等写寄存器操作,反复确认我们写寄存器的操作是准确的。

我们来看光模块的I2C读写标准SFF-8431里面的图,可以看到一次读操作和前面叙述的一样,分为两个部分,中间用一个START隔开。


1.Master写device address =0XA2和写命令

2.Master发出word address 0X6E

3.Master插入第二个Start

4.Master再次发出device address =0XA2和读命令

5.Master接收光模块的数据0X82

6.Master发NAK

7.Master发Stop结束本次操作

注意:图中黑色部分是我们在Vendor的平台上用准确的方式读写抓到的波形。

如果按照上面所说的,我们在中间加了一个Stop会产生什么样的现象呢?

为啥读数据会不正确,但是I2C总线并没有出错信息吖?前面我们已经林林总总的叙述了一些,下面给出最终的描述。

看下面这张图,是我们和Samtec的FAE在出问题的板子上一起抓到的波形图,很明显我们看到多了一个Stop,下面我们来进行分析:


1.Master写device address =0XA2和写命令

2.Master发出word address 0X6E

3.Master多插入了一个Stop

4.Master插入第二个Start

注意:下面被插入了一笔完整的写数据的操作。

5.Master又发出device address =0XA2和写命令

6.Master发出word address 0X7F

7.Master发出写的数据0X80

注意:开始接着上面未完成的读操作继续

8.Master又多插入了一个Stop

9.Master又插入第二个Start

10.Master再次发出device address =0XA2和读命令

11.Master接收光模块的数据,我们看到读到的数据是全0,为什么呢?

12.Master发NAK

13.Master发Stop结束本次操作

相信很多人已经晕了,这到底是咋回事啊?

原来:一笔读操作,由于中间多了一个Stop,所以系统软件进程误以为前面读操作完成了,所以横空插进来一个写的操作,并且这里的写操作准确的完成了。


在写操作完成了,我们看到Master试图继续完成刚刚被中断掉的读操作,其实这也可以啊,大不了分两次,只要最终数据能准确读出来也行,可我们此时得到的数据却不是刚刚的0X82了,而是0X00,这又是为什么呢?


我们来结合这张图描述一下发生错误的过程:

1.Master开始读操作的第一步把0X6E写入Device;

2.此时被插入另外一个写操作;

3.写操作顺利完成并且把Pointer写成了0X7F(注意已经不是原来的0X6E);

4.Master继续刚才被中断的写操作;

5.注意此时Pointer的值是0X7F,所以读到的值是0X7F这个地址的值。

这里就清楚了:一次完整的I2C读操作访问,如果中间加了不应该有的Stop,就会被其它进程强占,从而插入另外的写操作,导致访问memory或者寄存器的地址指针被覆盖,Master然后接着完成刚刚被中断的操作,也不能正确读写到要访问的值。


这里分享几件有趣的事:

1. 既然只有读操作存在问题而写操作正常,产品功能便被认为是正常的,这也导致产品在市场上销售多时未被察觉问题,这确实有些讽刺;

2. I2C的读操作中,持续的NAK响应要求SDA保持高电平,由于SDA默认就是高电平(之前讨论过开漏和上拉电阻),即使设备没有响应,也会让期待NAK的设备误以为NAK信号已经发出;

3. 有时系统软件与硬件之间会发生冲突,只有双方协作才能找到问题的核心,否则相互指责只会让问题解决变得更加棘手;

4. 识别并解决问题,撰写文章看似简单,但调试过程却是充满挑战,尤其是对于I2C这样的接口,仅由两根线组成,许多人对其不够重视,这是不应该的。


I2C第七罪


第七罪姗姗来迟,既然是大结局,当然就应该让大家更加深刻的来理解I2C。我们先来复习一下大家共有的对I2C的认知:

1. I2C的SCL(Clock)总是由Master来驱动;

2. I2C的SDA (Data)则不同,Master和Slave分别都驱动。

这两点想必读者都没有什么疑问,因为前面的六宗罪都已经说得比较多了,我们今天要说的是另外一种特殊情况,就是Slave也会去Drive。


先来看一个例子,我的一个项目发生过这样一件事情,CPU在访问板子上的另外一颗SENSOR时,一直Fail,我们非常仔细检查了时序,都是准确的。

注意:这里我们发现是有一个知识盲点,导致一直找不到Root Cause。


细心的工程师会在测量过程中发现, SCL上有下图这样的尖尖的毛刺,毛刺有矮的也有高的,于是我们认为SCL上这种尖毛刺会导致Slave的状态机误触发,跑飞了。


整整一个星期我们都是这么认为的,尝试各种方法试图去消除这个毛刺。

因为信号从主板通过连接器到子板,我们认为这里的SCL信号很容易受到外部的干扰,比如从空间耦合过来的噪声。

所以在信号上加电容、加匹配、降低上拉电阻等等,试图滤除毛刺。

得到的效果是,即便我们滤除的毛刺有改善,可问题依然存在,只是稍有好转。

虽然说,现在拿着结果来讲故事听起来很轻松,其实那个过程真的很难受,我们接着往下看。


俗话说三人行必有我师,有个聪明的工程师突然想到I2C有个Clock stretch的机制,来看这张图,我们看到Slave把SCL拉到低,这是什么情况呢?


先来叙述一下Clock Stretch:

1. 当Master是高速I2C设备,Slave是Low Speed设备时,Master输出的SCL的频率超出了Slave的承受范围,此时Slave跟不上Master的速度怎么办? Slave就要想办法告诉Master。


大家知道为什么这里会看到右边矮矮的Glitch吗?

因为Master此时是想驱动SCL高电平,而Slave却拖住SCL不让变为高电平,这个其实是一种想象,实际测试是看不到的,这里是为了方便大家理解。

2. 我们再来看一张图,加深一下理解,Slave会强行把SCL拉低,拖住Master,这和之前我们对I2C的认知是完全相反的,此时Slave 是输出SCL信号,而Master则是检测SCL状态的输入信号。


举个例子:


正常情况下Master/Slave都是通过在SDA上的ACK信号来确认一笔操作的成功,如图:


但是如果Slave来不及怎么办?

(看下图)Slave直接把SCL信号拖住告知Master:兄弟我还没有准备好,你先等等我啊。

此时Master要做的事情就是乖乖等着,并且一直检测SCL的状态(输入信号),当Slave松开SCL信号,由于上拉电阻的存在,SCL自然变高,Master检测到SCL变高后,才开始检测ACK信号,然后继续下一步操作。


说了那么多I2C stretch,想必大家应该理解了。

回到最上面我们遇到的CPU和SENSOR之间的I2C问题,我们测量得到毛刺确实是罪魁祸首。

因为此时CPU和SENSOR进入了Clock Stretch, Sensor拉低了SCL,而CPU Master不断地检测SCL的状态,期待高电平的到来,此时毛刺就误导了CPU,CPU看到尖的毛刺就认为Slave已经松开了SCL,就立刻开始下一步的动作,此时Slave很冤枉了,自己明明拉低了SCL让Master等着,可是这位兄弟怎么这么不听话呢?


解决的方法很简单,CPU的I2C控制器在进入Clock Stretch时,检测SCL并且判断高电平时有一个De-Glitch的功能,我们之前没有打开,打开后就可以滤除Glitch这样的窄脉冲了。

简单一点讲,就是当检测到一个SCL的高,用一个计数器继续连续计数,只有发现连续的40个高电平才认为是SCL真的拉高了,否则就认为是毛刺,不予理睬。


聪明的人很多,我们再来说说最近碰到的另外一件关于Clock Stretching的事情。


紫色的线有一段半高, 其实原因也简单,就是因为此时Slave拉低SCL,但是Master不支持Clock Stretch,此时就发生了冲突。

Master不支持Clock Stretch,我们就需要通过软件的方式去模拟,此时有两件事需要实现:

1. Master要把SCL切换为输入,然后不停检测SCL的状态;

2. Master在检测SCL状态一定要做De-bounce或者De-Glitch的滤波。

看完了上面的叙述,想必大家都可以理解原因。可是这样会让软件工程师们很麻烦,那么应该怎样绕开Clock Stretch呢?聪明的工程师总有自己的办法。


我们和Slave芯片的Vendor确认,每次Slave在做ACK后,芯片需要最多5ms的Clock Stretch延时。

我们拿到这个数字后,软件工程师只要注意在Master每次得到ACK后,先等待5ms后再对Slave做下一笔操作。

在这个5ms期间Master完全不用关心 SCL信号上是高电平和低电平,因为5ms以后Slave肯定松开了SCL,也就是说SCL肯定是高电平了。

这种方法就避免Master的SCL信号切换为输入,还要不停地检测SCL的状态,最重要的是不需要做软件的De-bounce或者De-Glitch算法。


自此I2C的七宗罪就结束了,希望这七宗罪可以cover所有硬件设计过程中的I2C问题,到目前我还没有发现有其它超出这七个范围的问题。


评论
  • 随着科技的飞速进步,智能家电已成为现代家庭生活中密不可分的一部分。不论是自行出动,清扫地板的扫地机器人、还是可提前准备食材清单的智能冰箱,或者是可自动调节洗衣程序的智能洗衣烘干机,这些智能家电装置正以前所未有的方式改变着我们的日常生活。除了上述提到的智能家电,还有更多你想象得到的便利装置,例如智能除湿机、空气清净机、净水器、智能风扇、语音助理及智能灯具等等。这些装置不仅为现代人的居家生活中带来了许多便利,让我们能够更轻松地管理家务,还可进一步提升生活质量,节省宝贵的时间和能源。正所谓「科技始终来
    百佳泰测试实验室 2024-12-26 16:37 30浏览
  • 施密特触发器光耦施密特触发器光耦(Schmitt Trigger Optocoupler)是一种将光耦和施密特触发器电路相结合的电子元件。它不仅具备光耦的电气隔离功能,还具备施密特触发器的噪声抑制和信号整形能力。施密特触发器光耦的详细结构LED部分:LED是由半导体材料制成的,通常封装在一个透明的塑料或玻璃外壳中。其主要功能是在输入端电流流过时产生光信号。光接收器部分:光接收器通常是一个光敏晶体管或光敏二极管,其基区(或PN结)对光信号敏感。当接收到来自LED的光信号时,光接收器产生一个与光强度
    晶台光耦 2024-12-26 17:19 27浏览
  • 今年AI技术的话题不断,随着相关应用服务的陆续推出,AI的趋势已经是一个明确的趋势及方向,这也连带使得AI服务器的出货量开始加速成长。AI服务器因为有着极高的运算效能,伴随而来的即是大量的热能产生,因此散热效能便成为一个格外重要的议题。其实不只AI服务器有着散热的问题,随着Intel及AMD 的CPU规格也不断地在提升,非AI应用的服务器的散热问题也是不容小觑的潜在问题。即便如此,由于目前的液冷技术仍有许多待克服的地方,例如像是建置成本昂贵,机壳、轨道、水路、数据中心等项目都得重新设计来过,维修
    百佳泰测试实验室 2024-12-26 16:33 98浏览
  • 近日,紫光展锐正式推出基于RTOS系统的旗舰产品W337,它拥有丰富特性和超低功耗,进一步壮大紫光展锐的智能穿戴产品组合,面向中高端和广阔的智能穿戴市场,提供先进的技术解决方案。  性能卓越,成就强悍RTOS穿戴芯 双核CPU架构:紫光展锐W337基于RTOS系统首创双核CPU架构,可根据系统的负载情况动态调整功耗,当系统负载较低时,降低一个或两个核心的频率和电压。由于有两个核心分担负载,每个核心的发热相对较低,进一步降低了系统整体的散热需求。双核架构更好地实现了负
    紫光展锐 2024-12-26 18:13 36浏览
  • 在谐振器(无源晶振)S&A250B测试软件中,DLD1到DLD7主要用于分析晶体在不同驱动功率下的阻抗变化。此外,还有其他DLD参数用于反映晶振的磁滞现象,以及其频率和功率特性。这些参数可以帮助工程师全面了解KOAN晶振在不同功率条件下的动态特性,从而优化其应用和性能。磁滞现象晶振的磁滞现象(Hysteresis)是指在驱动功率变化时,晶体的阻抗或频率无法立即恢复至初始状态,而表现出滞后效应。1. DLDH: Hysteresis Ratio (MaxR/MinR)在不同驱动
    koan-xtal 2024-12-26 12:41 103浏览
  • 发明阶段(20世纪80年代至90年代)起源:当时ASIC设计成本高,周期长,流片失败率高,业界需要一种通用的半导体器件进行流片前测试和验证,可编程逻辑器件就此产生。诞生:1980年,Xilinx公司成立。1985年,Ross Freeman制造了第一片PFGA芯片XC2064,采用4输入,1输出的LUT和FF结合的基本逻辑单元。发展阶段(1992年至1999年)容量提升:FPGA容量不断上涨,芯片面积逐渐增大,为架构穿心提供空间,复杂功能可以实现。布线问题凸显:缩着芯片复杂度增加,片上资源的互连
    Jeffreyzhang123 2024-12-27 10:26 45浏览
  •       在科技日新月异的今天,智能手机已不再仅仅是通讯工具,它更成为了我们娱乐、学习、工作的核心设备。特别是在游戏体验方面,用户对于手机的性能要求愈发严苛,追求极致流畅与沉浸感。正是基于这样的市场需求,一加品牌于2024年12月26日正式推出了其最新的游戏性能旗舰——一加 Ace 5系列,包括一加 Ace 5与一加 Ace 5 Pro两款力作。这一系列深度聚焦于性能与游戏体验,旨在为用户带来前所未有的游戏盛宴。骁龙8系旗舰平台,性能跃升新高度
    科技财经汇 2024-12-26 22:31 45浏览
  • 起源与基础20 世纪 60 年代:可编程逻辑设备(PLD)的概念出现,一种被称为 “重构能力” 的芯片的可编程性吸引了许多工程师和学者。20 世纪 70 年代:最早的可编程逻辑器件 PLD 诞生,其输出结构是可编程的逻辑宏单元,它的硬件结构设计可由软件完成,设计比纯硬件的数字电路更灵活,但结构简单,只能实现小规模电路。诞生与发展20 世纪 80 年代中期:为弥补 PLD 只能设计小规模电路的缺陷,复杂可编程逻辑器件 CPLD 被推出,它具有更复杂的结构,能够实现较大规模的电路设计。1988 年:
    Jeffreyzhang123 2024-12-27 10:41 36浏览
  • 在科技飞速发展的今天,汽车不再仅仅是一种交通工具,更是一个融合了先进技术的移动智能空间。汽车电子作为汽车产业与电子技术深度融合的产物,正以前所未有的速度推动着汽车行业的变革,为我们带来更加智能、安全、舒适的出行体验。汽车电子的发展历程汽车电子的发展可以追溯到上世纪中叶。早期,汽车电子主要应用于发动机点火系统和简单的电子仪表,功能相对单一。随着半导体技术的不断进步,集成电路被广泛应用于汽车领域,使得汽车电子系统的性能得到了显著提升。从电子燃油喷射系统到防抱死制动系统(ABS),从安全气囊到车载导航
    Jeffreyzhang123 2024-12-27 11:53 26浏览
  • 新能源汽车市场潮起潮落,只有潮水退去,才能看清谁在裸泳。十年前,一批新能源汽车新势力带着创新的理念和先进的技术,如雨后春笋般涌入中国汽车市场,掀起一场新旧势力的角逐。经历市场的激烈洗礼与投资泡沫的挤压,蔚来、理想、小鹏等新势力车企脱颖而出,刷爆网络。不曾想,今年新势力车企杀出一匹“超级黑马”,爬上新势力车企销量榜前三,将蔚来、小鹏等昔日强者甩在了身后,它就是零跑汽车。公开数据显示,11月份,零跑汽车实现新车交付量约4.02万辆,同比增长117%,单月销量首次突破4万辆;小鹏汽车当月共交付新车约3
    刘旷 2024-12-26 10:53 153浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦