弘毅 发表于 2011-12-5 18:54:04

arduino学习笔记30 - EEPROM读写实验

本帖最后由 弘毅 于 2013-5-3 21:41 编辑


EEPROM,或写作E2PROM,全称电子抹除式可复写只读存储器 (英语:Electrically-Erasable Programmable Read-Only Memory),是一种可以通过电子方式多次复写的半导体存储设备。

arduino的控制芯片中都自带了EEPROM,它的用途比较广,掉电后需要保存的信息都可以存储到这里。比如电子保险箱的密码。

咱们先用官方库做一个实验,在第五个地址中写入99,然后再读出来,通过串口输出。#include <EEPROM.h>

int address = 5;
int value;

void setup() {
Serial.begin(9600);

// 在address 5上写入数值99
EEPROM.write(address, 99);

// 读取address 5上的内容
value = EEPROM.read(address);

//将value输出到Serial port
Serial.print(value,DEC);// 串口输出99
Serial.print("\n");
}

void loop() {
}下面就是实验的效果



官方的EEPROM库只能一个地址一个地址的进行读写,如果遇到int(2 bytes),long(4 bytes)或者数组等等,就需要自己进行拆分成1 bytes才能进行存储,不是很方便。

我们可以使用eeprom_anything这个程序来处理这种复杂的问题。
我们把附件下载下来后,一定要把eeprom_anything的两个文件,与需要编译的程序文件放在一个目录中。



下面这个是程序代码,在EEPROM中存储,long、float、int、char四种不同类型的数据。#include "eeprom_anything.h"

// 定义一个包含long, int, float, char[] 等各种类别的 config_type 结构
struct config_type
{
long alarm;
float average;
int mode;
char password;
};

// 测试EEPROM_writeAnything()与测试EEPROM_readAnything()两个命令
void eeprom_test()
{
// 1) 定义结构变量config,并定义config的内容
config_type config;
config.alarm = 12345;
config.average = 3.14;
config.mode = 1;
strcpy(config.password, "arduino");

// 2) 把变量config存储到EEPROM,从地址0开始写入。
EEPROM_writeAnything(0, config);

// 3) 从EEPROM地址0开始读取,结果存储在config_readback
config_type config_readback;
EEPROM_readAnything(0, config_readback);

// 4) 通过串口输出读取回来的资料
Serial.print("alarm: ");
Serial.println(config_readback.alarm);
Serial.print("average: ");
Serial.println(config_readback.average);
Serial.print("mode: ");
Serial.println(config_readback.mode);
Serial.print("password: ");
Serial.println(config_readback.password);
}

void setup()
{
// 开启Serial port,设置波特率为9600
Serial.begin(9600);

// 运行测试程序
eeprom_test();
}

void loop()
{
// do nothing
}
此代码目前只能在0018或者更早的IDE中编译,0022或者更新的是无法使用的。



第二段代码用到的附件:


感谢ASSISS童鞋对问题代码进行了优化编写,现在只要2行代码就能解决这个问题了。而且0022,0023也可以使用#include <EEPROM.h>
#define EEPROM_write(address, p) {int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) EEPROM.write(address+i, pp);}
#define EEPROM_read(address, p){int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) pp=EEPROM.read(address+i);}

// 定义一个包含long, int, float, char[] 等各种类别的 config_type 结构
struct config_type
{
long alarm;
float average;
int mode;
char password;
};

// 测试EEPROM_writeAnything()与测试EEPROM_readAnything()两个命令
void eeprom_test()
{
// 1) 定义结构变量config,并定义config的内容
config_type config;
config.alarm = 12345;
config.average = 3.14;
config.mode = 1;
strcpy(config.password, "arduino");

// 2) 把变量config存储到EEPROM,从地址0开始写入。
EEPROM_write(0, config);

// 3) 从EEPROM地址0开始读取,结果存储在config_readback
config_type config_readback;
EEPROM_read(0, config_readback);

// 4) 通过串口输出读取回来的资料
Serial.print("alarm: ");
Serial.println(config_readback.alarm);
Serial.print("average: ");
Serial.println(config_readback.average);
Serial.print("mode: ");
Serial.println(config_readback.mode);
Serial.print("password: ");
Serial.println(config_readback.password);
}

