單元 3 · 感測器通訊介面

I2C/SCCB、register

OV5640 I2C 配置

項目
7-bit 位址0x3C(寫)/ 0x3D(讀)
register 位址寬度16-bit(高 byte 在前)
page 切換0xFD/0xFE 或 0x3008[6]

動手一:掃描匯流排

i2cdetect(匯流排依板卡調整)
sudo i2cdetect -y 3
# 預期在 0x3c 出現 "3c"

動手二:讀感測器 ID

讀 0x300A / 0x300B
sudo i2cget -y 3 0x3c 0x300a   # 預期 0x56
sudo i2cget -y 3 0x3c 0x300b   # 預期 0x40
為什麼讀 ID:最快驗證「感測器活著 + I2C 對」。讀不到 → 電源/reset/位址問題(單元 6)。

動手三:register 讀寫(16-bit 位址)

i2ctransfer
# 寫 register 0x3500 = 0x00(曝光高位)
sudo i2ctransfer -y 3 w3@0x3c 0x35 0x00 0x00
# 回讀
sudo i2ctransfer -y 3 w2@0x3c 0x35 0x00 r1

OV5640 關鍵 register 群

Register功能用途
0x300A/0x300B感測器 ID驗證
0x3035/0x3036PLL 分頻單元 6
0x3500–0x3503曝光時間單元 10
0x350A–0x350B類比增益單元 10
0x4740–0x4742MIPI 介面單元 6
警告:正式流程走感測器驅動;手動 i2ctransfer 只做除錯理解。寫壞可重上電回復。

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

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

S0x0x3c<<1|0位址高位址低資料/讀P
讀感測器 ID
sudo i2ctransfer -y 3 w2@0x3c 0x30 0x0a r1   # 預期 OV5640 ID 高位
排查:讀回全 0xFF → 上拉/位址錯;讀回全 0x00 → 上電/reset 問題。用 i2ctransfer 讀寫並回讀驗證。

3.10 深入原理:SCCB vs I2C 與傳輸時序

OV5640 的介面原始規格叫 SCCB(Serial Camera Control Bus),本質上是 OmniVision 對 I2C 的變體。實務上兩者相容,但有三個差異要記住:

項目SCCB(OV)標準 I2C
起始/停止有(SCCB 也認 S/P)S/P 同位元
重複起始傳統 SCCB 不建議支援(combined transfer)
位址寬度可 8-bit/16-bit register由裝置決定
START0x78(寫)reg 高 bytereg 低 byte資料 byteSTOP

實務上 i2ctransferw3@0x3c 就是「寫 3 byte」:前 2 byte 是 16-bit register 位址(big-endian),第 3 byte 是資料。回讀用 w2@0x3c ... r1:先送位址、再重複起始讀 1 byte。

匯流排電氣細節:I2C 需要上拉電阻;SDA/SCL 在 idle 都是高電位。讀回 0xFF(全 1)幾乎總是「沒有裝置回應」——不是電源問題就是位址/上拉問題。

3.11 完整 Worked Example:從掃描到讀寫的完整流程

步驟 1|掃描找裝置
sudo i2cdetect -y 3    # 找到 0x3c 表示 OV5640 在位
步驟 2|讀 ID 驗證
sudo i2cget -y 3 0x3c 0x300a   # → 0x56
sudo i2cget -y 3 0x3c 0x300b   # → 0x40
步驟 3|寫一組 register 並回讀
# 寫曝光低位 0x3503 = 0x03(open aperture,曝光的位元展開)
sudo i2ctransfer -y 3 w3@0x3c 0x35 0x03 0x03
# 回讀確認
sudo i2ctransfer -y 3 w2@0x3c 0x35 0x03 r1   # 應回 0x03
步驟 4|頁面切換(高位 register 需切 page)
# OV5640 有些 register 在 page 1:先寫 0x3108 = 0x01
sudo i2ctransfer -y 3 w3@0x3c 0x31 0x08 0x01
紀律:手動 i2ctransfer 只做除錯/理解。正式流程一律走驅動——驅動會處理 page、時序與 init table。改壞 register 的最快復原方式是重上電,不必慌。

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

症狀讀回值方向
i2cdetect 沒看到 0x3c檢查電源、reset、位址選擇腳、bus 編號
讀 ID 全 0xFF0xFF無上拉 / 裝置沒通電 / 位址錯
讀 ID 全 0x000x00裝置在 reset 狀態或電源不足
讀回值每次都不同亂跳bus 衝突、pull-up 太弱、時脈過快
常見錯誤與陷阱:① 用了錯誤的 -y N(相機可能不在 bus 3,先 i2cdetect -l);② 以為位址是 0x3c 就永遠對——OV5640 可被 SCCB_ID 腳切到 0x21;③ register 位址搞混 8/16-bit,導致「寫到錯的 register」。

