寫在前面的話:由于外設(shè)I/O涉及到GPIO损痰、PWM福侈、和串行通信三部分,而串行通信有講了I2C(IIC)卢未、SPI肪凛、UART,這樣導(dǎo)致本文的篇幅過長不便于閱讀辽社,特此將本文分成幾部分來方便閱讀
- Android Things 外設(shè)I/O-GPIO
- Android Things 外設(shè)I/O-PWM
- Android Things 外設(shè)I/O-I2C(IIC)
- Android Things 外設(shè)I/O-SPI
- Android Things 外設(shè)I/O-UART
串行通信
使用這些API在連接在同一本地總線上的兩個(gè)或多個(gè)智能設(shè)備之間傳輸更大的數(shù)據(jù)有效負(fù)載伟墙。 下表概述了每個(gè)支持的串行協(xié)議的基本屬性:
協(xié)議 | 傳輸類型 | 電線數(shù)量 | 外圍設(shè)備數(shù)量 | 傳輸速度 |
---|---|---|---|---|
I2C | 同步 | 2 | Up to 127 | Low |
SPI | 同步 | 4+ | Unlimited | High |
UART | 異步 | 2 or 4 | 1 | Medium |
UART
復(fù)雜的外圍設(shè)備,如GPS模塊爹袁,LCD顯示器和XBee無線電通常使用通用異步收發(fā)器(UART)端口(通常簡稱為串行端口)進(jìn)行通信远荠。
??UART是用于與外圍設(shè)備交換原始數(shù)據(jù)的通用接口。 它是通用的失息,因?yàn)閿?shù)據(jù)傳輸速度和數(shù)據(jù)字節(jié)格式都是可配置的譬淳。 它是異步的,因?yàn)椴淮嬖谟糜谕絻蓚€(gè)設(shè)備之間的數(shù)據(jù)傳輸?shù)臅r(shí)鐘信號(hào)盹兢。 設(shè)備硬件以先進(jìn)先出(FIFO)緩沖區(qū)收集所有傳入數(shù)據(jù)邻梆,直到應(yīng)用程序讀取為止。
??UART數(shù)據(jù)傳輸是全雙工的绎秒,意味著數(shù)據(jù)可以同時(shí)發(fā)送和接收浦妄。 它通常比I2C快,但缺乏共享時(shí)鐘意味著兩個(gè)設(shè)備必須達(dá)成一個(gè)共同的數(shù)據(jù)傳輸速率见芹,每個(gè)設(shè)備可以獨(dú)立遵守與最小的定時(shí)誤差剂娄。
UART外設(shè)通常有兩種類型:
- 3線端口包括數(shù)據(jù)接收(RX),數(shù)據(jù)發(fā)送(TX)和接地參考(GND)信號(hào)玄呛。
- 5線端口添加用于硬件流控制的請求發(fā)送(RTS)和清除發(fā)送(CTS)信號(hào)阅懦。 流控制允許接收設(shè)備指示其FIFO緩沖器臨時(shí)滿,并且發(fā)送設(shè)備在發(fā)送任何更多數(shù)據(jù)之前應(yīng)該等待徘铝。
與SPI和I2C不同耳胎,UART僅支持兩個(gè)器件之間的點(diǎn)對點(diǎn)通信惯吕。
管理連接
為了打開到特定UART的連接,您需要知道唯一的端口名稱怕午。 在開發(fā)的初始階段或?qū)?yīng)用程序移植到新硬件時(shí)废登,可以通過 getUartDeviceList()從PeripheralManagerService找到所有可用的設(shè)備名稱:
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);
}
一旦知道目標(biāo)名稱,就可以使用PeripheralManagerService連接到該設(shè)備郁惜。 與外圍設(shè)備通信后堡距,關(guān)閉連接以釋放資源。 此外扳炬,在現(xiàn)有連接關(guān)閉之前吏颖,無法打開與設(shè)備的新連接。 要關(guān)閉連接恨樟,請使用設(shè)備的close()方法半醉。
public class HomeActivity extends Activity {
// UART設(shè)備名稱
private static final String UART_DEVICE_NAME = ...;
private UartDevice mDevice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 嘗試訪問UART設(shè)備
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);
}
}
}
}
配置端口參數(shù)
建立連接后,配置數(shù)據(jù)傳輸速率和幀格式以匹配連接的外圍設(shè)備劝术。
數(shù)據(jù)幀
通過UART發(fā)送的每個(gè)字符都包裹在一個(gè)數(shù)據(jù)幀中缩多,該數(shù)據(jù)幀包含以下組件:
- 啟動(dòng)位 - 在發(fā)送數(shù)據(jù)之前,該行保持活動(dòng)狀態(tài)养晋,持續(xù)1位持續(xù)時(shí)間的固定時(shí)間間隔衬吆,以指示新字符的開始。
- 數(shù)據(jù)位 - 表示數(shù)據(jù)字符的各位绳泉。 UART可以配置為在5-9個(gè)數(shù)據(jù)位之間發(fā)送以表示字符逊抡。 較少的位減少了數(shù)據(jù)的范圍,但可以增加有效的數(shù)據(jù)傳輸速率零酪。
- 奇偶校驗(yàn)位 - 可選錯(cuò)誤校驗(yàn)值冒嫡。 如果UART配置為偶校驗(yàn)或奇校驗(yàn),則會(huì)在幀中添加一個(gè)額外的位四苇,以指示數(shù)據(jù)位的內(nèi)容是否為偶數(shù)或奇數(shù)值孝凌。 將此設(shè)置為none會(huì)從幀中刪除該位。
- 停止位 - 在傳輸所有數(shù)據(jù)之后月腋,線路被重置一個(gè)可配置的時(shí)間間隔蟀架,以指示該字符的結(jié)束。 這可以配置為保持空閑1或2位持續(xù)時(shí)間榆骚。
注:大多數(shù)UART器件的默認(rèn)配置為8個(gè)數(shù)據(jù)位片拍,無奇偶校驗(yàn)和1個(gè)停止位(8N1)。
UART上的數(shù)據(jù)傳輸速率稱為波特率妓肢。 它表示接收和發(fā)送的速度(以位/秒為單位)穆碎。 由于在通過UART連接的兩個(gè)器件之間沒有共享時(shí)鐘,因此必須提前配置這兩個(gè)器件职恳,才能使用相同的波特率正確解碼數(shù)據(jù)所禀。
??常用波特率包括9600,19200,38400,57600,115200和921600.該速率包括數(shù)據(jù)幀(開始,停止和奇偶校驗(yàn)位)的開銷放钦,因此有效數(shù)據(jù)傳輸速率將略低色徘,并且根據(jù) 您配置的幀位數(shù)。
??以下代碼將UART連接配置為以115200波特操禀,8個(gè)數(shù)據(jù)位褂策,無奇偶校驗(yàn)和1個(gè)停止位(8N1)操作:
public void configureUartFrame(UartDevice uart) throws IOException {
// 配置UART端口
uart.setBaudrate(115200);
uart.setDataSize(8);
uart.setParity(UartDevice.PARITY_NONE);
uart.setStopBits(1);
}
注意:選擇不常見的波特率可能導(dǎo)致數(shù)據(jù)傳輸中的高錯(cuò)誤率。 您應(yīng)該始終驗(yàn)證您選擇的波特率是否得到設(shè)備硬件的良好支持颓屑。
硬件流控制
如果您的設(shè)備支持5線UART端口斤寂,則啟用硬件流控制可以提高數(shù)據(jù)傳輸?shù)目煽啃浴?通常這也意味著您可以安全地使用更快的波特率,而丟失傳入數(shù)據(jù)的機(jī)會(huì)更低揪惦。
在硬件流控制使能的情況下遍搞,當(dāng)器件上的接收緩沖器已滿并且不能接受任何更多數(shù)據(jù)時(shí),UART將發(fā)送請求發(fā)送(RTS)信號(hào)器腋。 一旦緩沖液被排空溪猿,信號(hào)將被清除。 類似地纫塌,UART監(jiān)視清除發(fā)送(CTS)信號(hào)诊县,并且如果它看到由外圍設(shè)備斷言的線,則將暫停發(fā)送數(shù)據(jù)措左。
??要啟用硬件流控制依痊,請使用帶有HW_FLOW_CONTROL_AUTO_RTSCTS的setHardwareFlowControl()方法。 默認(rèn)值為HW_FLOW_CONTROL_NONE怎披,表示禁用流控制胸嘁。
public void setFlowControlEnabled(UartDevice uart, boolean enable) throws IOException {
if (enable) {
// 啟用硬件流控制
uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_AUTO_RTSCTS);
} else {
// 禁用流量控制
uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_NONE);
}
}
傳輸輸出數(shù)據(jù)
要通過UART將數(shù)據(jù)緩沖區(qū)傳輸?shù)酵庠O(shè),請使用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字節(jié)是一個(gè)8位值钳枕。 如果使用setDataSize()配置較小的數(shù)據(jù)寬度缴渊,每個(gè)字節(jié)的高位將被截?cái)唷?/p>
偵聽傳入數(shù)據(jù)
使用read()方法將輸入數(shù)據(jù)從UART FIFO緩沖區(qū)拉入應(yīng)用程序。 此方法接受空緩沖區(qū)以填充傳入數(shù)據(jù)和要讀取的最大字節(jié)數(shù)鱼炒。 UART讀取是非阻塞的衔沼,如果FIFO中沒有可用的數(shù)據(jù),則會(huì)立即返回昔瞧。
??UartDevice將在讀取時(shí)返回FIFO中的可用字節(jié)數(shù)指蚁,直到所請求的計(jì)數(shù)。 要確保所有數(shù)據(jù)都恢復(fù)自晰,請循環(huán)UART凝化,直到它報(bào)告沒有更多的數(shù)據(jù):
public void readUartBuffer(UartDevice uart) throws IOException {
// 一次讀取的最大數(shù)據(jù)量
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");
}
}
為了避免在緩沖區(qū)為空時(shí)不必要地輪詢UART,用UartDevice注冊一個(gè)UartDeviceCallback酬荞。 當(dāng)有可用于讀取的數(shù)據(jù)時(shí)搓劫,此回調(diào)調(diào)用onUartDeviceDataAvailable()方法瞧哟。 當(dāng)應(yīng)用程序不再偵聽傳入數(shù)據(jù)時(shí),應(yīng)該取消注冊回調(diào)枪向。
public class HomeActivity extends Activity {
private UartDevice mDevice;
...
@Override
protected void onStart() {
super.onStart();
// 開始監(jiān)聽中斷事件
mDevice.registerUartDeviceCallback(mUartCallback);
}
@Override
protected void onStop() {
super.onStop();
// 中斷事件不再需要
mDevice.unregisterUartDeviceCallback(mUartCallback);
}
private UartDeviceCallback mUartCallback = new UartDeviceCallback() {
@Override
public boolean onUartDeviceDataAvailable(UartDevice uart) {
// 從UART設(shè)備讀取可用數(shù)據(jù)
try {
readUartBuffer(uart);
} catch (IOException e) {
Log.w(TAG, "Unable to access UART device", e);
}
// 繼續(xù)偵聽更多中斷
return true;
}
@Override
public void onUartDeviceError(UartDevice uart, int error) {
Log.w(TAG, uart + ": Error event " + error);
}
};
}
onUartDeviceDataAvailable()回調(diào)返回一個(gè)布爾值勤揩,指示回調(diào)是否應(yīng)該在接收未來中斷事件時(shí)自動(dòng)取消注冊。 此處返回true秘蛔,以便在每次UART FIFO中顯示數(shù)據(jù)時(shí)繼續(xù)接收事件陨亡。