void setup()
{
// 开启Serial port,设置波特率为9600
Serial.begin(9600);

// 运行测试程序
eeprom_test();
}

void loop()
{
// do nothing
}
再放出一个代码,是ASSISS童鞋改用AVR自带的eepron函数进行读写操作,大家可以研究下。。和调用arduino的封装有什么不同。我发现调用AVR编译出来的问题件居然还要大一些。。。晕了晕了。
#include <avr/eeprom.h>
#define EEPROM_write(address, var) eeprom_write_block((const void *)&(var), (void *)(address), sizeof(var))
#define EEPROM_read(address, var) eeprom_read_block((void *)&(var), (const void *)(address), sizeof(var))
// 定义一个包含long, int, float, char[] 等各种类别的 config_type 结构
struct config_type
{
long alarm;
float average;
int mode;
char password;
};

// 测试EEPROM_writeAnything()与测试EEPROM_readAnything()两个命令
void eeprom_test()
{
// 1) 定义结构变量config,并定义config的内容
config_type config;
config.alarm = 12345;
config.average = 3.14;
config.mode = 1;
strcpy(config.password, "arduino");

// 2) 把变量config存储到EEPROM,从地址0开始写入。
EEPROM_write(0, config);

// 3) 从EEPROM地址0开始读取,结果存储在config_readback
config_type config_readback;
EEPROM_read(0, config_readback);

// 4) 通过串口输出读取回来的资料
Serial.print("alarm: ");
Serial.println(config_readback.alarm);
Serial.print("average: ");
Serial.println(config_readback.average);
Serial.print("mode: ");
Serial.println(config_readback.mode);
Serial.print("password: ");
Serial.println(config_readback.password);
}

void setup()
{
// 开启Serial port,设置波特率为9600
Serial.begin(9600);

// 运行测试程序
eeprom_test();
}

void loop()
{
// do nothing
}

悠然小调 发表于 2011-12-5 22:39:32

太实用了,谢谢弘毅的最新教程。盼望着有高手来改改这个库,让0022也能用上。

Ansifa 发表于 2011-12-6 00:07:28

{:soso_e144:}这个好,以后EEPROM就完全没有障碍了。。。

assiss 发表于 2011-12-6 00:10:06

刚才研究了一下avr libc的代码库,发现这个库已经有了很全的EEPROM处理函数了,ARDUINO只不过包装了其中最简单的一种:read_byte/write_byte.
更全的参见:

http://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html

<avr/eeprom.h>: EEPROM handling

Defines

#define         EEMEM   __attribute__((section(".eeprom")))
#define         eeprom_is_ready()
#define         eeprom_busy_wait()   do {} while (!eeprom_is_ready())
Functions

uint8_t         eeprom_read_byte (const uint8_t *__p) __ATTR_PURE__
uint16_t         eeprom_read_word (const uint16_t *__p) __ATTR_PURE__
uint32_t         eeprom_read_dword (const uint32_t *__p) __ATTR_PURE__
float         eeprom_read_float (const float *__p) __ATTR_PURE__
void         eeprom_read_block (void *__dst, const void *__src, size_t __n)
void         eeprom_write_byte (uint8_t *__p, uint8_t __value)
void         eeprom_write_word (uint16_t *__p, uint16_t __value)
void         eeprom_write_dword (uint32_t *__p, uint32_t __value)
void         eeprom_write_float (float *__p, float __value)
void         eeprom_write_block (const void *__src, void *__dst, size_t __n)
void         eeprom_update_byte (uint8_t *__p, uint8_t __value)
void         eeprom_update_word (uint16_t *__p, uint16_t __value)
void         eeprom_update_dword (uint32_t *__p, uint32_t __value)
void         eeprom_update_float (float *__p, float __value)
void         eeprom_update_block (const void *__src, void *__dst, size_t __n)
IAR C compatibility defines

#define         _EEPUT(addr, val)   eeprom_write_byte ((uint8_t *)(addr), (uint8_t)(val))
#define         __EEPUT(addr, val)   eeprom_write_byte ((uint8_t *)(addr), (uint8_t)(val))
#define         _EEGET(var, addr)   (var) = eeprom_read_byte ((const uint8_t *)(addr))
#define         __EEGET(var, addr)   (var) = eeprom_read_byte ((const uint8_t *)(addr))

