2017年
3月
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

setup diary

2007|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|06|08|11|
2015|01|02|03|04|05|06|07|08|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|

2017-03-17 同じアドレスを持つデバイス

_ アドレスの重複するデバイスとI2Cで通信

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

_ 2017/3/22追記

実際にarduino UNOで使ってみたら、無事に動いた。ただ、自分では理解しているつもりだったが、mulWireEndRequest()が必要なのを忘れていて、最初は失敗してしまった。