Android Things具有独特的功能,可通过Peripheral API和内置设备支持轻松连接到外部电子组件。 在本文中,您将了解可以连接到不同类型的外围设备,以便使用Android Things自定义IoT设备。
使用不同的界面
Android Things通过使用Peripheral API将大多数设备连接到原型板上,该API支持GPIO,PWM,I 2 C,SPI和UART接口,每种接口都是用于与外设进行通信的行业标准接口。 在本节中,您将学习这些接口是什么,以及如何使用这些连接与已连接到Android Things原型开发板的设备进行通信。
通用输入输出
通用输入/输出(GPIO)引脚用于与组件进行数字(二进制)通信,例如读取是否按下按钮,或打开或关闭LED。 在本教程中将看到的I / O方法中,GPIO使用最简单,只需要一个引脚,并为高或低状态使用布尔值。
在连接到GPIO引脚之前,您需要知道该引脚的唯一名称。 您可以通过检索PeripheralManagerService
并调用getGpioList()
来获取所有可用引脚的名称。 在Raspberry Pi上,这将返回以下列表:
[BCM12, BCM13, BCM16, BCM17, BCM18, BCM19, BCM20, BCM21, BCM22, BCM23, BCM24, BCM25, BCM26, BCM27, BCM4, BCM5, BCM6]
要弄清楚每个引脚代表哪些引脚,可以参考Raspberry Pi I / O图。
获得要读取或写入的引脚的名称后,可以通过从PeripheralManagerService
调用openGpio(String pin_name)
来获取对该引脚的Gpio
对象引用。
try {
mGpio = service.openGpio(PIN_NAME);
} catch (IOException e){
}
GPIO引脚可用于输入或输出。 如果您将使用该引脚读取信息,则需要将引脚方向配置为DIRECTION_IN
,并设置该引脚的触发类型,以便它知道何时让您的应用知道发生了什么事情。
mGpio.setDirection(Gpio.DIRECTION_IN);
mGpio.setEdgeTriggerType(Gpio.EDGE_BOTH);
触发类型包括EDGE_NONE
, EDGE_RISING
, EDGE_FALLING
和EDGE_BOTH
。 如果按下按钮,则在按钮电路完成并且物理引脚上出现高压信号时,将发生上升事件。 释放按钮时发生下降事件。 系统会根据您设置的触发器类型将更改通知您的代码。
现在,您的GPIO正在监听边沿触发,您将需要创建一个GpioCallback
来在您的应用程序中注册GPIO组件的值。
private GpioCallback mCallback = new GpioCallback() {
@Override
public boolean onGpioEdge(Gpio gpio) {
try {
Log.d("Tuts+", "GPIO value: " + gpio.getValue());
} catch( IOException e ) {
}
return super.onGpioEdge(gpio);
}
@Override
public void onGpioError(Gpio gpio, int error) {
super.onGpioError(gpio, error);
}
};
创建回调后,请向Gpio
对象注册它。
mGpio.registerGpioCallback(mCallback);
如果您的GPIO引脚正在写入信息,则需要将方向设置为DIRECTION_OUT_INITIALLY_LOW
或DIRECTION_OUT_INITIALLY_HIGH
,具体取决于您希望组件以打开还是关闭的方式启动。 输出引脚不需要触发类型。
mGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
为了写入设备,您可以调用Gpio
对象上的setValue(boolean)
来设置组件的状态。
mGpio.setValue(true);
应用程序运行完成后,您将需要注销输入回调(如果已创建和注册),并使用onDestroy()
的close()
方法close()
对外围设备的访问。
@Override
protected void onDestroy() {
super.onDestroy();
if( mGpio != null ) {
try {
mGpio.unregisterGpioCallback(mCallback);
mGpio.close();
mGpio = null;
} catch( IOException e ) {
}
}
}
脉宽调制
脉宽调制(PWM)设备使用数字状态的交替(称为比例控制信号)来起作用。 要注意比例控制信号的三个主要部分:
- 频率:这描述了输出脉冲重复的频率。 频率的测量单位为赫兹(Hz)。
- 占空比:代表设定频率内脉冲的宽度。 占空比表示为某个频率内高信号的百分比,因此,占一半时间的周期的占空比为50%。
- 周期:这表示每个上/下周期发生的时间。 周期与频率成反比。
通过调整信号的占空比,您可以控制波形的平均“接通”时间。 您可以在下面看到一个50%周期信号的示例。
可以使用PWM信号的某些设备包括:伺服电动机和LED矩阵,伺服电动机使用频率确定其位置; LED矩阵可以使用PWM信号调整亮度。
即使仅使用电动机和GPIO设备,您也可以创建大量的IoT设备,例如带有电动激光的“智能”猫树。
与GPIO相似,您可以通过创建PeripheralManagerService
并调用getPwmList()
来检索可用PWM端口的列表。 在Raspberry Pi上,此列表如下所示:
[PWM0, PWM1]
一旦知道要使用的PWM引脚的名称,就可以使用openPwm(String)
方法打开与该外设的连接。 这将需要包装在try/catch
块中,以处理引发IOException
的可能性。
try {
mPwm = service.openPwm(PIN_NAME);
} catch( IOException e ) {
}
打开与PWM引脚的连接后,就可以控制其设置,例如频率和占空比。
mPwm.setPwmFrequencyHz(120);
mPwm.setPwmDutyCycle(25);
mPwm.setEnabled(true);
在结束应用并销毁Activity
,您需要关闭连接并释放对PWM设备的引用。
@Override
protected void onDestroy() {
super.onDestroy();
if( mPwm != null ) {
try {
mPwm.close();
mPwm = null;
} catch( IOException e ) {
}
}
}
2号
集成电路(I 2 C)总线使您的项目可以通过一个物理连接与多个设备进行通信,并且它允许您仅使用Pi或其他嵌入式设备的几个引脚发送复杂的数据。 I 2 C使用设备之间的同步通信,并依靠时钟信号来确保设备在适当的时间做出响应。
发出时钟信号的设备(通常是您的Android Things设备)被称为主设备,接收该信号的所有连接的外围设备都被称为从设备。
与仅需要一个引脚的PWM和GPIO不同,I 2 C设备需要三个连接:
- 共享时钟信号(缩写为SCL),用于将时钟信号从主设备发送到从设备。
- 共享数据线(缩写为SDA),这是用于实际数据传输的连接。 由于I 2 C是同步(半双工)通信标准,因此意味着数据一次只能在一个方向上移动,因此只有一个设备可以在任何特定时间点使用此连接。
- 电气接地连接。
值得注意的是,每个I 2 C从设备都用一个设置的地址编程,并且仅在主设备对该特定地址发出数据请求时才响应。
可以使用此连接方法的某些外围设备包括分段LED矩阵显示器和各种高级传感器。
每个Android Things设备都有一组用于I 2 C的引脚,您可以通过查看特定原型板的文档来找到。 对于Raspberry Pi,您可以参考本文顶部的引脚图,其中指出SDA和SDL是引脚3和5。要查找I 2 C的名称,可以运行以下代码:
PeripheralManagerService manager = new PeripheralManagerService();
List<String> deviceList = manager.getI2cBusList();
if( !deviceList.isEmpty() ) {
Log.d( "Tuts+", deviceList.toString() );
}
上面的代码片段将在Raspberry Pi上输出以下内容:
[I2C1]
一旦知道I 2 C总线的名称,就可以使用其软件地址连接到该总线上的设备。 您可以在外围组件的“数据表”中找到软件地址和其他低级连接详细信息,如果您想将外围设备用于嵌入式项目,则必读!
private I2cDevice mDevice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
PeripheralManagerService manager = new PeripheralManagerService();
mDevice = manager.openI2cDevice(I2C_DEVICE_NAME, I2C_ADDRESS);
} catch (IOException e) {}
}
通过I 2 C与设备通信时,可以使用系统管理总线 (SMBus)协议将数据放入寄存器中,或检索已保存在每个外围设备的寄存器中的数据。 这是通过使用设备地址和寄存器地址来完成的,Android Things允许您使用以下方法读取或写入寄存器中的单个字节或来自多个寄存器的字节组:
-
readRegByte()
和writeRegByte()
:从特定寄存器读取或写入单个字节。 -
readRegWord()
和writeRegWord()
:从外围设备上的两个顺序寄存器以小字节序格式读取或写入字节。 -
readRegBuffer()
和writeRegBuffer()
:最多读取32个连续寄存器中的字节。 值以byte array
形式写入或检索。
public void singleByte(I2cDevice device, int address) throws IOException {
// Read one register from slave
byte value = device.readRegByte(address);
// Write the value back to slave
device.writeRegByte(address, value);
}
public byte[] multipleBytes(I2cDevice device, int startAddress) throws IOException {
// Read three consecutive register values
byte[] data = new byte[3];
device.readRegBuffer(startAddress, data, data.length);
return data;
}
当您准备好要在Android Things设备上关闭应用程序时,请确保注销I 2 C总线。
@Override
protected void onDestroy() {
super.onDestroy();
if (mDevice != null) {
try {
mDevice.close();
mDevice = null;
} catch (IOException e) {}
}
}
SPI
串行外围设备接口(SPI)连接与I 2 C连接类似,但它们支持全双工通信。 这意味着可以同时读取数据并将数据写入外围设备,而不需要主设备向从属外围设备请求信息。 仍然需要时钟信号来控制来自SPI信号总线上多个设备的数据流。 SPI至少需要四个连接才能起作用:
- 主输出从动(MOSI)
- 主进从出(MISO)
- 时钟信号(CLK)
- 共用接地(GND)
此外,如果将多个SPI设备连接到SPI总线,则需要第五个连接。 这是片选(CS),用于发信号通知外设的硬件地址,以便您的主设备可以与特定的从设备进行通信。
使用SPI的外围设备的一些示例包括LED点矩阵板和SD卡读卡器,值得注意的是,许多支持I 2 C的外围设备也将支持SPI。
就像前面的示例一样,您将需要在Android Things板上知道SPI连接的名称。 您可以通过创建PeripheralManagerService
并对该对象调用getSpiBusList()
来找到它。 在Raspberry Pi上,您可以使用以下功能:
[SPI0.0, SPI0.1]
一旦知道将要使用的SPI总线的名称,就可以打开与其的连接。
private SpiDevice mDevice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
PeripheralManagerService manager = new PeripheralManagerService();
mDevice = manager.openSpiDevice(SPI_DEVICE_NAME);
} catch (IOException e) {
}
}
为了使设备能够通过SPI总线进行通信,它们都必须“说相同的语言”,也就是说,必须为它们配置相同的时钟速率和数据格式。 虽然某些外设可以调整其数据格式和时钟速率,但其他一些则不能。 由于SPI总线上的所有设备都必须以相同的配置运行,因此在尝试将其包括在SPI总线上之前,了解外围设备的功能非常重要。 SPI总线需要设置四个设置/参数:
- SPI模式:SPI模式有两个主要组成部分:无数据传输时时钟信号是高电平还是低电平,以及脉冲的哪个边沿用于传输数据。
- 频率:这表示以Hz为单位的共享时钟信号,并且很可能是由从设备的功能确定的值。
- 位对齐:用于设置数据的字节序。 默认情况下,Android Things将使用big-endian,将最重要的big(MSB)放在首位。
- 每字位数(BPW):控制在切换芯片选择信号之前将多少位发送到从设备。 默认情况下,每个字将发送八位。
在您的Android Things Java代码中,您可以在SPI总线上设置这四个值,如下所示:
// Low clock, leading edge transfer
device.setMode(SpiDevice.MODE0);
device.setFrequency(16000000);
device.setBitsPerWord(8);
device.setBitJustification(false);
在SPI总线上与设备交互的方式取决于您是在全双工还是半双工模式下运行。 如果您的设备配置为半双工,则您将需要使用SpiDevice
对象上的read()
和write()
方法在从属设备和主设备之间交换数据。 如果您在全双工模式下运行,则需要将transfer()
方法与两个字节数组缓冲区一起使用:一个缓冲区包含要发送的数据,另一个缓冲区用于存储响应数据。
//Half-duplex mode
public void sendCommand(SpiDevice device, byte[] buffer) throws IOException {
// Shift data out to slave
device.write(buffer, buffer.length);
// Read the response
byte[] response = new byte[32];
device.read(response, response.length);
}
// Full-duplex mode
public void sendCommand(SpiDevice device, byte[] buffer) throws IOException {
byte[] response = new byte[buffer.length];
device.transfer(buffer, response, buffer.length);
}
当您准备在Android Things设备上关闭应用程序时,请确保注销SPI设备。
@Override
protected void onDestroy() {
super.onDestroy();
if (mDevice != null) {
try {
mDevice.close();
mDevice = null;
} catch (IOException e) {}
}
}
串口
与主机设备通信时,诸如LCD显示屏,SD卡读卡器和GPS模块等更复杂的外围设备通常会通过UART端口使用串行通信。 UART允许设备将原始数据异步发送到另一个设备上的缓冲区。 然后以先进先出的顺序读取该缓冲区。 它还允许在两个连接的设备上配置数据格式和传输速度。 由于UART不支持时钟信号,因此两个设备必须在传输速度上达成共识,但是传输速度往往比I 2 C快,并且可以在设备之间以全双工模式传输数据。 与I 2 C和SPI不同,每个UART连接只允许连接一个外设。
UART外设具有三芯线:
- RX,用于接收数据。
- TX,用于传输数据。
- GND,电气接地。
在外围组件上,这些引脚通常将标记如下:
此外,某些UART设备可能还包括两个用于控制数据流的连接:
- 要求发送(RTS)
- 清除发送(CLS)
与前面的所有示例类似,您将需要知道Android Things设备的UART连接的名称。 您可以使用以下代码找到它:
PeripheralManagerService manager = new PeripheralManagerService();
List<String> deviceList = manager.getUartDeviceList();
if (!deviceList.isEmpty()) {
Log.d("Tuts+", deviceList.toString());
}
在Raspberry Pi上,日志输出将如下所示:
[UART0]
知道UART连接的名称后,就可以用打开其他设备连接的相同方式创建Android Things UART设备。
PeripheralManagerService manager = new PeripheralManagerService();
mDevice = manager.openUartDevice(UART_DEVICE_NAME);
拥有设备后,您将需要配置将在设备之间发送的数据帧的格式。 有四个要注意的属性:
- 数据位数:UART可以发送五到九位来表示数据字符。 发送较少的位可以加快数据传输速度,但会限制可以发送的字符范围。
- 奇偶校验位:用于检查传输数据内容的总和是否为偶数或奇数。 如果奇偶校验设置为无,则奇偶校验位将从传输中删除。 这用于对传输进行错误检查。
- 停止位:可以设置为1或2位。 传输将以等于停止位数量的持续时间空闲,以指示数据帧已结束。
- 波特率:波特率是数据帧的传输速度。 由于UART中没有时钟信号,因此必须在传输开始之前为两个设备配置波特率。
这些设置中的每一个都可以在UART设备上进行配置,如下所示:
uart.setBaudrate(9600);
uart.setDataSize(6);
uart.setParity(UartDevice.PARITY_NONE);
uart.setStopBits(2);
先前您了解到UART器件有两种接线设置,一种是带有附加控制信号的设置,另一种是没有。 您可以使用setHardwareFlowControl()
方法在代码中配置UART设备。
uartDevice.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_AUTO_RTSCTS);
//or
uartDevice.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_NONE);
配置完设备后,就该从Android Things板和UART外设读取和写入数据了。 通过使用带有字节数组缓冲区和该数组长度的write()
方法来处理write()
。
uartDevice.write(buffer, buffer.length);
尽管您可以连续轮询设备的读取缓冲区中的数据,但更好的选择是创建一个UartDeviceCallback
对象,该对象提供了一个名为onUartDeviceDataAvailable()
的方法,该方法将在数据可用时触发。 启用后,您可以从设备的数据缓冲区读取数据。 您将能够通过registerUartDeviceCallback
将此回调与您的UART设备相关联,尽管您需要记住在完成回调后调用unregisterUartDeviceCallback
。 如果有可用数据,则可以使用UART read()
方法进行检索。
private UartDeviceCallback mUartCallback = new UartDeviceCallback() {
@Override
public boolean onUartDeviceDataAvailable(UartDevice uart) {
byte[] buffer = new byte[MAX_BUFFER_SIZE];
try {
uartDevice.read(buffer, buffer.length)
//do something with the data in the buffer byte array
} catch (IOException e) {}
//Returning true keeps the callback active. If you return false,
//the callback will automatically unregister.
return true;
}
@Override
public void onUartDeviceError(UartDevice uart, int error) {}
};
@Override
protected void onStart() {
super.onStart();
mDevice.registerUartDeviceCallback(mUartCallback);
}
@Override
protected void onStop() {
super.onStop();
mDevice.unregisterUartDeviceCallback(mUartCallback);
}
完成Activity
,请确保关闭对UART设备的引用并使之无效。
@Override
protected void onDestroy() {
super.onDestroy();
if (uartDevice != null) {
try {
uartDevice.close();
uartDevice = null;
} catch (IOException e) {}
}
}
高级I / O和将来的更新
尽管Peripheral API允许您与几乎可以连接到Android Things板上的任何设备进行通信,但Google提供了一些额外的支持,使您的IoT设备的构建变得更加容易。 本节将介绍Android Things提供的一些其他外围设备支持。
本机I / O
尽管Android Things为Android开发人员提供了一种使用Android SDK和Java进行IoT开发的简单方法,但是许多IoT应用已经存在于C和C ++中,并且出于各种原因,其他开发人员更喜欢使用这些语言。 为了支持此功能,Google对Peripheral API进行了补充,以支持本机应用程序和NDK。 我不会深入探讨这个主题,但是建立和使用的连接方式与上面讨论的方式非常相似。
WiFi和蓝牙
如果没有连接到Internet的方法,则IoT设备将仅仅是物联网 。 借助内置的无线支持,您的Android Things设备可以连接到Internet并使用各种在线资源,例如Firebase和其他Google服务或任何其他后端服务。 另外,使用诸如Google Play服务和Nearest Connections API之类的工具,可以允许用户通过其WLAN网络直接与您的设备进行通信。
尽管当前未在Android Things的开发人员预览版中启用,但蓝牙连接性也是重要的IoT功能。 使用蓝牙,您可以创建多个IoT设备或配件,并使它们在短距离内通信 。 这允许每个设备具有有关其环境的其他上下文信息,例如用户的家或工作区,以便为用户提供最佳服务。
USB
尽管在Android Things开发人员预览版的当前版本中并未完全启用原型设备的USB端口,但某些设备仍可使用它们。 在第二次开发人员预览中,Google已将USB音频和文本添加到语音支持中,并且开发人员已确认其他设备(例如USB麦克风)也可以正常工作。 随着USB API在更高版本的Android Things中经过测试和批准,将会有各种各样的设备可以作为附件轻松插入到IoT设备中。
相机和显示器
某些设备(例如Raspberry Pi)带有附加的内置I / O连接,例如HDMI,Display和Camera。 幸运的是,所有这些都已通过Android Things启用,可让您使用15厘米带状电缆连接相机...
...或通过带状电缆或HDMI连接的显示器:
结论
恭喜你! 您已经学到了很多东西,但是学到了有关使用Android Things板连接和控制外围设备的宝贵信息。 使用此知识,您应该能够连接各种硬件组件并与之通信。
在本系列的下一篇文章中,我们将使用对Peripherals API的了解来创建GPIO运动传感器组件的软件驱动程序,然后将其用于更大的Android Things项目中。
翻译自: https://code.tutsplus.com/tutorials/android-things-peripheral-inputoutput--cms-27891