3.13 練習

  1. 從「掃描→讀 ID→寫回讀」完整跑一遍,記錄每步輸出。
  2. 故意把位址改錯一個 bit,觀察讀回值變化,寫下你的判讀。
  3. 查你板卡 BSP 中 ov5640 DT node 的 reg = <0x3c>,確認與實際相符。
  4. 寫一支 shell script:probe_ov5640.sh,自動掃描、讀 ID、印出「OK/失敗」。

3.14 回顧練習

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

3.15 進階真實情境 Worked Example:RK3588 I2C 多 bus 衝突排除

場景:Orange Pi 5 Plus 接 OV5640,I2C 掃描在 bus 3 看到 0x3c 但讀 ID 回 0xFF,懷疑 bus 衝突。

步驟 1|確認 bus 編號與 pull-up
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 腳位
步驟 2|量測 SDA/SCL 電位(需示波器)
# idle 時 SDA/SCL 都應為高電位(>2.8V for 3.3V I2C)
# 若 SDA 被拉低 → 可能有裝置卡住 bus
步驟 3|用 i2cset 嘗試 reset 卡住的裝置
# 發送 9 個 SCL clock 讓卡住的 SDA 釋放
sudo i2cset -y 3 0x3c 0x00 0x00   # 若回 ERR → bus 被鎖
設計決策:RK3588 的 I2C 控制器有 6 個 bus,但 DT 可能只啟用部分。若 OV5640 實際接在 bus 3 但 DT 指到 bus 1,probe 會失敗——這是最常見的「看似 I2C 問題但其實是 DT 錯誤」。

3.16 深入原理擴充:SCCB 與 I2C 的電氣差異

OV 感測器的 SCCB 雖然與 I2C 相容,但有一個電氣層面的關鍵差異:

項目標準 I2COV SCCB
SDA hold time依速率(100/400kHz)感測器端可能更短
Clock stretching支援(裝置可暫停 SCL)OV 感測器多不支援
ACK 檢查每個 byte 後 ACK有時回 NACK 但仍可讀寫(NDA register)

容易忽略的邊界案例:當你用 i2ctransfer 寫入 NDA 感測器的 protected register 時,感測器可能回 NACK 但資料實際已寫入。這會讓除錯工具誤判「寫入失敗」——實際上需要用回讀驗證而非依賴 ACK。

實務提醒:在 Orange Pi 的 I2C 驅動中,i2c_transfer 的 return value 可能包含 ACK 狀態——如果你的驅動把 NACK 當 error,可能導致 probe 失敗但硬體其實正常。

3.17 診斷式疑難排解表

症狀可能原因解決方案
i2cdetect 掃到 0x3c 但讀 ID 回 0xFFI2C bus pull-up 太弱或 bus 速率過高降低 I2C 速率到 100kHz;確認 SDA/SCL pull-up 到 3.3V
i2ctransfer 回 EREMOTEIOI2C 控制器硬體錯誤或 DMA 衝突重設 I2C 控制器(i2c_adapter unbind/bind)
讀 ID 正常但寫 register 無效RESET 未釋放就寫 register確認 reset GPIO 已 high(active-low 時);等 t_PW_UP 後再寫
OV5640 位址跳到 0x21SCCB_ID 腳被拉高查模組 PCB 的 SCCB_ID 腳位;改 DT reg 或接線
I2C 讀寫速度明顯變慢bus 上有其他裝置拉低 clocki2cdetect 掃全部位址確認只有一顆感測器

3.18 進階挑戰題

  1. i2ctransfer 寫入 OV5640 的 0x3500(曝光高位),再讀回驗證。故意寫入 0xFF,觀察感測器的反應(是否 NACK、是否保留舊值)。
  2. 在 RK3588 上,同時開兩個 I2C terminal(一個讀 0x300A,一個讀 0x3500),測試 I2C 控制器的併發能力。記錄是否有 bus 衝突。
  3. 設計一支 probe_ov5640.sh,自動判斷 I2C bus 編號(掃全部 bus),讀 ID,並根據 ID 判斷感測器型號,印出「OK/失敗/未知感測器」。

3.19 專案級 Worked Example:I2C 自動化探測與 register 備份專案

把單元 3 的 I2C/SCCB 知識做成一個可交付的自動化專案:自動掃描 bus、讀 ID、備份整組關鍵 register。這是 bring-up 與除錯時「快速建立現場快照」的標準工具。

階段 1|自動找 bus 與位址
#!/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
階段 2|備份關鍵 register 群
#!/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.txt
階段 3|差異偵測(回歸用)
diff ov5640_register_backup.txt ov5640_register_backup_new.txt
# 無輸出 = register 狀態一致;有輸出 = 找出哪個 register 被改
專案完成標準:你有一支 sensor_snapshot.sh,任何時刻 30 秒內能產出「bus/位址/關鍵 register 快照」,並能與上次快照比對差異。這是跨 sessions 除錯最重要的資產。

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

