# I2C 通訊協定深度解析:從底層物理特性、通訊時序到Bus 鎖死排除實戰指南
###### tags: `protocols `
## I2C 介紹
* I2C協定使用2條線,一條是資料線(SDA),另一條是時鐘線(SCL),可以連接多個裝置,每個裝置有一個唯一的7 bit或10 bit addres。
* I2C分為Master and slave.
* Master: 主動發送
* Slave : 被動發送(需要機器訪問才會回答)
### I2C Master and slave
* master transmit — master node is sending data to a slave
* master receive — master node is receiving data from a slave
* slave transmit — slave node is sending data to the master
* slave receive — slave node is receiving data from the master
### I2C Speed
1. 標準模式 (Standard-mode), 速率可高達 100 kbit/s
2. 快速模式 (Fast-mode), 速率可高達 400 kbit/s
3. 快速模式PLUS (Fast-mode Plus), 速率可高達 1 Mbit/s
4. 高速模式 (High-speed mode), 速率可高達 3.4 Mbit/s.
## I2C bus 優缺點
* 優點:
1. 節省引腳:I2C使用2條線路進行通信,比其他協議需要的引腳更少。
2. 能夠多主機:支援多主機 (Multi-master) 模式:I2C 具備內建的總線仲裁與衝突偵測機制,允許多個主機設備共享同一總線,並根據硬體狀態決定通訊權,擴展性強。
3. 傳輸速率規格:靈活的通訊頻寬:相容多種傳輸速率規範,涵蓋標準模式 (100 kbps)、快速模式 (400 kbps)、快速模式 Plus (1 Mbps) 以及高速模式 (3.4 Mbps),可根據功耗與效能需求靈活調整。
4. 輕量化的通訊協定:原生 I2C 僅具備位元層級的 ACK/NACK 握手機制以確認數據送達,並無內建 Checksum 或 CRC 校驗。若應用環境雜訊較大,需透過軟體層實作校驗或改用基於 I2C 擴展的 SMBus (具備 PEC 封包錯誤檢查) 以確保數據正確性。
5. 定址空間與保留位限制。
- 在 7-bit Address 模式下,理論上有 $2^7 = 128$ 個地址組合(0x00 - 0x7F)。
- 實際可用性:需扣除 I2C 規範中的保留地址(如 0x00-0x07 的廣播與特殊指令,及 0x78-0x7F 的 10-bit 定址引導),實際可供一般設備分配的地址約 112 個。
- 限制:當總線上存在多個同型號 IC 時,若其硬體地址腳(Address Pins)不足,容易產生地址衝突,需透過 I2C Switch 或 GPIO 控制解決。
* 缺點:
1. 傳輸速度較慢:I2C的傳輸速度比其他協議如SPI和CAN慢。
2. I2C設備多容易有address衝突:I2C最多只能有64組address,且不同的設備可能使用相同的address,可能會產生address衝突,可透過GPIO 或 I2C Switch IC去解決。
3. 抗雜訊較弱:I2C傳輸數據的電壓範圍較小,受到電磁干擾(EMI)和其他噪聲或抗雜訊的影響,可能導致通信失敗。
## I2C Protocol

* SDA : 資料線,所以的資料交換都是在SDA上完成
* SCL : 時鐘線,由Master輸出用
* I²C的傳輸包括了四個部分:
* 最開頭的Start信號,接下來會接上設備的I²C地址與傳送的資料,最後會接上一個Stop信號。
* 每一個封包8-bit(I2C是以1-byte為單位傳送data) 的資料傳輸結束後,會跟著一個 acknowledge bit or non-acknowledge bit(第9個bit)。這個 acknowledge bit or non-acknowledge bit 固定由接收方產生,有兩種用法:
### ACK
1. 當 master傳送方(Transmitter)、slave接收方(Receiver),也就是說這個傳輸是 master 寫入資料到 slave 時,這個 acknowledge bit 是用來讓 slave 告訴 master「收到!了解!正確!」
2. 當 master 是接收方(Receiver)、slave (Transmitter)是傳送方,也就是說這個傳輸是 master 從 slave 讀取資料時,這個 acknowledge bit 是用來讓 master 告訴 slave「我還要接著讀,請繼續準備下一筆資料」或者是「夠了,我讀完了」。
(1) master正常收到data的話SDA會在第9個bit拉LOW也就是回應ACK,SCL則會拉HING
- slave正常收到data的話SDA會在第9個bit拉LOW也就是回應ACK,SCL則會拉HING
(2) 如果接收方正常收到data的話,會在第9個bit將SDA拉low,也就是回應ACK。
(3) 如果接收方有任何狀況,或是 bus 上面根本就沒有那個 address 的 slave 裝置,到了第 9 個 SCL 週期時,當傳送方釋放SDA後,就沒有人去驅動 SDA,這時 SDA 就會因為上拉電阻(Rp)的關係維持在 high,如此一來,傳送方就知道出問題了,不會繼續接下來的傳輸,而要進行錯誤處理,這種情況稱之為「non-acknowledge」,或簡稱為「NACK」。

