I2C EEPROM (AT24C02) 学习与实战笔记
1. 核心概念理解
-
EEPROM (AT24C02): 单片机的“笔记本”。
-
特点: 掉电不丢失(Non-volatile)。
-
用途: 保存设置参数(如密码、阈值、状态)。
-
-
RAM (变量): 单片机的“黑板”。
- 特点: 读写快,但掉电即失。
-
交互逻辑:
-
开机时: 必须执行
EEPROM_Read,把“笔记本”里的数据抄到“黑板”上。 -
修改时: 执行
EEPROM_Write,把“黑板”上的新数据刻录到“笔记本”里。
-
2. 代码解析与记忆 (重点)
2.1 驱动函数对比
| 步骤 | Write (写函数) | Read (读函数) |
|---|---|---|
| 1. 起手式 | Start → 0xA0 → Addr | Start → 0xA0 → Addr |
| 2. 关键转折 | (直接写数据) | Start (重启) → 0xA1 |
| 3. 循环操作 | 发送数据 (SendByte) |
接收数据 (RecByte) |
| 4. 核心细节 | 必加 Delay(5ms) (等存储) |
判断 ACK/NACK (是否读完) |
2.2 记忆口诀
写 (Write):一路绿灯,写完记得歇一会 (Delay)。
读 (Read):先假写,回马枪 (Restart) 变 0xA1,收完数据分 0 和 1。
2.3 标准代码模板
C
// 写函数:指针强转,循环写入,写完延时
void EEPROM_Write(unsigned char* String, unsigned char addr, unsigned char num)
{
IIC_Start();
IIC_SendByte(0xA0); IIC_WaitAck(); // 呼叫写
IIC_SendByte(addr); IIC_WaitAck(); // 设地址
while(num--) {
IIC_SendByte(*String++); // 发送
IIC_WaitAck();
IIC_Delay(200); // 【重点】物理写入需要时间
}
IIC_Stop();
}
// 读函数:假写地址,重启读模式,ACK/NACK判断
void EEPROM_Read(unsigned char* String, unsigned char addr, unsigned char num)
{
IIC_Start();
IIC_SendByte(0xA0); IIC_WaitAck();
IIC_SendByte(addr); IIC_WaitAck(); // 假写,为了设指针
IIC_Start(); // 【重点】重启
IIC_SendByte(0xA1); IIC_WaitAck(); // 【重点】呼叫读 (0xA1)
while(num--) {
*String++ = IIC_RecByte(); // 接收
if(num) IIC_SendAck(0); // 还有数据?回 ACK (0)
else IIC_SendAck(1); // 没了?回 NACK (1)
}
IIC_Stop();
}
3. 常见逻辑陷阱 (Bug 预警)
3.1 地址冲突 (Address Collision)
-
现象: 保存了变量 B,结果变量 A 的值变了。
-
原因: 多个变量使用了同一个 EEPROM 地址(如都往
addr 0写)。 -
解决: 地址规划。给每个变量分配独立的“房间”。
3.2 页边界回卷 (Page Wrap)
-
现象: 连续写入超过 8 字节,或者跨越了 8 的倍数地址,数据覆盖了页首。
-
解决: 尽量按 8字节一页 进行对齐,不要一次性跨页写入。
4. 进阶技巧:存储 int/long 类型
4.1 存储空间换算
EEPROM 的最小单元是 1 Byte (字节)。
-
char: 占 1 个地址。 -
int: 占 2 个地址。 -
long: 占 4 个地址。
4.2 强制类型转换 (Type Casting)
为了把 int 拆成字节存进去,必须使用指针强转。
写入时:
C
int speed = 500;
// (unsigned char*)&speed : 把 int指针 伪装成 char指针
// 2 : 写入长度为 2 字节
EEPROM_Write((unsigned char*)&speed, 0, 2);
读取时:
C
int speed;
EEPROM_Read((unsigned char*)&speed, 0, 2);
4.3 为什么必须强转?
-
如果不转,指针加法
ptr++会按int的步长(跳2个字节)移动,导致数据漏写。 -
转为
char*后,ptr++按 1 个字节移动,保证数据被完整切片保存。
## 5. 完美的 Main 函数逻辑示例
C
void main()
{
// 1. 上电第一件事:恢复数据
// 注意:变量a和数组dat地址错开,互不干扰
EEPROM_Read(&a, 0, 1); // 从地址0读 a (1字节)
EEPROM_Read(dat, 8, 2); // 从地址8读 dat (2字节, int的话也是2)
// 2. 初始化系统
System_Init();
Timer0Init();
// 3. 进入循环
while (1)
{
Key_Proc(); // 按键修改并保存数据
Seg_Proc(); // 显示数据
}
}