Android Things中的I2C
上一講中,我們說到 Android Things 的 API,以及 Peripheral I/O 設(shè)備包含的 API 的類型。但是作為程序員的我們,怎么理解這些 API 呢?
我們就拿 I2C 的 API 來說吧。看看我們怎樣在 Android Things 中添加一個 I2C 的設(shè)備?首先得知道,I2C 是做什么的?怎么用?
實際上,I2C 是同步的串行通信總線,一般用于控制信號,比如控制 LCD, Camera 等設(shè)備。另外,大部分傳感器有 I2C 的接口。I2C 是依靠時鐘信號來傳遞數(shù)據(jù)的,所以有主設(shè)備(產(chǎn)生時鐘的信號)和從設(shè)備(接收時鐘的信號)之分。I2C 的通信每一次操作都是由主設(shè)備的發(fā)起的。
既然 I2C 是依靠時鐘傳遞的信號,那么在連線上就有時鐘錢 (SCL) 和數(shù)據(jù)線 (SDA),然后為了電勢與大地相同,自然少不了地線 (GND)。為了方便沒有接觸過 I2C 總線的同學(xué)們理解這三個名詞,貼上名詞的全稱:
- Shared clock signal (SCL)
- Shared data line (SDA)
- Common ground reference (GND)
單看上面的圖,為啥有三個 I2C 設(shè)備連接在一起呢?這三者之間又是什么關(guān)系?
其中,寫有 Master Device 的 I2C 設(shè)備,稱為主設(shè)備,另外兩個為從設(shè)備。從主設(shè)備引出的 SDA 和 SCL 線構(gòu)成 I2C 的總線。一個 I2C 的主設(shè)備可以提供一條 I2C 的總線,一條總線上***可以連接 127 個 I2C 的從設(shè)備。
等等,為啥是 127 個呢?主要是 I2C 的地址有 7 位和 10 位兩種地址。也就意味著,對于 7 位的地址表達(dá)的數(shù)據(jù)***可以到 2^7=128,減去一個主設(shè)備,就是 127 個從設(shè)備了。這里的 I2C 設(shè)備地址,就是上圖的 Address: 0x3c 和 0x4c,I2C 的主設(shè)備是通過從設(shè)備的地址,來找到從設(shè)備的。請注意,I2C 的主設(shè)備,是沒有設(shè)備地址這一說法的。
我們還需要了解 I2C 的一些硬件信息:
I2C 是半雙工,可以有主 -> 從方向的數(shù)據(jù),也可以有從 -> 主方向的數(shù)據(jù),但是同一時刻,只能有一種傳輸方式。這點和 SPI 是有差別的,SPI 總線支持全雙工模式。但它同時只能訪問一個從設(shè)備,由片選信號 (CS) 來決定。這就很明顯了,I2C 通常用于控制命令的傳輸。而 SPI 通常用數(shù)據(jù)的批量傳輸。
了解完 I2C 的基本硬件信息。我們來了解一下 I2C 的從設(shè)備操作方式。不多不多,就是三大步。
- 連接從設(shè)備
- 對從設(shè)備進(jìn)行讀操作
- 對從設(shè)備進(jìn)行寫操作
設(shè)備連接
先要檢查我們的物聯(lián)網(wǎng)設(shè)備上有沒有 I2C 總線。這時需要補(bǔ)充一下,有可能你的開發(fā)板上有多個 I2C 的總線。這時候, I2C 的總線地址 (此處非 I2C 的設(shè)備地址) 是有多個的,要明確你的 I2C 設(shè)備是接在哪個 I2C 的總線上。這也可以理解,為什么得到的 I2C 總線的數(shù)據(jù)是用 List 類型進(jìn)行存放的。
如果有總線,我們再查找當(dāng)前的 I2C 總線上對應(yīng)的 I2C 設(shè)備。
關(guān)鍵的接口是 manager.openI2CDeivce(..),這個函數(shù)有兩個參數(shù),DEVICE_NAME 是用戶定義的一個字符串,表示設(shè)備的名稱。I2C_ADDRESS,也是之前所說的 I2C 從設(shè)備上的地址,這個地址在當(dāng)前的 I2C 總線上是唯一的。
讀寫操作
首先得把 I2C 的操作流程搬出來說了。
這張圖翻譯成中文就是這樣子的:
這樣就完成了向設(shè)備地址為 0x30、寄存器地址為 0x10 的設(shè)備上讀或者寫入 0x06 這個數(shù)據(jù)。
那怎么知道是讀數(shù)據(jù),還是寫數(shù)據(jù)呢?實際上 I2C 是 7 位的地址位。但是一個字節(jié)是 8 位,其中有一位叫做讀寫位。如果那一位設(shè)為讀,就是去讀操作,如果設(shè)為寫,就是寫操作。實際上,在示波器上我們還能看到另外的一個 ACK 位,保證硬件上傳輸正常,ACK 位在軟件上不需要處理。
那么,加上 I2C 的讀寫位之后, I2C 數(shù)據(jù)傳輸會是什么樣的呢?
我們可以看到, I2C 數(shù)據(jù)傳輸?shù)臅r序,從硬件上來說 SCL 是按周期發(fā)的時鐘信號,當(dāng) SCL 是高電平時,SDA 產(chǎn)生一個下降沿,這時候開始數(shù)據(jù)傳輸。其中傳輸 I2C 的從設(shè)備地址共有 8 位,1-7 位是地址,第 8 位是讀寫位,0 表示寫,1 表示讀。然后硬件自動產(chǎn)生 ACK 位。接下來就是數(shù)據(jù)傳輸?shù)恼麄€過程,***當(dāng)數(shù)據(jù)傳完后,SCL 為高電平,SDA 產(chǎn)生上升沿時,產(chǎn)生 STOP 操作。事實上,在 I2C 做讀操作需要往 I2C 的設(shè)備寫入隨機(jī)值,再去讀,不過這些操作在 I2C 相關(guān)的接口中已經(jīng)為我們封裝好了。
這么大篇幅介紹了 I2C 的原理,還有 I2C 的時序,操作流程。實際上,Android Things 已經(jīng)幫我們把讀寫接口封裝好了,我們只需要在理解的基礎(chǔ)上,調(diào)用接口就行了。
可以看出,Android Things 已經(jīng)給我們封裝好了 I2C 的讀寫操作 ,我們直接用就可以了。
這里面還有個細(xì)節(jié)比較繞。之前提到, I2C 的設(shè)備地址可以是 7 位,也可以是 10 位,但是 I2C 設(shè)備的寄存器可以是 8 位,也可以是 16 位。這里面就涉及到 8 位的設(shè)備,以及 16 位設(shè)備的讀寫問題。
六大函數(shù)出場:
- 8 位地址讀寫操作- readRegByte() 和 writeRegByte()
- 16 位地址讀寫操作 - readRegWord() 和 writeRegWord()
- 批量讀寫操作- readRegBuffer() and writeRegBuffer()
其中 Byte 是針對 8 位的 I2C 設(shè)備,Word 是針對 16 位的設(shè)備。
- 讀操作:用寄存器的地址做為參數(shù)。
- 寫操作:兩個參數(shù),寄存器地址,和你要寫入的值。
上面的代碼中,把寄存器的第 6 位置 1。所以操作流程是
- 讀出寄存器的值
- 將這個值的第6位置1 (value |= 0x40;)
- 然后把新的值寫回寄存器
不過對于 16 位的地址操作還有一個大小端的問題。(什么是大小端?去 Google 吧 )現(xiàn)在的 API 是依照小端模式來讀寫的 16 位設(shè)備地址。
直接批量數(shù)據(jù)操作,可以***讀到 32 個連續(xù)的寄存器的數(shù)值。
那么,我們怎么使用接口進(jìn)行批量操作呢?
傳輸原始數(shù)據(jù)
還是先來張圖吧:
這種操作方法,不同于上面的讀寫寄存器。在 I2C 的操作中,屬于 burst 操作方法。即一次性的讀寫多少字節(jié),***再停止。
跟一個例子:
這樣傳輸能帶來更高的傳輸效率,解決了 I2C 傳輸?shù)暮诵膯栴},我們也解決了支持 I2C 的任何外設(shè)的讀寫問題。
后記
Android Things 的 SDK 中,Peripheral I/O 部分是包括三種總線的,UART, I2C, SPI。對于軟件開發(fā)人員來說,有下面幾點需要注意:
- UART 開發(fā),需要了解 UART 的波特率、流控等概念。
- SPI 開發(fā),需要了解 MISO, MOSI,CLK, GND, CS 這些連線的作用,還有 SPI 的操作模式等,SDK 中的接口,與上文的 I2C 的開發(fā)流程相似。
- I2C開發(fā),就不用接著說了吧
【本文是51CTO專欄機(jī)構(gòu)“谷歌開發(fā)者”的原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者(微信公眾號:Google_Developers)】