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