极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 95632|回复: 34

Arduino入门教程--第二十五课--EEPROM的读写与获取串口数据流

[复制链接]
发表于 2013-5-3 22:36:38 | 显示全部楼层 |阅读模式
EEPROM (Electrically Erasable Programmable Read-Only Memory),电可擦可编程只读存储器,他是一种掉电后数据不丢失的存储芯片。

具体能做什么呢?比如~数字密码保险箱的密码掉电后不能丢失,他一般都是存放在EEPROM里面的。再比如,一些设备需要根据不同场合,输入特定的环境参数,每次启动要加载这些参数,这些参数,也是放在EEPROM里面的。


下面的代码,我们是让Arduino不停地从0号地址位开始的读取EEPROM,把读取到的数值通过串口输出到电脑上。同时,我们通过串口向Arduino输入数字,更新从0号地址位开始的EEPROM信息。间接的更新了输出到电脑上的数值。

我们这次使用的是Arduino官方的EEPROM库,但是官方库有一个遗憾,每次只能读取或者写入一个地址位~~所以我们在代码开头。。定了了两个函数EEPROM_write与EEPROM_read。括号内第一个参数是起始地址位,第二个就是写入的变量。

  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. unsigned long incomingByte = 0;   // 定义无符号长整数型变量incomingByte初始值为0

  5. void setup() {
  6.   Serial.begin(9600);     // 打开串口,设置数据传输速率9600
  7. }

  8. void loop() {


  9.   if (Serial.available() > 0) {
  10.     incomingByte = Serial.parseInt();  //读取串口传入的下一个有效整数,把该整数赋值给incomingByte变量
  11.     EEPROM_write(0,incomingByte)   //把incomingByte变量从0地址位开始写入EEPROM
  12.    
  13.   }
  14.   EEPROM_read(0,incomingByte) //从0地址位开始读取EEPROM,把值写入变量incomingByte
  15.   Serial.println(incomingByte); //通过串口输出
  16.   delay(1000);
  17. }
复制代码


上效果视频~请点击全屏看。。。



这次我们用到了一个串口命令Serial.parseInt(),他的作用是查找传入的串行数据流中的下一个有效的整数。Serial.read()只能每次读取一个字节。。。使用起来并不是很方便。Serial.parseInt()能一次读取一个数据流。。弥补了这方面使用上不便的缺陷。

Serial.parseInt()更多资料请查看WIKI
回复

使用道具 举报

发表于 2013-11-13 20:21:17 | 显示全部楼层
chaoser 发表于 2013-8-24 23:33
看到 byte *pp=(byte*)&p 的时候晕了一下,果然C语言的指针概念都还给老师了。

这里的意思应该是:定义了 ...

我来回答下==
1。我们下载的程序在 Flash空间里,不在EEPROM, 二者都可以断电保存数据,但是EEPROM的擦写次数(100000次)比Flash(10000次多)。。EEPROM可以在程序随时擦写,但是Flash不行(flash保存程序)。
2。最大字节表示可用程序存储空间,当然是Flash;
3。应该是可以放在括号里的;
4。注意 #define EEPROM_read(address,p)  的关键字是define,所以这是个宏定义,而不是函数,所以不用加分号;
回复 支持 1 反对 0

使用道具 举报

发表于 2013-8-24 23:33:37 | 显示全部楼层
看到 byte *pp=(byte*)&p 的时候晕了一下,果然C语言的指针概念都还给老师了。

这里的意思应该是:定义了一个字节型数组指针pp,将它指向了变量p。(byte*)是为了将&p强制转换为字节型指针,以便赋值给*pp的。

整个程序是先从串口读了一个不知道哪儿来的数值,然后就把这个数值写入EEPROM,之后又从EEPROM里读出一个数,最后把这个数显示出来,让你看看跟输入数是否相同。

