Android Things(二)-外部I/O接口協(xié)議

目錄

  • 概述
  • 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):

pwm-signal

這個(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接口有三根線,分別是

  1. SCL:同步時(shí)鐘信號(hào)線
  2. SDA:數(shù)據(jù)傳輸線
  3. GND:地線

由于I2C只有一根數(shù)據(jù)線瞬雹,所以只能是半雙工的昧谊。

I2C可以同時(shí)接入多個(gè)設(shè)備,如圖:


i2c-connections

連接的每個(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)容稍刀。

i2c-smbus

具體函數(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ù)幀格式如下:

i2c-raw

需要注意的是:打開一個(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è)備連接圖:

uart-connections

打開一個(gè)UART端口也是同樣需要知道唯一的端口名稱觉痛,類似的也是PeripheralManagerService的getUartDeviceList()方法可以獲得UART端口列表,然后openUartDevice(UART_DEVICE_NAME)即可獲得UartDevice類型的對(duì)象茵休。

UART傳輸?shù)臄?shù)據(jù)幀格式如下圖:

uart-frame

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è)備扇调,如下圖:

uart-flow-control

多出的兩根線能保證較高速度傳輸?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>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末燃领,一起剝皮案震驚了整個(gè)濱河市士聪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猛蔽,老刑警劉巖剥悟,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異曼库,居然都是意外死亡区岗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門毁枯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)慈缔,“玉大人,你說我怎么就攤上這事种玛∶旰祝” “怎么了瓤檐?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)娱节。 經(jīng)常有香客問我挠蛉,道長(zhǎng),這世上最難降的妖魔是什么肄满? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任谴古,我火速辦了婚禮,結(jié)果婚禮上稠歉,老公的妹妹穿的比我還像新娘掰担。我一直安慰自己,他們只是感情好轧抗,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布恩敌。 她就那樣靜靜地躺著瞬测,像睡著了一般横媚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上月趟,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天灯蝴,我揣著相機(jī)與錄音,去河邊找鬼孝宗。 笑死穷躁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的因妇。 我是一名探鬼主播问潭,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼婚被!你這毒婦竟也來(lái)了狡忙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤址芯,失蹤者是張志新(化名)和其女友劉穎灾茁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谷炸,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡北专,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旬陡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拓颓。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖描孟,靈堂內(nèi)的尸體忽然破棺而出驶睦,到底是詐尸還是另有隱情腻格,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布啥繁,位于F島的核電站菜职,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏旗闽。R本人自食惡果不足惜酬核,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望适室。 院中可真熱鬧嫡意,春花似錦、人聲如沸捣辆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)汽畴。三九已至旧巾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間忍些,已是汗流浹背鲁猩。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罢坝,地道東北人廓握。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嘁酿,于是被迫代替她去往敵國(guó)和親隙券。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容