I2C 的連線與測試環境
I2C (Inter-Integrated Circuit) 是一個廣泛被實現的通訊協定,通常用於不同晶片之間的通訊。 I2C有兩條通訊線路: SDA (Serial Data Line) 和 SCL (Serial Clock Line),此兩條線路皆為Open Drain。
I2C 的裝置有兩種模式: master (主設備) 和多個 slave (從設備),資料存取模式為 master 對 slave 進行資料的寫入與讀取。在一個 I2C 匯流排上可以有多個 master 和 slave,其中,為了避免 master 之間相互干擾, master 在傳送資料前會先聽其他 master 的動作,避免互相影響,其中原理可以參考以下連結:
實作中所使用的 IC (EEPROM) 是 24LC02。其SPEC文件可以參考:
其中,A0-A2 用以定義其位置,表示法為: [1 0 1 0 A2 A1 A0],考慮到我們把 A0-A2 都接到GND,因此其裝置的位址為 0101000 也就是16 進位的 0x50。
若是要考慮正確的 I2C 的匯流排上,則要按照下圖來完成電路:
I2C 的軟體安裝與環境設定
GL-AR750S 已經宣告了 I2C 使用 GPIO 的腳位 (gpio-5 sda; gpio-21 scl),卻沒有安裝好相對應的I2C套件,因此,我們仍必須先下載 GPIO 所需的操作軟體。
Copy root@GL-AR750S:~# opkg update
Downloading http://www.gl-inet.com/lede/3.0/ar71xx/nand/Packages.gz
Updated list of available packages in /var/opkg-lists/packages
Downloading http://www.gl-inet.com/lede/3.0/ar71xx/nand/Packages.sig
Signature check passed.
root@GL-AR750S:~# opkg install kmod-i2c-gpio-custom
Installing kmod-i2c-gpio-custom (4.9.109-2) to root...
Downloading http://www.gl-inet.com/lede/3.0/ar71xx/nand/kmod-i2c-gpio-custom_4.9.109-2_mips_24kc.ipk
Installing kmod-i2c-algo-bit (4.9.109-1) to root...
Downloading http://www.gl-inet.com/lede/3.0/ar71xx/nand/kmod-i2c-algo-bit_4.9.109-1_mips_24kc.ipk
Installing kmod-i2c-gpio (4.9.109-1) to root...
Downloading http://www.gl-inet.com/lede/3.0/ar71xx/nand/kmod-i2c-gpio_4.9.109-1_mips_24kc.ipk
Configuring kmod-i2c-algo-bit.
Configuring kmod-i2c-gpio.
Configuring kmod-i2c-gpio-custom.
root@GL-AR750S:~# opkg install i2c-tools
Installing i2c-tools (3.1.2-1) to root...
Downloading http://www.gl-inet.com/lede/3.0/ar71xx/nand/i2c-tools_3.1.2-1_mips_24kc.ipk
Configuring i2c-tools.
假若沒有將 GPIO 對應至 I2C 設定,其對應的指令如下:
Copy # insmod /lib/modules/3.18.29/i2c-gpio-custom.ko bus0=0,1,17
I2C 的操作與實驗結果
在此 OpenWRT 上只有一個 I2C 模組,編號為0, 一個模組對應到一個 I2C 的匯流排。若是有多個模組,就可以同時操控多個匯流排。
Copy root@GL-AR750S:~# i2cdetect -l
i2c-0 i2c i2c-gpio0 I2C adapter
列出匯流排上的 I2C 裝置 i2cdetect -y 0
讀取在此 I2C 模組上的 slave 裝置, 我們可以看到, 0x50 的位置上有我們掛上去的 EEPROM。此位置的設定和 A0-A2 的設定相關。
讀取 I2C EEPROM i2cdump -y -f 0 0x50
此時裝置內都沒有數值,初始為1。 讀取裝置的指令為 i2cdump -y -f [#mode] [dev addr]
我們可以看到此 EEPROM 的定址空間為 16*16*16 bits。
寫入 I2C EEPROM i2cset -y -f 0 0x50 0x00 0x41
寫入裝置的指令為 i2set -y -f [#mode] [dev addr] [data addr] [data value]
使用 MCP23017 擴充 GPIO
MCP23017 是一顆將 I2C 轉換成 GPIO 的 IC, 一共可以輸出 16 個 GPIO 的輸出 (GPA0-7,GPB0-7),輸入則藉由 I2C 的介面 (VCC-VDD、GND-VSS、SCL、SDA)。
在接線部分,就只要將上述 4 個 I2C 介面對接,並透過 A0、A1、A2 的設定給定 I2C 位址,假設把這三個腳位接到 GND,位址為 0x20。詳細的接法,可以參考這一篇文章中的接線方式,雖然這篇文章用的是 Raspberry Pi 作為開發板,但在接線上兩者是一致的。
Copy root@GL-AR750S:~# i2cdetect -l
i2c-0 i2c i2c-gpio0 I2C adapter
root@GL-AR750S:~# i2cdetect -y 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
設定好線路之後,接著,要對 MCP23017 進行設定,MCP23017 的指令表如下:
我們可以先看到其初始值設定和上表一致。
Copy root@GL-AR750S:~# i2cdump -y -f 0 0x20
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
10: 00 00 ff 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
雖然,MCP23017 有許多功能,但是這次的測試,只把它當作 GPIO 的擴展器,將原有 I2C 的輸入單向擴展為 16 個 GPIO 腳位,因此,我們需要先將 GPIO 設定為輸出的模式:
Copy root@GL-AR750S:~# i2cset -y 0 0x20 0x00 0x00
root@GL-AR750S:~# i2cset -y 0 0x20 0x01 0x00
接著,我們可以藉由更改 0x12、0x13 的數值,來改變 GPIO 的輸出,以下為指令與結果:
Copy root@GL-AR750S:~# i2cset -y 0 0x20 0x12 0x0F
root@GL-AR750S:~# i2cset -y 0 0x20 0x13 0x0F
root@GL-AR750S:~# i2cdump -y -f 0 0x20
No size specified (using byte-data access)
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
10: 00 00 0f 0f 0f 0f 00 00 00 00 00 00 00 00 00 00 ..????..........
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
其中,可以看到 0x14 (OLATA) 以及 0x15 (OLATB) 的數值會隨設定改變,這兩個地方的數值代表兩組 Latch,會將所輸入的數值儲存下來。
由於 LED 為了限流而反接,因此亮燈代表 0,反之則是 1。這樣的結果和實驗的設定一致。
MCP23017 反應時間量測
考慮到 MCP23017 是透過 I2c 的介面更改 GPIO 的數值,其中一定牽涉了資料的寫入、儲存等工作,因此比起直接存取 GPIO 而言,會有較長的延遲。為了測試此延遲時間,我們寫了一個簡單的 script,如下所示:
Copy PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo 18 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio18/direction
i2cset -y 0 0x20 0x00 0x00
i2cset -y 0 0x20 0x01 0x00
echo "===== Initial: I2C and gpio 18 set to LOW ====="
i2cset -y 0 0x20 0x12 0x00
i2cset -y 0 0x20 0x13 0x00
echo 0 > /sys/class/gpio/gpio18/value
echo "===== Phase 1: togging gpio 18 10 times ====="
i=1
while [ "$i" -le 10 ];do
i=$((i+1));
echo 1 > /sys/class/gpio/gpio18/value
echo 0 > /sys/class/gpio/gpio18/value
done
echo "===== Phase 2: togging I2CA 10 times ====="
i=1
while [ "$i" -le 10 ];do
i=$((i+1));
i2cset -y 0 0x20 0x12 0xFF
i2cset -y 0 0x20 0x12 0x00
done
echo "===== Phase 3: togging I2CB 10 times ====="
i=1
while [ "$i" -le 10 ];do
i=$((i+1));
i2cset -y 0 0x20 0x13 0xFF
i2cset -y 0 0x20 0x13 0x00
done
一開始,我們先初始化 GPIO 18 和 I2C 的狀態,接著把兩組 I2C (A、B) 和 GPIO 都設為 0,接著 (phase 1) 切換 GPIO 10 次, (phase 2) 切換 I2CA 10 次,(phase 3) 之後再切換 I2CB 10 次。每一次切換之間並沒有延遲。
我們用示波器來量測 I2C A 轉出來的 GPIO 訊號,以及 GPIO 18 訊號,並假設波型反映後,就執行下一行指令。以下為量測的照片:
結果顯示於下圖,綠色是 GPIO 的波型變化,黃色是透過 I2C 轉換後的波型變化。
根據上圖的分析,我們可以得到以下結果:
從 GPIO 下指令之後到反映在波型上的時間約為 100 us (0.1 ms)
從 I2C 下指令之後到反映在波型上的時間約為 3 ms
當然,這只是一次的量測數據,並不代表精確的量測結果,每一次量測的結果應該有些許不同。但是,透過這次實驗大概可以發現透過 I2C 下指令會產生約 3 ms 的延遲,相較之下,若是直接操作 GPIO 延遲約為 0.1 ms,兩者反應速度約差 30 倍。