news 2026/9/24 2:51:37

EEPROM 软件设计规范

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
EEPROM 软件设计规范

编制日期 2026-09-23 | 版本号 V1.0

面向XTX 串行 EEPROM(I²C 24Cxx / SPI 25xx 系列)的固件驱动设计约定 —— 覆盖ACK 轮询 / WIP 轮询页写边界回绕写保护体系1M 次写 endurance掉电原子提交,逐条给出可落地的命令序列、轮询判据与超时预算。

文档编号EEPROM-SW-SPEC-001
版本 / 状态V1.0 · 正式发布
适用器件XTX 串行 EEPROM 24 系列(I²C)/ 25 系列(SPI),容量 1 Kbit ~ 2 Mbit 各档
参考标准JEDEC / 厂商 I²C-Compatible Serial EEPROM 与 SPI Serial EEPROM 协议;I²C-bus Specification
配套文档EEPROM_电路设计指南.html(硬件 / 原理图 / 上拉 / 布线约束)

0. 目的与范围

本规范约束 EEPROM 器件在嵌入式固件中的软件行为:命令序列、写完成检测(ACK / WIP 轮询)、页写边界、写保护、endurance 预算与掉电保护。凡本文与器件 datasheet 冲突,以 datasheet 为准

  • 统一写完成检测:I²C 用ACK 轮询,SPI 用RDSR 轮询 WIP;绝不靠固定延时替代轮询。
  • 统一页写边界规则:一次页写不得跨页边界,超出部分会回绕到本页首地址覆盖
  • 统一写保护策略:WP# 引脚 + BP/WPEN 位 + 块保护组合,上电默认状态必须明确。
  • 统一掉电保护:给出写窗口、提交协议与写入频次预算,明确哪些操作是原子的。

不在本规范范围:硬件原理图与 PCB 布线(见《EEPROM 电路设计指南》);FRAM / NVSRAM / Microwire / UNI-O / 并行 28Cxx 等特殊接口的算法差异仅作提示; 文件系统与磨损均衡算法选型(EEPROM 的 endurance 已达百万级,通常无需 FTL)。

一句话结论:EEPROM 是「字节可改、页内聚合」的器件 —— 所有写都必须先WREN(SPI)或起始条件(I²C)使能,再以内部写周期 tWR(典型 5 ms)落到阵列;写完之后器件进入busy状态(I²C 不响应 ACK,SPI 的 WIP=1),不轮询完成就继续发命令,数据会被静默吞掉。没有坏块、没有 ECC,但页边界回绕写 endurance 累积是它独有的两个坑。

文档使用说明

  • 本文是《EEPROM 电路设计指南》的软件配套篇:硬件篇解决「怎么接、怎么供电、怎么拉上拉」,本文解决「驱动怎么写、写完怎么等、错误怎么救」。
  • 命令码、寄存器位、时序值均取自典型 I²C 24Cxx 与 SPI 25xx 协议;其它厂商/其它密度型号存在差异,移植前必须逐条核对。
  • 文中 C 代码为协议骨架伪代码,演示流程与判断条件,非特定平台可直接编译的完整实现;i2c_*()spi_*()now_ms()等为平台相关底层。

贯穿全文的三条铁律

绝不用固定延时替代写完成轮询——tWR 随温度、电压波动,且器件 busy 时直接拒绝新命令; ②一次页写绝不跨页边界——超出的字节会回绕覆盖本页开头,不会报错; ③超时门限一律取 datasheet max 以上——typ 值只用来安排轮询节奏,不能当失败判据。

1 · 器件定位与地址组织

EEPROM 不是「能改几个字节的 Flash」。它保留了字节可寻址、就地改写的能力,但写是以一个内部页为单位聚合提交的——软件设计的第一件事,是把「地址空间」「页粒度」和「写完成窗口」搞清楚。

1.1 器件定位与关键参数

8~256 B

页写粒度

同一内部写周期最多写入的字节数

2 Mbit

单片最大容量

1 Kbit ~ 2 Mbit 多档;24 系列 2 字节地址可达 512 Kbit

1,000,000

写 endurance

每字节/每页 100 万次(典型)

100 年

数据保持

常温典型;高温下缩短

5 ms

写周期 tWR

最大 5 ms(部分型号 10 ms),期间器件 busy

