进阶制作

IIC\I2C\TWI通信

字号+ 作者:PLC工程师 来源:未知 2016-03-03 16:16 我要评论( )

IIC\I2C\TWI通信

官网示例:
http://arduino.cc/en/Tutorial/MasterReader
材料:
  两块arduino开发板+4条导线
  Master 主      从 SLAVE
  GND      --------  GND
   A4      --------  A4   (SDA, data)
   A5      --------  A5   (SCL, clock)
   5V      --------  Vin
第四线是从机板子沒有自己的电池或USB供电时才需要 !   如果 Slave 用电池供电, 那就不必从 Master 的 5V 连接到到 Slave 的 Vin.


注意图片中没有把 Master 的 5V 连接到 Slave 的 Vin 是假设 Slave 有独立电源 !
这样就可以进行测试了 !
一块为Master主机以下简称M, 另一块Slave从机以下简称S !
注意:IIC (I2C) 规定每个Slave 要有一个编号或者地址 (Address)!
我们设定从机S的地址为1。

主机的代码如下:

#include <Wire.h>
 
void setup()
{
  Wire.begin();        // join i2c bus (主机不用定义地址)
  Serial.begin(9600);  //串行输出
}
 
void loop()
{
  Wire.requestFrom(2, 6);    // 请求2号从机通过 Wire 发送 6 char 
 
  while(Wire.available())    //
  { 
    char c = Wire.read(); // 从Wire读取一个char
    Serial.print(c);      // 送到串口监视器查看
  }
 
  delay(5500);  // 等 5.5秒
  Serial.println( ); // 换行
} 

从机的代码如下:
#include <Wire.h>
 
void setup()
{
  Wire.begin(2);   // 声明为2号IIC从机,等待wire通讯
  Wire.onRequest(ggyy); // 如果收到主机命令, 就調用函数 ggyy( )
}
 
void loop()
{
  // 从机, 平时为空闲状态
}
 
// 此函数必须在 setup( ) 內用 onRequest( ) 调用!
void ggyy()
{
  Wire.write("Hello "); // 送出 6个 char 給 IIC 上的主机
} 

把四条线连接好:  (注意这里是以 UNO为例, 其他板子的 SCL, SDA 可能不是 A5 和 A4!)
  Master 主机         从机 SLAVE
  GND       --------  GND
   A4         --------  A4   (SDA, data)
   A5         --------  A5   (SCL, clock)
   5V         --------  Vin

第四线是从机板子沒有自己的电池或USB供电时才需要 !   如果 Slave 用电池供电, 那就不必从 Master 的 5V 连接到到 Slave 的 Vin.

(4)打开串口监控 观察收发的数据

必要时按Reset进行复位。


==================================================================================================================

另外官網還有一個簡單的範例:
   http://arduino.cc/en/Tutorial/MasterWriter
好了, 測試過範例之後, 我們來稍微講解一下:
(1)要在 setup( ) 內用 Wire.begin( ) 加入 IIC 通訊
   (A)Master  只要這樣 Wire.begin( );   
   (B)Slave 要用一個 1 到 127 的整數當作參數, 代表 Slave 的 address,
      例如  Wire.begin(2);  // 我是 2 號
(2)要由 Master 下命令要求 Slave 送數據過來,例如:
      Wire.requestFrom(2, 6);    // 要求 2號僕人 透過 Wire 送 6 char
 過來!

     但是, 請注意, 這裡的 6 其實只是一個 byte 的命令, 只是"希望"從機送 6 byte (最多只可要求 32 byte) !
  這裡的 6 到底是啥意思是由 Master 和 Slave 的程序設計者自己約定好即可 !
  因為..
     Wire.requestFrom( ); 只是送個命令(一個 byte)給某個 Slave, 
  然後等著(注意會等著不立即往下一句做喔!),
  直到至少一個 char 送過來或 time out 才會往下做下一行!
  所以, 這時 Master 在這句下方要用 Wire.read( ) 讀取資料!
  那怎知是 timeout 呢?
  都沒 Wire.available( ) 就是沒資料啊!
  不過, 其實 Wire.requestFrom( ); 會回傳一個整數, 可以這樣:
    int kkk = Wire.requestFrom(2, 6); 
  然後檢查實際收到幾個 byte 的 kkk 是否為 0, 是表示 time out 都沒收到任何 byte !
