Arduino - I2C通信协议& mlx90614红外温度传感器

Arduino主从机之间的I2C通信

在数字通信各种协议中,相对Ethernet, USB, SATA, PCI-Express等传输速度达数百上千兆字节每秒的总线,I2C和SPI常称为“小”协议。但是,我们不能忘记的是各种总线的用途是什么。“大”协议是用于系统外的整个系统之间通信的,“小”协议是用于系统内各芯片间的通信,没有迹象表明“大”协议有必要取代“小”协议。I2C和SPI的存在和流行体现了“够用就好”的哲学。

  • I2C总线定义:

    I2C(‘intel’ -Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。在主从通信中,可以有多个I2C总线器件同时接到I2C总线上,通过地址来识别通信对象。

    I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率100kbps。各种被控制电路均并联在这条总线上,但就像电话机一样只有拨通各自的号码才能工作,所以每个电路和模块都有唯一的地址,在信息的传输过程中,I2C总线上并接的每一模块电路既可以是主控器(或被控器),又可以是发送器(或接收器),这取决于它所要完成的功能。
    在这里插入图片描述

  • Wire Library

    I2C是非常普通的通信方式,Arduino单片机集成了I2C库,可以直接调用几个函数实现I2C通信。这个库允许您与I2C / TWI 设备进行通信。在Arduino板上的R3布局(1.0 pinout), SDA(数据线)和SCL(时钟线)都在靠近AREF引脚的针头上。Arduino Due有两个I2C /TWI接口SDA1和SCL1靠近AREF pin,另外一个在20和21上。详情参考这里
    注:当连接SDA/SCL引脚时上拉电阻需要被连接,mega2560的上拉电阻在20-21引脚上。(没另外设置,调试成功,原因暂且未知?)

  • Arduino的I2C库是Wire.h
    常用库函数如下:
    Functions

    begin()

    requestFrom()

    beginTransmission()

    endTransmission()

    write()

    available()

    read()

    SetClock()

    onReceive()

    onRequest()


    Wire.beginTransmission(Addr)

    Wire.write(data)

    Wire.endTransmission(void)

    Wire.requestFrom(Addr, num)

    Wire.available()

    Wire.read()

  • 测试Demo

    引脚连接:
    1 VCC ------VCC

    2 GND ------ GND

    3 SCL ------ 21(SCL)或者SCL1

    4 SDA ------20(SDA)或者SDA1

  • 测试代码

#include <Wire.h>
void setup() {
  // put your setup code here, to run once:
  Wire.begin(); // join i2c bus (address optional for master)master因为不需要地址,Wire.begin()就可以了。
  Serial.begin(9600);  // start serial for output
}
 
uint16_t result;
float temp;
 
void loop() {
   
  // put your main code here, to run repeatedly:
  Wire.beginTransmission(0x5A);//发送起始信号和I2C总线地址
  Wire.write(0x07);            // 只发送一次数据,地址自动加一
  Wire.endTransmission(false);     // 停止信号
 
  Wire.requestFrom(0x5A, 3);//向已知地址slave获取连续3个数据,这时候需要注意,数据只是存起来了,并没有真正返回
  result = Wire.read(); //Receive DATA
  result |= Wire.read() << 8; //Receive DATA
 
  uint8_t pec = Wire.read();
   
  temp =  result*0.02-273.15;//
 
  Serial.print(temp);
 
  Serial.println();
  // delay(500)

}

问题:Arduino如何读取I2C总线上连接设备的地址?

  • 操作步骤 :
    1、将一个I2C器件连接至arduino的SDA、SCL引脚(UNO R3 连接A4、A5)
    2、将如下代码烧入arduino,打开串口界面即可得到I2C设备的地址。


#include <Wire.h>
void setup()
{
  Wire.begin();
  Serial.begin(9600);
  Serial.println("nI2C Scanner");
}
void loop()
{
  byte error, address;
  int nDevices;
  Serial.println("Scanning...");
  nDevices = 0;
  for(address = 1; address < 127; address++ ) 
  {
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    if (error == 0)
    {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");
      nDevices++;
    }
    else if (error==4) 
    {
      Serial.print("Unknow error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices foundn");
  else
    Serial.println("donen");
  delay(5000);           // wait 5 seconds for next scan
}

Arduino主从机之间的双向I2C通讯实验

我现在采用老版本A rduino-0018编程,老版本I2C通讯的发送数据命令是send(),接受数据命令是receive(),最新版本Arduino 1.0的发送数据的命令是write(),接受数据的命令是read(),这一点要注意啊,看清楚自己下载应用的是什么版本的Arduino编程软件。Arduino程序下载地址:点击这里下载

  • I2C实验说明:
    主机向从机循环发送字符串" light is "和字节 x,x 为 1 或 0,从机接收后,把数据显示在它的串口监视器中,如上图。然后当主机通知从机向它上传数据时,会把 x 值再上传回主机,然后赋值给变量c。当主机程序判断 c 为 1,则点亮主机数字引脚13相连的LED,否则熄灭LED。
    通过这个实验把主从机之间的双向通讯都诠释出来了。
    在这里插入图片描述

  • Arduino主机程序:

    /*主机向从机循环发送字符串"light is "和字节x,x为1或0

    从机接收到主机发来的数据后,当主机通知从机向它上传数据时
    会把x值再上传回主机,然后赋值给变量c。
    当主机程序判断c为1,则点亮LED,否则熄灭LED。*/

    #include <Wire.h> //声明I2C库文件

    #define LED 13
    byte x = 0; //变量x决定LED的亮灭 这里byte就是unsigned char

    //初始化
    void setup()
    {
    Serial.begin(9600);
    Wire.begin(); // 加入 i2c 总线,作为主机
    pinMode(LED,OUTPUT);//设置数字端口13为输出
    }

    //主程序
    void loop()
    {
    Wire.beginTransmission(4); //WriteSender—发送数据到设备号为4的从机

    Wire.write("light is "); // 发送字符串"light is "
    Wire.write(x); // 发送变量x中的一个字节
    Wire.endTransmission(); // 停止发送

    x++; //变量x加1
    if(x==2) //如果变量x的值为2,则把x值转为0
    x=0;
    delay(1000); //延时1s

    Wire.requestFrom(4, 1); //通知4号从机上传1个字节

    while(Wire.available()>0) // ReadReceiver-----当主机接收到从机数据时
    {

    byte c = Wire.read(); //接收一个字节赋值给c

    Serial.println©;

    if(c==1) //判断c为1,则点亮LED,否则熄灭LED。
    {
    digitalWrite(LED,LOW);
    }
    else
    {
    digitalWrite(LED,HIGH);
    }
    }

    delay(1000); //延时1s
    }

  • Arduino从机程序:

    /*循环接收主机发送来的数据包,同时显示在串口监视器上

    把数据包的最后一个字节,再上传回主机
    */

    #include <Wire.h> //声明I2C库文件

    int x; //变量x值决定主机的LED是否点亮

    //初始化
    void setup()
    {
    Wire.begin(4); // 加入 i2c 总线,设置从机地址为 #4
    Wire.onReceive(receiveEvent); //注册接收到主机字符的事件
    Wire.onRequest(requestEvent); // 注册主机通知从机上传数据的事件
    Serial.begin(9600); //设置串口波特率
    }

    //主程序
    void loop()
    {
    delay(100); //延时
    }

    void receiveEvent(int howMany) //** Read**–Receiver--------当从机接收到主机字符,执行该事件
    {
    while( Wire.available()>1) // 循环执行,直到数据包只剩下最后一个字符
    {
    char c = Wire.read(); // 作为字符接收字节

    Serial.print©; // 把字符打印到串口监视器中
    }

    //接收主机发送的数据包中的最后一个字节

    x = Wire.read(); // 作为整数接收字节

    Serial.println("");
    Serial.println(x); //把整数打印到串口监视器中,并回车
    }

    //WriteSender --------当主机通知从机上传数据,执行该事件
    void requestEvent()
    {
    //把接收主机发送的数据包中的最后一个字节再上传给主机

    Wire.write( x); // 响应主机的通知,向主机发送一个字节数据
    }

文章/视频:
很清晰的解读I2C协议
Arduino之间的I2C通讯

Arduino主从机之间的I2C通信之MLX90614红外测温
关于MLX90614红外测温模块SMBus 协议的学习笔记

UART SPI IIC的详解及三者的区别和联系

什么是BootLoader?
Arduino 烧录BootLoader

什么是I2C?
Arduino CH02-12 I2C通訊 主端讀取從端傳送

Arduino通过I2C(PCF8574T)驱动1602LCD

猜你喜欢

转载自blog.csdn.net/Naiva/article/details/84891152