LV020-AT24C02读写时序分析
AT24C02 是一个 256 字节(2Kbit)的 EEPROM,它使用的通信接口就是标准 I2C。与 LV010-I2C时序与软件模拟 里讲到的"裸"总线收发相比,它多了一层字地址的概念,是分析实际器件读写时序的最好例子。这一篇就结合它的芯片手册(AT24C02B 数据手册),把字节写、页写、当前地址读、随机读、顺序读共 5 种读写时序逐一过一遍。
一、分析前的准备:设备地址与字地址
1. 设备地址
I2C 总线靠设备地址区分不同器件。AT24C02 的 7 位设备地址由高 4 位固定值 1010 和随后 3 位地址引脚 A2、A1、A0 的电平组成;发送时再在末尾拼接 1 位读写方向位 R/W,形成 8 位地址字节——R/W 为 0 表示写、为 1 表示读。以 A2~A0 全部接地为例:
(1)写操作时发送的地址字节是 1010 0000 = 0xA0;
(2)读操作时发送的地址字节是 1010 0001 = 0xA1。
引脚接法与容量分页的更多细节,可以看外设篇的笔记 LV010-AT24C02简介。
2. 字地址(Word Address)
I2C 协议本身没有"存储地址"的概念,总线上传输的都是普普通通的数据字节,是器件自己约定了这些字节的含义。AT24C02 内部有 256 个存储单元,每个单元的编号(0x00 ~ 0xFF)就是字地址。它的约定是:
(1)写操作中,设备地址之后发送的第一个数据字节总是被解释为字地址,之后的字节才是真正写入的数据;
(2)读操作可以不带字地址(顺着内部地址计数器读),也可以先用一段"假写"把字地址装进计数器再读。
理解了"同一个字节,按其在时序中的位置被解释为地址或数据",下面 5 种时序就都顺理成章了。
二、写时序
1. 字节写(Byte Write)
字节写是向单个地址写入一个字节,时序图如下:

同一时序的标注版(红色数字依次标出起始、地址位、读写位、应答、字地址、数据、停止等节点):

(1)主机产生起始信号 S;
(2)主机发送设备地址字节 0xA0,R/W 位为 0 表示写;
(3)地址匹配的从机在第 9 个时钟把 SDA 拉低,回 ACK;
(4)主机发送第二个字节——字地址,指定要写入的存储单元;
(5)从机再次回 ACK;
(6)主机发送要写入的数据字节,8 位、高位在前;
(7)从机回 ACK;
(8)主机产生停止信号 P,传输结束。
【注意】字节写全程只有一次起始信号,没有重复起始信号,方向也始终是主机到从机——它就是 LV010 里"主机写时序"套上"第一个数据字节是字地址"的约定而已。
停止信号之后,器件进入内部写周期(手册记为 tWR,最大 5 ms),把收到的数据写入非易失存储阵列,期间器件不响应总线。主机若想紧接着再次访问,可以使用"应答轮询"(Acknowledge Polling):反复发起设备地址并检测是否收到 ACK,一旦收到即说明写周期结束。这种"每写一段就要等一次写周期"的特性,正是页写要优化的对象。
2. 页写(Page Write)
页写用一个字地址连续写入最多一页(本文手册对应的 AT24C02B 为 8 字节,其他版本以各自手册为准)的数据:

(1)前半段与字节写完全相同:起始信号 → 设备地址 0xA0 → ACK → 字地址 n → ACK;
(2)主机不再只发一个数据,而是连续发送 DATA(n)、DATA(n+1)、……、DATA(n+x),每个字节之后从机各回一个 ACK,内部地址计数器随之自动 +1;
(3)数据发送完毕,主机产生停止信号 P,器件开始内部写周期。
三个要点:
(1)AT24C02B 每页 8 字节,一次页写最多连续写 8 个字节,但不要求写满整页,只写一两个字节也允许(部分页写入);
(2)页写期间地址计数器只有低 3 位自增,高 5 位(页号)不变。如果一次写入超过 8 个字节、或从页中间写到页尾还继续,计数器会回卷到本页页首,把本页开头刚写的数据覆盖掉——这是实际使用中最经典的坑;
(3)页写把"每个字节一次完整时序 + 一次写周期等待"压缩成"一次时序 + 一次写周期",无论通信效率还是写入速度都高得多。
对比字节写:字节写适合修改零星几个单元,页写适合成块写入,两者不是谁取代谁。
三、读时序
1. 当前地址读(Current Address Read)
先认识一个关键角色——内部地址计数器。AT24C02 会记住上一次读或写操作所访问的字地址,操作完成后把它 +1 保存;只要芯片保持供电,这个地址在不同操作之间一直有效。当前地址读就是"顺着这个计数器读",因此全程不需要发送字地址:

(1)主机产生起始信号 S;
(2)主机发送设备地址字节 0xA1,R/W 位为 1 表示读;
(3)从机回 ACK;
(4)从机把地址计数器指向单元的内容逐位送上 SDA,8 位、高位在前;
(5)主机只想要这一个字节,接收完不拉低 SDA,回 NACK;
(6)主机产生停止信号 P,读操作结束。
它适合"接着上一次访问的位置继续读一个字节";要读任意指定单元,则需要下面的随机读。
2. 随机读(Random Read)
想读任意指定单元,就得先把字地址告诉器件,但读时序里器件不会再接收"数据"。所以要先做一段假写(Dummy Write)把字地址装进地址计数器,再用重复起始信号 Sr 把方向切换到读——这正是 LV010-I2C时序与软件模拟 中"复合通信"时序的实际应用:

