EEPROM(24FC1025)を使う

概要

 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;                   
}

まとめ

 ある程度書き込み可能であることが判ってから、いろいろと確認・検証に時間がかかりました。仕様書には記載があるのかもしれませんが、英語記載なので正確に理解するのが難しいです。
 いろいろ試行錯誤しましたが、とりあえず使えそうな感覚を持つことが出来ました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です