坏块 / ECC

EEPROM 无出厂坏块、无片内 ECC

100/400 kHz

I²C 时钟

标准模式 / 快速模式(部分 1 MHz)

10/20 MHz

SPI 时钟

25 系列典型最大时钟

数据取自典型 I²C 24Cxx 与 SPI 25xx datasheet 的 Features 与 DC/AC 特性章节;具体数值随器件型号与厂商不同,移植时以对应 datasheet 为准。

项目I²C 24Cxx 系列SPI 25xx 系列软件影响
接口2 线(SDA / SCL)4 线(CS / SO / SI / SCK)底层总线抽象必须分离
寻址器件地址 1010 + A2A1A0(或块地址)+ R/W;字地址 1/2 字节命令字节内嵌 16/24 位字地址I²C 有独立器件地址,SPI 靠 CS# 选片
写完成检测ACK 轮询(busy 时 NACK)05h读 SR 的 WIP 位两类驱动路径不同
写保护WP# 引脚(高=整片保护)+ BP 位(部分型号)WP# 引脚 + SR 的 BP0/1/2 + WPENSPI 保护更细;I²C 经典器件靠 WP 引脚
页大小8 / 16 / 32 / 64 / 128 / 256 B(随容量)同左页写长度上限必须按型号配置
状态寄存器无(仅靠 ACK 行为反映 busy)有(RDSR / WRSR)I²C 无法读 SR,保护位靠 WP 引脚

1.2 容量、页与地址映射

EEPROM 的地址是一段连续的字节线性空间,软件最容易在这里出错的是「页边界」:

器件密度总字节数页大小(典型)字地址宽度页数量
1 Kbit(128 B)1288 B 或 16 B1 字节8 ~ 16
2 Kbit(256 B)2568 / 16 B1 字节16 ~ 32
4 Kbit(512 B)51216 / 32 B1 字节16 ~ 32
8 Kbit(1 KB)102416 / 32 B1 字节32 ~ 64
16 Kbit(2 KB)204816 / 32 B1 字节64 ~ 128
32 Kbit(4 KB)409632 / 64 B2 字节64 ~ 128
64 Kbit(8 KB)819232 / 64 / 128 B2 字节64 ~ 256
128 Kbit(16 KB)~ 2 Mbit16384 ~ 26214464 / 128 / 256 B2 字节(大容量 3 字节)128 ~ 1024

1 Device = N × 256 B(线性字节空间)容量从 128 B 到 256 KB(2 Mbit);无块/页坏块概念1 Page = 8 ~ 256 B一次内部写周期最多写入的粒度;边界回绕的边界1 Byte = 最小读写单位字节可寻址、可随机改写;无需擦除即可写写完成窗口 tWR ≈ 5 ms窗口内器件 busy:I²C 不 ACK,SPI 的 WIP=1

图 1 · EEPROM 阵列组织:字节可寻址、以「页」为单位聚合写入、以 tWR 为单位提交

页大小随容量变化,绝不能硬编码一个值

很多驱动把页大小写死成 32 B,移植到 64 B 或 128 B 页的器件后,会发生:要么每次只写 32 B(浪费 tWR、写得很慢),要么超过真实页边界造成回绕覆盖

正确做法:页大小作为器件表的配置项,启动阶段按型号查表或读标识页得到。

1.3 与 SPI NOR / NAND 的关键差异(移植必读)

维度SPI NOR / NANDEEPROM移植后果
擦除必须先擦除再写(NOR 按扇区,NAND 按块)无需擦除,字节可直接改写照搬「先擦后写」会多花无意义操作
写完成检测轮询 OIP / 状态位I²C 用 ACK 轮询;SPI 用 WIP 轮询NOR 的 05h 轮询在 EEPROM 上含义不同
页写边界NOR 按页/扇区,NAND 按 cache严格页边界,超出回绕覆盖跨边界写被静默吞掉且不报错
坏块NOR 无;NAND 必有坏块 + BBT无坏块EEPROM 不需要坏块管理
ECCNAND 必有片内 ECC通常无 ECCEEPROM 读到的就是实际存储值
enduranceNOR 10⁵、NAND 6×10⁴ 次10⁶ 次(百万级)EEPROM 可直接当小参数存储,不必上 FTL
写保护BP 位 + WP#BP 位 + WP# + WPEN(SPI);WP#(I²C 经典)I²C 经典器件仅靠 WP 引脚整片保护