同一时序的标注版(下半部分标出了"假写"覆盖的范围,底色区分设备地址、字地址与数据段):

(1)主机产生起始信号 S;
(2)主机发送设备地址字节 0xA0(写方向);
(3)从机回 ACK;
(4)主机发送字地址 n,从机回 ACK——到这一步为止就是一段"假写":后面没有数据字节、也不会产生停止信号,什么都没写进去,只是把 n 装进了地址计数器;
(5)主机不发停止信号,而是再次产生起始信号(重复起始 Sr),保住总线控制权;
(6)主机重新发送设备地址字节 0xA1(读方向);
(7)从机回 ACK;
(8)从机从字地址 n 开始送出数据 DATA n;
(9)主机只读这一个字节,回 NACK;
(10)主机产生停止信号 P,读操作结束。
【注意】这里使用重复起始信号 Sr,可在不释放总线的情况下切换方向;若改成"停止 + 重新开始",总线会在两段之间释放,多主机环境下其他主机可能趁机占用总线。不能把这个需求扩展为"所有 I2C 方向切换都必须使用 Sr",具体事务格式仍应以器件手册为准(见 LV010-I2C时序与软件模拟 的复合通信一节)。
3. 顺序读(Sequential Read)
3.1 时序过程
顺序读是另外两种读的"连续版":以当前地址读或随机读作为开头,区别只在数据阶段——主机每收到一个字节后不回 NACK,而是回 ACK,从机就会把地址计数器 +1,继续送下一个字节:

(1)开头与随机读(或当前地址读)完全相同,主机收到第一个数据 DATA n;
(2)主机回 ACK,从机继续发送 DATA n+1、DATA n+2、……、DATA n+x;
(3)主机读够了,对最后一个字节回 NACK,表示"到此为止";
(4)主机产生停止信号 P,读操作结束。
结束方式固定为 NACK + 停止信号:数据阶段的 ACK 和结束时的 NACK 是同一位的两种取值,前者是"接着送",后者是"到此为止",随后主机释放总线。
3.2 地址自增与回卷
顺序读能一直读下去,靠的是从机内部的地址计数器在整个 256 字节空间内自增,读到最后一个单元 0xFF 后回卷到 0x00 继续读——对比页写的"跨页覆盖",读操作可以无限跨页连续。
这条不是从时序图推出来的,手册写得很直接(AT24C02B 数据手册 5126H–SEEPR–8/07,第 10 页 8. Read Operations → SEQUENTIAL READ,对应波形见第 12 页 Figure 8-6. Sequential Read):
After the microcontroller receives a data word, it responds with an acknowledge. As long as the EEPROM receives an acknowledge, it will continue to increment the data word address and serially clock out sequential data words. When the memory address limit is reached, the data word address will "roll over" and the sequential read will continue.
译:主机每收到一个数据字就回一个 ACK;只要 EEPROM 收到 ACK,它就把数据字地址加一,继续串行送出下一个数据字;地址到顶后回卷,连续读照常进行。
注意手册把两种回卷分开写,别混用:读是 "the last byte of the last memory page to the first byte of the first page",即 0xFF 之后回到 0x00,整片循环;写才是 "the last byte of the current page to the first byte of the same page",即 0x17 之后回到 0x10,只在 8 字节页内循环。所以"页"只是写的边界,不约束读。
四、五种时序小结
1. 对比一览
| 时序 | 是否发送字地址 | 是否用重复起始 Sr | 数据阶段应答方 | 结束方式 |
|---|---|---|---|---|
| 字节写 | 是 | 否 | 从机回 ACK | 主机发 P |
| 页写 | 是(1 个) | 否 | 从机逐字节回 ACK | 主机发 P |
| 当前地址读 | 否 | 否 | 主机回 NACK | NACK + P |
| 随机读 | 是(假写装入) | 是 | 主机回 NACK | NACK + P |
| 顺序读 | 视起点而定 | 视起点而定 | 主机逐字节回 ACK,末字节回 NACK | NACK + P |
2. 回到协议本身
把 5 张时序图放在一起看,AT24C02 其实只用了 I2C 协议的三个基础机制:
(1)地址与数据的复用:写操作的第一个数据字节永远是字地址,之后才是数据——"命令、地址、数据的本质都是数据",含义由其在时序中的位置决定;这一点在硬件 I2C 库函数里体现为"子地址"参数(如 HAL_I2C_Mem_Write() 的 MemAddress);
(2)重复起始信号 Sr:本文这种不释放总线的"先写后读"复合传输靠它完成方向切换,随机读就是最典型的例子;
(3)ACK / NACK 的方向语义:写时由从机应答,表示"收到了";读时由主机应答——ACK 表示"还要继续",NACK 表示"读完了"。
在 STM32 上用硬件 I2C 和 HAL 库实际读写这片 EEPROM 的完整代码(CubeMX 配置、字节写、页写与分页算法、测试程序),见外设篇的笔记 LV015-AT24C02使用实例。