☰
硬件I2C还是软件I2C?嵌入式驱动开发实战对比与避坑指南
2026/10/26 1:01:35 网站建设 项目流程

搞嵌入式的老哥应该都有过这种体验:手头项目里挂了个传感器或者 EEPROM,量不大,频率也不高,但偏偏在调 I2C 的时候卡了一下午甚至一整天。问题不出在“这个从机地址是多少”“读写时序对不对”这种入门问题上,而是出在一个更基础、更让人头疼的岔路口——到底是直接调芯片自带的硬件 I2C 外设,还是用两个 GPIO 手搓一个软件 I2C?

这个选择题,几乎每一个写驱动的人都会遇到。我调过 STM32、GD32、ESP32 上的硬件 I2C,也在 51、合泰、甚至某些国产 M0 核上用 GPIO 模拟过 I2C,两边踩过的坑都不少。网上关于“硬件 I2C 和软件 I2C 哪个好”的讨论很多,但真正把两种方案的坑、原理和适用场景讲透的很少。这篇文章我就结合自己的实际驱动调试经历,把硬件 I2C 和软件 I2C 从头到尾掰开揉碎聊一遍,包括它们各自容易翻车的地方、实测对比数据,以及我现在的选型习惯。不管你是刚入门写驱动的新人,还是被 I2C 折腾过的老油条,这篇应该都能让你少走点弯路。

1. 先从 I2C 这摊子事说起

1.1 硬件 I2C 到底是个什么东西

硬件 I2C,说的是芯片内部集成了 I2C 控制器外设。这个外设本身就是一个独立的状态机,负责产生 SCL 时钟、控制 SDA 数据输入输出、检测起始/停止条件、对比从机地址、处理 ACK/NACK 应答。CPU 只需要往寄存器里写要发送的数据、配置好控制位,剩下的大部分时序细节都由这个外设自己完成。以 STM32 为例,你配置好 I2C_CR1、I2C_CR2 这些寄存器之后,往 I2C_DR 里丢一个字节,然后等事件标志或者中断,硬件就会自动把字节按位送到 SDA 线上,同时翻转 SCL 产生时钟。读数据也一样,硬件自动做时钟同步和应答。

很多人对硬件 I2C 的理解就停留在“芯片自带,比软件模拟高级”这个层面,其实它内部远比你想的复杂。一个完整的 I2C 控制器要处理从机地址匹配、10 位地址扩展、时钟延长(clock stretching)、多主机仲裁、SMBus 总线超时检测等一堆功能。像 STM32 的硬件 I2C,如果外设被配置为主机模式,收到 NACK 之后状态机会进入特定状态,你必须先做“软件复位”或者重新初始化才能恢复通信。

硬件 I2C 最大的优势是 CPU 介入少。发送一串读操作的时候,CPU 只需要配置好寄存器、触发 start 条件、然后等着接收完成标志,中间大量的比特级时序操作完全由外设担任。这在高速率传输或者系统负载比较重的时候特别有用。带了 DMA 之后,数据搬运也不占 CPU,做大批量读取(比如从 Flash 芯片里搬固件、从 SD 卡里读扇区)会有明显性能优势。

但硬件 I2C 也有它天然的尴尬——它的状态机是芯片厂商设计死的,一旦你在极端情况下把它搞进了一个莫名其妙的状态,比如总线上一瞬间的毛刺把状态机打乱了,它可能就一直忙等在那,你再写寄存器它都不搭理你。这种“外设锁死”的案例,在 STM32 上尤其臭名昭著。后面专门有一节讲这个。

1.2 软件 I2C 又是什么路子

软件 I2C,也叫“位翻转 I2C”、“GPIO 模拟 I2C”或者 “bit-banging I2C”。说白了就是我不用芯片自带的 I2C 外设,而是用两个普通的 GPIO 引脚,一个模拟 SCL,一个模拟 SDA,然后写的每一行代码负责一个比特级别的动作:SCL 拉高、SDA 拉低、SCL 拉低、读 SDA 电平……整个 I2C 协议里的起始条件、停止条件、数据位、应答位,全部靠延时加 GPIO 翻转来“手动”实现。

