單元 3 · 感測器通訊介面

I2C/SCCB、register

I2C 與感測器位址

Thor 上感測器經 I2C 控制;走 HSB 時 I2C 也經乙太網路橋接。OV9281 常見位址 0x36

讀感測器 ID
sudo i2cdetect -y 
sudo i2cget -y  0x36 0x300a     # OV9281 ID 高位 = 0x92
sudo i2cget -y  0x36 0x300b     # = 0x81

HSB 的 I2C 橋接

HSB 關鍵:感測器資料走乙太網路,I2C 控制也經橋接。要先確認「橋接通道」通,才讀得到感測器。

OV register 慣例

Register功能
0x300A/0x300B感測器 ID
0x3500–0x3503曝光時間
0x350A–0x350B類比增益

3.8 深入:I2C 讀寫的位元組序與 ACK 檢查

OV 感測器用 16-bit register 位址(big-endian):

S0x0x36<<1|0位址高位址低資料/讀P
讀感測器 ID
sudo i2cget -y 0 0x36 0x300a   # 預期 OV9281 ID 高位
排查:讀回全 0xFF → 上拉/位址錯;讀回全 0x00 → 上電/reset 問題。用 i2ctransfer 讀寫並回讀驗證。

3.9 練習

  1. 讀感測器 ID,確認與 datasheet 相符。
  2. 寫一組 register 後回讀驗證。
  3. 記錄 I2C 位址與匯流排編號。

3.10 深入原理:register 位元級——曝光與增益怎麼拆

OV 感測器用 16-bit register 位址(big-endian),多欄位暫存器把 16-bit 值拆成多個 register。以曝光與增益為例(unit-10 會詳述控制):

Register位元布局說明
0x3501/0x3502AEC[15:8] / AEC[7:0]曝光時間(行數)高低位
0x350A/0x350BAGC[7:0] / AGC[0.5:0]類比增益整數+小數
把「曝光 1000 行」寫成 register(位元級)
# 1000 decimal = 0x03E8 → 高 byte 0x03、低 byte 0xE8
i2cset -y <bus> 0x36 0x3501 0x03
i2cset -y <bus> 0x36 0x3502 0xE8
# 回讀驗證(單元 3.9 的練習)
i2cget -y <bus> 0x36 0x3501   # 應回 0x03
i2cget -y <bus> 0x36 0x3502   # 應回 0xE8
陷阱:直接寫 16-bit 值時,i2cset 預設可能是 8-bit 寫入,會把值截斷。務必確認單一 register 寬度(多數 OV 是 8-bit register),否則曝光「看似寫了卻沒變」。

3.11 Worked Example:完整 I2C 讀取流程(含 ACK)

讀 OV9281 ID(0x300A=0x92)逐步拆解
# 1. 掃描找 bus
sudo i2cdetect -l
sudo i2cdetect -y <bus>          # 0x36 出現 = 有回應
# 2. 讀 ID 高位(兩種寫法等價)
sudo i2cget -y <bus> 0x36 0x300a      # 預期 0x92
sudo i2ctransfer -y <bus> w2@0x36 0x30 0x0a r1
# w2 = 寫 2 bytes(register 位址高、低),r1 = 讀 1 byte
為什麼 i2ctransfer 更可靠:它把「寫位址 + 讀資料」做成單一 transaction,保證 register 位址在讀前設定——避免其他執行緒在同一 bus 插入。檢查回讀值是否與 datasheet 相符,即完成最小 I2C 驗證。

3.12 疑難排解決策樹:讀不到感測器 ID

「i2cdetect 看不到 0x36」決策樹
1. bus 選對了嗎?
   ├─ 錯 → 依 board 文件找正確 bus 編號(可能不是 0)
   └─ 對 ─┐
2. 感測器供電(AVDD/DVDD)到位?
   ├─ 否 → 查電源(unit-06)
   └─ 是 ─┐
3. RESET/GPIO 是否已釋放?
   ├─ 否 → 依上電時序釋放 reset
   └─ 是 ─┐
4. (HSB) I2C 橋接通道通?
   ├─ 否 → 檢查 HSB 網路與橋接設定
   └─ 是 ─┐
5. 全部確認仍 0xFF?→ 換一顆感測器 / 檢查焊接與位址 pin

⚠️ 位址除了 0x36,部分感測器依 ID pin 可變(如 0x3C)。用 i2cdetect 全域掃描最保險。

3.13 常見錯誤與陷阱