最容易踩的一条

把 NOR 驱动改两行就拿去驱动 EEPROM,最常见的死法是写完后用「延时 1 ms」代替轮询:EEPROM 的 tWR 是 5 ms 量级,1 ms 后器件仍在内部写,此时发下一条命令会被忽略或无视——表现为「偶尔丢数据、测多了又正常」。

2 · 术语与缩写

本章统一术语口径,避免「页/块/sector/word address」在文档与代码里指的不是同一件事。

2.1 结构与命令术语

缩写 / 术语全称本文中的含义
Word Address字地址EEPROM 内部字节的线性地址(1/2/3 字节),即数据在阵列中的位置
Page一次内部写周期(tWR)所能写入的最大字节数,是边界回绕的边界
tWRWrite Cycle Time内部写周期时间,典型 5 ms / 最大 5~10 ms;窗口内器件 busy
ACK / NACKAcknowledge / Not AcknowledgeI²C 应答位;EEPROM busy 时在第 9 个时钟返回 NACK
WREN / WRDIWrite Enable / DisableSPI:06h / 04h,置位/清零 WEL 锁存
RDSR / WRSRRead / Write Status RegisterSPI:05h / 01h
WIPWrite In ProgressSPI 状态位 bit0;1 = 内部写进行中,0 = 就绪
WELWrite Enable LatchSPI 状态位 bit1;写操作前必须先置位
BP0/1/2Block Protect bits保护阵列的上 1/4、1/2、全部等区段的组合位
WPENWrite Protect EnableSPI 状态位 bit7;为 1 时 WP# 引脚才生效
WP#Write Protect pin硬件写保护引脚;拉高(I²C 经典器件)或配合 WPEN(SPI)锁保护
Endurance耐久度同一字节/页可承受的擦写次数,典型 1,000,000 次
Retention数据保持断电后数据可保存年限,典型 100 年(常温)

2.2 本文书写约定

  • 命令码用小写十六进制带后缀h书写(如06h05h),与 datasheet 保持一致。
  • 状态位写作SR[0]形式,方括号内为 bit 位序(bit0 为最低位)。
  • 时序符号沿用 datasheet:tWR(写周期)、tSU:WP(WP 建立)、tBUF(停止到起始空闲)、fSCL / fCLK(总线时钟)。「典型值 / 最大值」成对给出时,软件超时必须按最大值预算
  • I²C 字节序列S <…> P简记:S=Start,P=Stop,Sr=Repeated Start,括号内为字节(含 R/W 位);[ACK]/[NACK]表示第 9 时钟的应答。
  • 代码示例为 C 伪代码,省略总线传输底层实现,只表达命令序列与判据。

3 · 驱动架构与初始化

EEPROM 的驱动比 Flash 简单:没有坏块映射、没有 ECC 决策层。但 I²C 与 SPI 两套总线要抽象干净,否则上层会写满分支。

3.1 分层职责

职责禁止做的事
应用层业务调用 read/write/ioctl;不感知总线类型直接拼 I²C/SPI 命令、直接算字地址边界
存储管理层地址越界检查、页写拆分、提交协议、写频次预算绕过保护检查直接下发写
命令协议层WREN + WRITE + 轮询(SPI);START + 字地址 + 数据 + 轮询(I²C)省略写使能、省略写完成轮询
硬件抽象层I²C / SPI 传输、CS# 时序、上拉与时钟在传输中途切换总线频率或改 GPIO 配置

3.2 上电初始化流程

上电后 EEPROM 处于就绪状态(I²C 立即可响应;SPI 的 SR 默认 WEL=0)。但 I²C 总线可能处于「中间字节」状态,SPI 的 CS# 需要正确建立。初始化必须按下列顺序执行:

