UART
复杂的外围设备,如GPS模块,液晶显示器和XBee无线电通常使用通用异步收发器(UART)端口(通常简称为串行端口)进行通信。
UART是用于与原始数据交换外围设备的通用接口。它是通用的,因为数据传输速度和数据字节格式均可配置。它是异步的,因为没有时钟信号用于同步两个设备之间的数据传输。设备硬件会以先进先出(FIFO)缓冲区收集所有输入的数据,直到您的应用程序读取。
UART数据传输是全双工的,意味着可以同时发送和接收数据。它通常比 I2C 更快,但缺乏共享时钟意味着两个设备必须达成一致的数据传输速率,每个设备可以以最小的定时误差独立地遵循。
UART外设通常有两种:
- 3线端口包括数据接收(RX),数据发送(TX)和接地参考(GND)信号。
- 5线端口添加发送(RTS)和清除发送(CTS)信号用于硬件流控制的请求。流量控制允许接收设备指示其FIFO缓冲区临时充满,发送设备在发送任何更多数据之前应该等待。
与 SPI 和 I2C 不同,UART仅支持两个设备之间的点对点通信。
管理连接
为了打开与特定UART的连接,您需要知道唯一的端口名称。在开发初始阶段,或将应用程序移植到新硬件时,使用 getUartDeviceList()
从 PeripheralManagerService
发现所有可用的设备名称:
PeripheralManagerService manager = new PeripheralManagerService();
List<String> deviceList = manager.getUartDeviceList();
if (deviceList.isEmpty()) {
Log.i(TAG, "No UART port available on this device.");
} else {
Log.i(TAG, "List of available devices: " + deviceList);
}
知道目标名称后,使用 PeripheralManagerService
连接到该设备。完成与外围设备通信后,关闭连接以释放资源。此外,在现有连接关闭之前,您无法打开与设备的新连接。要关闭连接,请使用设备的 close()
方法。
public class HomeActivity extends Activity {
// UART Device Name
private static final String UART_DEVICE_NAME = ...;
private UartDevice mDevice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Attempt to access the UART device
try {
PeripheralManagerService manager = new PeripheralManagerService();
mDevice = manager.openUartDevice(UART_DEVICE_NAME);
} catch (IOException e) {
Log.w(TAG, "Unable to access UART 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 UART device", e);
}
}
}
}
配置端口参数
建立连接后,配置数据传输速率和帧格式以匹配连接的外围设备。
数据帧格式
通过UART发送的每个字符都被包装在一个数据帧中,其中包含以下部分:
- 起始位 - 在发送数据之前,该行被保持为1位持续时间的固定时间间隔,以指示新字符的开始。
- 数据位 - 表示数据字符的各个位。 UART可以配置为在5-9个数据位之间发送以表示字符。较少的位减少了数据的范围,但可以提高有效的数据传输速率。
- 奇偶校验位 - 可选错误检查值。如果将UART配置为偶校验或奇校验,则会向帧中添加一个额外的位,以指示数据位的内容是否与偶数或奇数值相加。将此设置为none将从帧中删除该位。
- 停止位 - 发送完所有数据后,该行将重置为可配置的时间间隔,以指示该字符的结尾。这可以配置为在1或2位持续时间内保持空闲。
UART上的数据传输速率称为波特率。它代表接收和发送的速度(以位/秒为单位)。由于通过UART连接的两个设备之间没有共享时钟,所以必须提前配置,以便使用相同的波特率来正确解码数据。
通用波特率包括9600,19200,38400,57600,115200和921600。这个速率包括数据帧(开始,停止和奇偶校验位)的开销,所以有效的数据传输速率会稍微降低一些,基于您配置的帧位数。
以下代码将UART连接配置为工作在115200波特,8个数据位,无奇偶校验和1个停止位(8N1):
public void configureUartFrame(UartDevice uart) throws IOException {
// Configure the UART port
uart.setBaudrate(115200);
uart.setDataSize(8);
uart.setParity(UartDevice.PARITY_NONE);
uart.setStopBits(1);
}
硬件流控制
如果您的设备支持5线UART端口,则可以启用硬件流控制 提高数据传输的可靠性。通常这也意味着你可以安全 使用更快的波特率,丢失传入数据的可能性更低。
启用硬件流量控制时,当设备的接收缓冲区已满时,UART将发出请求发送(RTS)信号,并且不能再接收任何数据。一旦缓冲液排空,信号将被清除。类似地,UART监视清除发送(CTS)信号,并且如果看到外围设备断言的线路将暂停发送数据。
要启用硬件流控制,请使用带有 HW_FLOW_CONTROL_AUTO_RTSCTS
的 setHardwareFlowControl()
方法。默认值为 HW_FLOW_CONTROL_NONE
,表示流控制被禁用。
public void setFlowControlEnabled(UartDevice uart, boolean enable) throws IOException {
if (enable) {
// Enable hardware flow control
uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_AUTO_RTSCTS);
} else {
// Disable flow control
uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_NONE);
}
}
输出数据
要通过UART将数据缓冲区传输到外设,请使用write()方法:
public void writeUartData(UartDevice uart) throws IOException {
byte[] buffer = {...};
int count = uart.write(buffer, buffer.length);
Log.d(TAG, "Wrote " + count + " bytes to peripheral");
}
读入数据
使用read()方法将UART FIFO缓冲区中的数据从您的应用程序中拉出。此方法接受一个空的缓冲区来填充输入的数据和要读取的最大字节数。 UART读取是非阻塞的,如果FIFO中没有可用的数据,它将立即返回。
UartDevice
将在读取时返回FIFO中的可用字节数,直到所请求的计数。为了确保所有数据都被恢复,循环通过UART,直到它报告不再有数据:
public void readUartBuffer(UartDevice uart) throws IOException {
// Maximum amount of data to read at one time
final int maxCount = ...;
byte[] buffer = new byte[maxCount];
int count;
while ((count = uart.read(buffer, buffer.length)) > 0) {
Log.d(TAG, "Read " + count + " bytes from peripheral");
}
}
为避免在缓冲区为空时不必要地轮询UART,请使用 UartDevice
注册 UartDeviceCallback
。当有可用数据可读时,此回调调用 onUartDeviceDataAvailable()
方法。当您的应用程序不再监听传入的数据时,您应该取消注册回调。
public class HomeActivity extends Activity {
private UartDevice mDevice;
...
@Override
protected void onStart() {
super.onStart();
// Begin listening for interrupt events
mDevice.registerUartDeviceCallback(mUartCallback);
}
@Override
protected void onStop() {
super.onStop();
// Interrupt events no longer necessary
mDevice.unregisterUartDeviceCallback(mUartCallback);
}
private UartDeviceCallback mUartCallback = new UartDeviceCallback() {
@Override
public boolean onUartDeviceDataAvailable(UartDevice uart) {
// Read available data from the UART device
try {
readUartBuffer(uart);
} catch (IOException e) {
Log.w(TAG, "Unable to access UART device", e);
}
// Continue listening for more interrupts
return true;
}
@Override
public void onUartDeviceError(UartDevice uart, int error) {
Log.w(TAG, uart + ": Error event " + error);
}
};
}
onUartDeviceDataAvailable()
回调返回一个布尔值,指示回调应该是否被自动注销以避免接收未来的中断事件。每当数据出现在UART FIFO中时,返回true继续接收事件。