Arduino - I2C通信协议(附.mlx90614红外温度传感器)

前言

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

一、I2C总线定义

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

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

二、Arduino 单片机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

常用库函数如下:

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()

示例程序
引脚接线:
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)

}

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

1、将一个I2C器件连接至arduino的SDASCL引脚(UNO R3 连接A4A5
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通讯实验

我现在采用老版本Arduino-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);         //**Write**--**Sender**---发送数据到设备号为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)               // **Read**--**Receiver**-----当主机接收到从机数据时
    {
      
    byte c = Wire.read(); //接收一个字节赋值给c

    Serial.println(c);

	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(c);                     // 把字符打印到串口监视器中
}

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

	x = Wire.read(); // 作为整数接收字节
	
	Serial.println("");
	Serial.println(x); //把整数打印到串口监视器中,并回车
}

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

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

参考资料(文章/视频)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naiva

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值