#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; /* ダンプ出力 */
/*
* 設定更新
*/
void updateSetting() {
if (LOW == digitalRead(4)) {
if (statusPin4 == HIGH) {
statusPin4 = LOW;
dumpEEPROM();
}
} else {
statusPin4 = HIGH;
}
if (LOW == digitalRead(3)) {
if (statusPin3 == HIGH) {
statusPin3 = LOW;
byte* p = (byte*) &lightSet;
unsigned int adr = 0;
for (unsigned int i = 0; i < sizeof(lightSet); i++)
*p++ = EEPROM.read(adr++);
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("設定書き込み");
byte* p = (byte*) &lightSet;
unsigned int adr = 0;
for (unsigned int i = 0; i < sizeof(lightSet); i++)
EEPROM.write(adr++, *p++);
Serial.println(" ->書き込み完了");
}
} else {
statusPin2 = 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.read(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);
Serial.begin(115200);
}
void loop() {
updateSetting();
delay(100);
}
ボタンは3つ用意。すべて内蔵プルアップ、LOWの場合がボタンを押した状態となります。
連続して処理されないよう、ステータス管理して最初の押下のみ処理
●動作確認
・ダンプ出力ボタン
EEPROM size=1024
0000:FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
0010:FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF F
※EEPROMの初期値はすべてのbitが1でした。・読み込みボタン
消灯時間(sec)=0 検出距離(cm) =0 設定値初期化 消灯時間(sec)=10 検出距離(cm) =5
unsigned int 型は2バイト使用するので 0x0000と0xFFFFと比較
・書き込みボタン+ダンプ出力ボタン
設定書き込み ->書き込み完了
EEPROM size=1024
0000:0B 00 06 00 FF FF FF FF FF FF FF FF FF FF FF FF
0010:FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
「0B 00」 =11、「06 00」=6 なので書き込み成功です。
●その他
・sprintf_P(ch, PSTR("%04X:"), adr);
PSTR("xxx")は、F("文字列")と同じくSRAMではなくFlashメモリにデータを置くときに使用します。
sprintfで F()マクロを使おうとしたらエラーになってしまったので、色々調べると上の書き方のようです。 後ろに「_P」が付いているのが対応関数のようです。
RAM使用量は数バイトしか変わりません。メモリが足りないとかでなければsprintfで良さそうです。
ちなみにsprintfをすべてsprintf_Pに置き換えた場合、スケッチのサイズは変わりませんが、両方混在する場合は70バイトくらい差がでました。使う場合は混在させない方が節約になるかと。
・EEPROM.length()
何を返しているのか、ヘッダファイルから定義内容を調べて見ました。
〜arduino-1.8.2/hardware/arduino/avr/libraries/EEPROM/src/EEPROM.h
uint16_t length() { return E2END + 1; }
E2ENDに+1を返しているけど、このファイルには記載されていない。
下記2つをインクルードしているので参照する。
#include <avr/eeprom.h>
#include <avr/io.h>
arduino-1.8.2/hardware/tools/avr/avr/include/avr/eeprom.h
arduino-1.8.2/hardware/tools/avr/avr/include/avr/io.h
どちらにも記載がなかったが、io.hには下記の様にターゲットCPU毎の分岐があった。
#if defined (__AVR_AT94K__)
# include <avr/ioat94k.h>
使用CPUはATmega328なので探すと以下がヒット
#elif defined (__AVR_ATmega328P__)
# include <avr/iom328p.h>
#elif (defined __AVR_ATmega328__)
#include <avr/iom328.h>
※iom328.hはiom328p.hをインクルードしているのみだった
以下の様に定義されていた。0x3FF=1023なので、EEPROM.length()は1024
#define E2END 0x3F
ちなみにATmega168の方は以下の定義となっているので、EEPROM.length()は512となる
#define E2END 0x1FF
データシートを確認しても同じ値なので、内蔵のダンプ出力に使っても問題ないです。
Table 2-1. Memory Size Summary
ATmega168A 512Bytes
ATmega168PA 512Bytes
ATmega328 1KBytes
ATmega328P 1KBytes
0 件のコメント:
コメントを投稿