步驟指令通過判據
1. 掃描 bussudo i2cdetect -y <bus>0x3c(或 0x21)出現
2. 讀 IDsudo i2cget -y <bus> 0x3c 0x300a回 0x56(OV5640)
3. 寫後回讀i2ctransfer w3@0x3c 0x35 0x03 0x03w2@0x3c 0x35 0x03 r1回 0x03
4. 時序驗證連續讀 100 次 ID,記錄失敗次數0 失敗;偶發失敗 → 檢查 pull-up/速率
5. 速率測試重複寫讀並計時記錄每筆 transaction 時間供回歸比較
SOP 紀律:手動 i2ctransfer 只用於驗證與備份,正式控制一律走 V4L2 controls 或驅動。備份檔是「改壞可還原」的保險,務必在動手前先做。

3.21 平台間對照:感測器通訊介面

面向Orange PiRPi5Orin NanoThor
OV5640 I2C busbus 3bus 22bus 0bus 0
I2C 位址0x3c0x3c0x360x36
預設速率100 kHz100 kHz400 kHz400 kHz
控制 APIV4L2 subdev controlslibcamerategracam / ArgusHoloscan / GXF
低層工具i2c-tools(全支援)i2c-toolsi2c-tools(需權限)i2c-tools(需權限)
跨平台要點:同樣是 OV5640,Orange Pi/RPi5 用 0x3c、Orin/Thor 用 0x36——不是感測器不同,是模組的 SCCB_ID strap 不同。跨平台移植第一步永遠是先 i2cdetect 掃實際位址。

3.22 互動式檢核清單:本單元進階驗收

checklist
- [ ] 我能寫一支 sensor_snapshot.sh 自動備份 register 並比對差異。
- [ ] 我能從 i2cget 回 0xFF 判讀出「無裝置回應」的含義。
- [ ] 我已驗證本平台 OV5640 的實際 bus 與位址。
- [ ] 我能區分 SCCB 與標準 I2C 的電氣差異。
- [ ] 我知道為什麼 Orin/Thor 用 0x36 而非 0x3c。
- [ ] 我已完成一次「寫→回讀」驗證並記錄結果。

3.23 Register 位元級完整工作流

I2C 的「讀→改→寫→驗證」是感測器驅動工程師的日常。以下是完整的位元級序列:

步驟暫存器/位址位元欄位操作預期值
1. 掃描 I2Cbus 3 全位址i2cdetect -y 30x3c 出現
2. 讀 ID high0x300A[7:0]i2cget -y 3 0x3c 0x300a0x56
3. 讀 ID low0x300B[7:0]i2cget -y 3 0x3c 0x300b0x40
4. 寫 SCCB mode0x3040[7:0]i2cset -y 3 0x3c 0x3040 0x00標準 I2C
5. 回讀驗證0x3040[7:0]i2cget -y 3 0x3c 0x30400x00
6. 複製 register dump所有 registersensor_snapshot.sh完整 dump 檔
位元級關鍵: I2C write 後必須回讀驗證。若回讀值 ≠ 寫入值,可能是:(1) 寫入時序太快(wait 10ms);(2) register 是 read-only;(3) SCCB 無 ACK(位址錯誤)。

3.24 多層疑難排解決策樹

決策樹 A:i2cdetect 掃不到 0x3c

決策樹 A
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 回應

決策樹 B
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

3.25 量測驗證完整 SOP

步驟指令預期輸出判讀標準
1. 確認 I2C busi2cdetect -l列出 bus 0~N知道感測器掛哪 bus
2. 掃描位址sudo i2cdetect -y 30x3c 出現I2C 可達
3. 讀 IDsudo i2cget -y 3 0x3c 0x300a0x56OV5640 正常
4. 寫入測試sudo i2cset -y 3 0x3c 0x3040 0x00無錯誤write 成功
5. 回讀驗證sudo i2cget -y 3 0x3c 0x30400x00read-modify-write 正確
6. 備份 registersensor_snapshot.sh > backup.txt完整 dump用於回歸比對

3.26 四平台終極對照

面向Orange PiRPi5Orin NanoThor推薦
I2C busbus 3(OV5640)bus 10/11I2C bus 0/1I2C bus 0/1先 i2cdetect
I2C 位址0x3c0x3c0x360x36依模組 strap
預設速率100 kHz100 kHz400 kHz400 kHzSCCB 用 100kHz
i2c-tools全支援全支援需 sudo需 sudoOrange Pi 最自由
控制 APIV4L2 subdevlibcamerategracamHoloscan底層→V4L2
低層工具i2c-toolsi2c-toolsi2c-tools(受限)i2c-tools(受限)Orange Pi 最佳

3.27 完整 Bring-up 專案 Checklist

checklist
- [ ] 確認感測器的 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
看完這單元你應該能說出:
  • OV5640 的 I2C 位址與 16-bit register。
  • i2cdetect / i2cget / i2ctransfer 實作。
  • 讀 0x300A/0x300B 驗證。
  • OV5640 關鍵 register 群。

延伸閱讀