硬件I2C与软件I2C谁更坑?嵌入式驱动实测避坑指南

驱动之路#44:硬件 I2C 和软件 I2C 谁更坑?

做了这么多年嵌入式驱动,I2C 这个协议可以说是老朋友了。小到 EEPROM、温湿度传感器,大到 OLED 屏幕、触摸芯片,几乎每个板子上都能见到它的身影。但每次聊到 I2C 的实现方式,总绕不开一个经典的争论:到底用硬件 I2C 外设,还是用 GPIO 模拟软件 I2C?网上说法五花八门,有人说硬件 I2C 一用就死机,有人说软件 I2C 时序不准没法用。今天这篇驱动之路,我就把自己的实测经验和踩坑记录好好掰扯一下,希望能给正在被 I2C 折腾的朋友一些参考。

这篇文章适合搞 STM32、ESP32、Linux 嵌入式或者正在调传感器的驱动开发人员。我会从协议本身的特点讲起,再把硬件 I2C 和软件 I2C 的实现细节、调试方法、典型坑点逐个拆开,最后给出我自己的选型建议。不管你是刚入门的小白,还是被 I2C 折磨过的老手,应该都能从中找到点有用的东西。

1. 先搞清楚 I2C 协议的本质,才能明白坑从哪来

1.1 I2C 到底是个什么样的协议

I2C 全称 Inter-Integrated Circuit,是 Philips 公司在 1982 年提出的一种两线制串行通信协议。之所以叫两线制,是因为它只需要 SCL(时钟线)和 SDA(数据线)两根线就能完成通信。总线上的设备通过不同的地址来区分,主机发起通信,从机响应请求,同一时刻只能有一个设备占用总线。

I2C 最核心的特点有两个。第一个是半双工,数据只能在 SCL 低电平期间进行变化,在 SCL 高电平期间必须是稳定的,这样才能保证采样不出错。第二个是开漏输出,也就是说总线上的设备不能主动输出高电平,只能通过释放总线让上拉电阻把电平拉高。这两个特性决定了 I2C 的时序容限并不像 SPI 那么宽松,对信号质量的要求更高。

在驱动层面,I2C 通信主要由几个基本动作组成:起始条件、停止条件、发送字节、接收字节、应答位。主机在 SCL 为高电平时拉低 SDA 产生起始信号,在 SCL 为高电平时释放 SDA 产生停止信号。每个字节传输 8 位数据,高位在前,第 9 个时钟周期用于应答。从机应答时拉低 SDA,主机作为接收方时也可以发送 NACK 来表示不再接收数据。

// I2C 起始条件模拟(软件方式) void i2c_start(void) { SDA_HIGH(); SCL_HIGH(); I2C_DELAY(); SDA_LOW(); I2C_DELAY(); SCL_LOW(); I2C_DELAY(); }
1.2 为什么 I2C 驱动的坑比 SPI 多

I2C 的坑比 SPI 多,我认为主要原因有三个。

第一,I2C 是一个协议栈很重的协议。它不像 SPI 那样简单粗暴,主机把时钟拉起来,数据就按位收发。I2C 有起始条件、停止条件、重复起始、应答位、NACK 处理、仲裁机制、时钟同步等等,这些逻辑如果全部用 GPIO 模拟,任何一个环节的时序出错,整个通信就会卡死。

第二,I2C 总线上挂了多个设备。在实际的板卡设计中,一个 I2C 总线上往往挂着好几个设备,比如 EEPROM 挂在地址 0x50,传感器挂在 0x38,OLED 挂在 0x3C。一旦某个设备的地址冲突或者总线被某个设备拉死,整条总线上的所有设备都会瘫痪。这就导致排查问题时,不仅要查驱动代码,还要查硬件连接、设备地址、上拉电阻,变量非常多。

第三,I2C 的时序容限比 SPI 严格。标准模式下 SCL 频率是 100kHz,快速模式是 400kHz,高速模式是 3.4MHz,但绝大多数 MCU 都只支持前两种。GPIO 模拟的时候,如果中断嵌套比较深,SCL 高低电平的持续时间就容易抖动,一旦超过从机手册里的时序要求,通信就会失败。

提示:SPI 的时序容限更高,因为它是同步协议,对时钟周期的绝对时间不那么敏感,而 I2C 对电平持续时间有明确要求,这也是为什么软件模拟 I2C 的稳定性往往不如 SPI 模拟。

