概要
ARDUINO等のマイコンで計測したデータを一旦保存できると便利かな(?)と思い、EEPROMの使い方を調査しました。
今回使用したのは、24FC1025-I/P という I2C通信で読み書きできる不揮発性メモリで、秋月電子通商で取り扱いしています。
接続
接続は下図の通りです。秋月電子通商製品ページの日本語データシート内に“SDA バスは VCC へのプルアップ抵抗を必要とします(通常、100kHz では 10kΩ、400kHz と 1MHzでは 2kΩ) ” との記載がありますので、2kΩプルアップ抵抗をつけています。
下記接続にて、I2Cアドレスは、block1:0x50,block1:0x54となります。
プログラム
EEPROM(24FC1025)側の1回の連続読み書きは、最大128バイトですが、今回の制御機器 Arduino Uno側の I2C送信バッファサイズ(32バイト)制約がある様です。大きいデータを送信する場合は、32バイト毎に分割送信が必要です。実際には、最初に書込アドレスを2バイト送信するので、1回の送信データは30バイトになってしまいます。また、メモリは128バイト単位で境界があるみたいで、境界を跨ぐ連続書き込みは出来ない様です。
あくまで、私個人の評価から経験的に得た理解なので、正確なものではありませんが、あまり、このメモリの評価に時間をかけられないこともあり、前記条件にてプログラム作成しています。
プログラムは起動時に32バイトステップで、ブロック番号とアドレス(ステップ番号(連番))をメモリに書き込みます。
書き込み後、シリアルモニター入力待ち状態になり、番号入力すると対応アドレスデータを読み込んでシリアルモニター出力します。
下図シリアルモニターの出力は、上側が書き込み(500ステップ毎に出力)、下側が指定メモリアドレスの読み込み状態です。
下表は、入力番号と呼び出すメモリアドレスの関係です。
プログラムコードです。
// BLOCK1:0~ffff(65535)
// BLOCK2:10000(65536)〜1FFFF(131071)
// 65536/32=2048 65536/64=1024
#include <Wire.h>
const int BTN_DN = 9;
const int BTN_UP = 8;
const int inp_swDelay = 50; // 50 ms (SW入力安定待ち時間)
byte EEPROM_AD[2] = { 0x50 , 0x54 }; // EEPROM 24FC1025 BLOCK1,2 I2Cアドレス
char MEM[32];
int wrt_blk = 32;
int loop_cnt = 2048 ;
int rd_no = 2045 ;
// 初期設定
void setup() {
Serial.begin(9600);
Wire.begin();
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DN, INPUT_PULLUP);
char myMsg[wrt_blk] ;
// 文字列単位でアドレス指定し書き込む場合
Serial.println("Memory Writing Start");
for(int ii = 0 ; ii < 2 ; ii++){
for(int jj = 0 ; jj < loop_cnt ; jj++){
sprintf( myMsg, "%s%d,%s%d %s", "BLK:",ii," ADRS:",jj,"$$$$$$$$$$$$$$$" );
//dtostrf( ii , 10 , 0 , myMsg );
writeEEPROM_SER( ii , jj * wrt_blk , myMsg , false);
if(jj%500==0){
Serial.println(myMsg);
}
}
}
Serial.println("Finished");
Serial.println("");
}
// ■連続処理■
void loop() {
if (Serial.available() > 0 ) {
String data = Serial.readStringUntil('\n'); // シリアルデータ受信 (改行まで)
//data.toCharArray(myMessage, sizeof(MEM)); // 受信データ出力
// 文字列の長さが1文字以上の場合
if (data.length() > 0) {
rd_no = data.toInt(); // 文字列を整数に変換する
rd_EEPROM_BY_SER();
}
}
// EEPROM 読み込み(位置アップ)
if(!inp_sw(BTN_UP)){
rd_no++;
rd_EEPROM_BY_SER();
}
// EEPROM 読み込み(位置ダウン)
if(!inp_sw(BTN_DN)){
rd_no--;
rd_EEPROM_BY_SER();
}
}
// ■関数■
// 書き込み(文字列単位→書込時間短縮)
// ARDUINO I2C送信バッファ32byte
void writeEEPROM_SER(unsigned int block, unsigned int address, byte *data, bool sp_wrt){
if(sp_wrt){
int adr_space = address + 30;
Wire.beginTransmission(EEPROM_AD[block]);
Wire.write((int)highByte(adr_space));
Wire.write((int)lowByte(adr_space));
Wire.write(0x20); // スペース
Wire.write(0x20); // スペース
Wire.endTransmission();
delay(5); // 書込サイクル完了待機
}
Wire.beginTransmission(EEPROM_AD[block]);
Wire.write((int)highByte(address));
Wire.write((int)lowByte(address));
for(unsigned int i=0; i < wrt_blk; i++){
Wire.write(data[i]);
}
Wire.endTransmission();
delay(5); // 書込サイクル完了待機
}
// 書き込み(1文字づつ)
void writeEEPROM(unsigned int block, unsigned int address, byte data){
Wire.beginTransmission(EEPROM_AD[block]);
Wire.write((int)highByte(address));
Wire.write((int)lowByte(address));
Wire.write(data);
Wire.endTransmission();
delay(5); // 書込サイクル完了待機
}
// アドレス指定データ読込(block1、2 連番)
void rd_EEPROM_BY_SER(){
//block1:0-2047,block2:2048-4095
if( rd_no < 0 ){ rd_no = 4095 ; }
else if( rd_no > 4095 ){ rd_no = 0 ; }
int blk_no = (int)(rd_no / loop_cnt) ;
int s_adrs = (int)(rd_no % loop_cnt) ;
for (unsigned int j = 0; j < wrt_blk; j++){
MEM[j] = readEEPROM( blk_no , j + wrt_blk * s_adrs );
}
char MSG[32];
sprintf( MSG , "< %d , %d , %d >", rd_no, blk_no, s_adrs );
Serial.print(MSG);
Serial.println(MEM);
}
// EEPROM データ読込(block、アドレス指定)
byte readEEPROM(unsigned int block , unsigned int address){
byte data;
byte EEP_AD = EEPROM_AD[block];
Wire.beginTransmission(EEP_AD);
Wire.write((int)highByte(address));
Wire.write((int)lowByte(address));
Wire.endTransmission();
Wire.requestFrom( EEP_AD , (byte)1 );
while(Wire.available() == 0);
data = Wire.read();
return data;
}
// ボタンSW入力安定待ち
boolean inp_sw(int pin){
boolean c_st;
boolean p_st;
p_st = digitalRead(pin);
for(int counter = 0; counter < inp_swDelay; counter++){
delay(5);
c_st= digitalRead(pin);
if( c_st != p_st){
counter = 0;
p_st = c_st;
}
}
return c_st;
}
まとめ
ある程度書き込み可能であることが判ってから、いろいろと確認・検証に時間がかかりました。仕様書には記載があるのかもしれませんが、英語記載なので正確に理解するのが難しいです。
いろいろ試行錯誤しましたが、とりあえず使えそうな感覚を持つことが出来ました。