I2C/SCCB、register
| 項目 | 值 |
|---|---|
| 7-bit 位址 | 0x3C(寫)/ 0x3D(讀) |
| register 位址寬度 | 16-bit(高 byte 在前) |
| page 切換 | 0xFD/0xFE 或 0x3008[6] |
sudo i2cdetect -y 3
# 預期在 0x3c 出現 "3c"sudo i2cget -y 3 0x3c 0x300a # 預期 0x56 sudo i2cget -y 3 0x3c 0x300b # 預期 0x40
# 寫 register 0x3500 = 0x00(曝光高位) sudo i2ctransfer -y 3 w3@0x3c 0x35 0x00 0x00 # 回讀 sudo i2ctransfer -y 3 w2@0x3c 0x35 0x00 r1
| Register | 功能 | 用途 |
|---|---|---|
| 0x300A/0x300B | 感測器 ID | 驗證 |
| 0x3035/0x3036 | PLL 分頻 | 單元 6 |
| 0x3500–0x3503 | 曝光時間 | 單元 10 |
| 0x350A–0x350B | 類比增益 | 單元 10 |
| 0x4740–0x4742 | MIPI 介面 | 單元 6 |
OV 感測器用 16-bit register 位址(big-endian):
sudo i2ctransfer -y 3 w2@0x3c 0x30 0x0a r1 # 預期 OV5640 ID 高位OV5640 的介面原始規格叫 SCCB(Serial Camera Control Bus),本質上是 OmniVision 對 I2C 的變體。實務上兩者相容,但有三個差異要記住:
| 項目 | SCCB(OV) | 標準 I2C |
|---|---|---|
| 起始/停止 | 有(SCCB 也認 S/P) | S/P 同位元 |
| 重複起始 | 傳統 SCCB 不建議 | 支援(combined transfer) |
| 位址寬度 | 可 8-bit/16-bit register | 由裝置決定 |
實務上 i2ctransfer 的 w3@0x3c 就是「寫 3 byte」:前 2 byte 是 16-bit register 位址(big-endian),第 3 byte 是資料。回讀用 w2@0x3c ... r1:先送位址、再重複起始讀 1 byte。
0xFF(全 1)幾乎總是「沒有裝置回應」——不是電源問題就是位址/上拉問題。sudo i2cdetect -y 3 # 找到 0x3c 表示 OV5640 在位sudo i2cget -y 3 0x3c 0x300a # → 0x56 sudo i2cget -y 3 0x3c 0x300b # → 0x40
# 寫曝光低位 0x3503 = 0x03(open aperture,曝光的位元展開) sudo i2ctransfer -y 3 w3@0x3c 0x35 0x03 0x03 # 回讀確認 sudo i2ctransfer -y 3 w2@0x3c 0x35 0x03 r1 # 應回 0x03
# OV5640 有些 register 在 page 1:先寫 0x3108 = 0x01
sudo i2ctransfer -y 3 w3@0x3c 0x31 0x08 0x01| 症狀 | 讀回值 | 方向 |
|---|---|---|
i2cdetect 沒看到 0x3c | — | 檢查電源、reset、位址選擇腳、bus 編號 |
讀 ID 全 0xFF | 0xFF | 無上拉 / 裝置沒通電 / 位址錯 |
讀 ID 全 0x00 | 0x00 | 裝置在 reset 狀態或電源不足 |
| 讀回值每次都不同 | 亂跳 | bus 衝突、pull-up 太弱、時脈過快 |
-y N(相機可能不在 bus 3,先 i2cdetect -l);② 以為位址是 0x3c 就永遠對——OV5640 可被 SCCB_ID 腳切到 0x21;③ register 位址搞混 8/16-bit,導致「寫到錯的 register」。ov5640 DT node 的 reg = <0x3c>,確認與實際相符。probe_ov5640.sh,自動掃描、讀 ID、印出「OK/失敗」。場景:Orange Pi 5 Plus 接 OV5640,I2C 掃描在 bus 3 看到 0x3c 但讀 ID 回 0xFF,懷疑 bus 衝突。
sudo i2cdetect -y 1 # RK3588 的 bus 1 通常掛 CSI0 sudo i2cdetect -y 3 # bus 3 也可能掛 CSI cat /sys/kernel/debug/gpio | grep -i i2c # 確認 I2C GPIO 腳位
# idle 時 SDA/SCL 都應為高電位(>2.8V for 3.3V I2C) # 若 SDA 被拉低 → 可能有裝置卡住 bus
# 發送 9 個 SCL clock 讓卡住的 SDA 釋放 sudo i2cset -y 3 0x3c 0x00 0x00 # 若回 ERR → bus 被鎖
OV 感測器的 SCCB 雖然與 I2C 相容,但有一個電氣層面的關鍵差異:
| 項目 | 標準 I2C | OV SCCB |
|---|---|---|
| SDA hold time | 依速率(100/400kHz) | 感測器端可能更短 |
| Clock stretching | 支援(裝置可暫停 SCL) | OV 感測器多不支援 |
| ACK 檢查 | 每個 byte 後 ACK | 有時回 NACK 但仍可讀寫(NDA register) |
容易忽略的邊界案例:當你用 i2ctransfer 寫入 NDA 感測器的 protected register 時,感測器可能回 NACK 但資料實際已寫入。這會讓除錯工具誤判「寫入失敗」——實際上需要用回讀驗證而非依賴 ACK。
i2c_transfer 的 return value 可能包含 ACK 狀態——如果你的驅動把 NACK 當 error,可能導致 probe 失敗但硬體其實正常。| 症狀 | 可能原因 | 解決方案 |
|---|---|---|
i2cdetect 掃到 0x3c 但讀 ID 回 0xFF | I2C bus pull-up 太弱或 bus 速率過高 | 降低 I2C 速率到 100kHz;確認 SDA/SCL pull-up 到 3.3V |
i2ctransfer 回 EREMOTEIO | I2C 控制器硬體錯誤或 DMA 衝突 | 重設 I2C 控制器(i2c_adapter unbind/bind) |
| 讀 ID 正常但寫 register 無效 | RESET 未釋放就寫 register | 確認 reset GPIO 已 high(active-low 時);等 t_PW_UP 後再寫 |
| OV5640 位址跳到 0x21 | SCCB_ID 腳被拉高 | 查模組 PCB 的 SCCB_ID 腳位;改 DT reg 或接線 |
| I2C 讀寫速度明顯變慢 | bus 上有其他裝置拉低 clock | 用 i2cdetect 掃全部位址確認只有一顆感測器 |
i2ctransfer 寫入 OV5640 的 0x3500(曝光高位),再讀回驗證。故意寫入 0xFF,觀察感測器的反應(是否 NACK、是否保留舊值)。probe_ov5640.sh,自動判斷 I2C bus 編號(掃全部 bus),讀 ID,並根據 ID 判斷感測器型號,印出「OK/失敗/未知感測器」。把單元 3 的 I2C/SCCB 知識做成一個可交付的自動化專案:自動掃描 bus、讀 ID、備份整組關鍵 register。這是 bring-up 與除錯時「快速建立現場快照」的標準工具。
#!/bin/bash
for bus in $(ls /dev/i2c-* | sed 's/.*i2c-//'); do
for addr in 0x21 0x36 0x3c; do
if sudo i2cget -y $bus $addr 0x300a 2>/dev/null; then
echo "找到候選: bus=$bus addr=$addr"; fi
done
done#!/bin/bash
bus=3; addr=0x3c
for reg in 0x300a 0x300b 0x3035 0x3036 0x3500 0x3501 0x3502 0x3503 \
0x350a 0x350b 0x4740 0x4741 0x4742 0x5180; do
h=${reg:2:2}; l=${reg:4:2}
val=$(sudo i2ctransfer -y $bus w2@$addr 0x$h 0x$l r1)
echo "$reg = $val"
done | tee ov5640_register_backup.txtdiff ov5640_register_backup.txt ov5640_register_backup_new.txt
# 無輸出 = register 狀態一致;有輸出 = 找出哪個 register 被改sensor_snapshot.sh,任何時刻 30 秒內能產出「bus/位址/關鍵 register 快照」,並能與上次快照比對差異。這是跨 sessions 除錯最重要的資產。| 步驟 | 指令 | 通過判據 |
|---|---|---|
| 1. 掃描 bus | sudo i2cdetect -y <bus> | 0x3c(或 0x21)出現 |
| 2. 讀 ID | sudo i2cget -y <bus> 0x3c 0x300a | 回 0x56(OV5640) |
| 3. 寫後回讀 | i2ctransfer w3@0x3c 0x35 0x03 0x03 後 w2@0x3c 0x35 0x03 r1 | 回 0x03 |
| 4. 時序驗證 | 連續讀 100 次 ID,記錄失敗次數 | 0 失敗;偶發失敗 → 檢查 pull-up/速率 |
| 5. 速率測試 | 重複寫讀並計時 | 記錄每筆 transaction 時間供回歸比較 |
| 面向 | Orange Pi | RPi5 | Orin Nano | Thor |
|---|---|---|---|---|
| OV5640 I2C bus | bus 3 | bus 22 | bus 0 | bus 0 |
| I2C 位址 | 0x3c | 0x3c | 0x36 | 0x36 |
| 預設速率 | 100 kHz | 100 kHz | 400 kHz | 400 kHz |
| 控制 API | V4L2 subdev controls | libcamera | tegracam / Argus | Holoscan / GXF |
| 低層工具 | i2c-tools(全支援) | i2c-tools | i2c-tools(需權限) | i2c-tools(需權限) |
i2cdetect 掃實際位址。- [ ] 我能寫一支 sensor_snapshot.sh 自動備份 register 並比對差異。 - [ ] 我能從 i2cget 回 0xFF 判讀出「無裝置回應」的含義。 - [ ] 我已驗證本平台 OV5640 的實際 bus 與位址。 - [ ] 我能區分 SCCB 與標準 I2C 的電氣差異。 - [ ] 我知道為什麼 Orin/Thor 用 0x36 而非 0x3c。 - [ ] 我已完成一次「寫→回讀」驗證並記錄結果。
I2C 的「讀→改→寫→驗證」是感測器驅動工程師的日常。以下是完整的位元級序列:
| 步驟 | 暫存器/位址 | 位元欄位 | 操作 | 預期值 |
|---|---|---|---|---|
| 1. 掃描 I2C | bus 3 全位址 | — | i2cdetect -y 3 | 0x3c 出現 |
| 2. 讀 ID high | 0x300A | [7:0] | i2cget -y 3 0x3c 0x300a | 0x56 |
| 3. 讀 ID low | 0x300B | [7:0] | i2cget -y 3 0x3c 0x300b | 0x40 |
| 4. 寫 SCCB mode | 0x3040 | [7:0] | i2cset -y 3 0x3c 0x3040 0x00 | 標準 I2C |
| 5. 回讀驗證 | 0x3040 | [7:0] | i2cget -y 3 0x3c 0x3040 | 0x00 |
| 6. 複製 register dump | 所有 register | — | sensor_snapshot.sh | 完整 dump 檔 |
決策樹 A:i2cdetect 掃不到 0x3c
i2cdetect -y 3 全空? ├─ 是 → I2C bus 問題 │ ├─ 檢查 /sys/bus/i2c/devices/ → bus 存在嗎? │ │ ├─ 不存在 → DT 未啟用 I2C controller │ │ └─ 存在 → 檢查 pinctrl(SCL/SDA pin) │ └─ 嘗試 i2cdetect -y 0/1/2 → 位址可能掛在其他 bus └─ 部分出現 → 有其他裝置但感測器不在 ├─ 感測器 power-off → 驗證 AVDD/DVDD/DVDD18 └─ 感測器 reset 狀態 → reset GPIO 極性
決策樹 B:0xFF / 0x00 回應
i2cget 回什麼?
├─ 0xFF → 無 ACK
│ ├─ 位址錯誤 → 嘗試 0x3c/0x36/0x21/0x10
│ ├─ power 異常 → 量測 AVDD (2.8V) / DVDD (1.5V)
│ └─ 時序不足 → 加 reset 後 delay 100ms
├─ 0x00 → reset 未釋放
│ └─ 檢查 reset GPIO: cat /sys/kernel/debug/gpio
└─ 正常值(非 0x00/0xFF)→ I2C 通,繼續測試 register| 步驟 | 指令 | 預期輸出 | 判讀標準 |
|---|---|---|---|
| 1. 確認 I2C bus | i2cdetect -l | 列出 bus 0~N | 知道感測器掛哪 bus |
| 2. 掃描位址 | sudo i2cdetect -y 3 | 0x3c 出現 | I2C 可達 |
| 3. 讀 ID | sudo i2cget -y 3 0x3c 0x300a | 0x56 | OV5640 正常 |
| 4. 寫入測試 | sudo i2cset -y 3 0x3c 0x3040 0x00 | 無錯誤 | write 成功 |
| 5. 回讀驗證 | sudo i2cget -y 3 0x3c 0x3040 | 0x00 | read-modify-write 正確 |
| 6. 備份 register | sensor_snapshot.sh > backup.txt | 完整 dump | 用於回歸比對 |
| 面向 | Orange Pi | RPi5 | Orin Nano | Thor | 推薦 |
|---|---|---|---|---|---|
| I2C bus | bus 3(OV5640) | bus 10/11 | I2C bus 0/1 | I2C bus 0/1 | 先 i2cdetect |
| I2C 位址 | 0x3c | 0x3c | 0x36 | 0x36 | 依模組 strap |
| 預設速率 | 100 kHz | 100 kHz | 400 kHz | 400 kHz | SCCB 用 100kHz |
| i2c-tools | 全支援 | 全支援 | 需 sudo | 需 sudo | Orange Pi 最自由 |
| 控制 API | V4L2 subdev | libcamera | tegracam | Holoscan | 底層→V4L2 |
| 低層工具 | i2c-tools | i2c-tools | i2c-tools(受限) | i2c-tools(受限) | Orange Pi 最佳 |
- [ ] 確認感測器的 I2C bus(用 i2cdetect -l) - [ ] 掃描實際 I2C 位址(0x3c / 0x36) - [ ] 讀取 0x300A + 0x300B 確認完整 ID - [ ] 寫入一筆 register 並回讀驗證 - [ ] 產出 register dump 備份檔 - [ ] 確認 I2C 預設速率(100kHz vs 400kHz) - [ ] 測試 400kHz 快速模式是否穩定 - [ ] 產出「I2C 通訊能力表」(bus/位址/速率/穩定性) - [ ] 確認 SCCB 與 I2C 的電氣差異 - [ ] 驗證 Orin/Thor 用 0x36 的 SCCB_ID strap