I2C(内部集成电路)的建立是为传感器和微控制器(如Arduino)之间的数字信息传输提供简单的方法。
I2C具有的有点是只需要两路信号连接到Arduino,在这两路连接上使用多路设备是相当容易的,你可以在信号已被正确接收后得到确认。缺点是数据速率比SPI慢,而且数据在同一时间只能在一个方向上传送,如果需要双向通信时,数据速率降低更多。信号电路还需要连接上拉电阻,以确保信号传输的稳定性。
I2C总线的两路连线是SDA和SCL,它们都可以在Arduino标准板上找到,以UNO为例,模拟引脚5复用SCL,它提供了一个时钟信号,模拟引脚4复用SDA,它用于数据传送(在MEGA2560板上,数字引脚20作为SDA,数字引脚21作为SCL,不同Arduino控制板的I2C引脚都有所不同,按原理图为准)
在I2C总线上的任一设备,都可作为主设备。它的任务是协调总线上的其他I2C设备(从设备)之间的信息传输,在I2C总线上只能有一个主设备,而且在多数情况下,主设备都为Arduino,控制连接Arduino的其他I2C通信模块
目前使用到的大多数Arduino相关I2C模块上,通常都已经添加了上拉电阻,所以只需要I2C从设备连接到Arduino的I2C接口上即可。
I2C设备的通讯需要共地,即Arduino的地线必须连接到每一个从设备的地线
从设备在I2C总线中以设备地址来确定,每个从设备在总线上的地址都是唯一的,就像门牌号一样,部分I2C模块会有自己固定的I2C地址,需要查询设备的数据手册了解发生什么指令让模块有所作用, 以及会收到什么回复,有些模块允许通过设置引脚的高低电平或发生初始化指令来修改设备地址,在Arduino上最多可接128个从设备
对于I2C总线的使用,Arduino IDE自带了一个第三方类库Wire,Wire库中隐藏了所有I2C的低级别的功能操作,只需要使用简单的命令就可以初始化I2C总线并与其他设备进行通信
在I2C通信中常用的函数介绍如下:
功能:初始化I2C连接,并作为主机或从机加入I2C总线
语法:
Wire.begin()
Wire.begin(address)
参数:
address,一个7位的I2C地址,当函数不带参数时,默认是以主机模式加入I2C总线;当填写了参数时,设备会以从机模式加入I2C总线,address可以设置成0 ~ 127中的任意地址
返回值:无
功能:主机向从机发送数据请求信号,使用requestFrom( )
后,从机端可以使用onRequest( )
注册一个事件以响应主机的请求;主机可以通过available( )
和read( )
函数读取这些数据
语法:
Wire.requestFrom(address,quantity)
Wire.requestFrom(address,quantity,stop)
参数:
address,需要获取数据的从设备地址
quantity,获取的字节数
stop,boolean型值,当其值为true
时将发送一个停止信息,释放I2C总线;当为false
时将发送一个重新开始信息,并保持I2C总线的有效连接
返回值:无
功能:(主机)传输数据到指定的从机地址,随后可以使用write()
函数发送数据,并搭配endTransmission()
函数结束数据传输
语法:Wire.beginTransmission(address)
参数:address,要发送数据的从机的7位地址
返回值:无
功能:(主机)结束数据传输
语法:
Wire.endTransmission( )
Wire.endTransmission(stop)
参数:stop,boolean型值,当其值为true
时将发送一个停止信息,释放I2C总线,当没有填写stop参数时,等效于使用true
;当其值为false
时,将发送一个重新开始信息,并继续保持I2C总线的有效连接
返回值:byte型值,表示本次传输的状态,取值为:
功能:当为主机状态时,主机将要发送的数据加入发送队列;当为从机状态时,从机发生数据至发起请求的主机
语法:
Wire.write(value)
Wire.write(string)
Wire.write(data,length)
参数:
value,以单字节发送
string,以一系列字节发送
data,以字节形式发送数组
length,传输的字节数
返回值:byte型值,返回输入的字节数
功能:返回接收到的字节数,在主机中,一般用于主机发送数据请求后;在从机中,一般用于数据接收事件中,作用类似于Serial.available( )
函数
语法:Wire.available( )
参数:无
返回值:可读字节数
功能:读取1B的数据,在主机中,当使用requestFrom( )函数发送数据请求信号后,需要使用reaf( )函数来获取数据;在从机中需要使用该函数读取主机发送来的数据
语法:Wire.read( )
参数:无
返回值:读到的字节数据
功能:该函数可在从机端注册一个事件,当从机收到主机发送的数据时即被触发
语法:Wire.onReceive(handle)
参数:handle,当从机接收到数据时可被触发的事件,该事件带有一个int型
参数(从主机读到的字节数)且没有返回值,如void myHandle(int numBytes)
返回值:无
功能:注册一个事件,当从机接收到主机的数据请求时触发
语法:Wire.onRequest(handle)
参数:handle,可被触发的事件,该事件不带参数和返回值,如void myHandle( )
返回值:无
#include
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
}
void loop() {
Wire.requestFrom(8, 6); // request 6 bytes from slave device #8
while (Wire.available()) { // slave may send less than requested
char c = Wire.read(); // receive a byte as character
Serial.print(c); // print the character
}
delay(500);
}
从机部分
#include
void setup() {
Wire.begin(8); // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
}
void loop() {
delay(100);
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent() {
Wire.write("hello "); // respond with message of 6 bytes
// as expected by master
}
#include
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
}
byte x = 0;
void loop() {
Wire.beginTransmission(8); // transmit to device #8
Wire.write("x is "); // sends five bytes
Wire.write(x); // sends one byte
Wire.endTransmission(); // stop transmitting
x++;
delay(500);
}
从机部分
#include
void setup() {
Wire.begin(8); // join i2c bus with address #8
Wire.onReceive(receiveEvent); // register event
Serial.begin(9600); // start serial for output
}
void loop() {
delay(100);
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
while (1 < Wire.available()) { // loop through all but the last
char c = Wire.read(); // receive byte as a character
Serial.print(c); // print the character
}
int x = Wire.read(); // receive byte as an integer
Serial.println(x); // print the integer
}