举个例子,你要发送一个起始条件,软件 I2C 做的就是先把 SDA 拉低,再把 SCL 拉低;而发送停止条件则是先把 SCL 拉高,再把 SDA 拉高。就这么简单,也这么繁琐。你要发送一个字节 0x5A(二进制 01011010),程序就要按照时钟节拍,把每一位依次写到 SDA 上,每一位之间必须保证 SCL 高低电平的时序宽度达标——通常你会在关键位置插几个 NOP 或者调用一个 delay_us 函数来保证时序。

软件 I2C 最大的优点是“不受芯片外设限制”。你想用哪两个引脚就用哪两个引脚,只要 GPIO 是普通的推挽或者开漏就行。芯片没有 I2C 外设也能写,外设坏了也能写,甚至同一个项目里你可以一口气模拟出三路四路 I2C,这在要做多路传感器采集的小板子上非常灵活。另外软件 I2C 的逻辑是完全自己掌握的,出了什么问题你可以一步步打断点调试,不会像硬件外设那样黑盒出问题找不到原因。

但软件 I2C 的代价也很大。因为所有时序都是 CPU 一条条指令跑出来的,它要占 CPU。在 100kHz 的标准模式下一秒钟要翻转 10 万次 SCL,而在每一个 SCL 周期里还得分出多个步骤来处理数据和状态,CPU 的空闲时间就被耗掉一大块。更麻烦的是它在高优先级中断下会不稳定,你用软件 I2C 干到一半,定时器中断一进来,时序就被拉长,从机那边可能就超时了。

2. 硬件 I2C 的坑,我替你踩过了

2.1 最“著名”的锁死问题:外设忙标志卡死

硬件 I2C 翻车排行榜第一名,绝对是“外设忙标志卡死”这一个。这个坑在 STM32 标准外设库时代就非常出名,官方社区里几十年了还在讨论。现象很典型:I2C 跑着跑着,或者刚上电还没开始通信的时候,你去读 I2C_SR2 寄存器,发现 BUSY 位(忙标志)已经被置 1 了,但总线上实际上什么活动都没有。接下来你写起始条件也好、写数据也好,问题都得不到响应,就像外设被按了暂停键。

为什么会这样?硬件 I2C 控制器内部有一种总线状态检测逻辑,它在 SCL/SDA 电平变化中寻找起始条件和停止条件来判定总线状态。如果初始化时序不对,或者总线上有毛刺干扰,状态机就可能把 SDA 的变化误判成了起始条件,从而认为总线被占用。芯片在上电瞬间,如果 GPIO 还没配置好,引脚处于浮空状态,SDA 和 SCL 上可能来回抖动产生伪起始信号,外设就这样“自锁”进去了。

我处理过最典型的一次:一块 STM32F103 板子,I2C1 接了 EEPROM 和温湿度传感器,上电后第一次读总是超时。我最后排查下来,发现是代码里初始化顺序有 bug——先把 I2C 外设使能了,然后才去配 GPIO 复用功能和上拉电阻。在使能和 GPIO 配置之间的那几百微秒里,引脚电平不受控,硬件外设误判总线忙,之后就一直忙不下来。解决办法就是严格按照“先配 GPIO,再使能外设”的顺序初始化,并且在上电后加一段固定延时的等待总线稳定。

这个坑催生了网上大量的“硬件 I2C 不好用”的论断,其实一半是芯片状态机的设计问题,另一半是工程师初始化顺序的问题。如果你不小心也卡在 BUSY 位上,常见的抢救办法是:软件复位 I2C 外设、切换 I2C 的开关位、或者干脆直接复位整个芯片。在 STM32 上,HAL 库的__HAL_I2C_ENABLE()配合__HAL_I2C_INIT()通常能救回来,但如果 BUSY 是引脚电平错误导致的,光复位外设没用,你得先把 GPIO 的电平正确地恢复成空闲状态。

2.2 中断优先级和抢占带来的“看不见”的故障

硬件 I2C 用起来省事,但一旦你依赖它的中断事件来驱动流程,就打开了另一扇潘多拉的盒子。I2C 外设的事件中断、错误中断、DMA 传输完成中断,每一个都有严格的时序要求。主模式下的地址发送阶段、数据传输阶段的标志位,必须在特定的时序窗口内清除,否则状态机就卡住。

拿 STM32 的 I2C 来说,主模式发送地址之后,会触发 EV5(SB 标志)事件。如果你在中断服务程序里处理了这个事件,但在传输下一个字节之前又被另一个高优先级中断抢占,返回后发现 I2C 状态已经变了,事件的顺序就乱了。我当时一个项目里,系统滴答定时器中断优先级设得比 I2C 中断还高,结果 I2C 每次连续读数据都偶尔丢字节。查来查去,最后发现是滴答中断在 I2C 传输的中间抢占了 CPU,硬件外设虽然在跑,但 CPU 没有及时清标志、喂下一个数据,从机就收到了断掉的时钟序列。

这种问题在逻辑分析仪上很难一眼看出来,因为它不是每次都发生,而是高负载时才偶发。我后来养成的习惯是:凡是用硬件 I2C 且开了中断,就把 I2C 中断优先级提到足够高,并且中断服务程序里只做“清标志 + 状态搬运”,绝不做延时、打印这类操作。如果你用了 DMA,还要额外注意 DMA 通道的优先级和 FIFO 配置,否则数据错位都不知道错在哪。

2.3 引脚复用、上拉电阻和电平转换那些事

硬件 I2C 的引脚是复用的,你必须查数据手册找到特定的 AF 映射。这个步骤本身不难,但很多人会掉进“看起来是 I2C 引脚,但是我怎么拉都不想”的坑。常见情况是 GPIO 忘了配置成开漏模式,配置成了推挽输出。I2C 协议要求 SDA/SCL 是开漏结构,配上外部上拉电阻才能实现线与功能。如果你用推挽,不仅会影响多主机仲裁,还可能和从机的开漏输出打架,导致数据读不出来。

再说上拉电阻。I2C 的上拉电阻阻值不是随便选的,它和总线上的电容、通信速率直接相关。标准模式 100kHz 下,总线上拉电阻常用 4.7kΩ~10kΩ;快速模式 400kHz 下,推荐值是 1kΩ~4.7kΩ;到了 1MHz 的快速模式以上,就得用更小的上拉,比如 330Ω~1kΩ。电阻选大了,信号上升沿太缓,可能不满足从机的时序要求;选小了,功耗增加,在低功耗设备上可能造成不必要的漏电流。我做过一个测量板,把 4.7kΩ 上拉换到 2.2kΩ 后,400kHz 下通信稳定性明显变好,原因是总线电容较大导致上升沿太慢。

还有一个两难问题:3.3V 的 MCU 和 5V 的传感器能不能直接挂一条 I2C 总线上?很多人想当然觉得“开漏+上拉到 3.3V”就行,但这取决于传感器是不是真正的开漏输出、它的输入阈值能不能被 3.3V 识别。很多 5V 器件的输入高电平阈值是 0.7 × VCC = 3.5V,你用 3.3V 上拉去喂它,它根本不知道你发的是 0 还是 1。这时候老老实实用电平转换芯片,例如 PCA9306 这类双通道 I2C 电平转换器,或者用 MOS 管搭一个低成本的转换电路,才是可靠的做法。

3. 软件 I2C 的坑,一个也不少

3.1 时序全靠“拖延艺术”,CPU 被焊死

软件 I2C 最大的代价是 CPU 占用。你可能觉得 100kHz 也不算高,算一下就知道:一个 SCL 周期是 10 微秒,一次完整字节发送需要 9 个时钟位(8 个数据位 + 1 个 ACK 位),主机操作加上 start/stop 条件,一个字节大概需要 100 微秒左右。如果你要读一个 256 字节的 EEPROM,光数据部分就要 25 毫秒,这个过程中 CPU 全程在 GPIO 翻转和延时里打转,几乎什么正事都干不了。

这还不是最恶心的。软件 I2C 的延时函数一旦被中断打断,时序就会被拉伸。比如你延时等待 SCL 高电平保持时间,高优先级中断进来跑 100 微秒再回来,SCL 高电平就被硬生生拉长了,从机不会因此报错(I2C 从机遵循时钟延长是可以容忍的),但你的读时序总时长就变得不可控。如果你在中断里又访问了同一个 I2C 从机,那问题就更大了,两个代码路径同时在操作 SDA,总线就废了。

因此,很多写软件 I2C 老手会在关键时序段里关中断。对 Cortex-M 内核来说,就是__disable_irq()/__enable_irq(),或者用PRIMASK暂时把可屏蔽中断都关掉。但是关中断本身就是一把双刃剑:你在 I2C 通信期间关中断,定时器中断、串口中断全部被挂起,系统的实时性大打折扣。如果你在关中断期间正好卡在 I2C 的某个等待循环里(比如从机拉低了 SCL 做时钟延长,迟迟不释放),那系统就被卡死了,这是软件 I2C 最危险的死锁场景。

