[i=s] 本帖最后由 zhengwanjun1314 于 2026-11-8 15:13 编辑 [/i]
附件:AT24C02.zip
#技术资源##申请原创#
G32F031K8 做 I2C 主机,用轮询方式读写 AT24C02。写完一页后做应答查询,读回的数据和写入数据一致时 LED2 闪烁,失败则 LED2 常亮并停住。 ## 硬件 AT24C02 的 A2、A1、A0 接地。8 位器件地址写为 0xA0,读为 0xA1(7 位地址 0x50)。

SCL、SDA 各接一只外部上拉,常用 4.7 kΩ。片内上拉只作辅助。WP 接地,允许写入。VCC 与 G32F031 同电平。I2C 速率 100 kHz,标准模式,AT24C02 可以直接用。

AT24C02 要按页写
容量 256 字节,字地址 8 位,页大小 8 字节。页内地址到边界后会回到本页开头。从 0x06 连续写 8 字节时,前 2 字节落在第 0 页,后 6 字节必须另起一次传输写到第 1 页。一次写超过页尾,多出来的数据会覆盖本页开头。
每一页以 STOP 结束。芯片随后进入内部写周期,最长约 5 ms。这段时间里它不应答地址。例程在 STOP 之后先等 1 ms,再发 START + 0xA0 做应答查询,收到 ACK 才认为这一页写完。查询窗口约 10 ms 。
随机读的时序是:
`START + 0xA0 + 字地址 + 重复 START + 0xA1 + 数据 + NACK + STOP`

主机接收要分三种长度 G32F031 的 I2C 在收到地址后才会开始移入数据,NACK 和 STOP 必须赶在对应字节的应答位之前设好。
- 读 1 字节:清 ADDR 之前关 ACK,清 ADDR 后立刻发 STOP,再读数据寄存器。
- 读 2 字节:发读地址前打开 POS。清 ADDR 后关 ACK,等到 BTF 再发 STOP,然后连续读两次数据寄存器。POS 让 ACK 位作用在下一字节,第二字节才会被 NACK。
- 读 3 字节及以上:前面的字节按 RXNE 读。还剩 3 字节时等 BTF,关 ACK,读出倒数第三字节,再等 BTF、发 STOP,读出最后两字节。

重复起始必须等字地址的 BTF 置位后再产生。这时当前字节已经发完。起始位出现之后再写一次 `0xA1`。
读写函数
`main` 从地址 `0x06` 写入 `0xA0` 到 `0xA7` 共 8 字节,这个范围跨过页边界。随后整段读回比较,再单独做 1 字节读和 2 字节读。返回长度或数据不一致时进入错误处理。 接口:
uint16_t at24c02_write(uint16_t addr, uint8_t *buf, uint16_t size);
uint16_t at24c02_read(uint16_t addr, uint8_t *buf, uint16_t size);
返回值是已经确认写入或已经读到的字节数。地址加长度超过 256 时直接返回 0。
加 EEPROM 的作用
- AT24C02 用来存掉电后还要留下的小数据,例如设备编号、校准系数、上次设定和累计计数。这些数不跟程序放在一起,更新固件时可以留着。
- 掉电还在。 写入并完成内部写周期后,数据存在 EEPROM 里,重新上电可以直接读回。
- **和程序分开。** 参数在片外,擦除或更新片内程序时,用户配置还在原来的地址上。
- **适合经常改的小块数据。** 容量 256 字节,按 8 字节页写入。改几十字节即可,数据手册给出的写寿命是每页约 100 万次。片内 Flash 一次擦除是一整页,拿来频繁改参数会把整页寿命耗在少量数据上。
- **只占两根线。** SCL、SDA 接到 I2C,同一总线上还可以再挂别的器件。地址由 A2、A1、A0 决定,本例接地,器件地址是 `0xA0` / `0xA1`。
- **应用层按地址读写。** `at24c02_write` 和 `at24c02_read` 把页拆分、写周期查询和按长度接收收在里面。业务代码给出地址、缓冲区和长度即可。
-
读写函数 写函数按 8 字节页拆开。一页以 STOP 结束,应答查询成功后才把这一页计入返回值。读函数先写入字地址,等 BTF 后再发重复起始和 0xA1。读 2 字节时,重复起始之前要先打开 POS。
-
使用时注意 地址阶段若没有 ACK,ADDR 不会置位,AF 会置位。要清 AF 再发 STOP,否则下一次传输会一直超时。主机自身地址设为 0x00,避开 EEPROM 的 0xA0。SCL 和 SDA 都要打开输入,主机才能看到时钟和应答。