Arduino - 使用EEPROM保存数据

本文详细介绍如何在Arduino中使用EEPROM存储数据,包括基本的写入、读取操作,以及利用共用体实现复杂数据类型的存储技巧。

EEPROM (Electrically Erasable Programmable Read-Only Memory),电可擦可编程只读存储器–一种掉电后数据不丢失的存储芯片。
arduino已经为我们准备好了EEPROM类库”EEPROM.h”。
我们要使用得先调用EEPROM.h,然后使用write和read方法,即可操作EEPROM。

一、写入方法:EEPROM.write(address, value)

#include <EEPROM.h>
int addr = 0;
void setup()
{
}
void loop()
{
  //每次写入1个字节,注意不要溢出。
  //比如要保存模拟值,模拟值读出后是一个0-1024的值,但每字节的大小为0-255,容纳不了,所以这里可以变通一下,将值除以4再存储到val,就能够容纳了。使用的时候读出来再乘以4还原回来。  
  int val = analogRead(0) / 4;
  EEPROM.write(addr, val);

  addr = addr + 1;
  if (addr == 512)  // 当读写位置到了最大容量时,注意不要溢出。
    addr = 0;
  delay(100);
}

二、读取方法:EEPROM.read(address)

#include <EEPROM.h>
int address = 0;
byte value;
void setup()
{
  Serial.begin(9600);
}

void loop()
{
  // 读入一个字节
  value = EEPROM.read(address);

  Serial.print(address);
  Serial.print("\t");
  Serial.print(value, DEC);
  Serial.println();

  // 到下一个地址
  address = address + 1;

  // 注意地址不要溢出
  if (address == 512)
    address = 0;

  delay(500);
}

三、清除数据:每个地址都写入0

#include <EEPROM.h>
void setup()
{
  // 让EEPROM的512字节内容全部清零
  for (int i = 0; i < 512; i++)
    EEPROM.write(i, 0);
}
void loop()
{
}

四、使用共用体,快捷的存储各类型数据到EEPROM

首先了解一下什么是【共用体】:将几个不同(类型不同,但长度相同)的变量放到同一段内存中,共同占用这一段内存数据,使各变量的内存数据始终保持一致。

你可能发现了,使用Arduino提供的EEPROM API,只能将【字节型】的数据的存入EEPROM,也就是一次只能写入1个字节。如果要存储多个字节的数据类型,又需要怎么做呢?

例如:一个float类型的数据,需要占用4个字节的存储空间。因此我们可以把一个float拆分为4个字节,然后逐字节的写入EEPROM,来达到保存float数据的目的。但是手动计算的话,比较麻烦。
这里我们使用共用体,通过其它数据结果,快捷的把float数据拆分为4个字节。

首先定义一个名为data的共用体结构,共用体中有两个类型不同的成员变量:

union data
 {
   float a;  // 浮点型,占用4个字节
   byte b[4];  // 字节型数组,占用4个字节。
   // 这两个类型的变量都是占用4个字节,且是相同的4个字节,所以能始终保持数据一致,其中一个变了,另一个也随之改变。
 };

再申明一个data类型的变量col:

data col;

现在你可以通过 col.a 访问这个共用体中的 float 类型成员 a,也可以通过 col.b 访问这个共用体中的 byte 类型成员 b。
col.a 和 col.b 共同占用四个字节的地址。
给 col.a 赋值后,通过 col.b 即可拆分 float 为4个字节。

(1)通过共用体写入:

#include <EEPROM.h>
union data
{
  float a;
  byte b[4];
};
data col;
int addr = 0;

void setup()
{
  col.a=987.65;  // 通过 a 赋值
  for(int i=0;i<4;i++)
  EEPROM.write(i, col.b[i]);  // 通过 b 拆分为4个字节 
}

void loop()
{
}

(2)通过共用体读取:

#include <EEPROM.h>
union data
{
  float a;
  byte b[4];
};
data col;
int addr = 0;

void setup()
{
  Serial.begin(9600);   
  for(int i=0;i<4;i++)
  col.b[i]=EEPROM.read(i);
  Serial.println(col.a);
}

