---
# System prepended metadata

title: I2C (Inter-Integrated Circuit) 筆記
tags: [嵌入式系統]

---

> [Reference](https://www.analog.com/en/resources/technical-articles/i2c-primer-what-is-i2c-part-1.html)

> 寫很清楚的中文教學: [1](https://makerpro.cc/2020/05/i2c-interface-part1/) [2](https://makerpro.cc/2020/05/i2c-interface-part2/) [3](https://makerpro.cc/2020/06/i2c-interface-part3/) [4](https://makerpro.cc/2020/07/i2c-interface-part-4/) [5](https://makerpro.cc/2020/07/i2c-interface-part5-clock-stretching/) [6](https://makerpro.cc/2020/07/i2c-interface-part6/) [7](https://makerpro.cc/2020/08/i2c-part7-pull-up-resistors/) [8](https://makerpro.cc/2020/08/i2c-interface-part8/)

# Addressing

* 7-bit
* 保留 16 個位址及特殊位址
    * `0000000`: 廣播位址。若搭配 R/W bit 為 0 (write)，則 bus 上所有 slave 都應該接收並處理這個訊息 (如果它們支援廣播功能)
* 最多可和 112 個節點通訊
* 新版 I2C 支援 10-bit
    * 傳輸 address 時分為兩 byte
        * 第一個 byte 為 `11110XXY`，`11110` 表示是 10-bit address、`XX` 為 address 的第 9 和第 8 bit (MSb)、`Y` 為 R/W bit，和 7-bit 模式相同
        * 第二個 byte 傳送 address 剩下的 8 個 bits

# 傳輸速率

* 標準模式 (100 Kb/s)
* 低速模式 (10 Kbit/s)
* 暫停通訊 (clock rate = 0)
* 新版 I2C 支援快速模式 (400 Kb/s)、高速模式 (3.4Mb/s)

# Bus

![1-3](https://hackmd.io/_uploads/HkxlvWjkSgg.png)

* **Serial (串列)** 、**雙向** 傳輸、Master/Slave 架構 (支援 multi-Master，但同一時間只能有一個 Master 控制 bus；支援 multi-Slave)
* SDA (Serial Data Line): 傳輸 data 及 address signal
* SCL (Serial Clock Line): 傳輸 clock signal
* 所有 device (包含 Master 及 Slaves) 的 SCL 及 SDA I/O 線皆需為 **open-drain (開集極)**，表示他們只能主動輸出 0，當所有裝置都輸出 float (高阻抗) 時才會讓 bus 拉高為 1 (故稱為 **wired AND**)
* VDD 常用 5V 或 3.3V
* [Rp (pull-up resistor (上拉電阻)) 需根據 bus 總電容及 rising time 來選擇](https://makerpro.cc/2020/08/i2c-part7-pull-up-resistors/)

## 詳細的 I2C I/O port 電路示意圖

![](https://makerpro.cc/wp-content/uploads/2020/05/1-5.png)

* SDA 及 SCL 皆為 quasi-bidirectional (同時為輸出及輸入) 接腳，透過一個緩衝器來輸入 (listen) 目前 bus 上的狀態

## Timing Diagram

![36689](https://hackmd.io/_uploads/HyPiSvA4xl.png)

:::warning
1. **I2C 並不是邊緣觸發，而是在某個準位的時間內都可以做事**
2. **SDA 線上的資料只能在 SCL 為 0 時改變**。當 SCL 是 1 時，SDA 必須 **穩定** (否則可能會被解釋成 Start/Stop Condition)。故 Receiver 需要在 SCL 為 1 時 read 資料
    * ![1-14](https://hackmd.io/_uploads/Sy6Tcoyree.png)
:::

1. 未傳輸時，Master 將 SCL 及 SDA 維持在 1
2. **Start Condition**: SCL 為 1、Master 將 SDA 由 1 變 0
3. 完成 Start Condition 後，Master 先在 SCL 為 0 時傳送 address 的 **MSb**，Slave 在下一個 SCL 為 1 的時間內讀取。並依次傳送至 LSb
    * 每個 Slave 皆會 listen 這 7 個 bit，只有 address 相符的 slave 才會繼續 listen 之後的東西
4. 傳送完 7-bit address 後，Master 傳送 1-bit 表示 **Read** (1, Master read Slave) 或 **Write** (0, Master write Slave) operation
    :::warning
    datasheet 上寫 8-bit I2C address，就表示已包含了此 R/W bit，若要做 R/W 要自己改變這個 address 的 LSb
    :::
6. 之後 Slave 會傳送 1-bit **ACK** (0 為成功、1 可能表示沒有符合此 address 的 slave 造成無應答或其他情況)
    * 這時此 Slave 會 assume 之後的所有 transaction 都是與自己有關，直到 Stop Condition 發生
    * 此時 slave 也會依照 Read/Write bit 決定自己是 transmitter 或 receiver
7. 之後 transmitter 依次傳送要傳送的多個 byte 資料 (**MSb** 依次傳送至 LSb、並且大部分是 MSB 依次傳送至 LSB (I2C 沒有嚴格規定 byte order))。每傳送完一個 byte，receiver 傳送 1-bit **ACK** (0 為成功、如果 Master 是 receiver，則此 ACK 傳 1 表示告訴 Slave 此次傳輸到此為止)
8. **Stop Condition**: Master 將 SCL 維持在 1、Master 將 SDA 由 0 變 1
9. **Repeated Start**: 可以不用每次都一定要先用 Stop Condition 來 terminate，可以直接產生下一個 Start Condition。如此可避免 Master 釋放 bus 的控制權，可形成 atomic operation

:::info
++Remark++
1. NACK 的常見情況
    * 無此 address 的裝置
    * Slave 忙碌中: Slave 成功接收到位址，但在後續的資料傳輸中，它可能因為內部正在處理其他任務 (例如: 正在將前一個 byte 寫入 EEPROM) 而無法再接收新的資料
    * 資料錯誤: Slave 接收到的資料或命令是它無法理解或處理的
2. 10-bit receive mode 較特別，addressing mode 需要送 **3** 個 byte，第一個 byte 的 R/W bit 要是 **W** 而不是 R (因為如果是 R 的話，資料流方向就會立刻變成 slave 驅動 SDA)。並且第二個 byte 送完時需要送 **Repeated Start**
    ![image](https://hackmd.io/_uploads/HJS-1xZwWe.png)
:::

# Clock Stretching

使用在當較低速的 Slave 忙不過來時，通知 Master 暫停傳送的方法

示意圖:
![3-1-768x322](https://hackmd.io/_uploads/SyZPZhyHle.jpg)

* **Master 每送出 SCL 的一個 clock 時，都會同時 listen SCL 的狀態**
* Slave 在接收完某個 bit 之後若要進行 clock stretching，則 **把 SCL 設為 0**
    * Master 隨後在 SCL 為 0 時繼續發送下一個 SDA data。當 Master 因為 I2C wired-AND 特性導致無法讓 SCL 設為 1 時表示發生 clock stretching

:::info
++Remark++
1. clock stretching 也會引發一些潛在問題，例如有裝置長時間把 SCL 設為 0 而癱瘓整個 bus
2. 由於 clock stretching 需要接腳同時進行輸入與輸出，要用軟體模擬 I2C 的話需要有 quasi-bidirectional port。在早期 8051 有 quasi-bidirectional port 可以使用，但現今 ARM Cortex-M MCU 已少見這種 port，故須用兩個接腳模擬 SCL 的行為
3. slave 只能在 SCL 為 0 時 hold SCL (i.e. 只能 **延長** SCL 0 的時間，而不能強行將 SCL 從 1 變為 0)
:::

# Multi-Master

當有 multi-Master 時需要考慮 **clock synchronization (時脈同步)** 及 **bus arbitration (bus 仲裁)**

## Clock Synchronization

用來讓不同 clock 週期的 Master 變為一樣

支援 clock synchronization 的 I2C Master 會有兩個 **clock period counter**，分別計算預定的 SCL high 與 SCL low 時間

* 當 Master 認為 SCL low period 應該結束 (即計數器達到門檻) 時，會偵測實際 SCL 線是否已回到 high。若 SCL 實際仍為 low，表示有其他更慢的 Master 尚未釋放 SCL，Master 必須等待
* 等到 SCL 回到 high，所有 Master 累加 high counter 的值直到 SCL 再次回到 low

故每個裝置的 counter 最後都會變成一樣的值:
* low counter = 把 SCL 拉低最久的 Master 決定
* high counter = 把 SCL 維持在 high 最短的 Master 決定

從下一個 SCL 脈波開始，所有的 Master 就會用這一組 low/high 時間來同步產生接下來的 clock

## Bus Arbitration

![1595688065858](https://hackmd.io/_uploads/H1WG2VMSlg.jpg)

若 Master 驅動 SDA 為 high，但實際從 SDA 線上讀到的是 low，表示有其他 Master 正在驅動 low，且優先權較高，因此該 Master 仲裁失敗，需停止傳輸並等待下一次 bus 空閒 (SCL 及 SDA 皆為 1 時)

# [Level Shifting](https://makerpro.cc/2020/08/i2c-interface-part8/)

當兩裝置的工作電壓不同時需有 level shifting 電路