陷阱 1:忘了 7-bit/8-bit 位址轉換——0x36 是 7-bit 位址;某些工具要求你傳 8-bit(0x36<<1)。i2c-tools 接受 7-bit,SCCB/其他工具可能不同。
陷阱 2:16-bit register 位址不是先低後高——OV 是 big-endian(先高位址)。倒過來寫會讀到錯的 register。
陷阱 3:HSB 下 I2C 延遲較高——走乙太橋接時,讀寫往返有額外延遲;嚴謹的驅動會做重試。快速連續寫多 register 時,留意「bridge 佇列滿」造成的無回應。

3.14 練習

  1. 用 i2ctransfer 讀 0x300A/0x300B 並記錄。
  2. 把曝光 2000 行拆成 register 高低位寫入並回讀驗證。
  3. 故意把 register 位址寫顛倒,觀察讀回值,建立「位址錯誤」的辨識能力。
看完這單元你應該能說出:
  • Thor 上 I2C 控制感測器。
  • HSB 的 I2C 橋接概念。
  • OV9281 ID register。
  • 「讀不到 ID」排查。

延伸閱讀

3.15 進階真實情境 Worked Example:HSB 橋接下的 I2C 批次寫入

場景:Thor T5000 透過 HSB 初始化一顆 OV5640(需要寫入約 200 個 register 完成 init table),I2C 走乙太網路橋接,每筆 write 往返延遲約 50 µs(遠高於本地 I2C 的 10 µs)。

HSB 下 init table 寫入策略
# 問題:200 個 register × 50 µs = 10 ms(本地只需 2 ms)
# 某些 OV5640 register 有嚴格時序要求(如 PLL 設定後需等待 5 ms)

# 方案 A:批次寫入(一次 transaction 多筆 register)
i2ctransfer -y <bus> w21@0x3c 0x30 0x0a ... # 21 bytes = 2 addr + 19 data
# 優點:減少往返次數,但橋接佇列深度有限(通常 8-16 筆)

# 方案 B:拆分 + 確認
# 把 init table 拆成 PLL / 感測器 / 輸出 三段
# 每段結尾回讀關鍵 register 驗證
段 1 (PLL):寫入 30 個 → 回讀 0x3034 確認 bit mode
段 2 (Sensor):寫入 120 個 → 回讀 0x300A 確認 ID
段 3 (Output):寫入 50 個 → 回讀 0x3035 確認 frame rate

# 設計決策:
# 1. 批次寫入降低 latency,但要控制在橋接佇列深度內
# 2. 每段結尾回讀:若 PLL 設錯,後面全錯——先確認 PLL 才繼續
# 3. 整個 init table 寫入 + 驗證控制在 20 ms 內完成
設計決策:HSB 的 I2C 延遲是 CSI 的 5 倍。解決方案不是「忍受」而是「重組」——把 init table 按功能分段、每段結束時做「關鍵 register 回讀」,確保前段正確才繼續後段。

3.16 深入原理擴充:I2C 在 Thor HSB 橋接中的封包化

走 HSB 時,I2C 讀寫不是直接走 PCB 上的匯流排,而是被封裝成乙太網路訊框。每次 I2C transaction 會產生一個帶有時間戳的 UDP 封包,經 25GbE 傳到 host 端橋接器再轉成 I2C 訊號。這代表:① 每筆 I2C 操作有固定的封包開銷(~20 bytes header)② 同一 I2C bus 上的多顆感測器可能因橋接佇列競爭而延遲 ③ 乙太網路的 jitter 會直接反映在 I2C timing 上。

容易忽略的邊界案例:若同時對 HSB 上的 4 顆感測器下 I2C 寫入,橋接器的 I2C 匯流排會被序列化(一次只能處理一筆),但乙太網路封包是並行的。這可能導致「先發的命令反而後到」——即 I2C command reordering。驅動層必須在時間敏感的 register(如 reset、PLL unlock)前加入 read-back 驗證,不能假設寫入順序。

3.17 診斷式疑難排解表