(3)至於 Slave 應該如何回應主人Master的命令呢 ? (請注意命令只有一個 byte)
   官網的範例是偷懶的寫法, 根本不管 Master 送過來是啥, 直接用 requestEvent() 函數送回 6 bytes!
      本來, 比較正確的方法應該是:
   (A) Master 在下達命令 .requestFrom(從機位址, 幾 byte); 之前:
        (A1)先用 .beginTransmission( ) + .write( ) ... + .endTransmission( ) 送一個訊息去給 Slave,
        (A2)然後緊接著下達 .requestFrom(從機位址, 幾 byte); 的命令;

  (B)在 Slave 這邊相對應於 (A)Master 的動作如下:
      (B1)透過 Wire.onReceive (註冊接收函數); 所註冊的函數把(A1) 的信息收下來
      (B2)透過 Wire.onRequest (註冊送訊息函數); 所註冊的函數送數據去給 Master,

            但是, 應該根據(B1)所接收的信息(通常是從機或傳感器的寄存器編號)決定要送的數據!
  (C)在 Master 這邊於 .requestFrom( ); 之後用 while 檢查 Wire.available( ) 並用 Wire.read( ) 接收數據 !

   Slave 與 Master 之間的信息到底是何意義通常由 Slave (從機或傳感器)決定, 所以要看傳感器的 datasheet !
   所以, 以官網的該簡單範例來說,
      Wire.requestFrom(2, 6);  的 6 其實是主人一廂情願希望Slave給它 6 char,
   但 Slave 可能不聽話只送出 3 char 就結束通信 ! 
   注意, 因為TWI/IIC的緩存區只有32bytes, 
   所以Master每次最多只可以要求 32 bytes;   如果你寫  int kkk = Wire.requestFrom(2, 33);  則會立即返回且 kkk 得到 0;

   因為目前的 .requestFrom( )調用 twi_readFrom( ) 之後檢查第二個參數如果超過 32 就不做事 ! (我去偷看庫的源代碼  !
   如果Slave不管主人送過來的信息是啥, Slave 都是閉著眼睛做自己的事,,
   那就可以照該官網的範例寫的方式:
       http://arduino.cc/en/Tutorial/MasterReader
   否則, 如果Master 在 .requestFrom( ); 之前有先送過來有意義的命令 (通常是一個 Byte),
///Master  主 arduino
//...假設從機 address 是 38  
char  myCMD = 23;  // 命令 byte
void setup() {
   //...
   Wire.beginTransmission(38);  //準備送信息去給 Slave 38
   Wire.write(myCMD);  // Queue 起來, 當然可以很多 .write( )
   Wire.endTransmission( );   // 真正開始傳送
   String gg = "";
   Wire.requestFrom(38, 6);   // 要求 Slave 38 送 6 bytes 過來
   while( Wire.available( ) ) gg += (char) Wire.read( );
   // ...
}
void loop( ) {

  //...
} // loop(
//////////////////////////////////////////////
   那Slave必須類似以下這樣寫:
///Slave arduino
//...
const  int  MY_ADDRESS = 38;   // 我位址編號是 38
volatile  char what = 0;  // 接收命令 byte
void setup() {
   Wire.begin (MY_ADDRESS);   // can be 1,2, .. 127
   Wire.onReceive (ggaaa);   // receiveEvent interrupt handler 
   Wire.onRequest (yybbb);   // requestEvent interrupt handler 
   //...
}
void loop( ) {
  //...
}
void ggaaa(int howMany) {  // howMany 通常是 1, 但 Master當然可以送不只一個 byte 給 Slave 從機
   what = Wire.read( ); // 讀取 Mater 送來的命令 byte, 通常是一個 byte 代表想要的寄存器編號(傳感器上的)

   /// 注意, 如果 howMany  不是 1 則應該用 while(Wire.available( ) ) { ccc = Wire.read( );  處理 ccc...} 全讀走 !
}
void yybbb( ) {  // 裡面不可用  Serial.print 不然會來不及 !
   switch(what) {
     case 23:  Wite.write("Hello "); break;  // 表示送 6 char歡迎 (故意!)
     case 2:  sendA0( ); break;  // 讀取 A0 並送往 Master ( 2 bytes)
     case 15:  // 送 15 char ? 還是要編號 15 的寄存器的數據 ? 由設計傳感器的人規定
        // 注意只可有一句 Wire.write( ), 所以要先把資訊準備在一個 char 的 Array
        break;  // 每個 case 都要記得 break;
     case 18:
       //...
        break;


    default:
       Wire.write("HaHa!"); break;  //其他 case
   }//switch(
} // yybbb(
void sendA0( ) { // 讀取 A0 並送往 Master, 注意 0~1023 會用2 byte
   int val = analogRead (A0);
   byte buf [2];
   buf [0] = val >> 8;   // 取左邊 byte
   buf [1] = val & 0xFF;  // 右邊的 byte 
   Wire.write (buf, 2);   // 只可用一次 Wire.write( ); 所以必須先準備到 Array
}  // sendA0(

/// Master 那邊要如何讀取這送過去的 A0  值?
/// int ans = Wire.read( ) * 256 + Wire.read( );

(4)I2cScanner 看看系統有沒有連接 IIC/I2C 裝置(Slave Device)
   在 Arduino 官網有人提供了一個 I2cScanner (I2C 掃描器),
   顧名思義, 就是可以幫忙找出系統上所有的 IIC Slave 僕人裝置
     http://playground.arduino.cc/Main/I2cScanner
   這個程序碼其實很簡單, 就是用 Loop 把 address 從 1 找到 126
   對每個 address 都做以下兩句:
     Wire.beginTransmission(address);
     int  error = Wire.endTransmission();
   然後就檢查 error 是否為 0 ?
   只有 0 才表示真的有個 Slave Device 的位址編號是 address   不是 0 則表示有錯, 目前可辨別四種錯誤(1,2,3,4),
   詳細可以參考官方網站:
      http://arduino.cc/en/Reference/WireEndTransmission


參考:
   http://www.gammon.com.au/i2c
   http://www.gammon.com.au/i2c-summary
也可看看這篇:
   http://tronixstuff.com/2010/10/2 ... no-and-the-i2c-bus/


-----------------------------------------------------------------------

通常 Slave 是一些支援 IIC (I2C) 的模塊,
如果你要了解更多, 
可以下載一些支援 IIC 模塊的庫來解壓縮偷看其源代碼 !

通常 Arduino 都是當 IIC/I2C/TWI 的 Master(主人),
如果你是想用 Arduino 模仿 IIC Slave Device / 傳感器,
可以參考以下這兩篇:
http://www.gammon.com.au/forum/?id=10896


http://dsscircuits.com/articles/arduino-i2c-slave-guide

如需更详细资料,如有好资源分享,请加入QQ交流群:214461008

欢迎阅读,欢迎转载,不用注明出处!本站只提供入门和进阶资料,做您学习的小伙伴!

请加群:214461008

相关文章
  • I2C 协议最全资料

    I2C 协议最全资料

    2016-03-19 23:23

  • IIC总线协议

    IIC总线协议

    2016-03-03 16:05

网友点评
Arduino
热门教程
关注我们
自动化世界