2. 硬件 I2C:外设一时爽,调试火葬场?

2.1 硬件 I2C 的工作机制

硬件 I2C 指的是 MCU 内部集成的 I2C 外设模块。以 STM32 的 I2C 外设为例,它内部有独立的时钟发生器、移位寄存器、比较器和状态机。CPU 只需要配置好寄存器,写入要发送的数据或者使能接收,剩下的时序产生、应答检测、错误标志处理都由硬件自动完成。

STM32 的 I2C 外设支持主模式和从模式,可以配置为 100kHz 标准模式或 400kHz 快速模式。它有几个关键寄存器:控制寄存器 CR1/CR2、状态寄存器 SR1/SR2、数据寄存器 DR 和时钟配置寄存器 CCR。主要的操作用例是:主机发送起始条件,硬件自动产生起始时序,之后 CPU 把第一个字节写入 DR,硬件自动发送,等待应答,然后接着发下一个数据。

在较新的 STM32 系列(比如 F4、F7、H7)中,I2C 外设的功能更完善,支持 DMA 传输、多主机仲裁、SMBus 模式等。F1 系列的 I2C 则是出了名的难用,状态机的设计非常绕,导致很多老工程师宁可软件模拟也不用 STM32F1 的硬件 I2C。

2.2 硬件 I2C 的优势和隐忧

硬件 I2C 最大的优势是不占 CPU 资源。整个时序产生、应答检测、错误处理都由外设完成,CPU 只需要在中断里处理收发完成的事件即可。在高负载系统中,比如需要同时驱动传感器、显示屏幕和存储设备的场景下,硬件 I2C 能减少 CPU 的开销,提高系统的整体响应速度。

另一个优势是时序精度高。硬件外设通过内部的时钟分频产生准确的 SCL 频率,不受中断延迟、编译器优化等级的影响。特别是一些对时序敏感的传感器,比如某些数字麦克风或高精度 ADC,硬件 I2C 的稳定性明显优于软件模拟。

但硬件 I2C 也有让人头疼的地方。不同厂商、不同系列 MCU 的 I2C 外设设计差异很大,寄存器配置繁琐,状态机逻辑复杂。我见过不少工程师在移植 I2C 驱动时,在起始条件产生后卡在等待标志位的循环里,或者因为在中断里做了太多耗时操作导致总线卡死。硬件 I2C 一旦出错,调试手段往往受限,因为外设内部的状态我们无法直接看到,只能通过读状态寄存器来间接判断。

注意:STM32F1 系列的硬件 I2C 是出了名的难调。总线错误、仲裁丢失、AF 标志位等各种问题层出不穷,这并非代码写得不对,而是该外设本身的设计缺陷。如果芯片选型允许,建议优先选 F4 以上系列,或者直接评估软件 I2C 方案。

2.3 硬件 I2C 的常见坑点分析

硬件 I2C 的坑主要集中在以下几个方面。

寄存器配置错误是最常见的。I2C 的 CCR 寄存器需要根据总线的 APB 时钟频率来计算,很多人忽略了分频系数,导致 SCL 实际频率和预期严重偏离,甚至超出从机允许的最大频率。比如 STM32F4 的 APB1 时钟是 42MHz,如果标准模式想配出 100kHz,CCR 的计算公式是CCR = APB1_CLK / (2 * SCL_FREQ),算出来是 210,但很多人直接写 42,结果 SCL 变成了 500kHz,传感器直接罢工。

中断处理不当也是一个大问题。硬件 I2C 在发送每个字节后都会产生中断,如果中断服务函数里做了过长的处理(比如打印日志、延时等待),就会拖慢总线节奏,导致从机超时。正确做法是在中断里只做必要的字节搬运和状态切换,其他操作放到主循环里。

总线错误恢复往往被忽视。硬件 I2C 在检测到总线错误后会卡在错误状态,如果不做处理,后续通信永远无法恢复。我见过在调试现场,硬件 I2C 一卡死就只能复位 MCU,这种体验非常糟糕。

常见问题典型表现排查方向
寄存器配置错误SCL 频率异常,通信不稳定检查 CCR、TRISE 寄存器计算
中断处理耗时数据传输卡顿、超时精简中断服务函数
总线错误未处理通信持续失败,只能复位增加错误恢复逻辑
时钟延展未处理低速从机无法响应开启时钟延展等待机制