症狀可能原因解決方案
i2cdetect 能看到感測器但 i2cget 回 0x00RESET pin 未釋放或 clock 未啟動,感測器「有電但不回應」確認上電時序(unit-06):電源穩定 → MCLK → RESET 釋放 → 等待 1ms → 再下 I2C
HSB 下 i2cctransfer 偶發 NAK橋接佇列滿或乙太網路 jitter 造成 I2C clock stretching 超時降低 I2C 頻率(400K → 100K);在密集寫入間插入 1 ms 延遲
回讀 register 值與寫入值不一致(bit flip)I2C 匯流排雜訊 / 上拉電阻值不對 / 線材過長縮短 I2C 線長;確認上拉電阻(4.7K 到 3.3V);用邏輯分析儀抓波形
多顆感測器在同一 I2C bus 時,A 的設定影響 B位址衝突或 broadcast register 被意外寫入用 i2cdetect 全域掃描確認各感測器位址無衝突;避免寫入 broadcast register
HSB 橋接的 I2C 比本地慢 5 倍但規格寫支援 400 KHz400 KHz 是 I2C 訊號速率,不含乙太網路封包往返延遲這是正常行為;批次寫入減少往返次數才是正解

3.18 進階挑戰題

  1. 在 HSB 環境下,設計一個 I2C init table 的「斷點續傳」機制:若寫入第 87 個 register 時 I2C 失敗,如何不從頭開始、只重試失敗段落並驗證前面已正確寫入?
  2. 用邏輯分析儀同時抓取 HSB 橋接器的乙太網路端與 I2C 端,量測一次 i2ctransfer 的「封包化延遲」分佈。畫出 histogram,找出 P99 延遲,並說明它對感測器 init 時序的影響。
  3. 若你需要在 Thor 上同時初始化 6 顆 OV9281(每顆 init table 約 50 個 register),計算在 I2C 橋接延遲 50 µs/筆下的最短初始化時間,並設計一個平行初始化策略讓六顆感測器在 10 ms 內全部就緒。

3.19 專案級端到端 Worked Example:多顆感測器 I2C 拓撲與 init 管理專案

場景:Thor 上要接 6 顆 OV9281。每顆有自己的 I2C bus(HSB 橋接)或共享 bus(CSI)。專案目標:建立一個「可重現、可平行初始化」的多顆 I2C 基礎架構。

里程碑規劃
M1 拓撲盤點
   ├─ 列出每顆感測器的 bus 編號、位址、橋接通道
   └─ 通過:i2cdetect 6 顆全出現、位址無衝突

M2 單顆 init 驗證
   ├─ 對 1 顆跑完整 init table
   ├─ 回讀關鍵 register(ID、PLL、mode)
   └─ 通過:回讀值全符合

M3 平行初始化
   ├─ 6 顆平行跑 init(背景程序)
   ├─ 監控橋接佇列深度
   └─ 通過:init 完成 < 20 ms、無 NAK

M4 位址衝突測試
   ├─ 故意將兩顆設為同位址,觀察現象
   └─ 通過:能識別衝突並用 ID pin 錯開

M5 產出 init 管理工具
   └─ 通過:repo 內有 per-sensor init 表 + 驗證腳本
專案要點:多顆感測器的 I2C 問題 90% 是「拓撲沒畫清楚」。先把每顆的 bus/位址/橋接通道畫成表格,所有除錯才有依據。

3.20 量測 / 驗證 SOP:I2C 通訊健康檢查

I2C SOP(Step 1–7)
Step 1 列出 bus
   i2cdetect -l
Step 2 掃描
   i2cdetect -y <bus>                  # 感測器位址出現
Step 3 讀 ID(單一 transaction)
   i2ctransfer -y <bus> w2@0x36 0x30 0x0a r1
Step 4 寫入→回讀驗證
   i2cset -y <bus> 0x36 0x3501 0x03
   i2cget -y <bus> 0x36 0x3501          # 回 0x03
Step 5 時序檢查(HSB)
   密集寫入間插入 1ms,觀察 NAK 是否消失
Step 6 錯誤計數
   dmesg | grep -i i2c                   # 無 i2c transfer error
Step 7 記錄
   位址、bus、回讀值、錯誤計數

判讀指標:
  0xFF 全回 → 位址/上拉/供電問題
  0x00 全回 → RESET 未釋放 / clock 未起
  偶發 NAK → 佇列滿 / jitter / 線材

3.21 平台間對照:I2C 介面與工具

面向Thor T5000RPi5Orange PiOrin Nano
I2C bus 數量多(CSI 各埠 + HSB 橋接)多(CSI 專用 + GPIO 擴充)多(CSI 各埠)
I2C 橋接(遠距)✅ HSB 橋接❌(本地)⚠️ GMSL 串行控制
工具i2c-tools / i2ctransferi2c-toolsi2c-toolsi2c-tools
7-bit 位址慣例0x36(7-bit)0x360x3c(部分模組)0x36
HSB 下 I2C 延遲~50 µs/筆
選擇思考:I2C 是「同一顆感測器、同一組 register」最有力的跨平台論點——讀 0x300A 在四平台只是 i2ctransfer 的 bus 編號不同。學會位元層,四平台通吃。

