1. 为什么I2C调试总是让人又爱又恨
搞嵌入式的人,十个里有八个在I2C上翻过车。这个总线只有两根线,SCL和SDA,硬件接线简单到不能再简单,协议看起来也不复杂——起始条件、地址、读写位、ACK、数据、停止条件,翻来覆去就这几个东西。但真正上手调设备的时候,你会发现事情远没有想象中那么顺利。屏幕不亮、EEPROM读出来全是0xFF、传感器时好时坏、换个板子就跑不起来,这些问题几乎每个嵌入式工程师都遇到过。
I2C的全称是Inter-Integrated Circuit,中文叫集成电路总线,是飞利浦在上世纪80年代搞出来的一种同步串行通信协议。它的核心优势在于只用两根线就能挂载多个设备,每个设备有独立的7位地址,理论上一条总线可以挂128个设备。这个特性让它在板级通信中非常吃香,温度传感器、EEPROM、OLED屏幕、陀螺仪、触摸芯片、IO扩展芯片,大量外设都是I2C接口。
但正因为挂载设备多、时序要求严格、硬件设计容易出问题,I2C调试成了嵌入式外设调试中的经典难题。我做过不少项目,从STM32的硬件I2C到GPIO模拟I2C,从Linux下的i2c-dev到RTOS里的驱动框架,踩过的坑可以说能写一本书。这篇文章就把我在I2C设备调试方面的思路和经验系统整理一下,从硬件排查到软件配置,从时序分析到问题定位,尽量把每个环节讲透。
不管你是刚接触嵌入式的学生,还是工作几年但一遇到I2C问题就头疼的工程师,这篇文章应该都能给你一些实用的参考。我不会只讲协议本身——协议网上到处都是——而是重点讲调试思路和实操方法,也就是遇到问题该怎么一步步定位、怎么快速解决。
2. I2C调试的底层逻辑与整体思路
2.1 先搞清楚是硬件问题还是软件问题
很多人一遇到I2C不通,第一反应就是去看代码,改时序、改时钟频率、换驱动。但实际上,I2C通信失败的原因里,硬件问题占了相当大的比例。我的习惯是先用示波器或者逻辑分析仪抓波形,确认硬件层面有没有信号。如果SCL和SDA上什么都没有,那代码改出花来也没用。
判断硬件还是软件问题,有一个很简单的分步方法。第一步,用万用表测SCL和SDA的对地电压。正常情况下,总线空闲时两根线都应该是高电平,大约是VCC的电压。如果测出来是0V或者很低,说明线路被拉死了,可能是某个设备把总线拉住了,也可能是上拉电阻没焊或者阻值不对。第二步,用示波器看有没有时钟信号。如果主机发出了SCL但SDA没有反应,可能是从设备地址不对或者从设备根本没工作。第三步,如果波形都有但数据不对,那就要看时序参数是否满足从设备要求了。
这个排查顺序很重要,因为从硬件到软件是逐层递进的。跳过硬件直接查软件,很容易在错误的方向上浪费时间。
2.2 上拉电阻:最容易被忽视的关键元件
I2C总线是开漏输出结构,这意味着任何设备都只能把线拉低,不能主动拉高。线要回到高电平,必须靠上拉电阻。这个设计的好处是可以做电平转换和多设备仲裁,但代价就是上拉电阻的选择非常关键。
上拉电阻的阻值怎么算?这取决于总线电容和通信速率。总线电容包括PCB走线电容、引脚电容和器件电容,一般在100pF到400pF之间。上升时间Tr和上拉电阻Rp、总线电容Cb的关系是:Tr ≈ 0.847 × Rp × Cb。以标准模式100kHz为例,上升时间要求小于1000ns,如果总线电容是200pF,那么Rp最大约为5.9kΩ。快速模式400kHz要求上升时间小于300ns,同样的电容下Rp最大约为1.77kΩ。
实际选型时,常见的取值是4.7kΩ和10kΩ。4.7kΩ适合大多数场景,10kΩ适合总线电容较小、速率较低的情况。阻值太小会导致功耗增加,而且灌电流可能超过器件的承受能力(一般要求不超过3mA);阻值太大则上升沿变缓,高速通信时波形会变形。
我遇到过一个典型案例:某项目用10kΩ上拉跑400kHz,逻辑分析仪抓出来的波形上升沿明显变圆,数据偶尔出错。换成2.2kΩ之后问题消失。所以上拉电阻不是随便放一个就行,要根据实际速率和总线情况来选。
2.3 地址冲突:多设备挂载时的隐形杀手
I2C的7位地址空间理论上有128个地址,但实际可用的没那么多,因为有些地址是保留的。更麻烦的是,很多外设芯片的地址是固定的,或者只有一两个引脚可以配置。如果你在一条总线上挂了两个地址相同的设备,那就必然冲突。
地址冲突的表现是什么呢?通常是两个设备都不正常工作,或者其中一个偶尔能通。因为当主机发出地址帧时,两个设备同时响应并拉低SDA,ACK信号虽然看起来正常,但后续的数据传输就会混乱。
解决地址冲突的方法有几种。一是选择地址可配置的器件,通过ADDR引脚接不同电平来改变地址。二是使用I2C多路复用器,比如TCA9548A,它可以把一条总线扩展成8条独立的总线,每条总线上挂地址相同的设备也没问题。三是在软件层面分时复用,通过控制某个GPIO来切换设备的使能状态,但这种方法比较麻烦,不推荐。
注意:在画原理图阶段就要规划好每个I2C设备的地址,标注在图纸上。等到PCB打样回来才发现地址冲突,改板成本就高了。
2.4 时钟频率与总线负载的平衡
I2C支持多种速率模式:标准模式100kHz、快速模式400kHz、快速模式+ 1MHz、高速模式3.4MHz。速率越高,对硬件的要求就越严格。很多初学者看到器件手册支持400kHz,就直接设成400kHz,结果通信不稳定。
这里要考虑几个因素。第一,总线上的电容负载。设备越多、走线越长,电容越大,高速通信时波形质量越差。第二,从设备的实际支持能力。有些器件标称支持400kHz,但内部处理速度跟不上,需要降低速率才能稳定工作。第三,主机的时钟精度。硬件I2C的时钟由外设产生,如果时钟配置不准确,可能导致采样点偏移。
我的经验是,调试阶段先用100kHz把功能跑通,确认数据读写都正常之后,再逐步提高到目标速率。如果提高速率后出现问题,就退回到上一个稳定的速率。不要一上来就追求高速,稳定可靠比快几毫秒重要得多。
3. 硬件层面的排查与实操要点
3.1 用万用表和示波器做基础检查
拿到一块新板子,I2C设备不工作,第一步不是写代码,而是做硬件基础检查。万用表调到直流电压档,黑表笔接地,红表笔分别测SCL和SDA的对地电压。正常情况应该是接近VCC的高电平。如果测到中间值比如1.5V,说明总线被某个设备拉住了,处于半死不活的状态。
接下来用示波器看波形。触发方式设为SCL的上升沿或者下降沿,时间基准根据通信速率来设。100kHz的话一个时钟周期是10微秒,示波器时基设成10微秒每格就能看到完整波形。重点看几个东西:SCL和SDA的上升沿是否陡峭、高电平是否达到VCC、低电平是否接近0V、有没有毛刺和振铃。
如果手头没有示波器,逻辑分析仪也是很好的选择。现在市面上几十块钱的USB逻辑分析仪配合开源软件就能解码I2C协议,直接看到地址、数据和ACK位,非常方便。我调试I2C的时候基本都会接一个逻辑分析仪,比盲猜效率高太多。
3.2 总线死锁的成因与解锁方法
I2C总线死锁是一个经典问题。现象是SCL一直为高,SDA被某个从设备拉低不放,主机无法发出起始条件。这种情况通常发生在通信过程中主机复位或者异常中断,从设备还在等待时钟继续,但主机已经不发了。
解锁的方法是在SCL上手动发送9个时钟脉冲,让从设备把剩余的数据位发完,然后发送停止条件。具体操作是把SCL配置成GPIO输出模式,手动翻转9次,每次高电平持续几个微秒,然后拉低再拉高。9个脉冲之后,从设备应该会释放SDA线。最后再手动产生一个停止条件——SDA在SCL为高时从低变高。
在STM32上,可以用下面的代码片段实现软件解锁:
void I2C_BusRecovery(void) { GPIO_InitTypeDef gpio; // 将SCL和SDA配置为通用推挽输出 gpio.GPIO_Pin = SCL_PIN | SDA_PIN; gpio.GPIO_Mode = GPIO_Mode_Out_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(I2C_GPIO_PORT, &gpio); GPIO_SetBits(I2C_GPIO_PORT, SDA_PIN); for (int i = 0; i < 9; i++) { GPIO_ResetBits(I2C_GPIO_PORT, SCL_PIN); delay_us(5); GPIO_SetBits(I2C_GPIO_PORT, SCL_PIN); delay_us(5); } // 产生停止条件 GPIO_ResetBits(I2C_GPIO_PORT, SDA_PIN); delay_us(5); GPIO_SetBits(I2C_GPIO_PORT, SCL_PIN); delay_us(5); GPIO_SetBits(I2C_GPIO_PORT, SDA_PIN); delay_us(5); // 重新配置为I2C复用功能 // ... }这个函数在初始化I2C之前调用一次,可以有效解决大部分总线死锁问题。我在多个项目里都加了这段恢复逻辑,尤其是那些需要长时间运行、可能受到电磁干扰的系统。
3.3 PCB布局对I2C信号的影响
I2C虽然速率不高,但PCB布局不合理照样出问题。几个关键点:SCL和SDA走线尽量等长、平行、靠近,最好走成差分对的形式,虽然I2C不是差分信号,但这样走线可以减小环路面积,降低干扰。走线不要跨过电源分割区,否则回流路径断裂,信号质量会急剧下降。上拉电阻尽量靠近主机或者总线中间位置,不要放在很远的末端。
还有一个容易忽略的点是走线长度。I2C的设计初衷是板内通信,走线一般不超过几十厘米。如果非要用排线连接到另一块板子,最好降低速率,并且增加上拉电阻的阻值补偿。我见过一个项目用20cm的杜邦线连OLED,400kHz死活不通,降到100kHz就正常了,就是线太长导致电容太大。
4. 软件层面的配置与调试技巧
4.1 硬件I2C与软件模拟I2C的选择
这是每个嵌入式工程师都会面临的选择。硬件I2C使用MCU内部的专用外设,不占用CPU时间,速率准确,但灵活性差,不同MCU的硬件I2C行为差异很大,有些还有已知的硬件缺陷。软件模拟I2C用两个GPIO口手动翻转电平,灵活性强,任何引脚都能用,但占用CPU时间,速率受限于GPIO翻转速度。
我的建议是:如果硬件I2C外设工作正常,优先用硬件I2C。如果遇到硬件I2C的坑(比如STM32某些型号的硬件I2C在特定条件下会卡死),果断换软件模拟。软件模拟I2C的代码其实很简单,网上有很多成熟的实现,移植起来也方便。
软件模拟I2C的关键是延时函数的精度。延时太长会导致速率上不去,延时太短可能导致从设备来不及响应。一般来说,100kHz的I2C,每个位的时间是10微秒,高电平和低电平各5微秒。用空循环实现延时的时候要注意编译器优化,变量要加volatile关键字,否则可能被优化掉。
4.2 用逻辑分析仪解码I2C协议
逻辑分析仪是I2C调试的利器。连接方式很简单:通道0接SCL,通道1接SDA,地线接GND。采样率要足够高,至少是通信速率的10倍以上。100kHz的I2C,采样率设1MHz就够了;400kHz的话建议设4MHz以上。
抓到的波形用软件解码之后,可以看到完整的通信过程:起始条件、地址帧、读写位、ACK/NACK、数据字节、停止条件。如果某个环节出错,一眼就能看出来。比如地址帧之后没有ACK,说明从设备没有响应,可能是地址不对或者设备没上电。数据字节之后主机发了NACK,说明主机不想继续读了,这是正常的读结束流程。
我习惯在调试的时候把逻辑分析仪的截图保存下来,和代码里的操作一一对应。这样能快速定位是代码逻辑问题还是硬件问题。比如代码里写的是读寄存器0x00,逻辑分析仪上看到的是写地址0x01,那就是代码里的地址定义写错了。
4.3 读写EEPROM的完整流程与注意事项
EEPROM是I2C调试的经典练手对象,也是很多项目的必备器件。以AT24C02为例,容量2Kbit(256字节),7位地址是1010xxx,其中低三位由A2/A1/A0引脚决定。
写一个字节的流程是:起始条件 → 发送设备地址+写位 → 等待ACK → 发送内存地址 → 等待ACK → 发送数据 → 等待ACK → 停止条件。然后EEPROM内部会启动写周期,大约5毫秒,这段时间内不会响应任何请求。所以连续写多个字节的时候,每个字节之间要加延时,或者用页写模式一次写一页(AT24C02一页8字节)。
读一个字节的流程稍微复杂一点:先起始条件 → 发送设备地址+写位 → 等待ACK → 发送内存地址 → 等待ACK → 重新起始条件 → 发送设备地址+读位 → 等待ACK → 读取数据 → 发送NACK → 停止条件。注意最后的NACK是主机发给从机的,表示“我读完了,你不用再发了”。
实操心得:调试EEPROM的时候,先用逻辑分析仪确认写周期是否满足。很多人在连续写的时候不加延时,导致后面的数据被丢弃。AT24C02的写周期最大5ms,保险起见可以延时6-8ms。
4.4 OLED屏幕的I2C驱动调试
OLED屏幕是I2C设备中比较特殊的一类,因为它通常不是标准的寄存器读写模型,而是命令和数据分开的。以SSD1306为例,I2C地址通常是0x3C或0x3D,每次传输的第一个字节是控制字节,0x00表示后面跟的是命令,0x40表示后面跟的是数据。
调试OLED的时候,最常见的问题是屏幕不亮或者显示乱码。不亮的原因可能是:初始化命令序列不对、供电电压不够、复位引脚没有正确操作。显示乱码通常是显存数据格式不对,SSD1306的显存是按页组织的,每页8行,共8页,对应128x64的点阵。
0.9寸OLED和0.96寸OLED在I2C兼容性上有时会有差异。0.9寸的驱动芯片可能是SSD1306,也可能是SH1106。SH1106的显存是132列,比SSD1306多4列,如果直接套用SSD1306的驱动代码,显示会偏移。解决方法是初始化的时候设置显示偏移量为2,或者在写显存的时候从第2列开始写。
// SSD1306初始化命令序列(部分) static const uint8_t ssd1306_init_cmds[] = { 0xAE, // 关闭显示 0xD5, 0x80, // 设置时钟分频 0xA8, 0x3F, // 设置多路复用率 0xD3, 0x00, // 设置显示偏移 0x40, // 设置起始行 0x8D, 0x14, // 使能电荷泵 0x20, 0x00, // 设置内存寻址模式 0xA1, // 段重映射 0xC8, // 扫描方向 0xDA, 0x12, // 设置COM引脚配置 0x81, 0xCF, // 设置对比度 0xD9, 0xF1, // 设置预充电周期 0xDB, 0x40, // 设置VCOMH 0xA4, // 全局显示开启 0xA6, // 正常显示 0xAF // 开启显示 };这段初始化序列在SSD1306上验证过很多次,可以直接用。如果是SH1106,需要在设置显示偏移的地方改成0x02,并且写显存的时候每页从第2列开始。
5. 常见问题排查与实战案例
5.1 I2C通信失败速查表
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| SCL/SDA都是高电平,无波形 | 主机没有发起通信 | 检查代码是否调用了发送函数 | 确认I2C外设已使能,GPIO配置正确 |
| SCL有波形,SDA一直为高 | 从设备没有响应 | 用逻辑分析仪看地址帧后是否有ACK | 检查从设备地址、供电、复位引脚 |
| SDA被拉低不放 | 总线死锁 | 测SDA对地电压是否接近0V | 发送9个时钟脉冲解锁 |
| 偶尔通信失败 | 时序余量不足 | 示波器看上升沿和建立保持时间 | 降低速率或减小上拉电阻 |
| 读出来全是0xFF | 从设备无响应或地址错误 | 确认地址和读写位 | 检查地址定义,确认设备在位 |
| 写进去读出来不对 | 写周期未完成 | 检查连续写之间是否有延时 | 增加5-10ms延时或使用页写 |
| 多设备时好时坏 | 地址冲突或总线负载过重 | 逐个挂载测试 | 使用I2C多路复用器或分时使能 |
这张表基本覆盖了I2C调试中80%的问题。遇到问题的时候按表排查,能省不少时间。
5.2 一个真实的调试案例:OLED时亮时不亮
之前做过一个项目,用STM32F103驱动0.96寸OLED,I2C接口。板子打样回来之后,发现OLED有时候能亮,有时候不亮,用手碰一下排线又亮了。一开始怀疑是接触不良,换了排线还是这样。
用示波器抓波形发现,不亮的时候SCL和SDA上都有信号,但OLED没有响应。仔细看波形,发现SCL的上升沿比较缓,从低到高用了大约2微秒。100kHz的I2C,高电平时间只有5微秒,上升沿占了2微秒,留给从设备采样的时间就不够了。
查原理图发现上拉电阻用的是10kΩ,而板子上I2C走线比较长,估计有15cm,总线电容偏大。把上拉电阻换成4.7kΩ之后,上升沿缩短到1微秒以内,OLED就稳定工作了。这个案例说明,上拉电阻的选型不能只看典型值,要结合实际走线和总线电容来算。
5.3 多设备总线的调试策略
当一条I2C总线上挂了多个设备时,调试策略要调整。我的做法是先把所有从设备都断开,只留主机和上拉电阻,确认主机能正常发出起始条件和停止条件。然后逐个挂载设备,每挂一个就测试一次,确认新挂的设备不影响已有的设备。
如果挂到某个设备时总线挂了,那问题就出在这个设备上。检查它的地址是否和已有设备冲突、供电是否正常、有没有把总线拉死。这种逐个挂载的方法虽然慢一点,但定位问题非常准确。
另外,多设备总线上要特别注意总电容。每个设备的引脚都有几pF的电容,加上PCB走线,很容易超过400pF的上限。如果设备比较多,可以考虑用I2C缓冲器或者多路复用器来分段驱动。
5.4 软件模拟I2C的时序优化
软件模拟I2C的时候,时序的精确控制很重要。下面是一个典型的软件I2C写字节函数:
void I2C_WriteByte(uint8_t data) { for (int i = 0; i < 8; i++) { I2C_SCL_LOW(); delay_us(2); if (data & 0x80) I2C_SDA_HIGH(); else I2C_SDA_LOW(); delay_us(2); I2C_SCL_HIGH(); delay_us(5); I2C_SCL_LOW(); data <<= 1; } // 释放SDA,等待ACK I2C_SDA_HIGH(); delay_us(2); I2C_SCL_HIGH(); delay_us(5); // 读取ACK uint8_t ack = I2C_SDA_READ(); I2C_SCL_LOW(); }这段代码里,SCL高电平的时间是5微秒,低电平是4微秒(2+2),总周期9微秒,接近100kHz。注意在SCL为高的时候SDA必须保持稳定,所以数据要在SCL拉低的时候改变。这是I2C协议的基本要求,违反了就会导致通信失败。
延时函数用空循环实现的时候,要注意不同编译器的优化等级会影响循环次数。建议用volatile变量做循环计数器,或者直接用汇编的NOP指令。如果MCU有DWT计数器,可以用它来做精确延时,不受编译器优化影响。
6. 从调试到设计:把经验固化下来
6.1 建立自己的I2C调试检查清单
调试做得多了,我总结了一份I2C检查清单,每次遇到问题就按这个清单过一遍,基本不会漏掉什么。清单分为硬件和软件两部分。
硬件部分:供电是否正常、上拉电阻是否焊接且阻值合适、SCL和SDA是否接反、从设备地址引脚是否配置正确、总线电容是否超标、有没有设备把总线拉死。
软件部分:I2C外设时钟是否使能、GPIO复用配置是否正确、通信速率是否匹配从设备、地址是否包含读写位、读写流程是否符合器件手册、连续写之间是否有足够延时、是否有总线恢复机制。
这份清单看起来简单,但真正遇到问题的时候,按部就班地检查比凭感觉乱试效率高得多。我现在带新人的时候,第一件事就是让他们把这份清单背下来。
6.2 代码层面的健壮性设计
产品级的I2C驱动不能只考虑正常情况,还要考虑异常恢复。我在每个I2C读写函数里都会加超时机制,如果等待ACK超过一定时间就返回错误,而不是死等。上层应用收到错误后可以重试或者报警,避免整个系统卡死。
另外,初始化的时候调用一次总线恢复函数,确保上电时总线状态是干净的。对于需要长时间运行的系统,还可以定期检测总线状态,如果发现异常就主动恢复。这些措施看起来增加了代码复杂度,但能大幅提升系统的可靠性。
// 带超时的ACK等待 uint8_t I2C_WaitAck(uint32_t timeout) { I2C_SDA_HIGH(); delay_us(2); I2C_SCL_HIGH(); delay_us(2); while (I2C_SDA_READ()) { if (--timeout == 0) { I2C_SCL_LOW(); return 1; // 超时,返回NACK } delay_us(1); } I2C_SCL_LOW(); return 0; // 收到ACK }这个超时机制在实际项目中救过我好几次。有一次一个传感器因为供电不稳偶尔不响应,如果没有超时,整个系统就卡在等待ACK的循环里了。加了超时之后,系统能检测到错误并重新初始化传感器,自动恢复。
6.3 工具链的搭建与使用建议
调试I2C,手头有几样工具会事半功倍。首先是逻辑分析仪,推荐至少8通道、采样率100MHz以上的型号,配合开源的 PulseView 或者厂家的上位机软件,能解码I2C、SPI、UART等多种协议。其次是示波器,看模拟波形质量、上升沿、毛刺这些,逻辑分析仪只能看高低电平,看不到细节。最后是万用表,基础检查必备。
软件方面,除了IDE自带的调试器,建议装一个I2C扫描工具。在Linux系统下,i2c-tools 包里的 i2cdetect 命令可以扫描总线上所有设备的地址,非常方便。在MCU上,也可以自己写一个扫描函数,遍历所有地址发起始条件,看哪个地址有ACK。
// I2C地址扫描函数 void I2C_Scan(void) { printf("Scanning I2C bus...\n"); for (uint8_t addr = 1; addr < 128; addr++) { I2C_Start(); if (I2C_SendAddr(addr << 1) == 0) { printf("Device found at 0x%02X\n", addr); } I2C_Stop(); } }这个扫描函数在调试新板子的时候特别有用,几秒钟就能知道总线上挂了哪些设备、地址是多少。如果扫描不到任何设备,那肯定是硬件问题,不用去查代码了。
6.4 从器件手册中提取关键信息
每个I2C器件的手册里都有几个关键信息必须确认:设备地址、支持的最高速率、写周期时间、寄存器映射、读写时序图。很多人调试失败就是因为没仔细看手册,凭经验瞎猜。
设备地址要注意手册给的是7位地址还是8位地址。有些手册写的是8位地址(包含读写位),有些写的是7位地址。如果搞混了,地址就会错一位,怎么都通不了。写周期时间决定了连续写之间的延时,EEPROM类器件尤其要注意。寄存器映射决定了你要读写哪个地址,写错了寄存器可能没有任何效果。
我习惯把关键信息摘录出来,贴在代码的注释里。这样以后维护的时候不用再翻手册,看代码注释就知道了。
7. 一些零散但实用的经验
调试I2C的时候,我一般会把逻辑分析仪的采样深度设大一点,至少1M点以上。因为I2C通信可能持续几毫秒甚至几十毫秒,采样深度不够的话抓不到完整过程。触发条件设成SDA的下降沿(起始条件),这样每次通信开始的时候自动触发,不会漏掉。
还有一点,如果总线上有多个主机(多主模式),调试会更复杂。不过实际项目中很少用到多主模式,大部分情况都是一个主机多个从机。如果确实需要多主,要特别注意总线仲裁的处理,两个主机同时发起通信时,谁先拉低SDA谁就获得控制权。
关于I2C的速率,再补充一点。有些器件手册标称支持400kHz,但实际测试发现只能跑到200kHz。这种情况不要怀疑自己,就是器件的问题。降速使用就行,I2C的速率对大多数应用来说不是瓶颈。一个温度传感器每秒读一次,100kHz和400kHz的差别完全可以忽略。
最后说一个关于I2C扩展的想法。如果项目里I2C设备特别多,可以考虑用I2C到SPI的桥接芯片,或者用MCU的多个I2C外设分成几条总线。不要把所有设备都挂在一条总线上,负载太重容易出问题。分而治之,每条总线挂3-5个设备,调试和维护都更简单。
我在实际项目中还遇到过一种情况:I2C设备在常温下工作正常,高温老化测试的时候偶尔通信失败。后来查出来是上拉电阻的温漂导致的,换成低温漂的电阻就解决了。所以如果产品有温度要求,元器件的温度特性也要考虑进去。
这些经验都是一次次踩坑积累下来的,希望能帮到正在跟I2C较劲的朋友。调试这件事,说到底就是耐心加方法,硬件软件都过一遍,总能找到问题所在。