Android Things 是 Android 操作系统的轻量级版本,可安装在以物联网为重点的设备上。Android Things 包括“用户驱动程序”功能,使开发人员能够轻松控制各种外设(LED、开关、按钮、伺服电机等)。现在,让我们来看看为 Android Things 编写一个新的外设驱动程序需要什么。
在写这篇文章的时候,Google 并没有宣布一个硬件要求运行 Android Things 的要求,但是硬件文档声明他们正在关注模块(SoM)板上的系统。虽然唯一支持 Android Things 的开发板是 Raspberry Pi 3、Intel Edison 和 NXP Pico i.MX6UL,但似乎几乎所有的开发板都能够运行 Android Things。支持这些的开发板不提供许多传感器、LED和按钮,但是一旦 Android Things 设备启动并运行,您就可以轻松插入外围设备。
其中一件让我兴奋的事情就是用户驱动程序。
如上图所示,开发人员可以直接使用 Android Framework API,并使用用户驱动程序构建应用程序。这些是访问和控制 Android Things 应用程序的外围硬件的一个薄层。这使我们能够专注于重要的事情:编写出色的应用程序。
您可以在 Github 上找到可用的用户驱动程序的列表。这些驱动程序的好处是,它们就像任何其他 Android 库一样,意味着你可以在项目的 build.gradle 依赖项中添加一行,驱动程序就能被导入。
如果您想使用没有 Android Things 驱动程序的外设,会发生什么情况? 你有2个选项:
如果您采取第二种选择,您可以通过共享驱动程序,让任何人(包括您)在未来的项目中重复使用该代码,从而回馈开源社区。 为此,您需要供应商提供的外设文档。
首先你需要了解外设使用哪种通信协议。Android Things 支持以下类型的通信:
通用输入/输出(GPIO):这是最简单的通信方式,您可以使用它从外设(输入)读取数据,并写入到外设(输出)2。 每个物理引脚代表一个输入或一个输出(您可以从您的应用程序配置每个引脚模式),它可以只有两个值:高和低(或1和0)。 GPIO 用于处理按钮,运动传感器等等。
脉冲宽度调制(PWM):与 GPIO 类似,PWM 使用一个物理引脚,但仅为输出(意味着 Android Things 板发送数据,从不读取)。使用 PWM,您可以控制更复杂的设备,如伺服电机,可调光灯和其他可以采用更宽范围值的设备,而不仅仅是1或0。请参阅 PWM 文档以进一步阅读该接口。
串行外设接口(SPI):如果您的外设稍微复杂一些,则可能需要使用 SPI,从 RGB LED 灯条到图形显示器等广泛的外设使用 SPI。SPI 允许主设备(在这种情况下为 Android Things 设备)与一个或多个从设备进行通信。它使用一个单向数据交换引脚,或两个双向交换引脚进行数据传输,一个引脚用于时钟,在多个从机的情况下,需要额外的芯片选择线。
内部集成电路(I2C):I2C 基于两个引脚,因为它需要数据连接和一个时钟。与 SPI 相似,I2C 可以与一个或多个从机一起使用,但每个从机都有自己的地址,主机可以指定。在这种情况下,主机一次发送一个字节的数据,从机必须确认是否已经正确接收到数据。此确认内置于I2C。该协议用于一些传感器,LCD显示器等。
通用异步收发器(UART):虽然 SPI 和 I2C 都是同步接口(因为它们都需要时钟线来同步),但 UART 是异步的。一个简单的 UART 实现需要两个引脚:一个数据输入和一个数据输出。与 SPI 和 I2C 不同,UART 不支持多个从机,数据被封装在数据帧中。数据帧包括起始位,5-9个数据位,可能是奇偶校验位和一个或两个结束位。 UART 用于 GPS 设备,XBee 无线电,一些打印机等。
请阅读外围设备供应商的文档,了解要使用的内容。如果使用上述方法之一,那么实现一个驱动程序应该是非常简单的。否则,您将必须实现与 GPIO 端口一起使用的任何协议。
我买了两块芯片上印有代码 WS2801 的 RGB LED 灯条。这是 WS2801 的数据表,我通过搜索 “WS2801 数据表”找到。在 PDF 中,功能列表提到芯片使用 PWM,但不要让这个骗过你。在第五页上有一个输入和输出引脚的列表,你可以看到芯片有一个时钟输入(CKI),一个数据输入(SDI),一个电源(VCC)和地(GND),也可以在 LED 灯条中找到。
调查使用哪个协议,这里的主要指标是所需的引脚数量。尽管电源和接地对于大多数外设是通用的,但是将时钟和数据输入作为单独引脚的需要表明,您必须使用 I2C 或 SPI(不是 PWM,因为它只使用一条线路)。
另一个指标是时钟速度:I2C 标准是 400kHz(但可以达到〜3MHz),而数据表中说 WS2801 接受 25MHz 的最大时钟频率。
第三个也是最后一个指标是数据格式:第 12 页显示芯片需要操作的唯一数据是 3 个字节(红色,绿色和蓝色值),它将为下一个芯片留下其余的数据(即带上的下一个 LED)。这里没有提到承认(acknowledgement)或双向沟通。列表中遗漏了 I2C 和 UART,所以你可以使用 SPI。
一旦你知道你必须使用哪种形式的通信(在上面的例子中是 SPI),你可以开始考虑连接和编码。
现在是时候找出主板上,哪些引脚需要用来与 Android 端的外设通话。最简单的方法是在 Android Things 开发者网站上找到开发板的引脚。引脚分配显示哪个引脚可用于每种通信形式。看一看 Raspberry Pi 3 的例子:
在这种情况下,我用针脚 2(或 4)作为电源(VCC),针脚 6(或 9,14 等)接地(GND),针脚 23 针对时钟(CKI),针脚 19 针对数据(SDI)。请注意
,有两个类似的引脚:19 - MOSI,它代表主从输入,引脚 21 - MISO 或主从输出。在这种情况下,我必须使用 MOSI,因为主设备(Android Things设备)将发送数据,从设备(LED灯条)将读取数据。
需要注意的是,不同的电路板有不同的输出电压,不同的外设需要不同的电压来操作,所以要小心。外设可能无法工作,可能会烧坏,因此在将外设插入电路板之前,请务必检查规格,并根据需要使用电平转换器。例如,关于 Raspberry Pi 3 GPIO 的这篇文章,解释了引脚可以输出 0V(低电平)或 3.3V(高电平),同时它也有两个 5V 引脚。如果 GPIO 引脚配置为输入,那么使用高于 3.3V 的任何值都会损坏电路板。
在这一点上,你应该有一个外围设备插入到你的 Android Things 开发板,你知道你将要使用的协议。 所以,现在是编写代码的时候了。
现在,我们来看看如何使用 SPI 来实现外设驱动程序。几天前,我向 Github 上的驱动程序存储库提交了一个 PR,添加了 WS2801 的驱动程序。编写驱动程序时,以下是您需要包含的主要内容。
首先创建一个标准的 gradle 模块,并在 build.gradle 中添加:
apply plugin: 'com.android.library'
android {
compileSdkVersion 24
buildToolsVersion '24.0.3'
defaultConfig {
minSdkVersion 24
targetSdkVersion 24
// Other default stuff...
}
}
dependencies {
provided 'com.google.android.things:androidthings:0.1-devpreview'
}
请记住,这和其他 Android 库一样。 你可以使用 com.android.library
插件,为了编写 Android Things 你至少需要使用 API 24 的 SDK 版本。您还应该指定 com.google.android.things:android things
作为提供的依赖项。这意味着你需要它能够编译应用程序,但是一旦它被发布,你就不想在驱动程序中分发这个依赖。
接下来,你需要添加一行到你的 AndroidManifest.xml
中:
<manifest package="com.xrigau.driver.ws2801" xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<uses-library android:name="com.google.android.things"></uses-library> // THIS
</application>
</manifest>
添加此行是指示应用程序只能在 Android Things 设备上运行,而不是在手机,平板电脑等上运行的方式。
现在是驱动程序本身。创建一个新的类(我称之为 Ws2801.java
)并编写驱动程序代码。 你需要做的第一件事是打开 SPI 端口,以便开始发送数据,然后关闭端口。 这里是主要部分:
package com.xrigau.driver.ws2801;
import com.google.android.things.pio.PeripheralManagerService;
import com.google.android.things.pio.SpiDevice;
import java.io.IOException;
public class Ws2801 implements AutoCloseable {
// ...
private final SpiDevice device;
public static Ws2801 create(String spiBusPort) throws IOException {
PeripheralManagerService pioService = new PeripheralManagerService();
try {
return new Ws2801(pioService.openSpiDevice(spiBusPort));
} catch (IOException e) {
throw new IOException("Unable to open SPI device in bus port " + spiBusPort, e);
}
}
Ws2801(SpiDevice device) throws IOException {
this.device = device;
device.setFrequency(1000000); // 1MHz clock frequency
device.setMode(SpiDevice.MODE0); // Mode 0 seems to work best for WS2801
device.setBitsPerWord(8);
}
public void write(int[] colors) throws IOException {
// … some computation
Int[] colorsToSend = new int[]{Color.RED, Color.WHITE, Color.parseColor(“#0FACE0”)}; // As many as LEDs in the strip
device.write(colorsToSend, colorsToSend.length);
}
@Override
public void close() throws IOException {
device.close(); // IMPORTANT: If you don’t close it then the resource can’t be opened again.
}
}
注意,首先你需要创建一个 PeripheralManagerService
的实例。它用于获取开发板所具有的不同输入/输出(I/O)接口,以及开放总线或 GPIO 引脚的信息。在上面的示例代码中,我调用了 openSpiDevice(String spiBusName)
方法来打开 SPI 总线接口。一旦你有了一个 SpiDevice
的参考,你可以配置总线,并开始发送数据。
注意:代码已被简化,请参阅 Github上的完整源代码。
这就是你所需要的。现在你可以开始使用驱动程序了。
到目前为止,我们已经看到了 Android Things 开发卡支持的不同类型的输入和输出,以及如何识别与外设进行通信所需的接口。我们已经介绍了针对每个不同接口使用的引脚,以及如何使用 Android Things SDK 实现驱动程序来控制外设。
在我的下一篇博客文章中,我们将介绍如何测试外设驱动程序,以提高您在使用或共享外设驱动程序时的信心。如果您想讨论这篇文章,或者对我的下一篇博客文章给我任何反馈意见,请在 Twitter 或 Google+ 上与我联系。
最后虽然这并不重要的一点,我要感谢 Daniele Bonaldo,Luis Valle,特别感谢 Paul Blundell 对本文的评论。
原文链接:https://www.novoda.com/blog/writing-your-first-android-things-driver-p1/
观光\评论区