書き換え可能な不揮発メモリと言えば、USBメモリ、メモリカード、SSD等に使われる、NAND型Flashメモリが思い浮かびます。ですが、昔から使われてきたEEPROM(Electrically Erasable and Programmable ROM)と言うメモリもまた、書き換え可能な不揮発メモリです。
ファイルシステムを持っているOS等で使用するなら、Flashメモリ一択なのでしょうが、マイコンなどの非力なOSでFlashメモリを制御するのは、かなりハードルが高いです。そんな思いを共有する皆様には、EEPROMを推薦いたします。
まず、ライバルのFlashメモリと比較してみました(下表)。
指標\メモリ | EEPROM |
Flashメモリ (NAND型) |
用途 |
機器の動作設定等、 小容量のデータ保持用 |
メモリカードやSSD等、 データストレージ用 |
書き換え可能回数 | 多(100k回〜1,000k回) | 中(100k回程度) |
メモリ容量 | 小 | 大 |
書き込み時間 | 長い(遅い) | 短い(速い) |
取り扱い | 容易 | 複雑 |
優位と思われる指標を青、劣位と思われる指標を赤く着色してあります。各々、一長一短がある、と言う事はご理解いただけると思います。
今回のテーマでは、EEPROMの取り扱いが容易であるという一点で、大きな優位と捉えています。取り扱い(制御容易性)の一点に絞って、各々の特徴をまとめてみると、以下の様になります。
Flashメモリの難点:
EEPROMの利点:
EEPROMの使い勝手は、1)書き込みに少し時間がかかる、2)書き換え回数に制限がある、等の特性を除くと、ほとんどRAMと同じ様に使用可能です。これはFlashメモリには真似できない、最大の長所と言えましょう。
今回購入したのは、24C256と言う型名のチップが搭載された、下図の様なモジュールです。
モジュールと言っても、小さなボード上に8DIPの24C256チップがICソケット実装されていて、必要な端子が写真右側のピンヘッダに引き出されているだけ、の物です。ピンヘッダの反対側には、WP,A2,A1,A0のジャンパピンが見えます。
8DIPの24C256チップに対して、ピンヘッダとジャンパピンが計8本なので、全端子が引き出されている勘定です。
ICソケット実装なので、何なら、ソケットから取り外して、ブレッドボードや万能基板などに実装する事も可能です。
購入したショップの販売ページでは、「AT24C256」と明記されていました。「AT24C256」と言う型名でデータシートを検索すると、AtmelのAT24C256が見つかります。
AT24C256のデータシートを検索する(別ウインドウが開きます)
ですが、入手したチップのマークは、どうも、Atmel品とは異なる様です(下図)。
Atmel AT24C256は、I2Cのアドレスを指定する端子が2本(A1,A0)なのですが、今回のチップにはA2端子が追加されています。どうやら互換品みたいですね。
メーカ名が不明なので、その物ズバリのデータシートは入手できませんでした。よって、電気的特性まで含めてAT24C256と互換だと思い込み、AT24C256データシートを参照しながら先に進めることとします。
AT24C256特徴:
「互換品」である以上、「当たらずとも遠からず」なはず、ですよね?
通信仕様はI2Cですので、信号線2本でインターフェイスできます。
データシートからI2Cアクセス仕様を読み解いてみましょう。
I2Cアクセス仕様:
今回はArduino UNO等、普通にArduinoを使って制御して見ようと思っています。となれば、I2C通信なのでWireライブラリを使う事になります。
しかし、以前にもどこかの記事で指摘した様に、Wireライブラリ(Wire.h)のマクロBUFFER_LENGTH=32によって、バッファサイズが32バイトに固定されています。今までI2Cで一気に大きなデータを送受信する事は少なかったため、あまり不便を感じた事は無かったのですが、今回は違いました。
今回、64バイトのページ書き込みが可能なのですが、BUFFER_LENGTH=32の制限によって、64バイトの書き込みは実行できません。
※BUFFER_LENGTHには#defineマクロを使っているのだから、コンパイルオプションにしておけば良いのに……。決め打ちって何?とんだ糞仕様だ(下品にも「糞」呼ばわり、失敬。でも事実)。
今回のお買い物は以下の通りです。
他の商品と同時購入で3,500円を超え、送料無料でした。
その代わり、amazon発送の商品は、送料分を商品価格で回収する為か、3個とか5個の抱き合わせ販売が多いです。今回も3個の抱き合わせ販売でした。3個で699円なので、1個当たり233円ですね……。まあ、3個使う予定があるなら、高くは無いかな?1個しか使わない場合は、割高なので、ご注意ください。
今後、データログ用として頻繁に書き込みを行うため、書き換え回数の上限に達して性能が低下した場合の予備としたり、他の用途も思案中のため、「まあ、いいか」と考えて購入しました。
Arduino UNOと接続して、早速使って見たいと思います(以下接続表)。
Arduino UNO | 接続 | 24C256モジュール |
5V(※) | → | VCC |
GND | ⇔ | GND |
A5(SCL) | ⇔ | SCL |
A4(SDA) | ⇔ | SDA |
※Arduino DUEを使う場合、VCCには3.3Vを接続してください。
実際には、この予備実験の後で作成したプログラムなので順番が逆転してますが、次回解説・リンクするプログラムを使って実験を進めて行きます。
初期データを確認して見ました。
実験サンプル:【 】内はシリアルモニタからの送信データ
【dump】
OK+ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
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 ff
0020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
:
: (中略)
:
7fd0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
7fe0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
7ff0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
OK: dump 0x0000 ~ 32768
24C256は、256Kビット=32KバイトのEEPROMなので、アドレス範囲は0x0000〜0x7fffです。途中省略していますが、全アドレス範囲が0xffのデータで埋まっていることを確認しました。
EEPROMには「消去」と言う概念は無いのですが、少なくとも、工場出荷時は全データに0xffを書き込んだ状態で出荷している様です。
続いて、I2Cバス上のスレーブアドレスをスキャンして見ました。
【scan】
OK+ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 -- -- -- -- -- -- -- 58 -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
OK: scan i2c slave address
EEPROMモジュールは1個しか接続していないのに、スレーブアドレス0x50と0x58の2か所で応答がありました。
更に、こちらのA2端子ジャンパを設定したところ、検出するアドレスが0x54と0x5cに移動しました。この実験結果より、A2端子も機能している事が判りました。A2〜A0端子で8通りと合わせて、0x50〜0x5f、16通りのI2Cスレーブアドレスが設定可能です。
この内、0x50〜0x53は、Atmel AT24C256と互換のスレーブアドレスです。その他は、何か別な同等品との互換性確保用でしょうか??詳細は不明です。
24C256チップ1個1個の容量は256Kビットと小さいですが、I2Cバス上にスレーブアドレスを変えながら、最大8個を接続する事で、容量を×8倍まで増大させて使用可能です。それでもFlashメモリと比べると小さな容量に留まりますので、そこで住み分けする必要がありそうです。
そう言う意味では、今回購入したのは24C256×3個の抱き合わせ販売でしたので、3つ同時にI2Cバスに接続すると、256Kビット×3個=96KバイトのEEPROMとして使うこともできます。
EEPROMは、書き込みに時間がかかる(遅い)ので、高頻度(従って高速)にログを記録する必要がある用途の場合、書き込み時間は重要な要素となります。
EEPROMに対して書き込みコマンドを発行すると、それを受け取ったEEPROM内部で書き込みサイクルに入ります。結果、EEPROMは、内部の書き込みサイクルが終わるまでI2C通信に反応しなくなります。
具体的には、I2C通信のアドレスバイト送信に対するアクノリッジACKが返ってきません。
本来: | S [ADR,W](A) P | ACKが返る。 | |
書き込みサイクル中: | S [ADR,W](N) P | NACKが返る。 |
※凡例はこちらをご参照ください。
EEPROM内部の書き込みサイクルが終了すると、再びI2C通信に対してACKが返って来るようになります。
通常は、データを書き込んだらACKをポーリングして、書き込みが終わるまで待った方が良いです。EEPROMが書き込みサイクル中に次の読み出しなどを発行してしまうと、ACKが返らずにエラーとなってしまうからです。ACKが返るまで待機するコードは、以下の通りです。
// wait to finish internal write cycle
do
{
Wire.beginTransmission (i2c_slv_adr);
errcod=Wire.endTransmission ();
} while (errcod);
と言う事で、書き込み後のACKポーリング時間を計測すると、EEPROMの書き込みに要する時間、つまり、書き込みサイクル時間tWRを計測することができます。
実験に使ったプログラムでは、micros()関数でtWRを計測できるようになっています。計測してみましょう。
【wr,0060,01】
OK+ EEPROM[0x60]=0x1
OK+ tWR=2476 [us] / 1 [byte]
OK: wrote 1 data
【wr,0061,02,03,04,05,06,07,08,09】
OK+ EEPROM[0x61]=0x2
OK+ EEPROM[0x62]=0x3
OK+ EEPROM[0x63]=0x4
OK+ EEPROM[0x64]=0x5
OK+ EEPROM[0x65]=0x6
OK+ EEPROM[0x66]=0x7
OK+ EEPROM[0x67]=0x8
OK+ EEPROM[0x68]=0x9
OK+ tWR=2468 [us] / 8 [byte]
OK: wrote 8 data
【dump,0060】
OK+ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
0060: 01 02 03 04 05 06 07 08 09 ff ff ff ff ff ff ff
OK: dump 0x0060 ~ 16
上の実験結果をまとめると、手元の24C256チップの書き込み時間tWR(実測)は、以下の通りです。
多少のばらつきは確認されますが、要は、1バイト書き込みであろうが、1ページ(最大64バイト)書き込みであろうが、約2.5msの書き込み時間がかかっている様です。書き込みサイクル時間は、1バイトでも1ページでも同じ時間がかかる、と言う事なので、1バイト毎に書き込むより1ページ同時に書き込んだ方が、書き込み速度的には有利だということも判りました。
※それ故に、Wireライブラリの糞仕様には、がっかりです。BUFFER_LENGTH=32バイト毎に書き込みを分割せざるを得ず、折角のページ書き込み機能を活かしきれません。
AT24C256データシートではtWR=10ms(MAX)なので、規格値に比べると随分速いことも判明しました。
単に「規格値としては10msだけど、実力では速い」と言う話なのか、手元の互換チップがAtmel AT24C256より速かったのか、については、手元にAT24C256チップが無いため不明です。
なお、書き込み時間tWRの間にもマイコン側で並列して処理を進めたい様な、高パフォーマンスを要求されるケースでは、
等の処置も可能です。工夫してみてください。
具体的な用途については次回、検討して応用プログラムを作成してみたいと思います。