1. 上电:等待 VCC 稳定I²C 需先满足 tBUF;SPI 需满足 tPU(上电到 CS# 低)2. 总线恢复(I²C):发 9 个时钟脉冲清 SDA若之前总线卡死(SDA 被拉低),用 GPIO 模拟 9 个 SCL 释放3. 探测器件:I²C ACK / SPI RDSRI²C:发 S + 器件地址[W] 看是否 ACK;SPI:06h 无关 + 05h 读 SR4. 读标识 / 容量:确定页大小与地址宽度I²C 按器件型号查表;SPI 可 RDID(0x9F,若支持)或查表5. 回读保护状态:确认 BP / WPEN / WP#SPI:RDSR 看保护位;I²C:确认 WP# 当前电平与期望一致6. 就绪:返回设备句柄(含页大小 / 容量 / 总线类型)此后所有读写都经管理层做边界与保护检查7. 自检(可选):回读已知区 / CRC 校验镜像验证上次提交标记是否完整,发现半写则回滚

图 2 · 上电初始化流程(I²C 与 SPI 共用骨架,差异在步骤 2/3/4)

/* SPI 25xx 上电初始化:返回 0 表示成功 */ int eeprom25_init(struct eeprom_dev *dev) { uint8_t sr; delay_ms(1); /* tPU:上电到 CS# 低至少数百 µs,留余量 */ spi_cs_low(); spi_cs_high(); /* 确保 CS# 有高→低跳变,SPI 从机进入可寻址态 */ if (rdsr(&sr) != 0) return -E_NODEV; /* 05h 应能读到字节 */ dev->page_size = dev->cfg->page_size; /* 来自器件表 */ dev->addr_bytes = dev->cfg->addr_bytes; /* 回读保护状态:确认 BP / WPEN 符合设计(I²C 此步检查 WP# 电平) */ if (sr & (SR_WPEN | SR_BP_MASK)) dev->protect_state = eeprom_read_protect(); return 0; }

I²C 上电初始化里最容易漏的两件事

① 总线恢复(清 SDA)。若上次通信在「主机发完地址但 EEPROM 正要回 ACK」时被复位,SDA 可能被 EEPROM 拉低,主机再发 Start 会被自己踩住——表现为「器件永远 NACK,I²C 总线锁死」。必须在初始化时先发 9 个 SCL 脉冲尝试释放 SDA。

② 不探测就直写。EEPROM 没有「写不进去就返回错误」的强约束(尤其 I²C 仅靠 ACK),地址接错或器件没贴都会表现为「写函数返回成功、读出来是 FFh」。初始化阶段务必做一次探测 + 回读校准。

3.3 器件识别与总线探测

  1. I²C 地址探测:发S + 1010 A2A1A0 0 + P,若收到 ACK 说明该地址上有器件;经典 24Cxx 用 A2/A1/A0 引脚区分同总线多片。
  2. SPI RDSR 探测:对每片拉 CS# 后发05h,应能读回一个字节(WEL=0 时通常为 0x00 或含保护位);读不到则器件不在位。
  3. 容量/页大小确认:I²C 按器件型号查表(24C02→256 B / 页 8 或 16 B,随料号);SPI 可试用9FhRDID(部分 25xx 支持)或直接查器件表。
  4. 写保护现状读取:SPI 读 SR 拿 BP/WPEN;I²C 经典器件读 WP# 引脚电平,确认与「期望的保护策略」一致。

什么时候用器件表而不是探测

BOM 固定的产品,页大小、地址宽度、保护位定义都应写死在器件表里,探测只用于「确认在位」。若固件要兼容同一总线上的多种 EEPROM,应以器件表为主键、探测为辅,避免把「4 KB 器件按 2 KB 地址访问」这类错误。

4 · 状态寄存器与锁存体系

SPI 25xx 有一张状态寄存器(RDSR/WRSR),I²C 24Cxx 则没有状态寄存器——它的「busy」完全靠 ACK 行为体现。理解这两套机制,就理解了 EEPROM 的写完成检测。

4.1 状态寄存器总览(SPI 25xx)

RDSR (05h) / WRSR (01h) —— 8 位状态寄存器WPENb7—b6BP2b5BP1b4BP0b3—b2WELb1WIPb0WIP = 1:内部写进行中(busy);= 0:就绪

图 3 · SPI 25xx 状态寄存器位图(不同密度 BP 位数量可能为 2~3 个)

名称读写上电默认值软件约定
bit7WPENR/W(经 WRSR)0为 1 时,WP# 引脚高电平才使能写保护
bit5~3BP2 / BP1 / BP0R/W(经 WRSR)0块保护组合(见第 8 章);默认 000 = 无保护
bit1WEL只读(由 WREN/WRDI 翻转)0写操作前必须为 1;写完自动清零
bit0WIP只读0写完成轮询的唯一判据

4.2 WIP / WEL 判读

写操作(WRITE 后)器件进入 busy:WIP=1;内部写完成后 WIP 自动清零。WEL 在每次写入结束后自动清零,下次写前必须重新 WREN。

/* SPI 25xx:读状态寄存器 */ uint8_t eeprom25_rdsr(void) { uint8_t tx = 0x05, rx = 0; spi_cs_low(); spi_transfer(&tx, 1); spi_read(&rx, 1); spi_cs_high(); return rx; } /* 写完成轮询:WIP 清零即完成 */ int eeprom25_wait_ready(uint32_t timeout_ms) { uint32_t t0 = now_ms(); while (eeprom25_rdsr() & SR_WIP) { if (now_ms() - t0 > timeout_ms) /* 建议 ≥ 10 ms(tWR max 5~10 ms) */ return -E_TIMEOUT; } return 0; }

WIP 只说忙不忙,写成功与否另看返回

SPI 25xx 的写失败(被保护)表现为:写被直接忽略,WIP 仍然正常清零,不会置任何错误位。也就是说「轮询到 WIP=0」只代表「器件闲了」,不代表「写进去了」。被保护的写要靠保护位检查提前拦截,而不是事后看状态。

4.3 写保护位与 WPEN

条件受保护区域说明
WPEN=0(默认)BP 位仍可通过 WRSR 配置,但 WP# 引脚不生效软件可自由改 BP,硬件锁不工作
WPEN=1 且 WP# 高阵列保护由 BP 位决定;SR 解锁,可改 WPEN/BP允许写且配置可改
WPEN=1 且 WP# 低阵列保护仍由 BP 位决定;但状态寄存器被锁(WRSR 被忽略)仅锁 SR(硬件写保护生效)

WPEN 是「是否允许 WP# 生效」的总开关,不是「是否保护」

很多工程师误以为 WPEN=1 就直接保护了阵列——其实阵列保护只由 BP 位决定(与 WP# 电平无关);WP# 低 + WPEN=1 仅锁状态寄存器(防改 BP/WPEN),WP# 拉高则 SR 解锁。只置 WPEN 不置 BP,阵列仍然可写。相反,I²C 经典 24Cxx 没有 WPEN/BP,WP# 一拉高就是整片保护,无需任何寄存器配置。

4.4 I²C 24Cxx 的无状态寄存器

I²C 24Cxx没有状态寄存器,也不提供读状态命令。它的「写入进行中」完全通过 ACK 行为体现:

  • 写周期(tWR)内,器件把 SDA 拉低,对S + 器件地址[W]返回NACK(第 9 时钟 SDA=1)
  • 写完成后,器件释放 SDA,再次探测时返回ACK(SDA=0)
  • 因此 I²C 的「写完成轮询」就是反复发起始 + 地址、看 ACK(详见第 6.2 节)。

I²C 写保护极简:只有 WP# 引脚

经典 24Cxx(含绝大多数 24C02/04/08/16/32/64/128/256)整片写保护只有一个开关:WP# 引脚为高→ 整片只读;为低 → 可写。没有 BP 区段、没有 WPEN。软件要保护某段数据,要么在硬件上把 WP# 接固定高电平,要么在固件里用「数据本身 + 校验 + 幂等提交」来兜底。

后续章节请下载「芯参谋」查看

……(篇幅原因, 还有17章· 详见芯参谋→方案设计)

当前分享仅开放前5章正文。详细资料请在芯参谋客户端中打开完整文档。

打开路径:🔧解决方案&应用市场分析 -> 原理方案设计 -> EEPROM_软件设计规范

📥 下载芯参谋客户端

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 2:14:43

【C++三方组件】Google Test:C++单元测试的事实标准

【C三方组件】Google Test&#xff1a;C单元测试的事实标准 【摘要】&#xff1a;main 里手写 if 断言再肉眼比对输出的年代&#xff0c;被 TEST() 宏终结——Google Test 用「宏注册 自动发现 独立运行」把测试变成一等代码。How 实测 TEST/EXPECT/ASSERT 断言语义、TEST_F …

作者头像 李华