目錄
- 概述
- GPIO: 簡(jiǎn)單0/1狀態(tài)協(xié)議
- PWM:方波信號(hào)接口
- I2C:低速同步串行接口
- UART:異步串行接口
1.概述
Android Things 提供了幾種對(duì)外I/O接口協(xié)議來(lái)連接各種外部設(shè)備褥芒。通過這些協(xié)議Android Things和外部設(shè)備就能互相交流嚼松。Android Things像一個(gè)軀干嫡良、一個(gè)核心,我們通過這些協(xié)議給他安裝各種肢體和外延功能献酗。這里介紹4種Android Things支持的協(xié)議:GPIO是一個(gè)最簡(jiǎn)單的協(xié)議寝受,只能讀寫高/低兩種電平信號(hào);PWM只能簡(jiǎn)單的對(duì)外發(fā)出方波信號(hào)罕偎;I2C很澄、UART是串行接口協(xié)議,能連續(xù)的讀寫大量數(shù)據(jù)颜及,用于比較復(fù)雜的設(shè)備甩苛。下面具體介紹。
2.GPIO
GPIO可用一句話概括:每次只可以讀或?qū)懸粋€(gè)高電平或低電平信號(hào)俏站。一般用于簡(jiǎn)單的外部設(shè)備讯蒲,比如開關(guān)、LED燈肄扎。
要獲得一個(gè)GPIO端口需要知道端口的唯一端口名爱葵,PeripheralManagerService 的getGpioList()方法可以獲取所有當(dāng)前的GPIO端口名,有了這個(gè)唯一名稱就可以獲取這個(gè)端口:
PeripheralManagerService manager = new PeripheralManagerService();
List<String> portList = manager.getGpioList();
if (portList.isEmpty()) {
Log.i(TAG, "No GPIO port available on this device.");
} else {
Log.i(TAG, "List of available ports: " + portList);
}
...
Gpio mGpio = manager.openGpio(GPIO_NAME);
獲得Gpio對(duì)象后反浓,就可以操作這個(gè)端口:包括1. 設(shè)置讀寫方向萌丈,即設(shè)置此端口的功能是讀還是寫;2. 設(shè)置讀寫類型雷则,即指定Gpio對(duì)像讀/寫的值代表高電平還是低電平辆雾,下面是讀的例子:
// 設(shè)置方向:DIRECTION_IN為讀,DIRECTION_OUT為寫
mGpio.setDirection(Gpio.DIRECTION_IN);
// 設(shè)置類型:指定getValue()==true或setValue(true)的意義月劈,ACTIVE_HIGH即true代表高電平度迂,ACTIVE_LOW即true代表低電平
mGpio.setActiveType(Gpio.ACTIVE_HIGH);
...
if (mGpio.getValue()) {
//讀到了高電平
} else {
// 讀到了低電平
}
為了能夠監(jiān)聽電平高低的變化,我們可以設(shè)置監(jiān)聽回調(diào):
public void configureInput(Gpio gpio) throws IOException {
//設(shè)置監(jiān)聽類型:EDGE_NONE:不回調(diào)猜揪;EDGE_RISING:低->高回調(diào)惭墓;EDGE_FALLING:高->低回調(diào);EDGE_BOTH:高低變化都回調(diào)
gpio.setEdgeTriggerType(Gpio.EDGE_BOTH);
gpio.registerGpioCallback(mGpioCallback);
}
private GpioCallback mGpioCallback = new GpioCallback() {
@Override
public boolean onGpioEdge(Gpio gpio) {
//根據(jù)讀類型讀取狀態(tài)
if(gpio.getValue()){...}
// 返回true則繼續(xù)監(jiān)聽而姐,false則不再監(jiān)聽
return true;
}
@Override
public void onGpioError(Gpio gpio, int error) {
Log.w(TAG, gpio + ": Error event " + error);
}
};
3.PWM
PWM是用來(lái)發(fā)出方波控制信號(hào)的腊凶,而且只能發(fā)出方波信號(hào),不能讀取拴念。先看一下方波信號(hào)钧萍,如下圖,高低電平周期出現(xiàn):
這個(gè)波形可以通過設(shè)置周期和占空比進(jìn)行調(diào)節(jié)政鼠,占空比即每個(gè)周期高電平占的比例风瘦。
獲取一個(gè)PWM端口也是通過唯一名稱的,依然是PeripheralManagerService 的 getPwmList()方法:
PeripheralManagerService manager = new PeripheralManagerService();
List<String> portList = manager.getPwmList();
if (portList.isEmpty()) {
Log.i(TAG, "No PWM port available on this device.");
} else {
Log.i(TAG, "List of available ports: " + portList);
}
...
Pwm mPwm = mPeripheralManager.openPwm(PWM_NAME);
//設(shè)置頻率
mPwm .setPwmFrequencyHz(120);
//設(shè)置占空比
mPwm .setPwmDutyCycle(25);
//設(shè)置PWM有效
mPwm .setEnabled(true);
4. I2C
I2C是同步串行接口公般,使用共享同步時(shí)鐘同步數(shù)據(jù)万搔,適合數(shù)據(jù)量較小的外部設(shè)備胡桨。
I2C接口有三根線,分別是
- SCL:同步時(shí)鐘信號(hào)線
- SDA:數(shù)據(jù)傳輸線
- GND:地線
由于I2C只有一根數(shù)據(jù)線瞬雹,所以只能是半雙工的昧谊。
I2C可以同時(shí)接入多個(gè)設(shè)備,如圖:
連接的每個(gè)設(shè)備都對(duì)應(yīng)一個(gè)唯一地址挖炬。
和上面兩個(gè)協(xié)議相同揽浙,I2C也可以通過PeripheralManagerService 的getI2CBusList()獲取I2C端口列表状婶,由于一個(gè)I2C端口鏈接多個(gè)設(shè)備意敛,所以還需要一個(gè)地址來(lái)定位某個(gè)設(shè)備。獲取設(shè)備的代碼如下:
PeripheralManagerService manager = new PeripheralManagerService();
I2cDevice mDevice = manager.openI2cDevice(I2C_DEVICE_NAME, I2C_ADDRESS);
I2C協(xié)議可以讀寫設(shè)備的寄存器膛虫,使用下圖的數(shù)據(jù)幀格式進(jìn)行讀寫草姻,其中前面兩個(gè)地址定位哪個(gè)設(shè)備的哪個(gè)寄存器,后面的一個(gè)地址一個(gè)數(shù)據(jù)代表要在這個(gè)設(shè)備的這個(gè)寄存器讀寫的內(nèi)容稍刀。
具體函數(shù)如下:
字節(jié)數(shù)據(jù):readRegByte()和writeRegByte()來(lái)讀或者寫一個(gè)單獨(dú)的8位寄存器數(shù)據(jù)撩独。
字?jǐn)?shù)據(jù):readRegWord()和writeRegWord()以一個(gè)16位的字來(lái)讀或者寫兩個(gè)連續(xù)寄存器的值。第一個(gè)寄存器的地址被翻譯為字中的最小有效字節(jié)(LSB)账月,其次是最重要的字節(jié)(MSB)综膀。
塊數(shù)據(jù):readRegBuffer()和writeRegBuffer()讀或者寫最多32個(gè)連續(xù)寄存器的值作為一個(gè)數(shù)組。
I2C還支持從數(shù)據(jù)線讀寫原始數(shù)據(jù)局齿,數(shù)據(jù)幀格式如下:
需要注意的是:打開一個(gè)設(shè)備的連接后剧劝,不可以同時(shí)再打開另一個(gè),需要先關(guān)閉一個(gè)才能連接另一個(gè)抓歼。關(guān)閉調(diào)用I2cDevice 的 close()方法.
5. UART
UART一般用作和外部設(shè)備交換原始數(shù)據(jù)讥此,它和其他的幾個(gè)協(xié)議不同之處在于數(shù)據(jù)傳輸速度和數(shù)據(jù)格式都可以自定義,而且它是異步傳輸數(shù)據(jù)的谣妻,是沒有同步時(shí)鐘信號(hào)的萄喳,設(shè)備會(huì)收集所有進(jìn)來(lái)的數(shù)據(jù)到一個(gè)先進(jìn)先出的緩存里,直到你的應(yīng)用來(lái)讀取蹋半。
UART是全雙工的他巨,讀數(shù)據(jù)和寫數(shù)據(jù)各用一根線。由于讀和寫可以同時(shí)進(jìn)行减江,一般它比I2C要快闻蛀,但是需要兩邊的設(shè)備都遵循一個(gè)傳輸速率以防止數(shù)據(jù)錯(cuò)誤,而且只能連接一個(gè)設(shè)備您市。如下是設(shè)備連接圖:
打開一個(gè)UART端口也是同樣需要知道唯一的端口名稱觉痛,類似的也是PeripheralManagerService的getUartDeviceList()方法可以獲得UART端口列表,然后openUartDevice(UART_DEVICE_NAME)即可獲得UartDevice類型的對(duì)象茵休。
UART傳輸?shù)臄?shù)據(jù)幀格式如下圖:
Start位:發(fā)送數(shù)據(jù)前薪棒,數(shù)據(jù)線被拉起 1 bit的固定的時(shí)間間隔來(lái)指明真正要發(fā)送的數(shù)據(jù)的開始手蝎。
Data部分:要傳輸?shù)臄?shù)據(jù),可以傳5-9 bit數(shù)據(jù)俐芯,數(shù)據(jù)位少棵介,傳輸?shù)臄?shù)據(jù)就少,但是可以提高速率吧史。
Parity位:校驗(yàn)位邮辽,如果UART設(shè)置奇偶校驗(yàn),那數(shù)據(jù)幀就會(huì)加上這一位贸营,當(dāng)然也可以不設(shè)置吨述,則沒有這一位。
Stop位:所有數(shù)據(jù)傳輸完畢后钞脂,數(shù)據(jù)線會(huì)被重置一段時(shí)間表明數(shù)據(jù)傳輸結(jié)束揣云,這段時(shí)間可以是1-2bit的時(shí)間。
默認(rèn)的數(shù)據(jù)幀一般是1bit start冰啃、8bit數(shù)據(jù)和1bit stop位邓夕,沒有校驗(yàn)位。
UART的傳輸速率稱為波特率阎毅,單位是 bit/秒焚刚,接收端和發(fā)送端必須使用相同的波特率。
代碼中實(shí)際使用時(shí)一般如下:
public void configureUartFrame(UartDevice uart) throws IOException {
// Configure the UART port
uart.setBaudrate(115200); //設(shè)置波特率
uart.setDataSize(8); //設(shè)置數(shù)據(jù)大小
uart.setParity(UartDevice.PARITY_NONE); //設(shè)置有無(wú)校驗(yàn)位
uart.setStopBits(1); //設(shè)置stop位大小
}
UART有一種五根線的設(shè)備扇调,如下圖:
多出的兩根線能保證較高速度傳輸?shù)那闆r下更少的傳輸失敗矿咕,可以稱為FlowControl功能。
可以使用如下代碼開啟或關(guān)閉著兩根線:
public void setFlowControlEnabled(UartDevice uart, boolean enable) throws IOException {
if (enable) {
// 打開 FlowControl功能
uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_AUTO_RTSCTS);
} else {
// 取消FlowControl功能
uart.setHardwareFlowControl(UartDevice.HW_FLOW_CONTROL_NONE);
}
}
UART的讀寫功能也比較簡(jiǎn)單如下代碼:
//寫數(shù)據(jù)
public void writeUartData(UartDevice uart) throws IOException {
byte[] buffer = {...};
int count = uart.write(buffer, buffer.length);
Log.d(TAG, "Wrote " + count + " bytes to peripheral");
}
//讀取數(shù)據(jù)一般在回調(diào)里監(jiān)聽
public class HomeActivity extends Activity {
private UartDevice mDevice;
...
@Override
protected void onStart() {
super.onStart();
// 注冊(cè)回調(diào)
mDevice.registerUartDeviceCallback(mUartCallback);
}
@Override
protected void onStop() {
super.onStop();
// 取消回調(diào)
mDevice.unregisterUartDeviceCallback(mUartCallback);
}
private UartDeviceCallback mUartCallback = new UartDeviceCallback() {
@Override
public boolean onUartDeviceDataAvailable(UartDevice uart) {
// 開始讀取
try {
readUartBuffer(uart);
} catch (IOException e) {
Log.w(TAG, "Unable to access UART device", e);
}
// 返回true則繼續(xù)監(jiān)聽肃拜,false則不再繼續(xù)監(jiān)聽
return true;
}
@Override
public void onUartDeviceError(UartDevice uart, int error) {
Log.w(TAG, uart + ": Error event " + error);
}
};
}
到這里這幾種協(xié)議就介紹完了痴腌,利用這幾種協(xié)議的特性并搭配好的想法和電路,就可以創(chuàng)造出各種有趣或?qū)嵱玫墓δ堋?/p>