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.");
}