I2C/SCCB、register
Thor 上感測器經 I2C 控制;走 HSB 時 I2C 也經乙太網路橋接。OV9281 常見位址 0x36。
sudo i2cdetect -ysudo i2cget -y 0x36 0x300a # OV9281 ID 高位 = 0x92 sudo i2cget -y 0x36 0x300b # = 0x81
| Register | 功能 |
|---|---|
| 0x300A/0x300B | 感測器 ID |
| 0x3500–0x3503 | 曝光時間 |
| 0x350A–0x350B | 類比增益 |
OV 感測器用 16-bit register 位址(big-endian):
sudo i2cget -y 0 0x36 0x300a # 預期 OV9281 ID 高位OV 感測器用 16-bit register 位址(big-endian),多欄位暫存器把 16-bit 值拆成多個 register。以曝光與增益為例(unit-10 會詳述控制):
| Register | 位元布局 | 說明 |
|---|---|---|
| 0x3501/0x3502 | AEC[15:8] / AEC[7:0] | 曝光時間(行數)高低位 |
| 0x350A/0x350B | AGC[7:0] / AGC[0.5:0] | 類比增益整數+小數 |
# 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
i2cset 預設可能是 8-bit 寫入,會把值截斷。務必確認單一 register 寬度(多數 OV 是 8-bit register),否則曝光「看似寫了卻沒變」。# 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
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 全域掃描最保險。
場景:Thor T5000 透過 HSB 初始化一顆 OV5640(需要寫入約 200 個 register 完成 init table),I2C 走乙太網路橋接,每筆 write 往返延遲約 50 µs(遠高於本地 I2C 的 10 µs)。
# 問題: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 讀寫不是直接走 PCB 上的匯流排,而是被封裝成乙太網路訊框。每次 I2C transaction 會產生一個帶有時間戳的 UDP 封包,經 25GbE 傳到 host 端橋接器再轉成 I2C 訊號。這代表:① 每筆 I2C 操作有固定的封包開銷(~20 bytes header)② 同一 I2C bus 上的多顆感測器可能因橋接佇列競爭而延遲 ③ 乙太網路的 jitter 會直接反映在 I2C timing 上。
| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
| i2cdetect 能看到感測器但 i2cget 回 0x00 | RESET 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 KHz | 400 KHz 是 I2C 訊號速率,不含乙太網路封包往返延遲 | 這是正常行為;批次寫入減少往返次數才是正解 |
場景: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 表 + 驗證腳本
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 / 線材
| 面向 | Thor T5000 | RPi5 | Orange Pi | Orin Nano |
|---|---|---|---|---|
| I2C bus 數量 | 多(CSI 各埠 + HSB 橋接) | 多(CSI 專用 + GPIO 擴充) | 少 | 多(CSI 各埠) |
| I2C 橋接(遠距) | ✅ HSB 橋接 | ❌(本地) | ❌ | ⚠️ GMSL 串行控制 |
| 工具 | i2c-tools / i2ctransfer | i2c-tools | i2c-tools | i2c-tools |
| 7-bit 位址慣例 | 0x36(7-bit) | 0x36 | 0x3c(部分模組) | 0x36 |
| HSB 下 I2C 延遲 | ~50 µs/筆 | — | — | — |
i2ctransfer 的 bus 編號不同。學會位元層,四平台通吃。本單元涉及的關鍵 register,以及「讀→改→寫→驗證」的完整位元級操作序列:
| Register | 位址 | 功能 | Bit Field 說明 |
|---|---|---|---|
SCCB_ID_REG | 0x0100 (OV9281) | Chip ID 讀取 | i2cget -y 1 0x60 0x0100 → expected 0x9281 |
SCCB_STREAM_REG | 0x0100 (OV9281) | Streaming On/Off | bit[0]=1 start, bit[0]=0 stop |
# 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"
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 再讀
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 屬性
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 |
| 面向 | Thor T5000 | RPi5 | Orange Pi | Orin Nano |
|---|---|---|---|---|
| I2C bus 數量 | 3–4 bus(1 dedicated to sensors) | 1 bus(GPIO 驅動) | 2 bus | 2–3 bus |
| SCCB/I2C 速率 | 100/400 kHz | 100 kHz | 100/400 kHz | 100/400 kHz |
| 韌體層 I2C 控制 | NVIDIA Camera HAL | V4L2 direct | V4L2 / libi2c | NVIDIA Camera HAL |
| I2C 除錯工具 | `i2cdetect`, `i2cget/set` | `i2cdetect`, `i2cget/set` | `i2cdetect`, `i2cget/set` | `i2cdetect`, `i2cget/set` |
| 多感測器位址衝突處理 | Hardware MUX / 分 bus | 分 bus(2 bus available) | Software MUX | Hardware MUX |
| I2C 傳輸失敗率目標 | < 0.01% | < 0.1% | < 0.1% | < 0.01% |
針對「感測器通訊介面」主題的完整 bring-up 步驟清單: