|
setup diary |
I2Cでは,複数のデバイスと二本の信号線だけで通信することができます.しかしそれには,アドレスが重複していないという条件が必要です.同じアドレスのデバイスが二個あり,これらと通信するためには,二系統のI2Cを使うか,切り替え機能のあるICを使って,通信を実現することができ,そうしている人が多いようである.
ICを使って切り替えるとしても,デバイスを選択するために信号線が必要なので,その信号線を使って通信を行なってしまえば,新たなICを使わないでも二個のデバイスと通信ができる.この際,SCLは共通に使って,SDAをそれぞれのデバイスに一本ずつ使えば,三本の信号線で二個のデバイスと通信できる.逆に,クロックが送られなければ通信は行なわれないと考えて,SDAを共通にして,SCLをそれぞれのデバイスに割り当てることもできるだろう.前者の場合には,クロックが共通なので,同時に二個のデバイスと,同じバイト数の通信を行なうことになる.後者の場合には,別々のタイミングで,それぞれのデバイスと通信することになる.
上記の考えを拡張すると,アドレスが重複しているn個のデバイスとの通信は,(n+1)本の信号線を使うと実現できることが分かる.ほぼ同じデータをやりとりする場合には,SCL共通の方が一度に通信できるという点で優れており,送受信するデータ数やタイミングがデバイス毎に違う場合には,SDA共通が良いだろう.
arduinoを使って,SCL共通でこれを実現するためのプログラムを試しに書いてみた.mulWireBegin()で初期化を行い,mulWireBeginTransmittion()とmulWireWrite()とmulWireEndTransmittion()を使って,デバイスに送信する.受信は,mulWireRequestFrom()とmulWireRead()とmulWireEndRequest()を使って行なう.バッファなどは無く,これらのコマンドの実行と同時に通信が行なわれるので,受信のときにもその終了操作を行なうコマンドが必要になる点が,通常のarduinoのI2Cのコマンドと異なる.参考にソースを載せておくが,まだ動作確認を行なっていないので,動作は保証できません.
#define MulWireDelay 6 // 6 micro second 100kHz #define mwNUM 2 #define mwSCL A5 uint8_t mwSDA[mwNUM]={A4,A3}; uint8_t mwadrs[mwNUM]={0x68,0x68}; void mulWireBegin(){ char i; pinMode(mwSCL,OUTPUT); for(i=0;i<mwNUM;i++){pinMode(mwSDA[i],OUTPUT);} digitalWrite(mwSCL,HIGH); for(i=0;i<mwNUM;i++){digitalWrite(mwSDA[i],HIGH);} delay(10); } void mulWireBeginTransmission(uint8_t *adrs){ char i; for(i=0;i<mwNUM;i++){digitalWrite(mwSDA[i],LOW);} delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,LOW); mulWireData(2,adrs); //*2 } void mulWireRequestFrom(uint8_t *adrs){ char i; for(i=0;i<mwNUM;i++){digitalWrite(mwSDA[i],LOW);} delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,LOW); mulWireData(3,adrs); //*2+1 } void mulWireEndTransmission(){ char i; for(i=0;i<mwNUM;i++){digitalWrite(mwSDA[i],LOW);} delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,HIGH); delayMicroseconds(MulWireDelay); for(i=0;i<mwNUM;i++){digitalWrite(mwSDA[i],HIGH);} } void mulWireEndRequest(){ mulWireEndTransmission(); } uint8_t mulWireWrite(uint8_t *data){ mulWireData(0,data); } uint8_t mulWireData(char mode,uint8_t *data){ // mode: 0:raw, 2:*2, 3:*2+1 char i,m,a; uint8_t temp; m=(mode>1)?2:1; a=mode&1; for(temp=0x80; temp; temp>>=1){ for(i=0;i<mwNUM;i++){ digitalWrite(mwSDA[i],((data[i]*m+a) & temp)?HIGH:LOW); } delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,HIGH); delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,LOW); } for(i=0;i<mwNUM;i++){pinMode(mwSDA[i],INPUT_PULLUP);} delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,HIGH); temp=0; for(i=0;i<mwNUM;i++){ temp<<1; if(digitalRead(mwSDA[i])){temp+=1;} } delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,LOW); for(i=0;i<mwNUM;i++){pinMode(mwSDA[i],OUTPUT);} for(i=0;i<mwNUM;i++){digitalWrite(mwSDA[i],HIGH);} return temp; } void mulWireRead(uint8_t *data){ char i; uint8_t temp; for(i=0;i<mwNUM;i++){data[i]=0;} for(i=0;i<mwNUM;i++){pinMode(mwSDA[i],INPUT_PULLUP);} for(temp=0x80; temp; temp>>=1){ delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,HIGH); for(i=0;i<mwNUM;i++){ if(digitalRead(mwSDA[i])){data[i] |= temp;} } delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,LOW); } for(i=0;i<mwNUM;i++){pinMode(mwSDA[i],OUTPUT);} for(i=0;i<mwNUM;i++){digitalWrite(mwSDA[i],LOW);} //ack delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,HIGH); delayMicroseconds(MulWireDelay); digitalWrite(mwSCL,LOW); }
実際にarduino UNOで使ってみたら、無事に動いた。ただ、自分では理解しているつもりだったが、mulWireEndRequest()が必要なのを忘れていて、最初は失敗してしまった。