2011年12月23日 星期五

多台 Arduino 間的通訊 - 透過 I2C #1

今天我們要介紹怎麼讓多台 Arduino 互相通訊。我們所用的通訊協定是 I2C Protocol,I2C 只需要兩支腳位就可以讓設備建立通訊,這兩支腳位一個叫作 SDA (Serial DAta line),另一個叫作 SCL (Serial CLock Line)。在大部份的 Arduino 板子上,SDA 是在 analog pin 4,而 SCL 是在 analog pin 5。

接線

參考底下的接線圖連接你的 Arduino:

image

接線方法很簡單,你只要把每一台 Arduino 的 SDA (analog pin 4), SCL (analog pin 5), GND 和 5V 分別連接在一起就可以了。另外,最好在 SDA 和 SCL 上加個接到 5V 的 4.7K 歐姆提升電阻以確保預設電壓為高電位。

I2C 是 Master-Slave 的架構,Master 可以向 Slave 發出需求要資料或傳送資料。I2C bus 上最多可以有 128 個設備。在 I2C bus 上可以有多個 Master 和多個 Slave,不過為了避免複雜,通常我們只會用一個 Master。每個 Slave 都會有一個識別的號碼,叫作 Slave address,Master 要跟 Slave 通訊的時候,就利用 Slave address 指定要跟哪個 Slave 建立對話。

底下將示範怎麼讓兩台 Arduino 透過 I2C 建立通訊。這兩台 Arduino 一台是 Master,一台是 Slave,其中 Slave 所用的 address 為 1。

程式

底下是 Master 的程式:

程式說明:

  • Master 使用 Wire.begin() 加入 I2C bus
  • 當 serial port 上有收到資料時,Arduino 會自動執行 serialEvent()。
  • 在 serialEvent() 函式中,Master 首先會從 serial port 讀取一個 byte 的資料,然後再利用底下三行程式將資料透過 I2C 送給 Slave 1。
  • 每當 Master 要送資料給 Slave 的時候,要先呼叫 Wire.beginTransmission() 建立傳輸,緊接著呼叫 Wire.write() 把資料放到 buffer,最後呼叫 Wire.endTransmission() 真正送出資料並結束傳輸。

底下則是 Slave 的程式:

程式說明:

  • Slave 一樣是使用 Wire.begin() 加入 I2C bus,但是必須傳入一個參數指定所用的 address
  • 利用 Wire.onReceive(receiveEvent) 註冊事件,之後當 Master 送資料給 Slave 時,Arduino 就會自動呼叫 receiveEvent()
  • 在 receiveEvent() 中,程式的邏輯很簡單,只是利用 Wire.available() 檢查是否有資料,接著利用 Wire.read() 將資料出來再丟到 serial port 上。
執行結果

程式執行的時候,首先你會看到 Master 丟出一行 "Type something to send: " 的訊息。你可以在上面輸入任何資料,例如:

image
▲ 由於 Arduino IDE 不能同時開兩個 Serial Monitor,所以我用 Tera Term 開啟跟 Master 的連線

這時候 Slave 端就會顯示從 Master 端收到的資料,如下:

image

注意,在使用 Tera Term 的時候,記得要先設定 Terminal,這樣才可以看得到你輸入的資料而且 Tera Term 才會正確換行:

image
▲ 記得到 Setup > Terminal 畫面把 Transmit 改成 CR+LF 並把 Local echo 打開

參考資料

16 意見:

Sven Wang 提到...

哈囉 cooper,
請問void receiveEvent(int howMany)
的howMany有什麼用途呢? 還是是不小心加上的?
謝謝~~~

Cooper Maa 提到...

howMany 是 number of bytes 的意思
也就是說從 howMany 可以知道 master 送了多少 bytes 過來:

http://arduino.cc/en/Reference/WireOnReceive

話說我玩過 Arduino Wire 一陣子,好像都沒用過 howMany 參教,哈!

Sven Wang 提到...

搞懂拉!!謝謝~~~

Unknown 提到...

Hi Cooper:
請問使用I2C功能時,一定要讓5V相接嗎?也一定要使用上拉電阻嗎?
如果我讓兩片Arduino都接上電源的話,是不是就可以不需要讓5V相接了呢?
謝謝您

Cooper Maa 提到...

如果兩塊 Arduino 各自有供電,可以不用接 5v, 不過還是要接 GND 以共地。上拉電阻是建議,不接可能也可以,只是如果環境有訊號干擾,可能通訊會受到影響。

Cooper Maa 提到...

如果兩塊 Arduino 各自有供電,可以不用接 5v, 不過還是要接 GND 以共地。上拉電阻是建議,不接可能也可以,只是如果環境有訊號干擾,可能通訊會受到影響。

Unknown 提到...

謝謝Cooper
不好意思,在請問一個問題。
如果只供電Master,slave以5V共接供電,那這樣子還能連接128個slave嗎?會不會造成電流不足的問題呢?

Cooper Maa 提到...

Arduino 板子上耗電的元件主要是 atmega328 和 FTDI 晶片,這兩個吃的電流加起來,大概要 26 到 27 mA ,如果不算 I/O 腳位吃的電, 保守估計,一塊標準 Arduino 板子可能要用 30 mA的電流。所以,就可以算一下可以連接多少個板子,如果是 2 A 的供應器,大概可以連接 60 多台吧。

提到...

請問這樣使用是不是會讓arduino的處理速度降低??或是容易當機??請指點迷津

Cooper Maa 提到...

不會的,只要程式跟硬體無誤就不會

Kestrel 提到...
作者已經移除這則留言。
Kestrel 提到...

您好:

最進入手了一塊9軸模組GY-82 LSM303DLH(加速器和磁力計)+L3G4200D(陀螺儀),但因之前都沒使用過i2c傳輸的元件,所以對此並不太了解,不過經過爬文以後大概了解i2c傳送前需先抓到ic的地址,也在網上看到LSM303DLH和L3G4200D各別的函式庫(沒找到兩個合在一起的函式庫),不過使用了L3G4200D的函式庫讀到的值也不正確,而且我也希望能不用函式庫,經由自己了解i2c後自行撰寫arduino的程式,所以想請問前輩能否以較好理解的方法跟我說一下i2c的原理,或是方便的話提供程式碼讓我較快理解。

懇請前輩救命啊!!!

Cooper Maa 提到...

Arduino 的 Wire library 很好用啊
I2C Protocol 是 Philips 發明的,如果要自己寫函式庫,那要 K 一下 NXP 這份 Spec: http://www.nxp.com/documents/other/39340011.pdf

Kestrel 提到...

謝謝前輩的回覆:

其實我是希望能了解i2c的傳輸原理,因為目前對i2c的傳輸方式還不太清楚,不知前輩有沒有這方面較淺顯易懂的資料?

I2c的傳輸方法是否是:讀ic位址>開始訊號>傳送資料>結束訊號。

類似以上這種較簡單的講解

謝謝

Cooper Maa 提到...

網路上的資料很多啊!
Google is your good friend.

Kestrel 提到...

我本來是想說如果前輩懂的話會比較快,如果不懂.......那也只好Google了