I2C EEPROM 读写时序:设备地址、字地址、页写与写周期轮询
更新时间:2026-09-19。前置:I2C 主机开漏/ACK/时钟拉伸。EEPROM 是 I2C 上最经典的从设备,理解它就掌握了「带内部地址指针的存储型从机」模型。
本文要回答的问题
- 一次 I2C EEPROM 读,为什么设备地址后面还要发「字地址」?
- 随机读/当前地址读/顺序读在时序上差在哪?
- 什么是页写回卷(page write rollover),为什么一次写有长度上限?
- 写一个字节为什么要等 5ms,怎么用 ACK 轮询替代 sleep?
一、器件模型与地址
EEPROM(如 24LC256 = 32KB)内部是一个「带地址指针的存储阵列」。一次访问分两层地址:
- 设备地址(总线寻址):
1010 + A2A1A0 + R/W#。固定高 4 位1010,低 3 位由引脚 A2/A1/A0 决定,故同一条总线最多挂 8 个同类器件。 - 字地址(片内偏移):小容量 8 位(≤2Kbit),大容量 16 位(24LC256 等);部分型号设备地址低几位也参与扩展寻址。
bash
设备地址字节: 1 0 1 0 A2 A1 A0 R/W#
固定 引脚选择 1=读 0=写二、写时序
字节写:
bash
S | 1010xxx0 A | WADDR_H A | WADDR_L A | DATA A | P
└ 拉高 STOP 后,内部烧写开始(tWR≈5ms)页写(page write):发完字地址后可连续送最多一页(8/16/32/64 字节,视型号),每字节 ACK,最后一个 STOP 才启动一次内部烧写——整页只付一次 5ms,这是页写的价值。
页写只在「当前页内」递增地址,低几位页内偏移自增、高位不变,超过页长会回卷覆盖本页开头,不会跨页。
三、读时序(三种)
| 类型 | 序列 | 用途 |
|---|---|---|
| 当前地址读 | S | 设备地址+R | A | 读数据 | NACK | P | 读「内部指针当前位置」 |
| 随机读 | 先发写:S|dev+W|字地址|(不发 STOP,用 repeated START)|dev+R|数据|NACK|P | 读任意地址,最常用 |
| 顺序读 | 随机读后每字节回 ACK,地址自增连续读 | 批量读,跨页不回卷 |
关键:随机读必须用 repeated START(Sr) 在写地址后切换方向,不能用 STOP(STOP 会终止传输、丢失内部寻址上下文)。
bash
S dev+W A addrH A addrL A | Sr dev+R A data NACK P
└── dummy write 设置指针 ──┘ └ 真正读 ┘四、写周期与 ACK 轮询
STOP 后 EEPROM 进入内部烧写(tWR 典型 5ms,最大 10ms),期间不响应设备地址(NACK)。两种等待法:
- 固定 sleep 5~10ms:简单但慢。
- ACK 轮询(acknowledge polling):周期性发 START + 设备地址,NACK 表示仍在写,一旦回 ACK 立即继续,平均更快。
verilog
// 伪代码:等到 EEPROM 恢复响应
task eeprom_wait_busy;
forever begin
send_start;
send_byte(7'b1010_000 << 1); // 写设备地址
if (get_ack == 0) disable; // 收到 ACK → 写完
send_stop; // NACK → 继续轮询
end
endtask五、可综合 RTL 要点
主机 FSM 与通用 I2C 主机一致,差别只在序列编排(两层地址、Sr、页计数、写后轮询)。核心位级引擎见 I2C 主机;这里给出页写回卷保护:
verilog
// 页大小 PAGE = 16 字节时,页内偏移 = addr[3:0]
wire [3:0] page_offset = cur_addr[3:0];
wire page_end = (page_offset == 4'hF);
always @(posedge clk) if (wr_state == DATA) begin
if (byte_cnt == len-1 || page_end) send_stop; // 到长度或页边界必须收尾
else cur_addr <= cur_addr + 1'b1;
end
// 驱动层应按页把一次大写拆成多次,避免回卷覆盖六、常见的坑
- 忘记字地址:把 EEPROM 当普通从机,直接读会读到内部旧指针位置。
- 随机读用了 STOP 代替 Sr:指针上下文丢失,读错地址。
- 页写超长回卷:一次发超一页,数据从头覆盖;驱动必须按页拆分。
- 不等 tWR 就下一条:写未完成时发命令被 NACK,读回旧值。
- 设备地址/字地址位宽错:24LC256 是 16 位字地址,只发一字节会把数据当高地址。
- A2/A1/A0 地址冲突:两片相同引脚配置并挂,总线仲裁异常;大容M器件部分地址位是「块选择」而非引脚。
- WP 脚悬空/拉高:写保护使能时写入静默失败。
- 上拉太弱或总线电容大:开漏上升沿变缓导致 ACK 采样出错,见 开漏与上拉。
- 顺序读不回 ACK 提前停:主机读最后字节必须 NACK+STOP,否则从机继续驱动 SDA。
- 跨型号假设页大小:8/16/32/64 字节页因型号而异,移植必须查手册。
相关与延伸
位级时序、时钟拉伸与总线挂死恢复见 I2C 主机;高速非易失对照 SPI NOR Flash/XIP;低速外设挂载见 APB 时序。
总结
I2C EEPROM 是「设备地址选片 + 字地址选偏移」的存储型从机:随机读要先做一次 dummy 写设指针并用 repeated START 切到读,页写可在一页内连续发但超页会回卷覆盖,且只有最后一个 STOP 才启动一次约 5ms 的内部烧写;烧写期间器件不回 ACK,用 ACK 轮询比固定 sleep 高效——字地址位宽、Sr、页边界和 tWR 是四大必查点。