写在前面的话:由于外设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 |
SPI
串行外设接口(SPI)设备通常被发现在需要快速数据传输速率的地方。 SPI非常适合高带宽使用情况,例如外部非易失性存储器和图形显示器。 除了I2C,许多传感器器件还支持SPI。
SPI是同步串行接口,这意味着它依赖于共享时钟信号来同步设备之间的数据传输。 主器件控制时钟信号的触发,所有其他连接的外设称为从器件。 每个设备连接到同一组数据信号以形成总线。
理论上,SPI的数据传输速率仅受主机切换时钟信号的速度的限制。 时钟速度通常在16MHz至25MHz范围内。 该高速共享时钟允许SPI外设比UART更快速地传输数据并且具有更少的错误。
SPI支持全双工数据传输,意味着主站和从站可以同时交换信息。 为了支持全双工传输,总线必须提供以下单独的信号,这使SPI成为最小的4线接口:
- 主出从入(MOSI)
- 主入从出(MISO)
- 共享时钟信号(CLK)
- 公共接地参考(GND)
SPI支持沿同一总线连接的多个从器件。 与I2C不同,从器件使用硬件寻址。 每个从设备需要外部芯片选择信号,以允许主设备将该特定设备寻址为数据传输目标。 如果只使用一个从机,则不需要该信号。
管理设备连接
为了打开到特定SPI从器件的连接,您需要知道总线的唯一名称。 在开发的初始阶段或将应用程序移植到新硬件时,可以使用getSpiBusList()从PeripheralManagerService找到所有可用的设备名称:
PeripheralManagerService manager = new PeripheralManagerService();
List<String> deviceList = manager.getSpiBusList();
if (deviceList.isEmpty()) {
Log.i(TAG, "No SPI bus available on this device.");
} else {
Log.i(TAG, "List of available devices: " + deviceList);
}
支持多个硬件片选的SPI控制器将报告每个可用从端口作为单独的设备名称。 例如,在同一SPI总线上支持CS0,CS1和CS2的电路板将返回类似于getSpiBusList()中的“SPI0.0”,“SPI0.1”和“SPI0.2”的名称。
一旦知道目标名称,就可以使用PeripheralManagerService连接到该设备。 与外围设备通信后,记得关闭连接以释放资源。 此外,在现有连接关闭之前,无法打开与设备的新连接。 要关闭连接,请使用设备的close()方法。
public class HomeActivity extends Activity {
// SPI 设备名称
private static final String SPI_DEVICE_NAME = ...;
private SpiDevice mDevice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 尝试访问SPI设备
try {
PeripheralManagerService manager = new PeripheralManagerService();
mDevice = manager.openSpiDevice(SPI_DEVICE_NAME);
} catch (IOException e) {
Log.w(TAG, "Unable to access SPI 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 SPI device", e);
}
}
}
}
配置时钟和数据模式
在与SPI总线建立连接后,配置数据传输速率和操作模式以匹配同一总线上的从器件。 为了使数据传输成功,总线上的所有器件都必须具有相同的时钟和数据格式行为。
注:有些从器件不能配置其SPI操作模式,因此在选择外设时请参考硬件文档。
-
设置SPI模式,该模式定义时钟信号的极性和相位。 您选择的模式基于三个属性:
空闲电平:当没有数据传输时,时钟信号的电平(低或高)。
前沿:每个时钟脉冲的前沿。
-
后沿:在每个时钟脉冲中与前沿相反的过渡。
支持以下模式:- MODE0 - 时钟信号空闲,数据在前沿时钟边沿传输
- MODE1 - 时钟信号空闲为低电平,数据在后沿时钟边沿传输
- MODE2 - 时钟信号空闲,数据在前沿时钟边沿传输
- MODE3 - 时钟信号空闲为高电平,数据在后沿时钟边沿传输
设置以下SpiDevice参数:
- 频率 - 指定以Hz为单位的共享时钟信号。 时钟信号能力因设备硬件而异。 在设置此值之前,应验证特定设备支持的频率。
- 对齐 - 指定每个字节中各个位在总线上传输时的顺序。 这也称为数据的字节顺序。 默认情况下,数据将首先使用最高有效位(MSB)发送。
- 每位字位 - 配置在切换给定从器件的片选线之间一次传输的位数。 默认值为每个字节8位。
以下代码使用模式0,16MHz时钟,每字8位和MSB优先级配置SPI连接:
public void configureSpiDevice(SpiDevice device) throws IOException {
// 低时钟,前沿传输
device.setMode(SpiDevice.MODE0);
device.setFrequency(16000000); // 16MHz
device.setBitsPerWord(8); // 8 BPW
device.setBitJustification(false); // MSB first
}
传输数据
SPI支持半双工和全双工数据传输。 大多数应用程序应该使用半双工write()或read()方法与从设备交换数据。
// 半双工数据传输
public void sendCommand(SpiDevice device, byte[] buffer) throws IOException {
// 将数据移出到从机
device.write(buffer, buffer.length);
// 阅读响应
byte[] response = new byte[32];
device.read(response, response.length);
...
}
要执行全双工交换,请改用transfer()方法。 此方法接受两个缓冲区进行读取和写入。 写缓冲区包含要发送到从设备的数据,而读缓冲区为空并接受来自从设备的数据。
数据长度必须小于或等于最小缓冲区大小。 对于全双工传输,缓冲区大小是相等的。
// 全双工数据传输
public void sendCommand(SpiDevice device, byte[] buffer) throws IOException {
byte[] response = new byte[buffer.length];
device.transfer(buffer, response, buffer.length);
...
}