I2C 只需要 一支 序列時脈 (Serial Clock (SCL)) 及 一支 序列資料 (Serial Data (SDA)) 便可以處理其他 集成電路 的 輸入 或 輸出
因此是一種在 集成電路 與 控制界面 的 內部集成電路
究竟 I2C 有甚麼作用?
Arduino Starter Kit 套件附送 焊接在 HD44780 2x16 LCD Screen 背後的 PCF8574 for HD44780 LCD backpack 就是運用 I2C 技術
PCF8574 for HD44780 LCD backpack 是專為 1x16 引腳排序的 HD44780 LCD Screen 整合的 I2C
I2C 將原本需要 16支引腳 來控制的 HD44780 ,只需要使用 4支引腳 便可以控制
PCF8574 for HD44780 LCD backpack 的側面
PCF8574 for HD44780 LCD backpack 的正面
PCF8574 for HD44780 LCD backpack 的背面
SMD 的 PCF8574T晶片
PCF8574 for HD44780 LCD backpack 使用 電位器 來控制 對比度
3362 電位器 需要使用 螺絲批 轉動來調整
轉動到左邊寫著 1 的方向電壓越低,轉動到右邊寫著 3 的方向電壓越高
PCF8574 for HD44780 LCD backpack 使用 跳線 (Jumper) 來控制 背光開關 ,但 不能微調光度
引腳功能
後 | 引腳16 | 引腳15 | 引腳14 | 引腳13 | 引腳12 | 引腳11 | 引腳10 | 引腳9 |
---|---|---|---|---|---|---|---|---|
前 | 引腳1 | 引腳2 | 引腳3 | 引腳4 | 引腳5 | 引腳6 | 引腳7 | 引腳8 |
引腳 (缺口在左邊) | VCC | SDA | SCL | INT | P7 | P6 | P5 | P4 |
A0 | A1 | A2 | P0 | P1 | P2 | P3 | GND |
編號 | 引腳 | 方向 | 用途 |
---|---|---|---|
1 | A0 | 自訂地置,第2位元;實體連接或中斷 | |
2 | A1 | 自訂地置,第1位元;實體連接或中斷 | |
3 | A2 | 自訂地置,第0位元;實體連接或中斷 | |
4 | P0 | 輸出 | 資料,第7位元;連接到目標裝置引腳 |
5 | P1 | 輸出 | 資料,第6位元;連接到目標裝置引腳 |
6 | P2 | 輸出 | 資料,第5位元;連接到目標裝置引腳 |
7 | P3 | 輸出 | 資料,第4位元;連接到目標裝置引腳 |
8 | GND | 接地 | |
9 | P4 | 輸出 | 資料,第3位元;連接到目標裝置引腳 |
10 | P5 | 輸出 | 資料,第2位元;連接到目標裝置引腳 |
11 | P6 | 輸出 | 資料,第1位元;連接到目標裝置引腳 |
12 | P7 | 輸出 | 資料,第0位元;連接到目標裝置引腳 |
13 | INT | 輸入 | 資料傳送或接收時,中斷連接 |
14 | SCL | 輸入 | 序列時脈;連接到控制器 |
15 | SDA | 輸入輸出 | 序列資料;連接到控制器 |
16 | VCC | 電源 |
訊號
發送指令
I2C 啟動後,會以每組 8位元資料 及 1位元確認 發送資料啟動 (必須) | 指派Slaver地址 (必須) | 發送資料 週期 (選用、可重覆) | 結束 (必須) | ||||||
---|---|---|---|---|---|---|---|---|---|
7位元 Slaver地址 | 1位元 模式 | 1位元 確認 | 8位元 資料 | 1位元 確認 |
- 啟動 I2C
- 指派Slaver地址
- 7位元 Slaver地址
- 1位元 模式
- 1位元 確認
- 發送資料 週期 (可重覆)
- 8位元 資料
- 1位元 確認
- 終止 I2C
啟動 I2C
啟動 I2C 需要讓 序列時脈 保持 高電壓,並將 序列資料 由 高電壓 下降到 低電壓引腳 | 啟動 | |
---|---|---|
Time | 延遲250微秒 | 延遲250微秒 |
SCL | 高 | |
SDA | 高 | 低 |
訊號波紋時序圖
Arduino AVR-C 例子
digitalWrite(sclPin, HIGH); digitalWrite(sdaPin, HIGH); delayMicroseconds(250); digitalWrite(sdaPin, LOW);
終止 I2C
終止 I2C 需要讓 序列時脈 保持 高電壓,並將 序列資料 由 低電壓 上升到 高電壓引腳 | 終止 | |
---|---|---|
Time | 延遲250微秒 | 延遲250微秒 |
SCL | 高 | |
SDA | 低 | 高 |
訊號波紋時序圖
Arduino AVR-C 例子
digitalWrite(sclPin, HIGH); digitalWrite(sdaPin, LOW); delayMicroseconds(250); digitalWrite(sdaPin, HIGH);
Slaver地址
啟動 I2C 後,指派執行指令的 Slaver地址指派 Slaver 需要發出
- 7位元 為 地址 (Address)
- PCF8574*版本 為 0100XXX(二進制)
- PCF8574A*版本 為 0111XXX(二進制)
- 指派次序必須由 最高有效位元 (Most Significant Bit (MSB)) (第6位元) 開始設定
- 1位元 為 模式 (Mode)
- 寫入為 0
- 讀取為 1
- 1位元 為 確認 (Acknowledge),必須為 0
Slaver地址 當中的 A0, A1, A2 的設定類似 整合驅動電子 (Integrated Drive Electronics (IDE))
使用 跳線 將 IDE裝置 區分 Master 及 Slaver
I2C 同樣使用類似的方式來區分不同的 Slaver
在 I2C 的情況下,發送指令為 Master (例如 Arduino),接收指令為 Slaver (例如 PCF8574)
由於 I2C 使用 7位元地址,所以理論上能夠區分 128個Slaver
PCF8574 使用 3個開關制 來設定 Slaver地址
不接地狀態為 1 ;接地狀態為 0
A2 | A1 | A0 | 數值 | PCF8574* | PCF8574A* | ||
---|---|---|---|---|---|---|---|
二進制 | 十六進制 | 二進制 | 十六進制 | ||||
接地 | 接地 | 接地 | 0 | 0100000 | 0x20 | 0111000 | 0x38 |
接地 | 接地 | 不接地 | 1 | 0100001 | 0x21 | 0111001 | 0x39 |
接地 | 不接地 | 接地 | 2 | 0100010 | 0x22 | 0111010 | 0x3A |
接地 | 不接地 | 不接地 | 3 | 0100011 | 0x23 | 0111011 | 0x3B |
不接地 | 接地 | 接地 | 4 | 0100100 | 0x24 | 0111100 | 0x3C |
不接地 | 接地 | 不接地 | 5 | 0100101 | 0x25 | 0111101 | 0x3D |
不接地 | 不接地 | 接地 | 6 | 0100110 | 0x26 | 0111110 | 0x3E |
不接地 | 不接地 | 不接地 | 7 | 0100111 | 0x27 | 0111111 | 0x3F |
即是使用 PCF8574 來實作 I2C 只使用 2支引腳 便可以區分 16個 PCF8574 Slaver 來控制 16個裝置
指派 Slaver地址
先將 序列時脈 下降至 低電壓,並將 序列資料 調整為 高電壓 或 低電壓 ,然後將 序列時脈 上升至 高電壓 ,便完成 1位元 資料由於指派 Slaver地址 總共需要 9個位元資料,因此需要重覆以上程序 9次
PCF8574*版本
引腳 | 第6位元 | 第5位元 | 第4位元 | 第3位元 | A2 | A1 | A0 | RW | ACK | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Time | 每次執行後,延遲250微秒 | |||||||||||||||||
SCL | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 |
SDA | 低 | 高 | 低 | 低 | 高/低 | 高/低 | 高/低 | 高/低 | 低 |
訊號波紋時序圖
PCF8574A*版本
引腳 | 第6位元 | 第5位元 | 第4位元 | 第3位元 | A2 | A1 | A0 | RW | ACK | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Time | 每次執行後,延遲250微秒 | |||||||||||||||||
SCL | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 |
SDA | 低 | 高 | 高 | 高 | 高/低 | 高/低 | 高/低 | 高/低 | 低 |
訊號波紋時序圖
發送資料
配對對應裝置後,便可以通過 I2C 指示目標裝置 發出 寫入指令 或 讀取指令指令會以 P7 至 P0 次序發出
引腳 | P7 | P6 | P5 | P4 | P3 | P2 | P1 | P0 | ACK | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Time | 每次執行後,延遲250微秒 | |||||||||||||||||
SCL | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 | 低 | 高 |
SDA | 高/低 | 高/低 | 高/低 | 高/低 | 高/低 | 高/低 | 高/低 | 高/低 | 低 |
訊號波紋時序圖
PCF8574 for HD44780 LCD backpack 是 PCF8574T 屬於 PCF8574* 版本
指派 Slaver地址 與,發出指令是相同
Arduino AVR-C 例子
void writeBit(bool enabled) { digitalWrite(sclPin, LOW); digitalWrite(sdaPin, ((enabled) ? HIGH : LOW)); delayMicroseconds(250); digitalWrite(sclPin, HIGH); } void writeByte(byte data, bool ack) { for (byte i = 0; i < 8; i++) { // 由第7位元開始發出指令 writeBit((data & 0x80) > 0); // 向左移動1位元 data <<= 1; } // 發出 Acknowledge 指令 writeBit(ack); } void setup() { // do something // 如果 Slaver 是 PCF8574 ,A2, A1, A0 都沒有接地,地址便是 0100111 // 0100111 = 0x27 // 0x27 << 1 = 0x4E 將地址向左偏移1位,以讓往後可以設定 寫入 或 讀取 模式 // 0x4E | 0 = 指派 01001110 地址 為 寫入模式 writeByte(0x4E, false); }
在下理解 PCF8574 for HD44780 LCD backpack 的線路圖
雖然 PCF8574 for HD44780 LCD backpack 的 A0, A1, A2 引腳 實際沒有 開關掣
不過使用者仍然可以根據需要自行焊接導電體連接接地來切換地址
在下亦設定 A引腳 為 開關掣 取代原本的 跳線 (因為沒有相似元件圖示)
PCF8574 8支P引腳 分別接駁到 HD44780 的特定引腳,讓 PCF8574 向 HD44780 發出指令
PCF8574 引腳 | HD44780 引腳 |
---|---|
P7 | D7 |
P6 | D6 |
P5 | D5 |
P4 | D4 |
P3 | A |
P2 | En |
P1 | RW |
P0 | RS |
由於 HD44780 可以使用 4位元模式 ,因此不接駁到 HD44780 的 D0 至 D3 引腳,便能夠讓 PCF8574 向 HD44780 發出足夠指令
PCF8574 for HD44780 LCD backpack 模組的接駁方式
其中有 4支引腳 接駁到 HD44780 D0 至 D3 實際上沒有線路只是固定用途
PCF8574 for HD44780 LCD backpack 的 4支屈曲引腳 在 HD44780 左邊伸出
PCF8574 for HD44780 LCD backpack 的 另外16支引腳 焊接到 HD44780
PCF8574 for HD44780 LCD backpack 焊接到 HD44780 背後
Fritzing HD44780 with PCF8574 組模圖示
Arduino AVR-C 例子
const byte GND_PIN = A0; const byte VCC_PIN = A1; const byte SDA_PIN = A2; const byte SCL_PIN = A3; void setup() { // 在下使用其中2支引腳作為 電源線 及 接地線 pinMode(GND_PIN, OUTPUT); digitalWrite(GND_PIN, LOW); pinMode(VCC_PIN, OUTPUT); digitalWrite(VCC_PIN, HIGH); // 設定 序列時脈 及 序列資料 為 OUTPUT pinMode(SDA_PIN, OUTPUT); pinMode(SCL_PIN, OUTPUT); // 啟動 I2C start(); // 設定 Slaver地址為 0100111 為 寫入模式 = 0x4E writeByte(0x4E, false); // 設定 使用4位元模式 writeByte(0x2C, false); // 設定 指令 為 0010 , A, EN 為 高電壓 , RW, RS 為 低電壓 writeByte(0x28, false); // 設定 指令 為 0010 , A 為 高電壓 , EN, RW, RS 為 低電壓 // 設定 使用4位元模式、使用2行、使用5x8點陣 (二進制數值 00101000) // 分開為 0010 及 1000 2組4位元資料 writeByte(0x2C, false); // 設定 指令 為 0010 , A, EN 為 高電壓 , RW, RS 為 低電壓 writeByte(0x28, false); // 設定 指令 為 0010 , A 為 高電壓 , EN, RW, RS 為 低電壓 writeByte(0x8C, false); // 設定 指令 為 1000 , A, EN 為 高電壓 , RW, RS 為 低電壓 writeByte(0x88, false); // 設定 指令 為 1000 , A 為 高電壓 , EN, RW, RS 為 低電壓 // 設定使用 顯示熒幕、顯示游標、顯示閃爍 (二進制數值 00001111) // 分開為 0000 及 1111 2組4位元資料 writeByte(0x0C, false); // 設定 指令 為 0000 , A, EN 為 高電壓 , RW, RS 為 低電壓 writeByte(0x08, false); // 設定 指令 為 0000 , A 為 高電壓 , EN, RW, RS 為 低電壓 writeByte(0xFC, false); // 設定 指令 為 1111 , A, EN 為 高電壓 , RW, RS 為 低電壓 writeByte(0xF8, false); // 設定 指令 為 1111 , A 為 高電壓 , EN, RW, RS 為 低電壓 // 清除熒幕資料 (二進制數值 00000001) // 分開為 0000 及 0001 2組4位元資料 writeByte(0x0C, false); // 設定 指令 為 0000 , A, EN 為 高電壓 , RW, RS 為 低電壓 writeByte(0x08, false); // 設定 指令 為 0000 , A 為 高電壓 , EN, RW, RS 為 低電壓 writeByte(0x1C, false); // 設定 指令 為 0001 , A, EN 為 高電壓 , RW, RS 為 低電壓 writeByte(0x18, false); // 設定 指令 為 0001 , A 為 高電壓 , EN, RW, RS 為 低電壓 // 在熒幕顯示 A (二進制數值 01000001) // 分開為 0100 及 0001 2組4位元資料 writeByte(0x4D, false); // 設定 指令 為 0100 , A, EN, RS 為 高電壓 , RW 為 低電壓 writeByte(0x49, false); // 設定 指令 為 0100 , A, RS 為 高電壓 , EN, RW 為 低電壓 writeByte(0x1D, false); // 設定 指令 為 0001 , A, EN, RS 為 高電壓 , RW 為 低電壓 writeByte(0x19, false); // 設定 指令 為 0001 , A, RS 為 高電壓 , EN, RW 為 低電壓 // 結束 I2C stop(); } void loop() { } void start() { digitalWrite(SCL_PIN, HIGH); digitalWrite(SDA_PIN, HIGH); delayMicroseconds(250); digitalWrite(SDA_PIN, LOW); } void stop() { digitalWrite(SCL_PIN, HIGH); digitalWrite(SDA_PIN, LOW); delayMicroseconds(250); digitalWrite(SDA_PIN, HIGH); } void writeBit(bool enabled) { digitalWrite(SCL_PIN, LOW); digitalWrite(SDA_PIN, ((enabled) ? HIGH : LOW)); delayMicroseconds(250); digitalWrite(SCL_PIN, HIGH); } void writeByte(byte data, bool ack) { for (byte i = 0; i < 8; i++) { writeBit((data & 0x80) > 0); data <<= 1; } writeBit(ack); }
執行效果
(在下使用比 Arduino UNO R3 細小的 Arduino Nano)
(在下同樣懶惰關係,直接將 PCF8574 安置到 麵包板 上)
同時控制多個Slaver
在下將其中一塊 PCF8574 for HD44780 LCD backpack 的 A0 焊接
當 A0 接駁到接地後, Slaver地址 會調整成 0100110 = 0x26
然後接駁兩塊 PCF8574 for HD44780 LCD backpack 的 序列時脈 及 序列資料
可以根據 Slaver地址 控制不同 PCF8574 for HD44780 LCD backpack 讓 HD44780 顯示不同資料而不需要接駁太多線路
補充資料
除了 PCF8574 for HD44780 LCD backpack還有具備 3-Pin DIP Switch 的 PCF8574(Red)版本
PCF8574(Red) 的前側面
PCF8574(Red) 的後側面
PCF8574(Red) 的正面
PCF8574(Red) 的 3-Pin DIP Switch 可以免焊接來調整 A0, A1, A2 的 Slaver地址
切換到 ON 方向為 1 ,反之為 0
同樣使用 SMD 的 PCF8574T晶片
將 HD44780 接駁到 PCF8574(Red)
再將 HD44780 接駁到 Arduino Nano
要留意 PCF8574 for HD44780 LCD backpack 及 PCF8574(Red) 的引腳次序不同
以正面擺放、4支引腳向左為準,引腳次序由下至上
PCF8574 for HD44780 LCD backpack 為 GND, VCC, SDA, SCL
PCF8574(Red) 為 SCL, SDA, GND, VCC
測試效果
其他資料
由於在下不太喜歡小型模組板的引腳在正面方向,因此將引腳除錫,再重新焊在組模板背面重新焊接引腳的 PCF8574(Red) 正面
重新焊接引腳的 PCF8574(Red) 背面
在下認為引腳焊接在背面可以方便安置在 麵包板 上接駁線路
總結
通常在 Arduino 上使用 I2C 控制 HD44780 LCD Screen 都會使用 LiquidCrystal_I2C.h(可以到 https://www.arduinolibraries.info/libraries/liquid-crystal-i2-c 下載函式庫)
在 LiquidCrystal_I2C.h 的原始碼中會發現使用 Wire.h 函式庫,是 Arduino 附帶的函式庫
在開發角度上,使用已經製成、穩定的函式庫,可以避免出錯、加快開發速度、節省時間
在下想了解 I2C 的運作原理,因此由零開始查看文件,但完全不明白文件描述的運作方式,便到網上尋找資料
不過很多資料都是使用 LiquidCrystal_I2C.h 並沒有詳細解釋 I2C 運作原理,亦沒有不使用 Wire.h 實作效果
因為函式庫是基於 Arduino AVR-C 編寫,在下嘗試尋找不是使用 Arduino AVR-C 的例子,希望有類似原始碼的程式可以參考
在下找到 Assembly 及 C 的程式,複製程式碼再修改成在下需要的效果,但結果都是失敗;最後還是只能認真、仔細、慢慢理解文件
除了理解文件,在下不想受到 Wire.h 的 序列時脈 及 序列資料 的引腳限制
有些使用 LiquidCrystal_I2C.h 的例子可以更改 序列時脈 及 序列資料 ,但在下查看原始碼發現不能使用
(可能有很多不同版本,或自行修改版本)
Arduino 允許使用者自行修改原始碼,將 SCL 及 SDA 修改成需要的引腳
(不同 Arduino 的 SCL 及 SDA 引腳會有不同,可參考 https://www.arduino.cc/en/reference/wire)
但修改後亦可能導致其他函式庫需要使用原來的 SCL 及 SDA 設定時出現問題
因此在下唯有自行編寫 I2C 程式
亦已經上載到 https://bitbucket.org/hkgoldenmra/hd44780i2cmodule
及 https://create.arduino.cc/editor/hkgoldenmra/34c270b4-8443-406f-8149-eb68011b9bcc/preview
可以在 Terminal 輸入
git clone "https://bitbucket.org/hkgoldenmra/hd44780i2cmodule.git" --depth=1
學習沒有捷徑
沒有留言 :
張貼留言