3. 软件 I2C:万物皆可 GPIO,又爱又恨的自由与麻烦

3.1 软件 I2C 的实现原理

软件 I2C 就是用两个 GPIO 引脚分别模拟 SCL 和 SDA 的功能,通过代码控制引脚的电平变化,按 I2C 协议的时序要求产生起始、停止、数据、应答信号。这种方式不依赖任何外设,只要有 GPIO 就能实现 I2C 通信。

核心代码量不大,一般 200 到 300 行就能写完一个完整的主模式软件 I2C 驱动。基本结构包括:初始化引脚、延时函数、起始条件、停止条件、发送字节、接收字节、发送应答、接收应答。

// 软件 I2C 发送一个字节 void i2c_write_byte(uint8_t data) { for (int i = 0; i < 8; i++) { if (data & 0x80) { SDA_HIGH(); } else { SDA_LOW(); } data <<= 1; I2C_DELAY(); SCL_HIGH(); I2C_DELAY(); SCL_LOW(); I2C_DELAY(); } }
3.2 软件 I2C 的优势

软件 I2C 最大的优势是移植性好。只要是带 GPIO 的单片机,都能跑软件 I2C,不受外设差异限制。项目里换了一颗完全不熟悉的 MCU,软件 I2C 代码改几个引脚宏定义就能跑,而硬件 I2C 需要重新研究几十页寄存器手册。

第二个优势是灵活性强。软件 I2C 任选引脚,硬件 I2C 只能使用固定的引脚组。在 PCB 布局紧张的板子上,软件 I2C 可以走任意空闲引脚,方便布线。另外如果想要在运行时动态切换引脚,软件 I2C 也是唯一的办法。

第三个优势是可控性强。每一个电平变化都是代码控制的,我们可以精确测量时序,也可以在任何步骤插入调试打印。出问题的时候,软件 I2C 能更直观地定位是哪一步卡住,或者哪个引脚的波形异常。

3.3 软件 I2C 的代价与坑点

软件 I2C 的代价主要在于时序精度和 CPU 占用。

如果没有使用硬件定时器辅助,软件 I2C 的延时通常由空循环实现,延时长短跟编译优化等级、CPU 主频都有关系。代码一优化、频率一变,原本正常的时序就可能出问题。另外如果有中断抢占 CPU,SCL 高电平和低电平的持续时间就会不对称,严重时直接引发通信故障。

CPU 占用也是一个现实问题。一个字节需要 9 个时钟周期(8 位数据 + 1 位应答),标准的软件 I2C 发送一个字节需要几十次 GPIO 翻转和循环延时。在低主频 MCU 上跑 400kHz 的软件 I2C,CPU 占用率会非常高,导致其他任务无法及时响应。

注意:软件 I2C 的可靠性完全取决于延时函数的稳定性。为了避免时序抖动,建议使用 DWT 时钟周期计数器或者 SysTick 来实现精确延时,而不是单纯依赖空循环。特别是在有操作系统(RTOS)的环境下,必须关中断或者用硬件定时器保证时序连续性。

3.4 软件 I2C 的适用场景

软件 I2C 适合用在哪些场景呢?我总结了几种情况。

第一,项目中使用不熟悉的 MCU。时间紧迫,不想花太多时间研究硬件 I2C 寄存器,那就先用软件 I2C 把功能调通,后续有精力再优化。

第二,引脚资源紧张。硬件 I2C 引脚不够用,软件 I2C 可以灵活选择。

第三,频率要求不高。设备工作在 100kHz 标准模式即可,软件 I2C 完全能胜任。

第四,调试排查问题。怀疑硬件 I2C 外设异常时,用软件 I2C 验证传感器好坏,快速隔离问题范围。

4. 实测对比:同一颗 EEPROM,两种实现的表现差异

4.1 测试环境说明

为了更直观地比较硬件 I2C 和软件 I2C 的差异,我找了一块熟悉的测试板来跑实验。主控用的是 STM32F103C8T6,板子上有一颗 AT24C02 EEPROM(地址 0x50)和一个 SSD1306 OLED(地址 0x3C)。OLED 的像素数据通过 I2C 传输,每帧约 1KB 数据,正好可以用来观察高负载情况下的表现。

AT24C02 本身支持 400kHz 写操作,但为了保证兼容性,我把两个场景都维持在 100kHz 标准模式。硬件 I2C 通过外设配置,软件 I2C 通过 GPIO 模拟。测试函数分为几类:单字节写入、页写入、随机读取、连续读取屏幕数据。

// 测试:从指定地址读取长度数据 void eeprom_read_test(uint16_t addr, uint8_t *buf, uint16_t len) { i2c_start(); i2c_write_byte(0xA0); // 设备地址+写 i2c_wait_ack(); i2c_write_byte(addr >> 8); // 地址高字节 i2c_wait_ack(); i2c_write_byte(addr & 0xFF); // 地址低字节 i2c_wait_ack(); i2c_start(); // 重复起始 i2c_write_byte(0xA1); // 设备地址+读 i2c_wait_ack(); for (uint16_t i = 0; i < len - 1; i++) { buf[i] = i2c_read_byte(); i2c_send_ack(); } buf[len - 1] = i2c_read_byte(); i2c_send_nack(); i2c_stop(); }
4.2 硬件 I2C 的实际表现

STM32F103 的硬件 I2C 果然名不虚传,这是整个测试里最折磨人的部分。我按照参考手册配置了寄存器,启动后读取状态寄存器,发现一直卡在 EV5 事件等待上。检查发现是配置时序时遗漏了 TRISE 寄存器。在标准模式下,TRISE 应该设置为APB1_CLK / 1000000 + 1,也就是 43,而我只配置了 CCR,忘了 TRISE。

调整好 TRISE 后,硬件 I2C 能正常通信了,但跑 OLED 刷新时出现了一个新问题。每次连续传输多行数据后,总线会产生一个 NACK 异常,然后状态机卡死。排查后发现,问题出在中断服务函数里调用了OLED_Update函数,这个函数内部有延时操作,导致 I2C 中断被长时间阻塞,总线数据被拖断。

优化办法很简单:把耗时操作移出中断,用标志位延迟到主循环处理。改完之后,AT24C02 和 OLED 的通信都稳定了,实测下来硬件 I2C 一旦跑顺了,传输效率确实高,读 1KB 数据,CPU 只占用了不到 10%。

4.3 软件 I2C 的实际表现

软件 I2C 的代码是从我的常用驱动库里直接移植的,底层延时用的是 SysTick 实现的毫秒级延时,级别比较粗。初版测试就出了问题。执行页写入时,第二个字节开始就出现 NACK,波形分析发现 SCL 频率严重超了,原因是 SysTick 的延时单位太大,在 72MHz 主频下,一个延时周期耗时远超一个 bit 的时序要求。

我换成了 DWT 周期计数器来实现微秒级延时,把每个电平阶段的延时控制在 5 微秒左右,此时 SCL 频率约为 100kHz。调换后,软件 I2C 读写 AT24C02 都正常了,连续读 1KB 数据也能跑通,CPU 占用率大概在 45% 左右。

为了模拟真实的干扰场景,我开了一个 1ms 周期的定时器中断。软件 I2C 在传输过程中频繁被打断,读 EEPROM 偶尔会出现数据错位的情况。原因很直白:中断在 I2C 传输的时钟周期里插入,改变了电平持续时间,导致从机采样到异常的数据。

对比维度硬件 I2C软件 I2C
时序精度高,外设自动产生依赖延时函数,受中断影响
CPU 占用低,外设处理时序高,CPU 全程参与
移植难度高,需要读寄存器手册低,改引脚宏定义即可
调试效率状态机复杂,不易观察每个步骤可控,便于定位
适用芯片必须支持 I2C 外设任何带 GPIO 的 MCU
4.4 测试中的关键发现

对比两组测试数据,能得出几个有价值的结论。

第一,硬件 I2C 在稳定运行后确实优秀,但从零到稳定运行的过程比较痛苦。寄存器配置、状态机处理、中断设计,每一步都有坑。到了 STM32F4 以上的系列,外设设计得好很多,但这个规律依然存在。

第二,软件 I2C 在小数据量、低频场景下完全可以替代硬件 I2C,但一旦中断变多、频率变高,稳定性明显下滑。如果项目周期短,又不需要高频率传输,软件 I2C 是性价比很高的选择。

第三,无论是硬件还是软件 I2C,前提条件都是对协议本身足够理解。不熟悉 I2C 的时序要求,用哪种方案都会踩坑。

