2015年8月1日 星期六

Arduino 之間的 I2C 通訊 (2) 由 master 向 slave 發送資料/發出指令 [slave 直接處理]

相關指令:

指令發出耆作用
Wire.begin([<address>]);master / slave啟動 Wire (由於 i2c 是用 Wire 的, 這就等同啟動 i2c 了)
Wire.beginTransmission(<address>);master開始對 <address> 的連線
Wire.endTransmission();master 關閉之前的連線
Wire.write(<data>);master 在連線上送出 一個 byte 的資料
Wire.onReceive(<function>)slave 設定用來接收資料的函數
Wire.available();slave檢查連線上是否有可接收的資料
Wire.read();slave讀取連線上的一個 byte 的資料


通訊也有不同程度的, 先做一個最簡單的單向通訊.
由於只有 master 可以主動發出通訊要求, 最簡單的就是由 master 向 slave 發送資料了.
這個例子是針對一些操控裝置, 例如你做了一個 i2C 的舵機, 由 arduino 發出指令, 要它轉到指定的角度, 而舵機是不會回傳任何資料的

程式同樣分開 mater 及 slave 的部份, master 發送, slave 接收.

master 發送資料

在 master 發送資料, 只有幾個簡單步驟就可以了:

  1. 執行 beginTransmission 並指定接收資料地址
  2. 以 Wire.write 把資料送出去
  3. 執行 endTransmission 作結束

以下是一個簡單的例子, master 把 串口收到的資料, 發送給 slave.

#include <Wire.h>

#define SLAVE_ADDRESS 0x12
#define SERIAL_BAUD 57600 

 
void setup()
{
  Wire.begin();
 
  Serial.begin(SERIAL_BAUD);
  Serial.println("I2C Master.02 started");
  Serial.println();
}
 
 
void loop()
{
  if (Serial.available()) {
    Wire.beginTransmission(SLAVE_ADDRESS);
    while(Serial.available()) {
      Wire.write(Serial.read());
      delay(1);
    }
    Wire.endTransmission();
  }
}


slave 接收資料

而 slave 接收, 是有點像 interrupt driven 的形式去做,  簡單的做法如下.


  1. 執行 Wire.onReceive 去設定接收資料的函數
  2. 在接收資料的函數中, 不斷以 Wire.available 檢查是否有資源, 並以 Wire.read 把一個 byute 資料提出, 直到所有資料提出後, Wire.available 為 false

注意, 因為 Wire 庫的 buffer 只有 32 個 byte, 每次發送的資料應限制在 32 個 byte 之內.
由於 master 進行 write 是沒有限制的, 如果發送超過 32 bytes, 之後的資料就會流失.

以下例子, 是在 接收的函數中, 直接處理接收回來的資料.



#include <Wire.h>

#define SLAVE_ADDRESS 0x12
#define SERIAL_BAUD 57600 

 
void setup() {
  Wire.begin(SLAVE_ADDRESS);    // join I2C bus as a slave with address 1
  Wire.onReceive(receiveEvent); // register event

  Serial.begin(SERIAL_BAUD);
  Serial.println("I2C Slave.02 started\n");
}

void loop() {
}

void receiveEvent(int count) {
  Serial.println("Receive Data:");
  while(Wire.available()) {
    Serial.print((char) Wire.read());
  }
  Serial.println("\n");
}


注意:  
這個例子中在 receiveEvent 中用到 Serial.print, 只是為了簡單展示結果, 雖然這裡沒有問題, 但其實是絕不應該的.
在正常的程式中, 應該盡量避免在 receiveEvent 中放入複雜的程序, 在下一章中會提及正確的做法.


執行程式後, 在 master 板子連線的 serial monitor 輸入資料, 就會發送到 slave 板子, 再在相關的 serial monitor 顯示出來.


相關程式下載:

2 則留言:

  1. 你好 請問關於mpu6050的data sheet有提到它有一個FIFO BUFFER有1024Bytes 請問跟WIRE庫的32-Byte buffer有相關性嗎?

    會因為wire的buffer大小限制了FIFO的BUFFER嗎?

    回覆刪除