EEPROM、 2kバイトと4kバイトの間には壁がある (24LC16で)
タクトスイッチを押してみる …何万回も その4 続き
この回路に、外付けメモリー(FRAM:強誘電体メモリ)を増設。
スケッチをゴソゴソしてました。
※スケッチ:基がArduinoなんであえてこう呼ぼう
手持ちのFRAMで容量が大なのが16kビット=2kバイト。
サイプレスの「FM24C16B」という型番。
※32kバイトのFRAMはちょい高価。
そのうち手配しときますわ。
使い方はI2C EEPROMの24LC16なんかと同じなんですが、
I2C EEPROMには「2kバイトと4kバイトの間に壁」がありま
すんで要注意。
これをちょっとまとめておきます。
よく使うEEPROM、32kバイトの24LC256でしょうか。
しかし、このスケッチをそのまま24LC16には移せません。
アドレスの指定方法が異なるのです。
32kバイトの24LC256のアドレスは0x0000~0x7FFFの15bit。
2kバイトの24LC16だと0x0000~0x07FFと11bitになります。
24LC256だと、メモリアドレスの2バイトをこんな具合にH,Lバイト
2回に分けてコマンド送出します。
ところが24LC16(これより小さいメモリ)だと、送出アドレスは
1バイト(下位の8bit:0x00~0xFF)だけになり、アドレスの上位は
コマンドの下位3ビットに含ませて指定しなければなりません。
それが、「2kバイトと4kバイトの間の壁」です。
これを理解していないと、小容量EEPROMが使いこなせません。
2kバイト以下のはデバイスアドレスの下位3bitにメモリアドレスの
上位を混ぜるのです。
メモリの大きさで、アドレスとして有効となるビット位置が
異なります。
さらに、この「壁」が、読み出しの時にも関係してくるのです。
読み出しのアドレスも、書き込みと同じように上位側はコマンドの
下位3ビットで指定します。
続いて読み出しコマンドを送出するのですが、この時にも書き込みと
同じように、デバイスアドレスに下位3ビットを加えておかなくては
ならないのです。
※長いこと使ってないと、これを忘れてしまって、
「ありゃ?」っとなってしまうわけでして・・・
この処理を忘れてデバイスアドレス(0xA0)だけを読み出し
コマンドに送ってしまうと、ページ0のデータが出てきて
しまい、0x01FF~0x03FFのページ1,2,3のデータが
読み出せません。
最初のアドレス指定コマンドと読み出し開始コマンドの
下位3bitは同じ値にしておかなくてはならないのです。
4kバイト以上の24LC32や24LC64、24LC256では2バイトで
アドレスを渡すので、ここらは気にしなくてかまいません。
・4kバイト以上のEEPROMでの書き込み例
Wire.beginTransmission(DEV_ADR); // デバイスアドレス ★1
Wire.write(mem_adr >> 8); // メモリアドレス MSB
Wire.write(mem_adr & 0xFF); // LSB
Wire.write(data); // 1バイトデータ書き込み
Wire.endTransmission(); // 転送開始
delay(10); // 書き込み時間待ち
・4kバイト以上のEEPROMでの連続読み出し例
Wire.beginTransmission(DEV_ADR); // デバイスアドレス ★1
Wire.write(mem_adr >> 8); // メモリアドレス MSB
Wire.write(mem_adr & 0xFF); // LSB
Wire.endTransmission(); // アドレスを転送
Wire.requestFrom(DEV_ADR, qty) // qtyバイトデータ要求 ★2
for(i = 0; i < qty; i++){ // loop
if(Wire.available()) bff[i] = Wire.read(); // bffにコピー
}
2kバイト以下のEEPROMの場合、★1と★2を加工しなければなりません。
そして、忘れるのが★2の処理。
これをDEV_ADRだけで実行してしまうと、欲しいアドレスのデータが
出てこずにページ0のメモリが出てきちゃうのです。
なお、連続読み出しする場合のページ境界は意識しなくて大丈夫です。
0x00FFの次は0x0100のデータが、0x02FFの次は0x0300のが出て
きます。
※書き込みはページサイズが関係しますんで、
連続書き込みは別の処理が必要です。 (ページライト処理)
※wireライブラリ、I2Cの送受バッファが32バイトなんで、
長大な連続読み書きは一度にはできません。
※ライブラリWireのI2Cクロックは100kHzで、ちょい遅い。
24LC256は400kHzで動作ok。
setup()内でこんな具合にして高速化。
Wire.begin(); // 外付けEEPROM用I2C
Wire.setClock(400000L); // 100kHz→400kHzに ちょい早く
小容量のEEPROMやFRAMを使う時は、ちょっと注意が必要
ということで。
※表はMICROCHIPの「I2C. シリアルEEPROM ファミリ データシート」
より。
※もうひとつ: デバイスアドレスについて
一般的な24LC256などのEEPROM、データシートを見ると
「0xA0 : 0b10100000」がデバイスアドレス(制御コード)
になってます。
ところが・・・ArduinoのWireライブラにに食わせるアドレスは
「0x50 : 0b01010000」。
右に1ビットシフトした値を与えます。
これ、ライブラリ「twi.c」の中で
twi_slarw = TW_WRITE;
twi_slarw |= address << 1;
と、address値が左シフト処理されてI2C関連レジスタに
渡されるのです。
わざわざ、「なんでこうなった?」が不明。
誰か教えて!
※参考
・ArduinoでI2Cの外付けEEPROMを使う(radiopench.blog)
・Arduinoで外付けEEPROMを使う 続編
※追記
Arduino WireへのI2Cのアドレス、
そのままで良いのか1/2(右シフト)すべきか・・・
・DHT20センサー : 7ビットで記載。これはそのまま
・AM2320センサー : おそらく、1/2だろう
・富士通FRAM : 1/2で
・ローム照度センサー : 7ビットで記載。
そして最下位ビット1なんで間違いなくそのまま。
・I2C液晶表示器 : 1/2で。
8bitのI2Cコマンドとして「絵」が書いてあったら、
そのままで良いのか1/2しないと動かないのかが
はっきりします。
サンプル・スケッチの無いI2Cデバイス、使う時はご注意を。
| 固定リンク
「Arduino」カテゴリの記事
- 1/nカウント方式とDDS方式の2相パルス発生回路(2024.10.13)
- おっと。map関数の計算桁に注意(2024.10.06)
- DDS方式の2相パルス発生回路、周波数スキャン機能を付ける(2024.10.05)
- 1クロックでも速くしたい 割込を「ISR_NAKED」で(2024.09.30)
- 1クロックでも速くしたい DDS方式の2相パルス発生器(2024.09.27)
コメント
7bitのAddressと1bitのR/_Wで8bitの制御コードを構成していますが、
I2Cのデバイスによってはデータシートに7bitのAddress(7'b101_0000)が記載されていることもあるので、
引数に7bitのAddressを指定させ、内部で1bit左シフトした上でR/_Wを設定しているのではないでしょうか?
投稿: とり | 2021年11月29日 (月) 11時08分
というのか・・・メーカーがデーターシートで示しているアドレス(コマンド)は8bit。
そのLSBであるbit0がR/W区分としてI2C信号列に現れます。
ArduinoでEEPROMを使う時、「メーカーが示すアドレスを1/2して食わせろ」を知らないと・・・動かない。
これを「なぜ?」と思ったのです。
I2C EEPROMのメーカー、ロームや富士通、マイクロチップなどざっと見た限りでは、デバイスコードの上位が「1010に固定」と記されています。
そして、下位側にA2、A1、A0、R/Wが続き、その動きが8ビットのコマンドで説明されています。
しかし・・・センサーチップなど確かに7bitでアドレスを示しているデバイスもあります。(R/Wを除いて)
ここらの注意、初めて使う石だと、Arduino内部の処理を知らないと「動かない」ということに。
投稿: 居酒屋ガレージ店主(JH3DBO) | 2021年11月29日 (月) 13時49分