写在前面的话:由于外设I/O涉及到GPIO、PWM、和串行通信三部分,而串行通信有讲了I2C(IIC)、SPI、UART,这样导致本文的篇幅过长不便于阅读,特此将本文分成几部分来方便阅读
- Android Things 外设I/O-GPIO
- Android Things 外设I/O-PWM
- Android Things 外设I/O-I2C(IIC)
- Android Things 外设I/O-SPI
- Android Things 外设I/O-UART
串行通信
使用这些API在连接在同一本地总线上的两个或多个智能设备之间传输更大的数据有效负载。 下表概述了每个支持的串行协议的基本属性:
协议 | 传输类型 | 电线数量 | 外围设备数量 | 传输速度 |
---|---|---|---|---|
I2C | 同步 | 2 | Up to 127 | Low |
SPI | 同步 | 4+ | Unlimited | High |
UART | 异步 | 2 or 4 | 1 | Medium |
I2C
内部集成电路(IIC或I2C)总线连接具有小数据有效载荷的简单外围设备。 传感器和执行器是I2C的常见用例。 示例包括加速度计,温度计,LCD显示器和电机驱动器。
I2C是同步串行接口,这意味着它依赖于共享时钟信号来同步设备之间的数据传输。 触发时钟信号的控制装置称为主机。 所有其他连接的外围设备称为从设备。 每个设备连接到同一组数据信号以形成总线。
I2C器件使用3-Wire接口连接,包括:
- 共享时钟信号(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 {
// IIC 设备名称
private static final String I2C_DEVICE_NAME = ...;
// IIC 从设备地址
private static final int I2C_ADDRESS = ...;
private I2cDevice mDevice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 尝试访问I2C设备
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总线,地址表示该总线上的各个从器件。 因此,I2cDevice是到相应I2C总线上的特定从器件的连接。
与寄存器交互
I2C从器件将其内容组织为可读或可写寄存器(由地址值引用的数据的单个字节):
- 可读寄存器 - 包含从机要向主机报告的数据,例如传感器值或状态标志。
- 可写寄存器 - 包含主控可以控制的配置数据。
称为系统管理总线(SMBus)的通用协议实现存在于I2C之上,以标准方式与寄存器数据交互。 SMBus命令由两个I2C事务组成,如下所示:
第一个事务标识要访问的寄存器地址,第二个事务在该地址读取或写入数据。 从设备上的逻辑数据通常可以占用多个字节,并且因此包含多个寄存器地址。 提供给API的寄存器地址始终是要引用的第一个寄存器。
注:根据SMBus协议,设备将在地址和数据事务之间发送“重复启动”条件。
外设I/O提供三种类型的SMBus命令来访问寄存器数据:
- 字节数据 - readRegByte()和writeRegByte()读取或写入一个8位寄存器值。
- 字数据 - readRegWord()和writeRegWord()读取或写入两个连续的寄存器值作为16位小端字。 第一个寄存器地址被解释为字中的最低有效字节(LSB),后跟最高有效字节(MSB)。
- 块数据 - readRegBuffer()和writeRegBuffer()读取或写入多达32个连续的寄存器值作为数组。
// 修改单个寄存器的内容
public void setRegisterFlag(I2cDevice device, int address) throws IOException {
// 从从器件读取一个寄存器
byte value = device.readRegByte(address);
// 设置bit 6
value |= 0x40;
// 将更新的值写回从器件
device.writeRegByte(address, value);
}
// 读取寄存器块
public byte[] readCalibration(I2cDevice device, int startAddress) throws IOException {
// 读取三个连续的寄存器值
byte[] data = new byte[3];
device.readRegBuffer(startAddress, data, data.length);
return data;
}
传输原始数据
当与不同于SMBus定义其寄存器的I2C外设交互时,或者根本不使用寄存器时,使用原始 read()和write()方法来完全控制通过线传输的数据字节。 这些方法将执行单个I2C事务,如下所示:
使用原始传输时,器件将在传输之前发送单个启动条件,并在之后发送单个停止条件。 不可能将多个事务与“重复开始”条件组合。
注意:原始事务没有明确的最大长度,但是设备上的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.");
}