### 產生NACK的五大原因:
1. 在multiple data read傳輸中,當Master要告知Slave停止傳輸,則會在最後一個data byte收下後,改發NACK以告訴Slave裝置data讀取已結束。
2. 沒有裝置去回應ACK (根本沒有接任何裝置或是發出的address不屬於bus上任一個裝置所有)。
3. 接收的裝置因為某種原因無暇進行指令的接收。
4. 傳輸過程中,接收裝置收到了他無法理解的資料。
5. 傳輸過程中,接收裝置的資料Buffer滿了,未處理前,再也收不下來。
6. 傳輸過程中,雜訊太多容易產生NACK。

## 用其他家IC來解釋I2C Protocol R/W
### NTC7904D

### LTC3350
* 屬於ADI IC,他們家IC在讀寫的時候要注意大端序(Big Endian)和小端序(Little Endian)
* 如果ARM base(NXP)在讀寫讀回來是Big Endian需要把他轉成Little Endian

### ADE7880
* 屬於ADI IC,他們家IC在讀寫的時候要注意大端序(Big Endian)和小端序(Little Endian)
* 如果ARM base(STM32)在讀寫讀回來是Big Endian需要把他轉成Little Endian.
* 這顆IC 做完init之後會有個上鎖,這個上鎖是假的會影響到ITHD and VTHD and HZ讀出來的數值.
1. Read

2. Write


## I2C 硬體

1. I2C BUS 上所有的裝置都是透過 open-drain 或 open-collector 的方式來驅動 SCL 或 SDA。一般 push-pull 的數位邏輯輸出電路會有 high/low 兩顆電晶體,各自負責把輸出拉到 high 或 low 的工作,但 open-drain 或 open-collector 的輸出則只有 low-side 一顆電晶體。
- MCU可以透過一開始寫預設值的時候來改變要open-drain or push-pull.

2. I2C 的實體界面只有兩根訊號,分別稱之爲 SCL(serial clock)與 SDA(serial data),而由於 I2C 是一個 bus,在這個 bus 上所有的裝置都得透過這兩個訊號線相連,也就是說 I2C 只需要兩根訊號線,就可以讓很多(多達上百個)裝置彼此之間互相通訊,如何讓多個裝置溝通而不打架呢?後續會再詳細說明。
* 上圖是一個典型的 I2C bus 電路,bus 上的所有裝置都透過 SCL/SDA 這兩根線相連。I2C 允許 bus 上可以有多個 master、多個 slave 存在,只要彼此的 address 不衝突、裝置數量沒有超過 bus 的電氣特性上限就沒有問題。
* 在上圖這個電路中,有兩個電阻,分別將 SCL 和 SDA 拉到 VCC。這兩顆電阻稱之爲 I2C bus 的「上拉電阻」(pull-up resistors, Rp),看起來好像沒什麼複雜的地方,但它們卻是 I2C bus 能正常運作的關鍵,而Rp的電阻值也需要工程師的精心設計或者可查看datasheet 建議或者公式換算。
## 遇到I2CBUS被拉住的解決辦法
* 基本上三種方式可以解決I2CBUS被拉住的情況
* 方案一:軟體強制恢復 (Clock Generator/Bit-Banging) 將 I2C 引腳切換為 GPIO 模式,手動送出 9 個 Clock 脈衝,強迫 Slave 釋放總線,隨後再發送一個 STOP 訊號。目的是讓卡住的 Slave 釋放 SDA 線。
* 原理:I2C 一幀資料為 8 bits + 1 bit ACK = 9 bits。最糟的情況是 Slave 正在傳輸 0 或處於 ACK 階段時 Master 當機,導致 SDA 被 Slave 永久拉低。手動補足 9 個 Clock 能強迫 Slave 走完該幀通訊並釋放 SDA。
* 最糟的情況是 Slave 正好在回傳 0 (ACK),且剛好 Master 當機。
* 在 9 個 Clock 之後,務必發送一個 STOP 訊號(SCL 為 High 時,SDA 由 Low 變 High),確保所有 Slave 回到待命狀態。。
* 方案二:硬體重置 (Master/Slave Reset) 執行 Master Reset 或透過 Reset 引腳重新啟動 Slave 裝置。如果硬體電路有拉 Reset Pin 到 MCU,這是最乾淨利落的方法。
* 方案三:Timeout跟Detection機制
* 異常偵測機制 (Detection Mechanism) 建立偵測機制,當發現 SDA 或 SCL 持續處於 Low 狀態超過特定時間(Timeout)時,自動觸發上述的恢復程序。
* 超時偵測與自動恢復 (Timeout Detection): 在程式碼中加入監控機制,若 SDA/SCL 處於非預期的低電位過久,即判定為 Bus 鎖死並自動執行復位程序。
* 優化上拉電阻:
* 如果上拉電阻過大,導致SDA上升沿太慢,請減小電阻值以增強驅動能力(例如從 10K歐姆 換成 4.7K歐姆 或2.2K歐姆
## 參考文獻
* [I2C的工作原理與技術分析](https://rexpighj123.pixnet.net/blog/post/219960237)