Arduino1台で複数のI2Cスレーブを実装する
ArduinoのI2CライブラリであるWireを使う場合、同時に複数のスレーブ(複数のアドレス)を
実装することが出来ません。
1つのI2Cアドレスを使って送信データで処理を振り分ければいいのですが、
現在取り組んでいる I2C EEPROMエミューレションの実装には必要な機能なのです。
エミュレションのターゲットは Microchip社の24LC1025(容量 128kバイト)ですが、
I2Cスレーブアドレスを2つもつデバイスです(内部的に 64kバイトEEPROMが二つある)。
仕様書ではスレーブアドレス部の2ビット目(B0)でバンク切り替えをしています。
(スレーブアドレス的には 0x50 と 0x54 となる ※ 7ビットアドレス指定の場合).
何とかならないかなぁと思い、Atmega328のデータシートを見ると、
複数のスレーブアドレスを同時実装することは可能であることが分かりました。
TWAMR (スレーブアドレスマスクレジスタ)を使えば良いと分かりました(下図)。
このレジスタをスレーブアドレス設定直後に、
Wire.begin(0x50);
TWAMR = B00001000;
とすれば、スレーブアドレスの2ビット目が1でも0でもアドレスとして判定されます。
結果としてスレーブアドレス 0x50と0x54が同時利用出来ます。
ただし、これだけでは受信したデータがスレーブアドレス0x50宛てなのか0x54宛てなのか
判定出来ません。
マスターからアドレスを受け取った直後にTWDRレジスタを参照してアドレス値
を取得する必要があります。
この対応にはライブラリの修正が必要となりました。
といっても、わずかな修正でマルチスレーブアドレス対応が出来ました。
twi.c の修正
・受信スレーブアドレスの保存変数の追加
static volatile uint8_t twi_adr; // 2016/04/30 add by Tamakichi
・割り込み処理ハンドラ関数 ISR(TWI_vect)のアドレス受信後にスレーブアドレス取得
するコードの追加
// Slave Receiver case TW_SR_SLA_ACK: // addressed, returned ack case TW_SR_GCALL_ACK: // addressed generally, returned ack case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack // save address 2016/04/30 by Tamakichi twi_adr = TWDR;
・保存したスレーブアドレス取得関数の追加、スレーブアドレスマスク設定関数の追加
uint8_t twi_get_address(void) { return twi_adr; } void twi_set_addressMask(uint8_t msk) { TWAMR = msk; }
twi.hの修正
・追加した関数のプロトタイプ宣言の追加
uint8_t twi_get_address(void); // 2016/04/30 add by Tamakitchi void twi_set_addressMask(uint8_t);// 2016/04/30 add by Tamakitchi
この修正で何とかいけそうです。
先日書いたスケッチで追加した関数を使って簡単な動作確認をしてみました。
下記はスケッチの抜粋(赤文字が追加部分)
// I2Cマスタからのデータ受信ハンドラ void receiveEvent(int len) { uint16_t d1,d2; uint16_t slaveAdr; int n = 0; slaveAdr = twi_get_address(); // ターゲットスレーブアドレス // アドレス情報取得 d1 = Wire.read(); d2 = Wire.read(); adr = (d1<<8) + d2; adr &= 0x3ff; #if MYDEBUG == 1 Serial.print("adr="); Serial.print(adr,HEX); Serial.write(' '); Serial.println(len,DEC); #endif // 送信されたデータを受信(データがある場合) while (Wire.available()){ buf[adr++] = Wire.read(); if (adr >= 1024) { adr= 0; } } } // セットアップ void setup() { adr = 0; Serial.begin(115200); Wire.begin(DEVICE_ADDRESS) ; // I2Cの初期化、自アドレスをA0とする twi_set_addressMask(B00001000); // 複数アドレス対応 DEVICE_ADDRESS+0x04 も許可) Wire.onRequest(requestEvent) ; // I2Cコマンド要求割込み関数の登録 Wire.onReceive(receiveEvent) ; // I2Cデータ受信割込み関数の登録 Serial.println("start"); }
IchigoJamからも二つのスレーブアドレスでの保存が出来ています。
(100~163がスレーブアドレス0x50の領域、164~227がスレーブアドレス0x54の領域)
slaveAdr = twi_get_address() で取得した値を出力して見るとちゃんとターゲットの
スレーブアドレスが取得出来ています。
もうちょっと動作確認し、問題が無ければWireオブジェクトのメソッドとして組み込んで
使いやすくしてみます。
« Arduino(あちゃんでいいの)でmicroSDカード利用の動作確認 | トップページ | Arduino(あちゃんでいいの)によるI2C EEPROMのエミュレーション (2) »
「arduino」カテゴリの記事
- Arduino IDE+Arduino STM32環境で指定と異なるgccが使われてしまう(2025.01.23)
- Zorin OSでArduino Uno互換機(CH340)が認識しない(2025.01.19)
- Arduino IDE 2.3.4でArduino STM32を利用する(2025.01.12)
- Arduino用 SKK日本語変換ライブラリの開発 その1(2024.12.28)
- NeoPixel(WS2812B)の制御 その5(2024.09.15)
« Arduino(あちゃんでいいの)でmicroSDカード利用の動作確認 | トップページ | Arduino(あちゃんでいいの)によるI2C EEPROMのエミュレーション (2) »
コメント