经验分享:做 I2C 调试时,强烈建议手边备一台便宜的逻辑分析仪。硬件 I2C 出问题时,用逻辑分析仪抓波形,一眼就能看出起始条件是否正确、应答位是否正常、SCL 频率是否超标,比反复读状态寄存器高效得多。

5. 驱动设计中的 I2C 选型决策框架

5.1 什么时候无脑选硬件 I2C

我当时做项目选型时,会看几个关键指标。

如果系统中有高吞吐的 I2C 设备,比如大容量的 EEPROM、高分辨率 ADC、图像传感器,这类设备往往需要连续读取大量数据,对传输效率和 CPU 释放有明确要求,硬件 I2C 是合适的选择。

如果芯片是STM32F4 以上的系列,I2C 外设较为成熟,不容易出现 F1 那种设计缺陷,也优先考虑硬件 I2C。

如果项目中有多主机或者快速模式(400kHz 以上)需求,软件 I2C 基本无法胜任,只能靠硬件外设。

5.2 什么时候无脑选软件 I2C

反过来,如果代码主要运行在以下场景,建议直接用软件 I2C。

芯片不熟悉、厂商没有提供成熟的库,硬件 I2C 需要大量调试时间,软件 I2C 反而更快。

PCB 布线后 I2C 引脚被复用,或者需要灵活选择引脚,这时候硬件 I2C 只能眼巴巴地看着别的引脚。

系统是电池供电,对功耗敏感,软件 I2C 的传输过程经常可以进入低功耗模式,而硬件 I2C 可能会不断产生事件。

5.3 关于混合方案

还有一种实用的做法是混合方案。在不开 RTOS 的裸机环境里,用软件 I2C 做低频的传感器采集,用硬件 I2C 做高频的显示刷新。我之前做过一个温湿度监控项目,温度传感器用软件 I2C 每小时采一次,OLED 用硬件 I2C 刷新,两者互不干扰,效果很好。

6. I2C 驱动的调试工具与问题排查实录

6.1 必备调试工具

我常用的调试工具有几个,按优先级排列:逻辑分析仪、示波器、I2C 总线工具。

逻辑分析仪是最常用的,采样率不用太高,24MHz 就够抓 400kHz 的 I2C 信号。它能实时解码 SCL 和 SDA 的时序,把起始位、地址、数据、应答位全部显示出来,非常直观。示波器则用来测量信号电平、上升沿时间和噪声干扰,排查硬件层面的问题。

I2C 总线工具比如 USB-I2C 适配器,用来单独验证从机设备是否正常,可以快速确认是主机驱动有问题还是从机本身有问题。

注意:有的工程师喜欢把 SDA 和 SCL 都接到示波器的两个通道上,然后用触发模式抓起始条件。这个方法可行,但效率远不如逻辑分析仪。逻辑分析仪只需要设置好协议解析,自动解码,省时省力。

6.2 排查问题的一般步骤

遇到 I2C 通信故障,我的排查顺序基本是固定的。

先看硬件连接,确认 SCL、SDA 是否接反、上拉电阻是否缺失或阻值过大。再测信号波形,看起始条件和数据是否正常。然后查设备地址,可用 I2C 扫描工具看看总线上有哪些设备能响应。最后再看驱动代码,检查寄存器配置、时序参数和中断处理。

如果设备地址扫描正常,但通信还是有问题,基本可以确定问题出在驱动代码层面。

6.3 常见问题速查表
故障现象可能原因解决方法
SCL 无波形上拉电阻缺失、引脚配置错误检查硬件连接,配置为开漏输出
SDA 被拉低总线死锁、从机故障排查从机上电状态,尝试复位
起始条件后无应答设备地址错误、从机未上电用扫描工具确认地址
SCL 频率超标寄存器配置错误、延时太短重新计算 CCR/延时参数
数据随机错位中断干扰、时序抖动精简中断处理,增加延时补偿
NACK 频繁设备忙、总线负载过高等待从机就绪,降低速率
一卡死就要复位缺少错误恢复机制设计总线释放和重新初始化逻辑
6.4 独家避坑经验

调 I2C 的过程中,我积累了一些比较实用的经验。

片选引脚和复位引脚别忽略。有些传感器有复位引脚和片选引脚,比如 EEPROM 的 WP 引脚和传感器的 RESET 引脚。如果这些引脚没有正确配置,可能会出现间歇性故障,排查起来非常痛苦。

