极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 98583|回复: 40

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

  [复制链接]
发表于 2011-12-5 18:54:04 | 显示全部楼层 |阅读模式
本帖最后由 弘毅 于 2013-5-3 21:41 编辑


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

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

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

  2. int address = 5;
  3. int value;

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

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

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

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

  14. void loop() {
  15. }
复制代码
下面就是实验的效果



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

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




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

  2. // 定义一个包含long, int, float, char[] 等各种类别的 config_type 结构
  3. struct config_type
  4. {
  5.   long alarm;
  6.   float average;
  7.   int mode;
  8.   char password[10];
  9. };

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

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

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

  24.   // 4) 通过串口输出读取回来的资料
  25.   Serial.print("alarm: ");
  26.   Serial.println(config_readback.alarm);
  27.   Serial.print("average: ");
  28.   Serial.println(config_readback.average);
  29.   Serial.print("mode: ");
  30.   Serial.println(config_readback.mode);
  31.   Serial.print("password: ");
  32.   Serial.println(config_readback.password);  
  33. }  

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

  38.   // 运行测试程序
  39.   eeprom_test();  
  40. }

  41. void loop()
  42. {
  43.   // do nothing  
  44. }
复制代码

此代码目前只能在0018或者更早的IDE中编译,0022或者更新的是无法使用的。




第二段代码用到的附件:


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

  4. // 定义一个包含long, int, float, char[] 等各种类别的 config_type 结构
  5. struct config_type
  6. {
  7.   long alarm;
  8.   float average;
  9.   int mode;
  10.   char password[10];
  11. };

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

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

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

  26.   // 4) 通过串口输出读取回来的资料
  27.   Serial.print("alarm: ");
  28.   Serial.println(config_readback.alarm);
  29.   Serial.print("average: ");
  30.   Serial.println(config_readback.average);
  31.   Serial.print("mode: ");
  32.   Serial.println(config_readback.mode);
  33.   Serial.print("password: ");
  34.   Serial.println(config_readback.password);  
  35. }  

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

  40.   // 运行测试程序
  41.   eeprom_test();  
  42. }

  43. void loop()
  44. {
  45.   // do nothing  
  46. }
复制代码

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

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

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

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

  26.   // 4) 通过串口输出读取回来的资料
  27.   Serial.print("alarm: ");
  28.   Serial.println(config_readback.alarm);
  29.   Serial.print("average: ");
  30.   Serial.println(config_readback.average);
  31.   Serial.print("mode: ");
  32.   Serial.println(config_readback.mode);
  33.   Serial.print("password: ");
  34.   Serial.println(config_readback.password);  
  35. }  

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

  40.   // 运行测试程序
  41.   eeprom_test();  
  42. }

  43. void loop()
  44. {
  45.   // do nothing  
  46. }
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2011-12-5 22:39:32 | 显示全部楼层
太实用了,谢谢弘毅的最新教程。盼望着有高手来改改这个库,让0022也能用上。
回复 支持 反对

使用道具 举报

发表于 2011-12-6 00:07:28 | 显示全部楼层
{:soso_e144:}这个好,以后EEPROM就完全没有障碍了。。。
回复 支持 反对

使用道具 举报

发表于 2011-12-6 00:10:06 | 显示全部楼层
刚才研究了一下avr libc的代码库,发现这个库已经有了很全的EEPROM处理函数了,ARDUINO只不过包装了其中最简单的一种:read_byte/write_byte.
更全的参见:

http://www.nongnu.org/avr-libc/u ... p__avr__eeprom.html

[code=cpp]<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))
[/code]
回复 支持 反对

使用道具 举报

发表于 2011-12-6 03:01:08 | 显示全部楼层
这实在也太巧了,我今天晚上刚刚好在研究这个问题~~~~~~嘿嘿!抛出去了一趟回来就0022也能用了!!~~
真的是人多力量大.........!
感谢弘毅,感谢assiss~!!
回复 支持 反对

使用道具 举报

发表于 2011-12-6 03:13:23 | 显示全部楼层
不对哇~~~~~我咋还是不能用捏?!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2011-12-6 04:14:12 | 显示全部楼层
恩~~~~~研究了一会儿以后又可以用了。
原来经过assiss大师的修改已经不需要eeprom_anything了,直接用Arduino原装的EEPROM库加上哪两行语句就可以了。

现在还希望assiss来给我们讲讲原理~~~起码我想知道。
是不是有点贪得无厌~??
回复 支持 反对

使用道具 举报

发表于 2011-12-6 07:44:38 | 显示全部楼层
太好了,感谢万能的QQ群!
回复 支持 反对

使用道具 举报

发表于 2011-12-6 07:45:56 | 显示全部楼层
还有个问题,如果只是想存个LONG类型,还需要创建结构变量config吗?
回复 支持 反对

使用道具 举报

发表于 2011-12-6 09:17:50 | 显示全部楼层
悠然小调 发表于 2011-12-6 07:45
还有个问题,如果只是想存个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);
回复 支持 反对

使用道具 举报

发表于 2011-12-6 09:18:54 | 显示全部楼层
坏鸟 发表于 2011-12-6 04:14
恩~~~~~研究了一会儿以后又可以用了。
原来经过assiss大师的修改已经不需要eeprom_anything了,直接用 ...

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

使用道具 举报

发表于 2011-12-6 09:20:06 | 显示全部楼层
其实我写的代码是多余的,avr libc库里已经有写任意类型到EEPROM的函数了:
eeprom_read_block
eeprom_write_block
回复 支持 反对

使用道具 举报

发表于 2011-12-6 09:37:59 | 显示全部楼层
正好需要,太及时了。我发现,经常到这里来转转,总可以捡到宝。
回复 支持 反对

使用道具 举报

发表于 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开始?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-3-28 20:25 , Processed in 0.046202 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表