tsaiwn 发表于 2015-4-5 03:39:36

【教程】Arduino IIC/I2C 实验示例补充

本帖最后由 tsaiwn 于 2015-4-5 15:49 编辑

关于 IIC/I2C 的通讯, 较完整的要算以下这:
   http://www.geek-workshop.com/thread-421-1-1.html

但是该篇主要用 老版本 Arduino-0018 IDE 编程,
我这边用官方网站最新范例作补充 :-)

以下这示范例子其实就在 Arduino 官网:
http://arduino.cc/en/Tutorial/MasterReader

材料:
两张 Arduino 开发板 + 4 条电导线
Master 主人      僕人 SLAVE
GND      --------GND
   A4      --------A4   (SDA, data)
   A5      --------A5   (SCL, clock)
   5V      --------Vin
*** 最后这第四条是当你 Slave 僕人板子没有自己的电池或USB供电才需要 !
    就是说如果 Slave 用电池供电, 那就不必从 Master 的 5V 连接到 Slave 的 Vin.
http://arduino.cc/en/uploads/Tutorial/Master_Sender_bb.png



注意图片中没有把 Master 的 5V 连接到 Slave 的 Vin 是假设 Slave 有电池供电 !
这样就可以测试了 !
一张板为 Master 主人, 另一张板为 Slave 僕人 !
注意, IIC (I2C) 规定每个 Slave 要有一个编號 (address)!
现在我们假设第二张板子(Slave)的编號为 2 號!

Master 主人程序码如下:
#include <Wire.h>

void setup()
{
Wire.begin();      // join i2c bus (我是主人, 不必报告 IIC 號码)
Serial.begin(9600);// 串口输出
}

void loop()
{
Wire.requestFrom(2, 6);    // 要求 2號僕人 透过 Wire 送 6 char 过来!

while(Wire.available())    // 如果Wire上还有 char 等我读取
{
    char c = Wire.read(); // 从 Wire 读取一个 char
    Serial.print(c);      // 送到串口监视器查看
}

delay(5500);// 等 5.5秒
Serial.println( ); // 故意换到下一行方便查看
} // loop(


接著来看 Slave 僕人的程序码:

#include <Wire.h>

void setup()
{
Wire.begin(2);   // 报告大家, 我是 2 號 IIC 设备喔, 在此等待服务主人
Wire.onRequest(ggyy); // 註册 ! 如果主人给我命令, 就调用函数 ggyy( )
}

void loop()
{
// 我是僕人, 平常没事做
}

// 这函数必须在 setup( ) 內用 onRequest( ) 註册!
void ggyy()
{
Wire.write("Hello "); // 送出 6 个 char 给 IIC 上的主人
} // loop(


只有一部 PC 要如何测试 ?
(1)PC 先用 USB 连接 第二个 Arduino 板子 (Slave 僕人)
   把 Slave 僕人的程序码 上载进第二个 Arduino 板子(Slave 僕人)

(2)拔掉 Slave Arduino 板子,
   PC 改连接 Master 板子,
   把 Master 主人程序上载到第一 Arduino 板子 (主人, Master)

(3)把四条线连接好:
Master 主人      僕人 SLAVE
GND      --------GND
   A4      --------A4   (SDA, data)
   A5      --------A5   (SCL, clock)
   5V      --------Vin
*** 最后这第四条是当你 Slave 僕人板子没有自己的电池或USB供电才需要 !
    就是说如果 Slave 用电池供电, 那就不必从 Master 的 5V 连接到 Slave 的 Vin.

(4)开启串口监视器, 可敲入 CTRL_Shift_M

必要时按板子上的 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 的命令, 是要求2號僕人 透过 Wire 送 6 char 过来 !
但是Slave 可能不听话只送出 3 char 就结束通信 !!
因为..
   Wire.requestFrom( ); 只是送个命令(一个 byte)给某个 Slave,
然后等著(注意会等著不立即往下一句做喔!),
直到至少一个 char 送过来或 time out 才会往下做下一行!
所以, 这时 Master 在这句下方要用 Wire.read( ) 读取资料!
那怎知是 timeout 呢?
都没 Wire.available( ) 就是没资料啊!
不过, 其实 Wire.requestFrom( ); 会回传一个整数, 可以这样:
    int kkk = Wire.requestFrom(2, 6);
然后检查 kkk 是否为 0, 是表示 time out 都没收到任何 byte !
(3)至於 Slave 应该如何回应主人Master的命令呢 ? (请注意命令只有一个 byte)
   官网的范例根本不管 Master 送过来是啥, 直接用 requestEvent() 函数送回 6 bytes!
      本来, 比较正確的方法应该是 Slave 要先把命令收下(一个 byte),
   然后检查该 Byte 內容, 至於是啥意思则由 Master 和 Slave 写程序的人自己定义,
   所以, Wire.requestFrom(2, 6);的 6 其实是主人一厢情愿希望Slave给它 6 char,
   但 Slave 可能不听话只送出 3 char 就结束通信 !
   因为TWI/IIC的缓存区只有32bytes, 所以最多只可以要求 32 bytes;   如果你写int kkk = Wire.requestFrom(2, 33);则会立即返回且 kkk 得到 0;
   因为目前的 .requestFrom( )调用 twi_readFrom( ) 之后检查第二个参数如果超过 32 就不做事 !!
   如果Slave不管主人送啥过来(一个 byte內容)是啥, Slave 都是做自己的事,,
   那就可以照该官网的范例写的方式:
       http://arduino.cc/en/Tutorial/MasterReader
   否则如果Master 送过来的一个 Byte 有意义,
   那Slave必须类似以下这样写:
///Slave arduino
//...constintMY_ADDRESS = 38;   // 我位址编號是 38
volatilechar 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 haha) {
   what = Wire.read( ); // 读取 Mater 送来的命令 byte
}
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
      // 注意只可有一句 Wire.write( ), 所以要先把资讯准备在一个 char 的 Array
      break;// 每个 case 都要记得 break;
   case 18:
       //...
      break;

    default:
Wite.write("HaHa!"); break;//其他 case
   }//switch(
} // yybbb(
void sendA0( ) { // 读取 A0 並送往 Master, 注意 0~1023 会用2 byte
   int val = analogRead (A0);
   byte buf ;
   buf = val >> 8;   // 取左边 byte
   buf = 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);
   interror = 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/20/tutorial-arduino-and-the-i2c-bus/

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

通常 Slave 是一些支援 IIC (I2C) 的模块,
如果你要了解更多,
可以下载一些支援 IIC 模块的库来解压缩偷看其源代码 !

suoma 发表于 2015-4-5 09:33:31

谢谢分享学习一下

x9988 发表于 2015-4-5 10:38:11

谢谢收藏了。

tsaiwn 发表于 2015-4-5 15:51:50



通常 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

yangh2961 发表于 2015-4-5 17:45:56

写的很有意思嘛,收下了

li23108 发表于 2015-4-6 12:50:13

写的不错,留个记号找时间实践下
页: [1]
查看完整版本: 【教程】Arduino IIC/I2C 实验示例补充