1 UART
复杂的外围设备如GPS模块、LCD显示器、XBee收音机等,通常使用通用异步收发器(UART)端口(通常简称为串行端口)与之进行沟通。
UART是与外围设备交换原始数据的通用接口。这是普遍的,因为数据传输速度和数据字节格式是可配置的。这是异步的,因为没有时钟信号来同步两个设备之间的数据传输。设备硬件收集所有传入的数据在一个先进先出(FIFO)缓冲区,直到您的应用程序读取。
UART数据传输是全双工的,意味着数据可以同时发送和接收。它通常比I2C快,但由于缺少一个共享的时钟,这两个设备必须同意使用一个共同的数据传输速率,每个设备都可以独立遵循最小的定时误差。
UART串行口通常有两种模式:
- 3线端口:包括数据接收(RX),数据传输(TX),地线(GND)信号。
- 5线端口:发送加入请求(RTS)和清除发送(CTS)用于硬件流控制信号。流量控制允许接
收设备在其FIFO缓冲区暂时存满了进行说明,这样发送设备在发送更多数据之前应该进行等
待。直到FIFO缓冲区间再次可用。
与SPI和I2C不同,UART只支持两个设备之间的点对点通信。
1.1 连接管理
连接到特定的UART,需要知道唯一的UART端口名称。在发展的初始阶段,或在应用程序移植到新的硬件,可以通过peripheralmanagerservice类的getuartdevicelist()方法发现设备上所有可用的
UART端口名称:
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);
}
确定了哪个目标UART端口后,使用peripheralmanagerservice服务连接外围设备。当完成与外围设备的通信后,需要关闭端口连接,释放资源。此外,在现有UART端口连接未被关闭之前,是无法使用该UART端口打开的新连接。关闭连接,可以调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);
}
}
}
}
1.2 配置端口参数
建立连接后,配置数据传输速率和帧格式以匹配连接的外围设备。
数据帧
通过UART发送的每个字符都包在一个数据帧中,它包含以下组件:
- 启动位-在发送数据之前,该行保持为1位持续时间的固定时间间隔,以指示新字符的开始。
- 数据位-代表数据字符的单个位。UART可配置为发送5-9数据位之间的代表人物。较少的比特
减少了数据的范围,但可以提高有效的数据传输速率。
- 奇偶校验位-如果UART配置为偶数奇偶校验,则将额外的位添加到帧中,以指示数据位的内
容是否与偶数或奇数值相加。设置此为无移除帧的位。
- 停止位-在所有数据被传输后,该行被重置为可配置的时间间隔,以指示该字符的结尾。这可以
被配置为1或2位时间无所事事。
注:对于大多数UART设备默认的配置是8个数据位,无奇偶校验,1个停止位。
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);
}
注意:选择不相同的波特率可能会导致高错误率的数据传输。应该始终验证所选择的波特率是否很好地支持设备硬件。
1.3 硬件流控制
如果设备支持5线UART端口,使用硬件流控制可以提高数据传输的可靠性。通常这也意味着可以安全地使用更快的波特率与丢失的传入数据的几率要低得多。
随着硬件流控制启用,UART端口请求发送(RTS)信号时,设备上的接收缓冲区已满,不能接受任何更多的数据。一旦缓冲被耗尽,信号将被清除。类似地,UART监视清除发送(CTS)信号,并且如果它看到外围设备的那根线信号生效,将暂停传输数据。
启用硬件流控制,使用的方法sethardwareflowcontrol(),传入参数值为:hw_flow_control_auto_rtscts 。默认值是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);
}
}
1.4 向外发送数据
发送缓冲区的数据在串口外设,使用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");
}
注:一个java字节包含8个bit位。如果调用setdatasize()方法配置一个较小的数据宽度,每个字节被截断。
1.5 监听输入数据
使用read()方法将输入的数据从异步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回调。当UART数据缓冲区有可以读取的数据时,使用这个回调函数的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()这个回调方法将返回一个布尔值,表示在下一次中断事件到来时,是否应该自动注册回调函数来接收该中断事件。自处返回一个true值,表明下次UART的FIFO缓冲区中下次再有数据,继续接收事件并读取数据。