坏鸟 发表于 2011-12-6 03:01:08

这实在也太巧了,我今天晚上刚刚好在研究这个问题~~~~~~嘿嘿!抛出去了一趟回来就0022也能用了!!~~
真的是人多力量大.........!
感谢弘毅,感谢assiss~!!:lol:lol

坏鸟 发表于 2011-12-6 03:13:23

不对哇~~~~~我咋还是不能用捏?!

坏鸟 发表于 2011-12-6 04:14:12

恩~~~~~研究了一会儿以后又可以用了。
原来经过assiss大师的修改已经不需要eeprom_anything了,直接用Arduino原装的EEPROM库加上哪两行语句就可以了。

现在还希望assiss来给我们讲讲原理~~~起码我想知道。:funk:
是不是有点贪得无厌~??;P;P:lol;P

悠然小调 发表于 2011-12-6 07:44:38

太好了,感谢万能的QQ群!

悠然小调 发表于 2011-12-6 07:45:56

还有个问题,如果只是想存个LONG类型,还需要创建结构变量config吗?

assiss 发表于 2011-12-6 09:17:50

悠然小调 发表于 2011-12-6 07:45 static/image/common/back.gif
还有个问题,如果只是想存个LONG类型,还需要创建结构变量config吗?

不用,这里的EEPROM_write和EEPROM_read是可以存储任意类型的。
如:
long a = 12345, b=0;
byte c = 12, d = 0;
EEPROM_write(0, a);
EEPROM_read(0, b);
EEPROM_write(0+sizeof(a), c);//因为前面sizeof(a)个字节已经写了a了,为了不覆盖,就从sizeof(a)位置写起。
EEPROM_read(0+sizeof(a), d);

assiss 发表于 2011-12-6 09:18:54

坏鸟 发表于 2011-12-6 04:14 static/image/common/back.gif
恩~~~~~研究了一会儿以后又可以用了。
原来经过assiss大师的修改已经不需要eeprom_anything了,直接用 ...

代码已经在那里了,其实很简单啊,就是按字节存储任意类型。读取的时候再按字节顺序读出来。

assiss 发表于 2011-12-6 09:20:06

其实我写的代码是多余的,avr libc库里已经有写任意类型到EEPROM的函数了:
eeprom_read_block
eeprom_write_block

shyjdn 发表于 2011-12-6 09:37:59

正好需要,太及时了。我发现,经常到这里来转转,总可以捡到宝。:lol:lol:lol

悠然小调 发表于 2011-12-6 10:48:14

在 ASSISS大神的耐心指导下菜鸟也学会了使用EEPROM

本帖最后由 悠然小调 于 2011-12-6 12:03 编辑

#include <avr/eeprom.h>
#define EEPROM_write(address, var) eeprom_write_block((const void *)&(var), (void *)(address), sizeof(var))
#define EEPROM_read(address, var) eeprom_read_block((void *)&(var), (const void *)(address), sizeof(var))
void setup()
{
Serial.begin(9600);
long a = 12345,b=0 ;
byte c = 12,d=0;
EEPROM_write(0,a);
EEPROM_read(0,b);
Serial.println(b,DEC);
EEPROM_write(4,c);
EEPROM_read(4,d);
Serial.println(d,DEC);
}
void loop()
{}

悠然小调 发表于 2011-12-6 16:53:39

还有点不明白:
long a = 12345,b=0 ;
byte c = 12,d=0;
EEPROM_write(0,a);
EEPROM_read(0,b);
Serial.println(b,DEC);
EEPROM_write(4,c);
EEPROM_read(4,d);这样存储a=12345有5个数字,从地址0开始存,最后一个地址不是到4了吗?到存c=12时不是应该从地址5开始,最后一个地址到6吗?
那么EEPROM_write(4,c);
EEPROM_read(4,d);为什么存第二个数值时却是从地址4开始?
页: [1] 2 3
查看完整版本: arduino学习笔记30 - EEPROM读写实验