void loop()
{
}
<think>我们正在讨论EEPROM连续读写1024字节的实现方法。根据之前的引用,我们知道在连续读写EEPROM(如24C02)时可能会遇到乱码问题,特别是连续写入多个字节时。以下将结合引用内容详细说明实现步骤和注意事项。 ### 1. **理解EEPROM的页结构** - 大多数EEPROM(如24C02)具有页写功能,每页通常有固定大小(例如24C02是8字节/页)[^2]。 - **关键限制**:连续写入不能跨页,否则会覆盖页起始地址。若写入数据跨越页边界,必须分成多次写入,每次写入一页内连续数据。 ### 2. **连续写入1024字节的实现步骤** 假设我们使用24Cxx系列EEPROM(页大小$P$字节),总容量大于1024字节(如24C1024,页大小128字节): #### (1) 计算页边界 - 设起始地址为$A$,页大小为$P$ - 首页可写字节数:$N_1 = P - (A \mod P)$ - 后续每页可写$P$字节,直到写完1024字节 #### (2) 分段写入伪代码 ```c void EEPROM_WriteMulti(uint16_t addr, uint8_t *data, uint16_t len) { uint16_t bytes_written = 0; while (bytes_written < len) { uint16_t page_offset = addr % PAGE_SIZE; uint16_t bytes_remaining = PAGE_SIZE - page_offset; uint16_t write_len = min(len - bytes_written, bytes_remaining); I2C_Start(); I2C_SendByte(DEV_ADDR | WRITE); // 器件地址+写 I2C_SendByte(addr >> 8); // 地址高字节(若需要) I2C_SendByte(addr & 0xFF); // 地址低字节 for(uint16_t i=0; i<write_len; i++) { I2C_SendByte(data[bytes_written++]); } I2C_Stop(); addr += write_len; // 地址递增 delay(5); // 页写入延时5ms[^1][^2] } } ``` ### 3. **连续读取1024字节的实现** 读取操作没有页限制,但需要遵循以下协议: ```c void EEPROM_ReadMulti(uint16_t addr, uint8_t *buffer, uint16_t len) { I2C_Start(); I2C_SendByte(DEV_ADDR | WRITE); // 器件地址+写(设置地址指针) I2C_SendByte(addr >> 8); // 地址高字节 I2C_SendByte(addr & 0xFF); // 地址低字节 I2C_Start(); // 重复起始条件 I2C_SendByte(DEV_ADDR | READ); // 器件地址+读 for(uint16_t i=0; i<len-1; i++) { buffer[i] = I2C_ReceiveByte(ACK); // 接收数据并发送ACK } buffer[len-1] = I2C_ReceiveByte(NACK); // 最后一个字节发送NACK I2C_Stop(); } ``` ### 4. **关键问题解决** #### (a) 乱码问题(引用[1][2]) - **原因分析**:连续写入超出页边界导致地址回卷或未等待页写完成。 - **解决方案**: 1. **分页写入**:严格按页边界拆分数据包 2. **插入延时**:每页写入后延迟5-10ms(具体时间查阅器件手册) 3. **电压稳定**:确保VCC无波动(可增加滤波电容)[^1] #### (b) 地址对齐(引用[4]) - STC单片机等内置EEPROM需注意: - 物理地址与逻辑地址转换(如STC8H需加4000h偏移)[^4] - 擦除操作以扇区(512字节)为单位,需先擦除再写入 ### 5. 性能优化 - **DMA传输**:若MCU支持I2C DMA,可减少CPU占用 - **双缓冲机制**:当一页数据在写入时,准备下一页数据 - **异步处理**:写入延时期间可执行其他任务 > **注意事项**: > 1. 每次写入后必须等待$t_{WR}$(典型值5ms)[^1][^2] > 2. 超过器件最大地址时需换页(例如24C1024有2个设备地址选择位) > 3. 读操作无需分页但需正确发送NACK结束 §§相关问题§§ 1. EEPROM页写入延时具体如何确定? 2. I2C连续读取时如何避免总线超时? 3. 不同容量EEPROM的地址设置有什么区别? </think>### EEPROM连续读写1024字节的实现方法 #### 1. **硬件选型与限制** - **容量要求**:选择大于1024字节的EEPROM(如24C1024,容量128KB) - **页大小限制**: - 24C02:8字节/页 - 24C1024:128字节/页 - **关键约束**:连续写入不能跨页边界,否则数据损坏[^1][^2] #### 2. **分页写入策略** 对于1024字节写入(以128字节/页为例): ```c #define PAGE_SIZE 128 void write_1024bytes(uint16_t start_addr, uint8_t *data) { for (int i = 0; i < 1024; ) { uint16_t page_offset = start_addr % PAGE_SIZE; uint16_t write_size = min(PAGE_SIZE - page_offset, 1024 - i); // 启动I2C传输 i2c_start(); i2c_send_byte(0xA0 | ((start_addr >> 8) & 0x07)); // 设备地址 i2c_send_byte(start_addr & 0xFF); // 地址低字节 // 写入数据 for (int j = 0; j < write_size; j++) { i2c_send_byte(data[i++]); } i2c_stop(); // 等待EEPROM内部写入完成(关键!) delay(5); // 5ms延时避免乱码[^1][^2] start_addr += write_size; } } ``` #### 3. **连续读取实现** 读取无页限制但需正确设置地址: ```c void read_1024bytes(uint16_t start_addr, uint8_t *buffer) { i2c_start(); i2c_send_byte(0xA0 | ((start_addr >> 8) & 0x07)); // 写模式设地址 i2c_send_byte(start_addr & 0xFF); i2c_start(); // 重复起始条件 i2c_send_byte(0xA1); // 切换到读模式 // 连续读取1024字节 for (int i = 0; i < 1023; i++) { buffer[i] = i2c_read_byte(ACK); } buffer[1023] = i2c_read_byte(NACK); // 最后字节发NACK i2c_stop(); } ``` #### 4. **关键注意事项** 1. **延时要求**: - 每页写入后必须延迟3-10ms(见器件手册) - 未延时会导致内部编程失败,产生乱码[^1][^2] 2. **地址处理**: - STC单片机需地址偏移:`物理地址 + 4000h`[^4] - 16位地址需分高低字节发送 3. **电压稳定性**: - 写入时Vcc波动需<5% - 建议增加10μF滤波电容 4. **错误处理**: ```c if (!i2c_check_ack()) { i2c_stop(); return ERROR; } // ACK检测 ``` #### 5. **性能优化** - **页写入合并**:优先写满整页(减少停止条件次数) - **DMA传输**:使用硬件I2C+DMA减少CPU占用 - **双缓冲机制**:写入时准备下一页数据 > **验证建议**: > 1. 写入后立即读取校验首尾字节 > 2. 使用逻辑分析仪捕获I2C波形[^3] > 3. 循环压力测试(>10万次)验证可靠性
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卢光庆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值