3.22 互動式檢核清單

3.18 Register 位元級完整工作流

本單元涉及的關鍵 register,以及「讀→改→寫→驗證」的完整位元級操作序列:

Register位址功能Bit Field 說明
SCCB_ID_REG0x0100 (OV9281)Chip ID 讀取i2cget -y 1 0x60 0x0100 → expected 0x9281
SCCB_STREAM_REG0x0100 (OV9281)Streaming On/Offbit[0]=1 start, bit[0]=0 stop
讀→改→寫→驗證 完整序列(以 SCCB_ID_REG 為例)
# Step 1: 讀取目前值
$ devmem2 0x0100 (OV9281) w
# 記錄 current_value

# Step 2: 計算新值(設定 bit[0]=1)
$ new_value=$((current_value | 0x0001))

# Step 3: 寫入
$ devmem2 0x0100 (OV9281) w $new_value

# Step 4: 驗證
$ devmem2 0x0100 (OV9281) w
# 確認 bit[0] = 1,其餘 bit 不變

# Step 5: 進階 — bitmask 操作
$ read_val=$(devmem2 0x0100 (OV9281) w | grep "Read" | awk '{print $NF}')
$ mask=0x0001
$ expected=0x0001
$ [ $(($read_val & $mask)) -eq $expected ] && echo "PASS" || echo "FAIL: bit[0] not set"

3.19 多層疑難排解決策樹

決策樹 1:I2C/SCCB 通訊失敗
1. `i2cdetect -y 1` 看不到 device?
   ├─ 地址 0x60 不見 → 檢查 SCCB/I2C SDA/SCL 接線、上拉電阻(2.2kΩ)
   ├─ 0x60 出現但多個地址 → bus 仲裁問題,減少 pull-up
   └─ 全掃描 0x03–0x77 都有 → bus stuck,power cycle 感測器
2. address 可見但讀 chip ID 失敗?
   ├─ `i2cget` 返回 0xFF → 設備未初始化,需先 strobe/reset pin
   └─ 返回 0x00 → power-on reset 未完成,等 100ms 再讀
決策樹 2:Register 讀值不預期
1. 讀值 = 0xFF(R/W register)?
   ├─ 設備地址錯 → 確認 OV9281 的 7-bit addr = 0x60
   └─ 16-bit sub-address 未送對 → 檢查 `i2cset` 格式
2. 寫入後讀回不同?
   ├─ 某些 register 寫後需等待 → 加 `usleep(1000)` 再讀
   └─ register 是 write-only → 查 datasheet 確認 R/W 屬性

3.20 量測驗證完整 SOP

I2C/SCCB 通訊驗證 SOP:

步驟動作指令/方法預期輸出
Step 1掃描 bus`sudo i2cdetect -y 1`0x60 出現(OV9281)
Step 2讀取 Chip ID`sudo i2cget -y 1 0x60 0x300A w`返回 0x9281
Step 3寫入測試`sudo i2cset -y 1 0x60 0x0103 0x01`(sw reset)感測器 reset
Step 4等待 reset`sleep 0.1`100ms 後感測器就緒
Step 5再讀 ID 確認`sudo i2cget -y 1 0x60 0x300A w`再次 0x9281
Step 6批量 dump`for i in $(seq 0x3000 0x30FF); do printf "0x%04X: " $i; sudo i2cget -y 1 0x60 $i w; done`建立 register baseline

3.21 四平台終極對照

面向Thor T5000RPi5Orange PiOrin Nano
I2C bus 數量3–4 bus(1 dedicated to sensors)1 bus(GPIO 驅動)2 bus2–3 bus
SCCB/I2C 速率100/400 kHz100 kHz100/400 kHz100/400 kHz
韌體層 I2C 控制NVIDIA Camera HALV4L2 directV4L2 / libi2cNVIDIA Camera HAL
I2C 除錯工具`i2cdetect`, `i2cget/set``i2cdetect`, `i2cget/set``i2cdetect`, `i2cget/set``i2cdetect`, `i2cget/set`
多感測器位址衝突處理Hardware MUX / 分 bus分 bus(2 bus available)Software MUXHardware MUX
I2C 傳輸失敗率目標< 0.01%< 0.1%< 0.1%< 0.01%

3.22 完整 Bring-up 小 Checklist

針對「感測器通訊介面」主題的完整 bring-up 步驟清單: