☰
STM32F103 I2C实战:AT24C02读写全流程与调试要点
2026/10/6 7:21:23 网站建设 项目流程

先把结论放在前面:玩 STM32F103 的 I2C,我最推荐做的第一个实验就是接一块 AT24C02 EEPROM。这芯片不到一块钱、逻辑简单、引脚少,一头挂在 I2C 总线上,另一头就是一页 256 字节的存储空间,非常适合把“时序、地址、应答、读写数据”这几件事一次串起来。这篇文章就把我从硬件连接、协议时序、软件模拟 I2C、读写出错排查到逻辑分析仪实测的完整过程拆开讲,新手可以直接照着做,老手也可以看看我在踩坑之后的处理方式。

很多人一上来就搜“STM32F103 I2C 读写 AT24C02 代码”,复制一个I2C_EE_BufferWrite()函数跑通了就完事,但真到自己画板子、换芯片、调速度的时候,问题就全冒出来了:为什么有时候写完读出来全是 0xFF?为什么写一页超过 8 个字节就把前面的数据覆盖了?为什么硬件 I2C 有时会卡死?这些问题背后不是“代码运气好”,而是对协议和芯片行为理解不够。所以这篇文章不打算只贴代码,而是尽量把每一步为什么要这么做讲透。

1. 这块芯片为什么是 I2C 练手的理想对象:AT24C02 的硬件底细

1.1 256 字节不等于 Flash,数据手册里最该先看的三张表

AT24C02 是 Microchip 系列 EEPROM 里最典型的 2Kb 容量器件,2Kb 除以 8 就是 256 个字节,内部按 8 字节一页组织,总共 32 页。它的存储单元是 EEPROM 工艺,可以字节擦写,寿命标称 100 万次写入,数据保持 100 年。很多新手把它和 STM32F103 内部的 Flash 混为一谈,实际上差别很大:Flash 要按扇区擦,EEPROM 可以单个字节改写,虽然内部也是先擦后写的过程,但芯片自己处理了,你不需要关心。

和 I2C 读写直接相关的是三个参数:最大时钟频率、写周期时间、器件地址。标准型号支持 100kHz 和 400kHz 两种模式,也就是 I2C 协议里的 Standard Mode 和 Fast Mode。写周期时间tWR是 5ms,意思是每次发出“写指令+数据+STOP”之后,芯片内部擦写需要最长 5ms 完成,在此期间芯片不会响应任何新的 I2C 操作。所有“为什么写完立刻读不出来”的现象,根子都在这里。

器件地址一共 8 位,其中高 4 位固定为1010,接着是 A2、A1、A0 三个硬件引脚的电平组合,最低位是读/写控制位R/W。当 A2、A1、A0 都接地时,8 位写地址是0xA0,读地址是0xA1。我见过不少人在代码里写错成0x50,那是 7 位地址的表示法,这要等会专门说。

1.2 引脚与地址线:上电后第一件要确认的事

AT24C02 是 8 引脚小封装。A0、A1、A2 是地址输入,一般直接接 GND 就行;WP 是写保护引脚,接地才能正常写入,接高电平会禁止写操作;SCL 和 SDA 就是 I2C 总线;VCC 供电范围一般是 1.8V 到 5.5V,所以接在 3.3V 的 STM32F103 上完全没问题。

有个细节值得注意:如果 WP 引脚悬空,芯片内部有下拉,多数情况下默认允许写入,但我不建议赌这个默认。因为不同批次芯片的内部下拉未必可靠,遇到“写不进去但读正常”的故障,第一步就应该量 WP 电压,而不是盯着代码怀疑人生。我吃过这个亏,最后发现是排线插反导致 WP 被拉到 3.3V。

另外,I2C 总线上必须接上拉电阻,这跟很多芯片的“推挽输出”完全不同。STM32F103 的 GPIO 配置为开漏输出后,输出高电平是靠外部上拉电阻拉到 VCC 的,MCU 内部虽然也有可选的内部上拉,但强度不够,而且和外部上拉同时用容易造成电平不稳定。所以板上必须给 SCL 和 SDA 分别接一个上拉电阻,阻值后面会专门讲。

2. I2C 总线的物理层和时序基础:先搞懂“怎么说话”再谈代码

2.1 开漏与线与逻辑:为什么两个引脚就能挂一堆设备

I2C 只有两根线:SCL 时钟线、SDA 数据线。所有设备都通过开漏输出接到这两根线上,开漏的意思就是 MOS 管只负责把线拉低,不主动拉高,线的默认高电平由外部上拉电阻提供。于是总线上任何一个设备只要拉低,整条线就是低电平;只有当所有设备都不拉低时,线才是高电平。这叫作“线与”逻辑。

这个设计带来两个好处:一是设备之间可以直接通信,不需要额外的片选信号;二是能做到真正的多主机仲裁,两个主机同时发数据时,谁先拉低谁获胜,另一个会检测到自己发出的电平和总线不一致而退出。对读写 AT24C02 来说,我们只要知道它是个从机,永远不主动发起通信,所以不需要关心仲裁,但需要理解 ACK 应答的底层逻辑:从机把 SDA 拉低表示“我收到了,请继续”。

很多人问为什么 STM32 的 GPIO 要配成开漏而不是推挽。如果用推挽输出再接上拉电阻,高电平时推挽管的强驱动和上拉电阻会形成冲突,轻则波形畸形,重则可能超过引脚灌电流规格。开漏输出才是 I2C 的标准接法。GPIO 初始化时把 SCL 和 SDA 都设为开漏输出,然后在 SDA 输入读取应答前再切换方向,这是软件模拟 I2C 的常规做法。

2.2 四个必须背下来的时序:起始、停止、数据有效、应答

I2C 通信的基本单位是一字节 8 位,传输时 MSB first。看起来简单,但协议里最关键的并不是“什么时候发 0 发 1”,而是三条边界条件:

  • 起始条件:SCL 为高电平时,SDA 从高电平跳变到低电平。
  • 停止条件:SCL 为高电平时,SDA 从低电平跳变到高电平。
  • 数据有效性:SDA 上的数据必须在 SCL 为低电平期间改变,在 SCL 为高电平期间保持稳定。

这第三条是无数人写错的地方。很多人想当然地认为,和数据时钟一样,数据在时钟上升沿发送。I2C 不是这样,它要求数据在 SCL 低电平期间准备好,然后在 SCL 高电平期间被从机采样。所以你写代码时必须保证:先把 SDA 的电平设置好,再拉高 SCL;拉低 SCL 之后才允许改变 SDA。如果顺序反了,或者为了提速把延时调得太短,就会出现偶发的误码。

应答机制是第九个时钟周期。主机发送完 8 位数据后,释放 SDA(也就是置为输入模式),此时如果从机正常工作,会把 SDA 拉低,主机检测到这个低电平就表示 ACK。如果从机没有应答,SDA 会维持高电平,主机检测到 NACK。对于写操作,NACK 往往意味着器件地址不对、WP 引脚保护、或者芯片还在写周期内;对于读操作,主机在最后一个数据字节前不给 ACK,而是发 NACK,再发停止条件,这是告诉从机“别再发了”,否则从机会继续输出下一个地址的数据。

3. 硬件 I2C 还是 GPIO 模拟 I2C:STM32F103 上的选择题

3.1 硬件 I2C 的“坑王”传说与真实情况

STM32F103 的硬件 I2C 外设一直是社区里争议最多的话题之一。很多人说它不好使,主要症状是:初始化后总线卡死、主模式发送事件标志不按手册出现、同时操作其他中断时状态机混乱、读操作收尾时容易卡在BERR或者AF。当年我做项目也遇到过硬件 I2C 在频繁读写后直接挂死,必须复位外设才能恢复。

但这件事要客观看。硬件 I2C 的“坑”多半来自两个地方:第一,芯片手册里的事件流程非常绕,EV5、EV6、EV8_1、EV8_2、EV8_3这些事件编号让新手晕头转向,一旦某个标志位没有及时清掉,下一个状态就永远等不来;第二,STM32F103 的 I2C 外设寄存器交互比较复杂,发送缓冲区DR和状态位TXE、BTF之间的关系需要严格控制,稍不留神就丢状态。所以并不是硬件完全不能用了,而是状态机管理成本太高。

我的建议很直接:学习阶段,特别是这次 AT24C02 的全流程拆解,优先用 GPIO 软件模拟 I2C。原因不是硬件 I2C 用不了,而是软件模拟把时序拆成了一个个可见的SDA_1()、SCL_0()操作,每一步在干什么都非常直观,出问题的时候用逻辑分析仪一抓,哪里不对一眼就能看出来。等你把 I2C 协议本身吃透了,再去挑战硬件外设,或者干脆换用 HAL 库的阻塞式接口,会顺手很多。

3.2 软件模拟 I2C 的代码骨架:延时为什么不能乱删

软件模拟的核心就是用 GPIO 控制 SCL 和 SDA 电平,并通过延时函数模拟时序宽度。一般点对点通信速率只需 100kHz 左右,延时可以用简单的delay_us,不需要精确到纳秒,但要注意不能把延时去掉太多。标准模式下,SCL 高电平最小宽度 4.0μs,低电平最小宽度 4.7μs,加上边沿时间,一个周期差不多 10μs。如果你用的是 400kHz Fast Mode,那高电平最小 0.6μs,低电平最小 1.3μs。

这里给一个最小骨架,但请先理解逻辑,不要当作万能代码直接抄:

#define SCL_H() GPIO_SetBits(GPIOB, GPIO_Pin_6) #define SCL_L() GPIO_ResetBits(GPIOB, GPIO_Pin_6) #define SDA_H() GPIO_SetBits(GPIOB, GPIO_Pin_7) #define SDA_L() GPIO_ResetBits(GPIOB, GPIO_Pin_7) #define SDA_IN() { GPIOB->CRL &= ~(0xF << 28); GPIOB->CRL |= (0x8 << 28); } #define SDA_OUT() { GPIOB->CRL &= ~(0xF << 28); GPIOB->CRL |= (0x3 << 28); } #define SDA_READ() ((GPIOB->IDR & GPIO_Pin_7) != 0) static void i2c_delay(void) { delay_us(5); } void I2C_Start(void) { SDA_H(); SCL_H(); i2c_delay(); SDA_L(); i2c_delay(); SCL_L(); } void I2C_Stop(void) { SDA_L(); SCL_H(); i2c_delay(); SDA_H(); i2c_delay(); } void I2C_Ack(void) { SCL_L(); SDA_L(); i2c_delay(); SCL_H(); i2c_delay(); SCL_L(); } void I2C_NoAck(void) { SCL_L(); SDA_H(); i2c_delay(); SCL_H(); i2c_delay(); SCL_L(); } int I2C_WaitAck(void) { SCL_L(); i2c_delay(); SDA_IN(); i2c_delay(); SCL_H(); i2c_delay(); uint8_t val = SDA_READ(); SCL_L(); i2c_delay(); SDA_OUT(); return val; // 0 表示 ACK,1 表示 NACK }

注意SDA_IN()和SDA_OUT()的切换是实现 ACK 检测的关键。主机发送完 8 位数据之后,必须把 SDA 引脚从输出模式切换到输入模式,否则从机拉低 SDA 时会和主机输出高电平打架,要么永远读不到 ACK,要么造成短路电流。这个方向切换看起来不起眼,但几乎所有软件模拟 I2C 的“偶发无应答”都跟它有关。等你把整套流程跑通了,再来考虑用硬件 I2C 提高效率也来得及。

4. 读写操作逐层拆解:字节写、页写、当前地址读、随机读和顺序读

4.1 设备地址和内存地址的封装:0xA0 不是 0x50

第一个容易错的地方是设备地址的表示。AT24C02 的 7 位地址在数据手册里通常写成1010 000,二进制高四位1010是 I2C 通用 EEPROM 家族标识,接着三位是 A2、A1、A0,所以 7 位地址就是0x50。I2C 发送时需要在 7 位地址后再补一个最低位作为读/写标志,于是 8 位写地址变成0xA0,8 位读地址变成0xA1。

很多时候你把0xA0写成0x50也能跑,因为在某些 I2C 主设备驱动里,API 自己会做左移一位的操作,传进去的是 7 位地址。但在自己的软件模拟代码里,你的I2C_SendByte()函数发送什么就发什么,必须发送完整的 8 位。所以我建议定义一个宏,把这个过程写清楚:

#define AT24C02_ADDR_W 0xA0 // 7位地址 0x50 左移一位 #define AT24C02_ADDR_R 0xA1 #define AT24C02_MEM_LEN 256 #define AT24C02_PAGE_SIZE 8

内存地址是单独的一字节,所以 AT24C02 最多只能寻址 256 字节。这也就意味着如果你用的是容量更大的 AT24C04(512 字节)、AT24C16(2048 字节),内存地址会变成 9 到 11 位,其中一部分要占用设备地址位。这块后面如果换芯片,一定要重查数据手册,而不是照搬 AT24C02 的代码。

4.2 五种操作的时序结构:它们不是各写各的

AT24C02 的读写操作看起来很多,其实只有两类:写操作给“设备地址+内存地址+数据”,读操作给“设备地址+内存地址”或只给“设备地址”。下面逐个拆:

  • 字节写:START → 发送 0xA0 → 发内存地址 → 发数据 → STOP。这是最基本的写单字节操作。
  • 页写:START → 发送 0xA0 → 发内存地址 → 连续发最多 8 个数据 → STOP。收到内存地址后,芯片内部的地址计数器会在低 3 位自动加一,所以同一页内的连续写可以一次完成。
  • 当前地址读:START → 发送 0xA1 → 直接读一个字节 → NACK → STOP。这时候芯片内部地址是刚写完或刚读完的位置。
  • 随机读:START → 发送 0xA0 → 发内存地址 → 再次 START → 发送 0xA1 → 读一个字节 → NACK → STOP。第一次 START 是为了把读指针设置到指定内存地址,有人把这叫做“伪写”,它并不会真正写入数据。
  • 顺序读:第一次读如果主机给出 ACK,芯片就会自动把地址加一并继续输出下一字节,直到主机发出 NACK 和 STOP。所以顺序读的收尾必须是 NO ACK。

这里有三个反直觉的点。第一,随机读要发两次 START,第二次 START 也叫重启动(Restart),很多人忘了在第二次 START 前发停止条件,这是 I2C 标准支持的,但不能写成“STOP 后再 START”那种结构,因为那样会让总线释放,期间如果别的主机插进来就乱了。第二,写操作只有字节写和页写,读操作里没有“写地址”这一步,当前地址读直接发读地址。第三,读多字节时主机必须主动发送 ACK,这和写操作时从机发 ACK 的电平方向完全相反,刚上手的人经常混淆。

4.3 把一次完整页面写入拆成状态机:为什么不能只调一次 SendByte

写页操作是踩坑重灾区,所以我单独用一小节说。假设你要从内存地址 0x05 开始连续写 6 个字节,因为这些地址低 3 位在 0x05 到 0x07 之间只有 3 个字节,再写一个就会到 0x08,那已经属于下一页了。如果不管这个边界,芯片内部地址计数器只加低 3 位,结果就是 0x07 之后回卷到 0x00,于是 0x00 到 0x02 的老数据会被你误写掉。

所以页写函数必须做分页处理。一个稳妥的思路是先计算当前地址距离本页页尾还剩多少字节:

uint8_t left = AT24C02_PAGE_SIZE - (mem_addr % AT24C02_PAGE_SIZE); uint16_t chunk = len > left ? left : len;

然后把len分成多个 chunk,每个 chunk 启动一次页写传输。这样虽然传输次数变多了,但安全性大大提高。实际做项目时,如果只是存一些配置参数,写入量不大,我更推荐干脆不用页写,一直用字节写。字节写虽然多耗一点时间,但不会因为跨页问题产生灾难性覆盖。只有需要写入连续的一批传感器校准数据、一次写几十字节时,才值得用页写并做分页封装。

下面是一个简单的随机写单字节函数:

uint8_t AT24C02_WriteByte(uint16_t mem_addr, uint8_t data) { if (mem_addr >= AT24C02_MEM_LEN) return 0; I2C_Start(); I2C_SendByte(AT24C02_ADDR_W); if (I2C_WaitAck()) return 1; I2C_SendByte((uint8_t)mem_addr); if (I2C_WaitAck()) return 1; I2C_SendByte(data); if (I2C_WaitAck()) return 1; I2C_Stop(); return 0; }

函数返回 0 表示成功,返回 1 表示 NACK 失败。注意发送完停止条件后,芯片进入内部写周期,要等待约 5ms。如果连续高速写入,可以在每个字节后加一个 ACK 轮询函数来提前退出等待,后面会讲。

5. 实际调试中绕不开的四类坑:写周期、页越界、上拉电阻、总线死锁

5.1 写完不能马上读:写周期与 ACK 轮询

AT24C02 数据手册规定tWR最大 5ms。这意味着你发出 STOP 之后,芯片内部需要时间把数据真正烧录进 EEPROM 单元。如果 STOP 后立刻发起读操作,芯片可能还在忙,它对你的读命令不会产生 ACK,或者读出来的数据还是旧值。很多“我明明写了但读出来是 FF”的案例都发生在这里。

最简单的处理方式是每次写完后调用延时 5ms 到 10ms。但如果你要一次性写入几百个字节,每次都要等 5ms 太慢了,这时候可以用 ACK 轮询优化:重复发送 START,发送设备写地址0xA0,然后检查芯片是否回应 ACK。如果芯片还在写周期内,它不回 ACK;一旦写周期结束,它回 ACK,此时你就可以继续发下一笔数据,不需要固定等待。这个技巧在实际项目里非常有用,轮询通常能把连续写入时间压缩好几倍。

5.2 页写越界:为什么地址计数器只加低 3 位

前面已经说了页写越界会导致回卷覆盖。这里再展开一个具体例子:内存地址 0x06,页写发送 4 个字节,按理说写入的是 0x06、0x07、0x08、0x09,但实际结果是 0x06、0x07、0x00、0x01。因为 AT24C02 页大小是 8 字节,地址计数器的低 3 位在 0x07 后自动清零。如果你的数据结构刚好布置在 0x20、0x21 这种紧挨页边界的位置,一次写操作就可能把重要配置洗掉。

页写对应的读取操作没有这个越界问题,顺序读的地址计数器是整个 8 位递增,读到 0xFF 之后会回卷到 0x00。读取回卷问题不大,最多只是多读一遍。但写操作必须严格规避。我的经验是:要么在封装层强制限制每次页写长度不超过 8 字节,要么干脆禁用页写,这样从根上消除问题。

5.3 上拉电阻阻值:不是随便接个 10K 就完事

I2C 上拉电阻阻值会影响信号上升时间和功耗。标准模式要求 SCL/SDA 的上升时间不超过 1000ns,400kHz Fast Mode 要求不超过 300ns。总线上每个设备的引脚都有等效电容,加上 PCB 走线电容,总电容通常在 50pF 到 100pF 之间。

最小阻值受低电平输出灌电流限制。I2C 规范要求器件在 3mA 灌电流下还能把输出拉到 0.4V 以下。以 VCC=3.3V 计算,R_min = (3.3 - 0.4) / 3mA ≈ 967Ω,所以上拉电阻不要小于 1kΩ。最大阻值由允许的上升时间和总线电容决定,R_max = t_r / (0.8473 * C_bus)。假设 C_bus=100pF,上升时间要求 1000ns,R_max ≈ 11.8kΩ。所以常见的 4.7kΩ、2.2kΩ、10kΩ 都在可以接受的范围内。

但这里有个实际经验:如果 STM32F103 和 AT24C02 之间连线很短,10kΩ 也能跑;如果连接线超过 20cm,或者板上有多个 I2C 设备,10kΩ 往往会导致上升沿太慢,偶发通信不稳定。这时候换成 2.2kΩ 或 3.3kΩ 通常能让系统稳定很多。另一个需要留意的是 VCC 超过 5V 时,最小阻值公式要用 5V 重新计算,同时要确认 STM32 引脚耐压,必要时加电平转换。

5.4 WP 引脚与总线死锁:写保护挂起和不释放 SDA 的处理

WP 引脚如果被拉高,AT24C02 会把所有写操作忽略。注意是“忽略”而不是返回错误,芯片照样 ACK,但数据不会被写入,读出来的还是原值。这比返回错误更隐蔽,因为程序流程完全正常,只有最后校验数据时才能发现。所以调试时第一件事就是确认 WP 接地。

总线死锁是另一个常见问题。软件模拟 I2C 最怕的是某次时序中途被打断,比如调试器暂停、看门狗复位、中断抢占,导致 STOP 条件没发完,SDA 被自己的代码或者从机拉低后没有释放。重新初始化后,主机一开始发送 START,发现 SDA 一直低,整个时序就卡死。这时候可以做一个恢复函数:把 SCL 配置为输出,连续发出 9 到 16 个时钟脉冲,同时把 SDA 设为输入读取;如果发现 SDA 已经恢复高电平,就发送一次 STOP,让从机退出异常状态。这个动作在很多教程里被叫做“释放总线”,是现场调试的救命手段。

6. 用逻辑分析仪看一次真实波形:从“能读”到“读对”的验证方法

6.1 抓 I2C 波形的设置和观察点

软件模拟 I2C 调通后,不要急着困在代码里,接一个逻辑分析仪,比盯着单步调试有用得多。逻辑分析仪不需要特别贵,采样率 24MHz 以上的 USB 逻辑分析仪就够用了,国产的几个常见型号都能胜任。接线时把 CH0 接 SCL,CH1 接 SDA,共地,然后设置协议解析为 I2C,标准模式,地址显示格式可以选 7 位或 8 位。

抓一次“写一个字节再读回来”的完整流程,你应该能在波形里看到三段:第一次 START 后的写地址 0xA0,接着是内存地址和数据字节,然后 STOP;等待写周期后再出现读地址 0xA1,然后是读出的数据字节,最后是主机 NACK 和 STOP。

我最常检查的是四点:起始条件位置是否正确,ACK 位是低电平还是高电平,数据位是否在 SCL 高电平期间稳定,以及 STOP 是否干净。如果 ACK 位是高电平,说明从机没有应答,这是排查一切 I2C 问题的起点。

6.2 一次真实的无应答定位:问题出在 GPIO 复用配置

我印象最深的一回,是移植代码到一块自制板上,写地址发送后始终等到 NACK。逻辑分析仪波形显示 SCL 和 SDA 电平转换正常,但 0xA0 之后第九个时钟 SDA 一直高。一开始我怀疑芯片损坏,换了芯片还是老样子。后来把示波器探头接到 AT24C02 的供电引脚,发现 VCC 只有 2.2V,而板子上的 LDO 标称 3.3V。

问题的根源是,LDO 输出端没有加足够的去耦电容,而 I2C 引脚翻转瞬间的电流导致供电跌落,芯片欠压后不稳定。单片机本身在低电压下还能跑,EEPROM 却进入了欠压保护状态,所以不响应。这个案例说明,I2C 问题不一定是协议问题,供电和硬件电路同样关键。你用代码单步调试永远找不到这类硬件问题,但拿到逻辑分析仪和示波器上,几秒钟就能锁定方向。

6.3 波形和代码互证之后,才能算真正“调通”

软件模拟 I2C 有个特殊优势:每个电平变化都是代码可控的,所以逻辑分析仪上的每一段跳变都能反推到源码里对应的某一行。比如果出现数据乱码,检查是不是I2C_SendByte里最后一位发送后马上拉了高 SCL,而没有给数据足够的保持时间;如果出现读写数据全部偏移一位,检查是不是 SDA 改变发生在 SCL 高电平期间,违反了数据有效性要求。

验证完成后,还可以顺手测一下最高稳定速率。把i2c_delay从 5μs 慢慢往下减,直到波形出现数据建立时间不足为止,这时候你就知道自己的板子在这个接法下极限在哪里。我一般留一倍以上余量,不会真按极限跑。测完这些,再回头去看硬件 I2C 外设,你会容易理解得多。

7. 收尾:我现在的 AT24C02 代码框架和个人经验

写了这么多,最后说点我现在做项目的固定套路。无论用标准外设库还是 HAL 库,我都会保留一个i2c_soft.c的软件模拟驱动,专门用于 AT24C02 这类简单 EEPROM。我通常会在上面板一个eeprom.c封装层,提供三个接口:EEPROM_ReadBuffer、EEPROM_WriteBuffer、EEPROM_EraseConfigArea。WriteBuffer内部处理页边界,ReadBuffer使用随机读加顺序读。这样上层逻辑只跟“缓冲区”打交道,不关心 I2C 底层是用模拟还是硬件实现。

另一个固定习惯是:每次写完配置后,立即读回来做校验,不校验就当没写成功。EEPROM 在极端情况下也会出现位错误,虽然概率很小,但配置参数写错会带来很隐蔽的设备故障。启动时加一个版本号和校验和字段,也是一个成本很低的保护手段。

如果你刚接触 STM32F103 和 I2C,我建议按这个顺序走:先用两块杜邦线把 AT24C02 和单片机接好,上拉电阻不能省;然后跑通软件模拟 I2C,完成字节读写;再用逻辑分析仪看清楚波形;接着写页写函数,主动处理跨页;最后再尝试切换硬件 I2C 和 HAL 库接口。每一步都验证过了再往下走,你会发现自己对 I2C 的理解比单纯抄代码的同事扎实很多。

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

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

立即咨询