2014/03/14

Arduino:自訂整組資料讀寫EEPROM

Arduino的AVR微控制器晶片裡,含有EEPROM,可在程式執行時動態讀寫資料,不因電源中斷而消失。可使用Arduino官方程式庫EEPROM進行存取,但其介面以「一個byte」為單位,用起來有點不便,底下介紹如何自訂C語言struct,作為讀寫單位。

我的環境是Arduino Uno板、軟體版本1.0.5。

分成兩個檔案,首先是eeprom_anything.h
#ifndef eeprom_anything_h
#define eeprom_anything_h

#include <Arduino.h>
#include <EEPROM.h>

// 利用這兩個C++模板,讀寫整組資料
// 參數address是EEPROM位址,參數data是想讀寫的資料結構
template <class T> int EEPROM_writeAnything(int address, const T &data);
template <class T> int EEPROM_readAnything(int address, T &data);

// 函式模板的定義
// 使用EEPROM程式庫寫入一個一個的byte
template <class T> int EEPROM_writeAnything(int address, const T &data)
{
  const byte *p = (const byte *)(const void *)&data;
  int i, n;
  for(i = 0, n = sizeof(data); i < n; i++)
    EEPROM.write(address++, *p++);
  return i;
}
template <class T> int EEPROM_readAnything(int address, T &data)
{
  byte *p = (byte *)(void *)&data;
  int i, n;
  for(i = 0, n = sizeof(data); i < n; i++)
    *p++ = EEPROM.read(address++);
  return i;
}

#endif

然後是主草稿碼EEPROM_struct.ino
#include <EEPROM.h>
#include "eeprom_anything.h"

// 自行定義struct,放入你想讀取的資料
struct dataType{
  char name[10]; 
  int age;
  char gender;
  float money;
  float bank;
};

void setup(){
  Serial.begin(115200);
 
  dataType d; // 宣告結構變數,放入資料
  strcpy(d.name, "Arduino");
  d.age = 9;
  d.gender = 'M';
  d.money = 9876.54;
  d.bank = 1234.56;
  Serial.print("name: ");
  Serial.println(d.name);
  Serial.print("age: ");
  Serial.println(d.age);
  Serial.print("gender: ");
  Serial.println(d.gender);
  Serial.print("money: ");
  Serial.println(d.money);
  Serial.print("bank: ");
  Serial.println(d.bank);

  // 寫入EEPROM,回傳值count代表總共寫入幾個byte
  int count = EEPROM_writeAnything(0, d);
  Serial.print(count);
  Serial.println(" bytes written.");
  Serial.println("---------------");
}
void loop(){
  // 讀取EEPROM,回傳值count代表總共寫入幾個byte
  dataType d;
  int count = EEPROM_readAnything(0, d);
 
  Serial.print("name: ");
  Serial.println(d.name);
  Serial.print("age: ");
  Serial.println(d.age);
  Serial.print("gender: ");
  Serial.println(d.gender);
  Serial.print("money: ");
  Serial.println(d.money);
  Serial.print("bank: ");
  Serial.println(d.bank);
  Serial.print(count);
  Serial.println(" bytes read.");
 
  while(1){
  }
}

其執行結果應輸出類似底下的訊息:
name: Arduino
age: 9
gender: M
money: 9876.54
bank: 1234.56
21 bytes written.
---------------
name: Arduino
age: 9
gender: M
money: 9876.54
bank: 1234.56
21 bytes read.

耶!

參考資料:

6 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. 請問這是讀寫Arduino本身的EEPROM用的程式嗎?如果要用Arduino讀外部的EEPROM呢?

    ReplyDelete
    Replies
    1. yes.

      需要適合外部EEPROM的arduino程式庫,
      譬如http://playground.arduino.cc/Main/LibraryForI2CEEPROM

      Delete
  3. 葉大,請問若要平均使用每個記憶體空間,程式碼要怎麼改會比較好。
    因為依照您的code是重複讀寫著address = 0 的位置。
    希望能平均使用記憶體,還請賜教

    ReplyDelete
    Replies
    1. 你需要的功能叫做wear leveling(耗損均攤)。

      有找到一個程式庫https://github.com/drjoju/Arduino-wear-leveling
      但我沒實際用過。

      Delete
    2. 謝謝你指引方向,不然google整天也不知道有這東西,再次感謝

      Delete