I2C
集成电路(IIC或I2C)总线将简单的外围设备与小数据有效载荷连接起来。传感器和执行器是I2C的常见用例。示例包括加速度计,温度计,LCD显示器和电机驱动器。
I2C是同步 串行接口,这意味着它依赖于共享时钟信号来同步器件之间的数据传输。控制触发时钟信号的器件称为主器件。所有其他连接的外围设备都称为从站。每个设备连接到同一组数据信号以形成总线。
I2C器件使用3线接口连接,包括:
- 共享时钟信号(SCL)
 - 共享数据线(SDA)
 - 公共接地参考(GND)
 

由于所有数据都通过一条线传输,所以I2C只支持半双工 通信。所有通信由主设备启动,并且从站必须在主站传输完成后响应。
I2C支持沿同一总线连接的多个从站器件。与 SPI 不同,从站器件使用 I2C软件协议进行寻址。每个设备都使用唯一的地址编程,只响应主机发送到该地址的传输。每个从站设备必须具有地址,即使总线只有单个从站。
管理从站设备连接
为了打开与特定 I2C从站的连接,您需要知道总线的唯一名称。在开发初始阶段,或将应用程序移植到新硬件时,使用 getI2CBusList() 从 PeripheralManagerService 发现所有可用的设备名称:
PeripheralManagerService manager = new PeripheralManagerService();
List<String> deviceList = manager.getI2cBusList();
if (deviceList.isEmpty()) {
    Log.i(TAG, "No I2C bus available on this device.");
} else {
    Log.i(TAG, "List of available devices: " + deviceList);
}
知道目标设备名称后,使用 PeripheralManagerService 连接到该设备。完成与外围设备通信后,关闭连接以释放资源。此外,在现有连接关闭之前,您无法打开与设备的新连接。要关闭连接,请使用设备的 close() 方法。
public class HomeActivity extends Activity {
    // I2C Device Name
    private static final String I2C_DEVICE_NAME = ...;
    // I2C Slave Address
    private static final int I2C_ADDRESS = ...;
    private I2cDevice mDevice;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Attempt to access the I2C device
        try {
            PeripheralManagerService manager = new PeripheralManagerService();
            mDevice = manager.openI2cDevice(I2C_DEVICE_NAME, I2C_ADDRESS);
        } catch (IOException e) {
            Log.w(TAG, "Unable to access I2C device", e);
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mDevice != null) {
            try {
                mDevice.close();
                mDevice = null;
            } catch (IOException e) {
                Log.w(TAG, "Unable to close I2C device", e);
            }
        }
    }
}
> 
与寄存器进行交互
I2C 从器件将其内容组织成可读或可写寄存器(由地址值引用的数据的单个字节):
- 可读寄存器 - 包含从站想要向主站报告的数据,如传感器值或状态标志。
 - 可写入寄存器 - 包含主机可以控制的配置数据。
 
被称为 系统管理总线(SMBus)的通用协议实现存在于 I2C 之上,以标准方式与寄存器数据进行交互。 SMBus命令由两个 I2C 事务组成,如下所示:

第一个事务标识要访问的寄存器地址,第二个事务在地址处读取或写入数据。从设备上的逻辑数据可能经常占用多个字节,因此包含多个寄存器地址。提供给API的寄存器地址始终是第一个引用寄存器。
>外设I / O提供访问寄存器数据的三种类型的SMBus命令:
- 
字节数据 -
readRegByte()和writeRegByte()读取或写入单个8位寄存器值。 - 
字数据 -
readRegWord()和writeRegWord()读取或写入两个连续的寄存器值作为16位小端字。第一个寄存器地址对应于字中的最低有效字节(LSB),后跟最高有效字节(MSB)。 - 
块数据 -
readRegBuffer()和writeRegBuffer()读取或写入最多32个连续的寄存器值作为数组。 
// Modify the contents of a single register
public void setRegisterFlag(I2cDevice device, int address) throws IOException {
    // Read one register from slave
    byte value = device.readRegByte(address);
    // Set bit 6
    value |= 0x40;
    // Write the updated value back to slave
    device.writeRegByte(address, value);
}
// Read a register block
public byte[] readCalibration(I2cDevice device, int startAddress) throws IOException {
    // Read three consecutive register values
    byte[] data = new byte[3];
    device.readRegBuffer(startAddress, data, data.length);
    return data;
}
转移原始数据
当与 I2C 外设进行交互时,它不同于SMBus定义其寄存器 - 或者根本不使用寄存器 - 使用原始的 read() 和 write() 方法来完全控制通过线传输的数据字节。这些方法将执行单个 I2C 事务,如下所示:

使用原始传输,设备将在传输之前发送单个启动条件,并在之后单个停止条件。不可能将多个事务与“重复启动”条件组合。
以下代码示例说明如何构造原始字节缓冲区并将其写入 I2C 从站器件:
public void writeBuffer(I2cDevice device, byte[] buffer) throws IOException {
    int count = device.write(buffer, buffer.length);
    Log.d(TAG, "Wrote " + count + " bytes over I2C.");
}