2017年9月12日火曜日

内蔵EEPROMの操作(おまけ)

前回はEEPROM.readとかEEPROM.writeを呼び出した。
<EEPROM.h>を参照すると EEPROMClassが実際に呼びだされている。
read、writeの他、以下の定義が気になったので試してみました。

結論から書くと、read→get、write→putの置き換えで、構造体の操作の箇所のコードは短くなりますが、スケッチサイズは20バイトほど増えました。
自前の関数を作らなくていいので自分としては許容範囲。


    EERef operator[]( const int idx )    { return idx; }

    void update( int idx, uint8_t val )  { EERef( idx ).update( val ); }

    template< typename T > T &get( int idx, T &t ){
        EEPtr e = idx;
        uint8_t *ptr = (uint8_t*) &t;
        for( int count = sizeof(T) ; count ; --count, ++e )  *ptr++ = *e;
        return t;
    }

    template< typename T > const T &put( int idx, const T &t ){
        EEPtr e = idx;
        const uint8_t *ptr = (const uint8_t*) &t;
        for( int count = sizeof(T) ; count ; --count, ++e )  (*e).update( *ptr++ );
        return t;
    }


●書き換えた内容
読み込み(read→get)
      byte* p = (byte*) &lightSet;
      unsigned int adr = 0;
      for (unsigned int i = 0; i < sizeof(lightSet); i++)
        *p++ = EEPROM.read(adr++)

      EEPROM.get(0, lightSet);

書き込み(write→put)
      byte* p = (byte*) &lightSet;
      unsigned int adr = 0;
      for (unsigned int i = 0; i < sizeof(lightSet); i++)
        EEPROM.write(adr++, *p++);

      EEPROM.put(0, lightSet);

ダンプ出力(read→EERef参照)
    sprintf_P(ch, PSTR( "%02X ") ,EEPROM.read(adr));

    sprintf_P(ch, PSTR( "%02X ") , *EEPROM[adr]);

●追加(updateでEEPROMを0xFFで初期化)
      // 0xFFで初期化
      Serial.print("EEPROM初期化");
      for(unsigned int adr = 0; adr < EEPROM.length(); adr++) {
        EEPROM[adr].update( 0xFF );
      }
      Serial.println(" ->初期化完了");
添字でアドレスを指定して、updateの引数に値を指定する。
updateはEERefのupdateを呼び出している。定義を見ると↓の様になっていて値が同じなら更新しない動作となっている。
EERef &update( uint8_t in )          { return  in != *this ? *this = in : *this; }

書き込み回数の寿命が10万回なので、無駄な書き込みをしないように考慮しているんですね。
EEPROM.putも書き込みにはupdateを使っているので、こちらを使う方がよさそうです。


●最終的なソース
#include <EEPROM.h>

/* 制御用のパラメータ定義 */
struct LightSetting_t {
  unsigned int offWaitSec;  /* 消灯時間(sec) */
  unsigned int border;      /* 検出距離(cm) */
} lightSet;

/* EEPROM用の操作ボタン */
int statusPin2 = HIGH;  /* 書き込み */
int statusPin3 = HIGH;  /* 読み込み */
int statusPin4 = HIGH;  /* ダンプ出力 */
int statusPin8 = HIGH;  /* 初期化 */

/*
 * 設定更新
 */
void updateSetting() {
  if (LOW == digitalRead(4)) {
    if (statusPin4 == HIGH) {
      statusPin4 = LOW;
      dumpEEPROM();
    }
  } else {
    statusPin4 = HIGH;
  }
  if (LOW == digitalRead(3)) {
    if (statusPin3 == HIGH) {
      statusPin3 = LOW;

      // EEPROM読み込み
      EEPROM.get(0, lightSet);

      Serial.println("設定値");
      Serial.print("消灯時間(sec)=");Serial.println(lightSet.offWaitSec);
      Serial.print("検出距離(cm) =");Serial.println(lightSet.border);
      if (lightSet.offWaitSec == 0x0000 || 0xFFFF == lightSet.offWaitSec ) {
        Serial.println("設定値初期化");
        lightSet.offWaitSec = 10;
        lightSet.border = 5;
        Serial.print("消灯時間(sec)=");Serial.println(lightSet.offWaitSec);
        Serial.print("検出距離(cm) =");Serial.println(lightSet.border);
      }
    }
  } else {
    statusPin3 = HIGH;
  }
  if (LOW == digitalRead(2)) {
    if (statusPin2 == HIGH) {
      statusPin2 = LOW;
      // それぞれ+1する
      lightSet.offWaitSec++;
      lightSet.border++;

      Serial.print("設定書き込み");

      // EEPROM書き込み
      EEPROM.put(0, lightSet);

      Serial.println(" ->書き込み完了");
    }
  } else {
    statusPin2 = HIGH;
  }

  if (LOW == digitalRead(8)) {
    if (statusPin8 == HIGH) {
      statusPin8 = LOW;
      // 0xFFで初期化
      Serial.print("EEPROM初期化");
      for(unsigned int adr = 0; adr < EEPROM.length(); adr++) {
        EEPROM[adr].update( 0xFF );
      }
      Serial.println(" ->初期化完了");
    }
  } else {
    statusPin8 = HIGH;
  }  
}

void dumpEEPROM() {
  unsigned int adr = 0;
  unsigned int adrLength;
  byte data;
  char ch[6];
  Serial.print(F("EEPROM size="));
  Serial.println(EEPROM.length());
  adrLength = 32; // 最初の32バイトまで表示
  for(adr = 0; adr < adrLength; adr++) {
    if ((adr % 16) == 0) {
      sprintf_P(ch, PSTR("%04X:"), adr);
      Serial.print(ch);
    }
    sprintf_P(ch, PSTR( "%02X ") , *EEPROM[adr]);
    Serial.print(ch);

    if ((adr % 16) == 15) {
      Serial.println();
    }
  }
}
void setup() {
  /* EEPROMのダンプ出力、読み込み、書き込み用のボタン */
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  
  Serial.begin(115200);
}

void loop() {
  updateSetting();
  delay(100);
}

0 件のコメント:

コメントを投稿