2015年7月31日 星期五

Arduino 之間的 I2C 通訊 (1) I2C 地址設定 及 I2C 地址掃瞄

相關指令:

指令發出耆作用
Wire.begin([<address>]);master / slave啟動 Wire (由於 i2c 是用 Wire 的, 這就等同啟動 i2c 了)
Wire.beginTransmission(<address>);master開始對 <address> 的連線
Wire.endTransmission();master 關閉之前的連線



i2c 地址設定

i2c address 就像是你家中的地址, 每個 slave 都有自己的地址, 由於線路上只有一個 master, 加上 slave 只可以向 master 通訊, 所以 master 是不需地址的.

設定 i2c 地址, 基本上是沒有限制的, 只要同一線路上, 沒有重複就可以了.
要設定 slave 板子的地址, 只需要執行 Wire.begin(<地址>); 而 master 因為不需要地址, 只要 Wire.begin() 就可以了,.

以下是一個 slave 設定的例子, 把 slave 板子的地址設定為 0x12, 上載到 slave 板子上去執行就可以了.

#include <Wire.h>

#define SLAVE_ADDRESS 0x12

void setup() {
  Wire.begin(SLAVE_ADDRESS);    // join I2C bus as a slave with address 0x12
}

void loop() {
}


i2c 地址掃瞄

slave 建立後, 就要看看 master 如果找到它了.
i2c scanner 可以說是 master 板子的最基本例子, 可以用作測試線路上連接了的設備的存在 (只測試存在性, 並非測試其功能).
i2c_scanner 其實也很簡單, 由 master 向所有地址發出 beginTransmission 再 endTransmission, 嘗試建立連線.  在 beignTransmission 中輸入 slave 的地址, 就可以測試該地址的裝置了.

如果 error = 0 (沒 error), 即代表這個地址有設備登記
如果 error = 4 (這是 Wire 庫的設定, 不要問我為什麼是 4), 即代表這個地址可能有設備, 但有錯誤.
否則, 就代表該地址沒有設備使用了.

以下是一個簡單的 i2c scanner 程式:

#include <Wire.h>

#define SERIAL_BAUD 9600 

void setup()
{
  Wire.begin();
 
  Serial.begin(SERIAL_BAUD);
  Serial.println("I2C Scanner started");
  Serial.println();
}
 
 
void loop()
{
  uint8_t error, i2cAddress, devCount, unCount;
 
  Serial.println("Scanning...");
 
  devCount = 0;
  unCount = 0;
  for(i2cAddress = 1; i2cAddress < 127; i2cAddress++ )
  {
    Wire.beginTransmission(i2cAddress);
    error = Wire.endTransmission();
 
    if (error == 0)
    {
      Serial.print("I2C device found at 0x");
      if (i2cAddress<16) Serial.print("0");
      Serial.println(i2cAddress,HEX);
      devCount++;
    }
    else if (error==4)
    {
      Serial.print("Unknow error at 0x");
      if (i2cAddress<16) Serial.print("0");
      Serial.println(i2cAddress,HEX);
      unCount++;
    }    
  }

  if (devCount + unCount == 0)
    Serial.println("No I2C devices found\n");
  else {
    Serial.println();
    Serial.print(devCount);
    Serial.print(" device(s) found");
    if (unCount > 0) {
      Serial.print(", and unknown error in ");
      Serial.print(unCount);
      Serial.print(" address");
    }
    Serial.println();
  }
  delay(5000);
}

在這個例子中, 應該可以得到以下的結果:
I2C Scanner started

Scanning...
I2C device found at 0x12
1 device(s) found

 I2C Scanner 是非常有用的, 當你買了一個新裝置, 如果送來的程式不成功, 先看看相關地址是否可找到裝置, 可簡單測試裝置是否有問題.


相關程式下載:


5 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
  2. 請問一個問題:
    如果在一個裝置中有5個slave,其中4個的I2C地址使用十進位,1個使用十六進位,這5個slave都只是用作開關用途,在程式操作上將會發生什麼問題?
    譬如使用十進位的4個slave:
    slave1 使用地址 0100100, 程式定義為36.
    slave2 使用地址 0100101, 程式定義為37.
    slave3 使用地址 0100110, 程式定義為38.
    slave4 使用地址 0100111, 程式定義為39.

    譬如使用十六進位的1個slave:
    slave5 使用地址 1001111, 程式定義為97.



    回覆刪除
    回覆
    1. 不要被 二進, 十進 及 十六進的表達方式影響吧.
      地址設定是根據你提供的數值, 用什麼表達方式都是一樣.

      5 個 slave 只要地址上沒衝突, 執行後應該可以全部 scan 出來.

      刪除
  3. 根據我從https://www.arduino.cc/en/Reference/WireBegin的理解:
    如果Arduino要當Master, 呼叫WireBegin(), 不用給address
    如果Arduino要當Slave, 呼叫WireBegin(address)

    回覆刪除
  4. 寫得挺詳細, 一般都只提到master 如何傳資料到slave端就結束了

    回覆刪除