问题:
1、EEPROM初始存了些什么?是不是就是我们下载过去的程序?
2、IDE总是显示:“二进制程序大小:2,906字节(最大32,256字节)”,后面的最大字节数是否就是EEPROM的大小?
3、LZ定义的两个函数,int i=0; 完全可以放进for函数里,否则老以为其他什么地方还用了i变量。
4、在loop里调用这两个自定义函数时,结尾部分LZ都没加分号,居然还调试过了,这是怎么一个情况??

  1. #include <EEPROM.h>
  2. #define EEPROM_read(address,p) {byte *pp=(byte*)&p; for(int i=0; i<sizeof(p); i++) pp[i]=EEPROM.read(address+i);}
  3. #define EEPROM_write(address,p) {byte *pp=(byte*)&p; for(int i=0; i<sizeof(p); i++) EEPROM.write(address+i, pp[i]);}
  4. unsigned long incomingByte = 0;   // 定义无符号长整数型变量incomingByte初始值为0

  5. void setup() {Serial.begin(9600);}
  6. void loop() {
  7.   if (Serial.available()>0) {
  8.     incomingByte=Serial.parseInt();  //读取串口传入的下一个有效整数
  9.     EEPROM_write(0,incomingByte);     //把incomingByte变量从0地址位开始写入EEPROM
  10.   }
  11.   EEPROM_read(0,incomingByte);        //从0地址位开始读取EEPROM,把值写入变量incomingByte
  12.   Serial.println(incomingByte);      //通过串口输出
  13.   delay(1000);
  14. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2013-9-6 17:57:07 | 显示全部楼层
请我的板子怎么写入值后怎么只能读取一次呢??后面就变成了0.怎么回事?

本帖子中包含更多资源

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

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-9-8 21:52:41 | 显示全部楼层
fairsky 发表于 2013-9-6 17:57
请我的板子怎么写入值后怎么只能读取一次呢??后面就变成了0.怎么回事?

换用1.0.4或者1.0.1的IDE试试。。。1.0.5的IDE出现好多代码编译后运行异常问题。。。估计官方在里面改了很多东西
回复 支持 反对

使用道具 举报

发表于 2013-9-23 21:46:20 | 显示全部楼层
chaoser 发表于 2013-8-24 23:33
看到 byte *pp=(byte*)&p 的时候晕了一下,果然C语言的指针概念都还给老师了。

这里的意思应该是:定义了 ...

顶贴!顶贴!顶贴!
回复 支持 反对

使用道具 举报

发表于 2013-10-17 19:55:16 | 显示全部楼层
fairsky 发表于 2013-9-6 17:57
请我的板子怎么写入值后怎么只能读取一次呢??后面就变成了0.怎么回事?

感觉是Serial.parseInt()留有未读完的数据(可能是串口工具的问题),下次串口有效,又读了一次,读出是零
在后面再加一条“Serial.parseInt();”,就没有这个问题了
回复 支持 反对

使用道具 举报

发表于 2013-10-28 14:04:53 | 显示全部楼层
可以对EEPROM进行空间划分分别存储一个数组吗???怎么实现啊???
回复 支持 反对

使用道具 举报

发表于 2013-10-28 14:06:16 | 显示全部楼层
怎么对其是否写入过数据进行判断???
回复 支持 反对

使用道具 举报

发表于 2013-11-27 17:36:06 | 显示全部楼层
强哥,编辑后出现这个,是什么情况呢。
sketch_nov27a.cpp: In function 'void loop()':
sketch_nov27a:14: error: 'class HardwareSerial' has no member named 'parseInt'
回复 支持 反对

使用道具 举报

发表于 2013-12-1 23:29:16 | 显示全部楼层
mark 下,以配以后使用
回复 支持 反对

使用道具 举报

发表于 2014-4-14 15:56:33 | 显示全部楼层
本帖最后由 Fortware 于 2014-4-14 16:00 编辑

弘毅大哥好 ,问个问题 宏定义里面
{int i = 0; byte *pp = (byte*)&(p);for(; i < sizeof(p); i++) EEPROM.write(address+i, pp);}

先获取把变量的地址,转化为byte类型的指针,然后复制给指针 *pp    引用时  为什么不是
*pp (或者*(pp+i))而是 pp        //抱歉,pp后面中括号显示不出来
回复 支持 反对

使用道具 举报

发表于 2014-6-30 11:22:44 | 显示全部楼层
本帖最后由 b9ss 于 2014-6-30 11:27 编辑

现学现用 想着这个不智能的IDE应该无法判断溢出,就试了试,结果发现EEPROM的实际寻址方式是用最大有效值取模。

  1. //ATmega328
  2. #include <EEPROM.h>


  3. void setup() {
  4.   Serial.begin(9600);
  5.   EEPROM.write(0,110);
  6.   EEPROM.write(1023,119);
  7.   EEPROM.write(1024,911);
  8. }

  9. void loop() {
  10. Serial.print("0::");
  11. Serial.println(EEPROM.read(0));
  12. Serial.print("1023::");
  13. Serial.println(EEPROM.read(1023));
  14. Serial.print("1024::");
  15. Serial.println(EEPROM.read(1024));
  16.   
  17. }

  18. /*输出
  19. 0::143
  20. 1023::119
  21. 1024::143
  22. */
复制代码
回复 支持 反对

使用道具 举报

发表于 2014-7-20 23:03:59 | 显示全部楼层
b9ss 发表于 2014-6-30 11:22
现学现用 想着这个不智能的IDE应该无法判断溢出,就试了试,结果发现EEPROM的实际寻址方式是用最大有效值取 ...

b9ss能帮我解释“EEPROM的实际寻址方式是用最大有效值取”这句话吗?还有输出结果我想不明白呢
回复 支持 反对

使用道具 举报

发表于 2014-8-11 20:33:24 | 显示全部楼层
迈步xxzj 发表于 2014-7-20 23:03
b9ss能帮我解释“EEPROM的实际寻址方式是用最大有效值取”这句话吗?还有输出结果我想不明白呢

其实就是转圈圈 比如说一个人原地空翻了720度 其实最后姿态和原始姿态的角度差为0
回复 支持 反对

使用道具 举报

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

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

Archiver|联系我们|极客工坊

GMT+8, 2024-4-20 13:00 , Processed in 0.043424 second(s), 31 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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