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值还是需要数学的计算公式,这里只是一个粗略的例子。
走兴趣的动手试试吧。