开漏输出和推挽输出不能搞混。I2C 引脚必须配置为开漏输出,这样才能通过上拉电阻产生高电平,实现线与逻辑。如果用推挽输出,两个设备同时输出不同电平时会直接短路,轻则数据错乱,重则烧毁芯片。

写 EEPROM 需要等待应答。这是新手最常忽略的点。EEPROM 在做页写入之后,内部需要一段时间来完成擦写操作,这段时间内不响应任何命令。如果驱动代码没有等待从机就绪的机制,连续写入一定会失败。

经验分享:AT24C02 之类的 EEPROM,在页写入后需要 5ms 左右的内部写周期。在驱动里需要加一个写轮询,即重复发送设备地址,直到从机返回 ACK 才算写完。很多人的 I2C 写失败就栽在这个细节上。

7. 不同 MCU 平台下的 I2C 实操差异

7.1 STM32 系列

STM32F1 的硬件 I2C 是很多人的噩梦,我建议新手直接绕开,用软件 I2C 过渡。F4 系列的外设完善了很多,支持 DMA,效率高。但中断处理仍然要注意,不能让中断函数里跑耗时操作。H7 系列的内核主频很高,硬件 I2C 和 DMA 搭配起来非常流畅,前提是把中断优先级分配好。

7.2 ESP32 系列

ESP32 的硬件 I2C 可以用,但在我看来用到它最大的优势还是软件模拟。ESP32 主频高,GPIO 翻转速度快,软件 I2C 跑 400kHz 也没太大压力。而且在 Arduino 生态里,Wire 库封装得不错,直接用很方便。如果做低功耗项目,注意 I2C 总线在睡眠前要释放,否则唤醒后通信容易异常。

7.3 Linux 平台(嵌入式 Linux / 单片机 SoC)

在 Linux 平台做 I2C 驱动,情况又不一样。Linux 内核提供了标准的 I2C 子系统,设备树里描述好 I2C 控制器和从设备地址,内核就会自动匹配驱动。应用层可以通过/dev/i2c-N设备节点,使用ioctl的I2C_RDWR命令和从设备通信。

// Linux 用户空间 I2C 读写示例 #include <linux/i2c-dev.h> #include <fcntl.h> #include <sys/ioctl.h> int fd = open("/dev/i2c-1", O_RDWR); if (fd < 0) return -1; ioctl(fd, I2C_SLAVE, 0x50); uint8_t buf[] = {0x00, 0x55}; write(fd, buf, 2);

Linux 里 I2C 的问题通常和设备树配置有关。最常见的是地址写错、触发模式配置不对、中断号冲突。还有一种情况是在设备树里把某个引脚配置成了普通 GPIO,导致 I2C 控制器无法正常工作。

7.4 国产 MCU 平台

国产 MCU(比如 GD32、AT32、CH32)的 I2C 硬件外设,很多是基于 ARM 标准 IP 魔改的,兼容性和稳定性参差不齐。在国产平台做 I2C 驱动,我建议先参考厂商提供的驱动库,再结合逻辑分析仪验证,不要轻易假设外设行为和 STM32 完全一样。

比如有些国产 MCU 的 I2C 外设在时钟延展的处理上不够完善,遇到低速从机容易死锁,这个时候软件 I2C 反而更稳妥。

8. 我个人的最终结论与建议

做了一堆测试和对比,我的结论其实挺明确的。

如果你用的是 STM32F4 以上的主流芯片,I2C 外设可靠,优先用硬件 I2C,配合 DMA 和中断,效率和稳定性都很好。如果你用的是老芯片或者不熟悉的国产芯片,建议先用软件 I2C 把功能跑通,再评估是否需要切换。

如果你的系统里有敏感的时序要求,比如音频采集、高速显示刷新,硬件 I2C 更适合。如果只是低频的传感器采集、数据记录,软件 I2C 完全可以胜任。

最关键的还是那句老话:I2C 协议本身并不复杂,复杂的是各种错误处理、时序细节和硬件差异。不管是硬件 I2C 还是软件 I2C,只要把协议读透,把时序调好,把错误处理做好,都不会太坑。

最后再分享一个心得:I2C 驱动调试最忌讳的是一头扎进代码里反复改参数。正确的路径是先看波形,再改代码。波形正确,就说明驱动方向是对的;波形不对,改再多参数也没用。用逻辑分析仪抓一次波形,往往比盲改一个小时代码更有价值。