3.2 GPIO 开到开漏,还是推挽?这是个问题

软件 I2C 用 GPIO 模拟,GPIO 的模式选择大有讲究。很多人图省事,把 SCL 和 SDA 都配置成推挽输出,这样输出电平强、翻转快,逻辑分析仪看着也漂亮。但这么做有个隐患:在读数据的时候,SDA 需要从输出方向切换成输入方向,用来读从机拉低的应答位和数据位。如果从机这时候刚好输出低电平,而主机还没来得及把 SDA 方向切回输入,两个输出就打架了。推挽下的高强度电平冲突可能会烧引脚,也可能只是导致读到的电平异常。

正确姿势是:把 SDA 配置成开漏输出,外部加上拉电阻。要读数据的时候,直接把 SDA 对应的 GPIO 输出寄存器写 1,由于开漏结构下写 1 就是把输出管断开,引脚电平由上拉电阻决定,这时候你把方向寄存器切到输入,读到的就是从机驱动后的真实电平。SCL 一般保持推挽输出就好,因为时钟永远由主机控制,但如果你要接多主机系统,SCL 理论上也应该开漏,方便其他主机做时钟同步。

软件 I2C 还有一个硬件 I2C 很少被注意的问题:GPIO 翻转速度不够。很多芯片的 GPIO 输出速率是可配置的,比如 STM32 的 GPIO 有 2MHz / 10MHz / 50MHz 三档。你在软件 I2C 中把 GPIO 配在最低档,输出信号的上升沿会非常缓,在长总线上很容易被从机误判。我有一回就是 GPIO 输出速率忘了改,导致 400kHz 模式下一个数据位都读不对,最后把 GPIO 速率提到最高档,问题消失。如果你在 STM32 上用软件模拟 I2C,建议把 GPIO 配置成开漏输出、输出速度选最高档,外部上拉 4.7kΩ,这样能获得最接近硬件 I2C 的波形质量。

3.3 从机不认账:ACK 和地址位的细节魔鬼

软件 I2C 没有硬件外设帮你做地址匹配和应答检测,所有细节都得自己算好。最常见的翻车点之一是:读操作时,从机地址最低位要置 1,写操作置 0。你在纸上写地址的时候很容易搞混,比如 EEPROM 的地址 0xA0 是写地址,读地址就是 0xA1。代码里如果你拿着设备地址 0xA0 去做读操作,从机根本不会 ACK,你还在那等它的响应,自然超时。

再一个坑是 ACK 检测的时机。软件 I2C 发送完第 8 个数据位之后,必须释放 SDA(也就是把 SDA 方向切到输入,或者写 1),然后在第 9 个时钟的高电平期间采样 SDA 电平。采样点必须放在 SCL 高电平的中点附近,不能一上来就采。因为 SDA 的建立时间(setup time)要求数据在 SCL 上升沿之前就已经稳定,而从机的 ACK 信号也是要在 SCL 高电平期间才有效。要是你的采样太靠前,可能读到的是还没稳定的电平,把 ACK 误判成 NACK;采样太靠后,又可能读到下一个数据的起始翻转。我在软件 I2C 里通常是在拉高 SCL 之后延时 3~5 微秒再读 SDA,这个窗口对大多数 400kHz 以内的从机都够用。

此外,从机有时候不按套路出牌。有些从机在收到地址后需要“思考时间”,这个思考时间可能长达几百微秒,它会把 SCL 拉低,来实现时钟延长。软件 I2C 如果傻等 SCL 被释放,可能会卡死。更聪明的做法是加一个超时退出机制:如果等待释放超过 N 毫秒,就主动放弃本次传输,把总线恢复到空闲状态。我用一个 10ms 软件定时检查来当“看门狗”,避免因为个别从机异常把整個系统拖死。

4. 实测对比:同一块板子,两条路线的差异到底有多大

4.1 测试环境与参数配比

光说不练假把式。为了直接对比硬件 I2C 和软件 I2C 的差异,我专门找了一块 STM32F103C8T6 的板子,挂了一颗 AT24C02 的 I2C EEPROM。我分别写了两个驱动:一个用芯片的硬件 I2C1 外设 + HAL 库阻塞方式读取,另一个用两个普通 GPIO(PB6/PB7,注意正好是 I2C1 默认复用脚)做软件模拟读取。两者都跑在 400kHz 目标速率下,外部上拉都用 4.7kΩ,供电都是 3.3V。

需要说明的是,软件 I2C 的 400kHz 不是外界给它的,是我用示波器反复微调延时参数调出来的。我在 SCL 高电平和低电平之间各插了 1.25 微秒左右的延时,加上 GPIO 翻转本身的时间和指令执行时间,实测 SCL 频率大约在 380kHz~420kHz 之间波动。硬件 I2C 则通过 I2C_CCR 寄存器直接配置,打开高速模式并设置 9MHz 的 APB1 分频,SCL 输出稳定在 400kHz 附近,抖动极小。

测试项目分三块:连续单字节写读 1000 次看成功率,连续 8 字节页写再读 500 次看成功率,以及用逻辑分析仪统计单次完整读操作的时间开销和 CPU 忙碌时间。

4.2 数据结果:谁更稳、谁更累

先看成功率。硬件 I2C 在两种测试里表现都不错,1000 次单字节写读全部成功;软件 I2C 单字节写读在调整好参数后也能做到 999/1000 成功,但出现了一次超时。这个超时的根因是测试过程中我开了一个串口打印,打印的中断打断了 I2C 时序,导致一个 NACK 没有被正确识别。如果我在整个测试过程中关闭串口中断,软件 I2C 也能做到 1000/1000,但这个“开中断就不稳”的特性,本身就是它最大的潜在风险。

然后是时间开销。单次写一个字节再读一个字节的完整操作,硬件 I2C 大约耗时 620 微秒,其中 CPU 在等待状态时的空闲时间比较多,如果不开中断,CPU 还能干点别的;软件 I2C 同样操作耗时约 950 微秒,而且这 950 微秒里 CPU 几乎全程都在跑指令,极少有可抢占的间隙。换算下来,同样一个操作,软件 I2C 的 CPU 时间开销大约是硬件 I2C 的 3 倍以上。

逻辑分析仪抓到的波形也有一点差异。硬件 I2C 的 SCL 周期稳定,高低电平宽度对称;软件 I2C 因为指令执行顺序的问题,SCL 高电平通常比低电平宽一点点,而且不同字节之间的间隙比较大。这是因为软件代码在每个字节之间还有循环控制和变量处理开销,在波形上会体现为字节间空隙大约多出 5~10 微秒。

4.3 调试体验:黑盒和白盒的取舍

稳定性数据只是一方面,调试体验的差异更难量化,但在实际项目中同样致命。

硬件 I2C 的优势是“黑盒但规范”:只要按照手册配置好寄存器/库函数,它输出的波形在电气层面基本没什么可挑的。尤其在从机是正规厂商、时序要求严格的情况下,硬件 I2C 更容易一次通过。但黑盒也有黑盒的缺点,一旦它出了问题,比如 BUSY 标志锁死、NACK 处理逻辑理解错、中断事件顺序搞错,你能做的只有一遍遍读寄存器、看手册、翻勘误表,非常难受。

软件 I2C 的优势是“白盒且可控”:每一行代码每一个 GPIO 动作都是自己写的,你可以随意插打印、打断点,甚至用 GPIO 翻转来标记程序运行位置,逻辑分析仪一目了然。我曾经用软件 I2C 查一个从机行为异常的问题,直接把每个 I2C 步骤写成一个枚举变量并映射到一个调试 GPIO 上,调试效率高得惊人。但软件 I2C 也有个致命弱点:你写的代码和实际波形之间的关系,有时会因为编译器优化、中断延迟而变得不好预测。同一段代码,开 O2 优化和开 O0 优化,时序完全不一样。

5. 那到底怎么选?我的选型建议和战斗经验

5.1 先看场景,再站队

我觉得争论“硬件 I2C 和软件 I2C 谁更好”本身没有太大意义,它们不是对立关系,而是适用场景不同。关键看你的约束条件是什么。

我现在的选型思维大致是这样的。如果你的芯片有硬件 I2C,并且你有把握把外设调好,那优先用硬件 I2C。特别是在这些场景里优势明显:高频率通信(400kHz 以上)、大批量数据搬运(配合 DMA)、系统对 CPU 空闲率有严格要求、总线上挂了多个不同地址的从机。硬件外设帮你处理仲裁、时钟延长这些底层协议细节,你只要把应用层逻辑写好就行。

如果你的项目遇到这几种情况,软件 I2C 反而是更聪明的选择:第一,芯片硬件 I2C 外设不够用,或者引脚被其他功能占满了,你只能用普通 GPIO 扩展额外总线;第二,你手头的芯片很便宜,压根没有硬件 I2C(很多 8 位 OTTO 芯片就是这样);第三,你遇到了一批“脾气怪异”的从机,比如某些国产传感器时序不规范,部分对上升沿速度要求很苛刻,软件 I2C 可以灵活调整时序参数来适配它;第四,你在做硬件调试初期,想先把功能跑通,软件 I2C 改引脚、改时序都更方便,快速验证完再决定是否切回硬件外设。

另外说一个很多套件教程里不会提到的点:有些芯片的硬件 I2C 存在设计瑕疵,比如某些国产 M0 内核芯片,硬件 I2C 在 NACK 处理或者异常恢复方面做得不完善,反而不如软件 I2C 来得可靠。这时候别迷信“硬件肯定比软件好”,你就当它是个不可用外设,用软件 I2C 顶上,往往更踏实。

5.2 两种方案通用的避坑技巧

不管用哪种方案,有几个经验我觉得特别值得分享。

第一,初始化的时候先把 GPIO 配置成开漏加上拉,再初始化外设和总线状态。这个顺序治好了我无数的疑难杂症。上电瞬间 GPIO 是浮空的,若是先使能 I2C 外设再配 GPIO,电平噪声可能让硬件外设误判总线忙,也可能让从机自己进入错误状态。先让 GPIO 处于已知状态(开漏输出高电平),总线才算安全。

第二,通信前最好加一个“总线恢复”流程。我在软件 I2C 里会写一个函数:先把 SDA 和 SCL 分别拉高,产生 9 个 SCL 时钟,观察 SDA 在被拉高后能否被从机拉低。这个操作可以把很多被异常中断卡住的从机“复位”到空闲状态。对于硬件 I2C 来说,这个总线恢复过程通常由外设内部的初始化流程覆盖了,但如果你切换了从机设备,这条流程仍然有效。

第三,一定要用逻辑分析仪看波形,不要靠猜。现在几十块钱的逻辑分析仪已经很好用了,接上 SCL/SDA,用上位机软件解码,I2C 的地址、数据、ACK 一目了然。我见过太多同事拿着示波器看 I2C 波形,看得头大,其实逻辑分析仪才是 I2C 调试的正道,示波器用来测量边沿和时序才是它的强项。

第四,软件 I2C 的延时最好用 DWT 或者定时器来计时,别用 NOP 空转。NOP 在编译器优化下可能被吃掉,或者因为总线时钟频率不同在你换芯片之后直接超时。我后来习惯用 Cortex-M 内核的 DWT->CYCCNT 来精确延时,不管主频怎么配,都能算出准确的微秒数,代码可移植性也好很多。

5.3 如果一定要选一个“默认项”

如果让我给一个放之四海而皆准的默认答案,我会说:在大多数容量在 STM32F103 这一级别及以上的 MCU 上,优先用硬件 I2C。不是因为排除异常后就不存在坑,而是因为一旦你花时间把硬件 I2C 的特性摸透了,它带来的长期收益是软件 I2C 没法比的。尤其在项目里还有其他中断、DMA、低功耗需求的时候,硬件 I2C 释放出来的 CPU 余量,是你未来应对需求变更的底气。

但我也要说个例外:如果你刚开始接触嵌入式驱动,第一次调 I2C,那我反而建议你先用软件 I2C 把协议吃透。因为软件 I2C 让你亲手写出起始条件、停止条件、数据位、ACK 检测,你对 I2C 协议的理解会非常深刻。之后再用硬件 I2C,你看寄存器的每个细节就会觉得它们是那么自然,调试的时候也能更快理解外设为什么会有那些诡异行为。

最后再说一个小经验。我见过很多人在代码里同时预留了硬件 I2C 和软件 I2C 两套实现,用宏定义切换。这个做法我挺推荐,前提是你真的把两套都调好了。我自己的驱动框架里写过一个通用的 I2C 操作接口,底层可以挂接不同的实现,这样切换只需要改一个宏。前期调试用软件 I2C,确认从机没问题之后切硬件,再做一轮硬件对接验证。这个流程既保证了功能的可行性,又能最终获得高性能实现,非常稳。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询