I2C是什么?对很多人来说可能都很陌生,其实就是两根电线,这两条线连著两个或者多个电子设备,比如一个温度感测器和一个自动室内温度控制系统(人话: 就是空调). I2C是荷兰飞利浦公司20多年前发明的一种通信方式,目前已经成为行业标准,你身边的许多电子设备里面都有它的身影。

之所以I2C使用的如此广泛,因为真的太容易使用了。就两根铜线,却可以连接非常多的设备,来实现相互之间的通信。(理论上,可连接128个设备如果使用7bit地址,或者1024个设备,如果使用10bit地址)。

接下来就要问了,how it works?

怎么可能,这么多设备的通信只需要两根线。其实,每个设备都有自己独特的地址,而master就可以通过选择地址来和指定的设备进行通信。

这两根线,一根叫Serial Clock (or SCL),顾名思义,就是提供一个时钟信号,它由master设备提供,来同步大家的交流节奏。另一根叫Serial Data (or SDA),就是提供数据的线。

因为两根线都是"漏极开路", 也就是说需要上拉电平来达到高电平输出的目的。很好的一个优点是通过改变上拉电源的电压,便可以改变传输电平。说人话就是,看下图(从左到右读图):

漏极开路

一般来说。2K欧姆的上拉电阻可以做到400kbps的传输速度,而10K欧姆,只可以达到100kbps。(晶体管源极和漏极之间是有寄生电容的, 过大的上拉电阻会引起延时,导致信号边缘的上升下降速度变慢)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

下面就来看看protocol, 协议,顾名思义,就是大家商量好的一种交流暗号。

数据信号是通过8比特的序列传输的,首先是经过一个特殊的开始信号。

数据线信号从高到低,而此时时钟信号依旧维持高位,则此时为开始传输数据的信号。之后每次时钟信号脉冲,都会传输1比特信号。接下来7bit描述了slave设备的地址,第8bit表示master设备是要从slave设备里面读取数据还是写入数据(低写高读)。接下来Ack.位表示,如果相应的slave设备成功收到master的呼唤,就会让SDA线处于低电位(0电位),表示主人的呼唤成功收到。如果没有反馈Ack.信号,说明slave没有成功响应,原因也许是相应的slave设备忙碌等等。如果是这种情况,master设备将会执行相应的措施,比如等待片刻,继续重复呼叫slave设备。

接下来,就是slave内部寄存器地址。slave里的不同寄存器里面存储著各种内存信息或者数据。

有了slave的地址,也有了slave内部的寄存器地址,这下就可以读写数据了。于是最后8bit的数据就要么被读取或者被写入。

最后会有一个结束信号,SCL保持高位,而SDA从低到高。

接下来看个例子

主板想要读取两个感测器数据通过I2C, 红线和黑线代表电源线。蓝线和绿线代表I2C线。

当然我们要首先找到各感测器的I2C地址信息,一般从说明书上都会写。

比如说我们想实时读取加速度感测器的x轴坐标,我们就要找到存储x轴坐标的内部寄存器地址。此例里,x轴坐标的数据是存储在0x32和0x33两个寄存器里面。下面就是相应的C代码:

/*
* How I2C Communication Protocol Works - Example01
*
* by Dejan Nedelkovski, How To Mechatronics
*
*/

#include <Wire.h>

int ADXLAddress = 0x53; // Device address in which is also included the 8th bit for selecting the mode, read in this case.

#define X_Axis_Register_DATAX0 0x32 // Hexadecima address for the DATAX0 internal register.
#define X_Axis_Register_DATAX1 0x33 // Hexadecima address for the DATAX1 internal register.
#define Power_Register 0x2D // Power Control Register

int X0,X1,X_out;

void setup() {
Wire.begin(); // Initiate the Wire library
Serial.begin(9600);
delay(100);
// Enable measurement
Wire.beginTransmission(ADXLAddress);
Wire.write(Power_Register);
// Bit D3 High for measuring enable (0000 1000)
Wire.write(8);
Wire.endTransmission();
}

void loop() {
Wire.beginTransmission(ADXLAddress); // Begin transmission to the Sensor
//Ask the particular registers for data
Wire.write(X_Axis_Register_DATAX0);
Wire.write(X_Axis_Register_DATAX1);

Wire.endTransmission(); // Ends the transmission and transmits the data from the two registers

Wire.requestFrom(ADXLAddress,2); // Request the transmitted two bytes from the two registers

if(Wire.available()<=2) { //
X0 = Wire.read(); // Reads the data from the register
X1 = Wire.read();
}

Serial.print("X0= ");
Serial.print(X0);
Serial.print(" X1= ");
Serial.println(X1);
}

当然最后的x值还是需要数学的计算公式,这里只是一个粗略的例子。

走兴趣的动手试试吧。


推荐阅读:
相关文章