單元 3 · 感測器通訊介面

I2C 位元級、register 讀寫、page 機制

3.1 I2C 協定位元級回顧

I2C 兩線:SDA(資料)+ SCL(時脈),開集極 + 上拉電阻(RPi 通常 1.8k–4.7k 到 3.3V/1.8V)。所有傳輸以「位元」進行:

事件電氣條件備註
STARTSCL=H 時 SDA 由 H→L
STOPSCL=H 時 SDA 由 L→H
ACK接收方每 byte 後把 SDA 拉低 1 bit
NACK接收方不拉低傳送方應中止
資料有效SDA 在 SCL=H 期間必須穩定在 SCL=L 期間改變

OV5647 寫入序列(16-bit register 位址,big-endian):

START0x36<<1|0(寫位元)0x30(位址高)0x0A(位址低)dataSTOP
位元組序陷阱:OV 感測器用高 byte 在前(big-endian)的 16-bit register 位址。讀 register 0x300A → 先送 0x30 再送 0x0A。用錯序 = 讀到「錯的 register」且無從察覺。

3.2 實作一:掃描匯流排(含預期輸出)

RPi5 的 Camera 0 對應 i2c-22(Camera 1 對應 i2c-10)。

i2cdetect -y 22
$ sudo i2cdetect -y 22
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
30:                   36                        --

看到 0x36 → 感測器在 I2C 上回應。全空 → 電源 / clock / reset / 位址 / 匯流排問題(決策樹見單元 6)。

3.3 實作二:讀感測器 ID(bring-up 第一驗證)

讀 0x300A / 0x300B
$ sudo i2cget -y 22 0x36 0x300a
0x56
$ sudo i2cget -y 22 0x36 0x300b
0x47
# 組合 0x5647 = OV5647 ✓
Register意義
0x300A0x56型號高位(ASCII 'V')
0x300B0x47型號低位(5647)
讀不到 0x5647?依序檢查:電源 → XCLK → RESET 釋放 → I2C 位址(7-bit vs 8-bit 轉換)→ 匯流排編號。完整決策樹在單元 6。

3.4 實作三:i2ctransfer 讀寫(16-bit 位址)

寫 register 0x3034 = 0x1E 並回讀
# w3 = 寫 3 byte:位址高、位址低、資料
$ sudo i2ctransfer -y 22 w3@0x36 0x30 0x34 0x1e
# 回讀:寫 2 byte 位址後讀 1 byte
$ sudo i2ctransfer -y 22 w2@0x36 0x30 0x34 r1
0x1e
參數解釋
w3@0x36 0x30 0x34 0x1e對 0x36 寫 3 byte(0x30 0x34 0x1e)
w2@0x36 0x30 0x34 r1寫 2 byte 位址後讀 1 byte

i2cget 對 8-bit 位址感測器方便;OV 用 16-bit 位址,習慣用 i2ctransfer

3.5 OV5647 的 page/bank 機制(易踩雷)

OV5647 部分 register 需先「切 bank」才能存取:

Register角色
0x3000sccb_bank 控制
0x3008[6]sccb_slave_id_en
0x3100sccb_slave_id(模式切換)
陷阱:OV 不同系列用不同切換機制——OV5640 用 0xFD/0xFE 切 page,OV5647 以 0x3000 為主。若把 OV5640 的習慣套到 OV5647,會寫到「錯的 bank」而不自知。務必以該顆感測器 datasheet 的 register table 為準。

3.6 關鍵 register 群總表(後續單元引用)

Register功能對應單元
0x300A/B感測器 ID3 / 6
0x3034–0x3037PLL / clock 分頻6
0x3100–0x3103時序控制6
0x3500–0x3503曝光時間(AEC)10
0x350A–0x350B類比增益(AGC)10
0x3800–0x3814裁切 / 解析度(timing)8
0x3A00–0x3A03banding filter(50/60Hz)10
0x4740–0x4742MIPI 介面6

3.7 安全準則

正式流程走驅動,手動只除錯:libcamera / 感測器驅動會維護 register 的一致性(例如曝光與幀率關係)。手動 i2ctransfer 適合驗證與理解;寫壞 register 可重上電回復。切勿在 streaming 進行中亂改 register。

3.8 深入:I2C 時序與上拉電阻

I2C 可靠度與上拉電阻、匯流排電容高度相關:

參數典型影響
上拉電阻1.8k–4.7k太小→電流大;太大→上升緣慢
匯流排電容≤ 400 pF線過長→訊號變形
速度400 kHz(Fast)OV 支援;不穩可降 100 kHz
「讀不到 0x36」的另一種可能:上拉電阻缺失或過大 → SDA 無法拉高 → 所有讀寫都回 0xFF。用示波器看 SCL/SDA 波形可一錘定音。

3.9 Worked Example:完整的 I2C 讀取時序

S0x6C(0x36<<1|0,寫)0x300x0ASr(重複起始)0x6D(讀)0x56NACK+STOP

i2ctransfer -y 22 w2@0x36 0x30 0x0a r1 就對應「寫 2 byte 位址後讀 1 byte」這整段時序。看懂這個,你就懂所有 I2C 感測器。

3.10 練習:寫一支 I2C 讀寫小工具

  1. i2ctransfer 讀回 0x300A/0x300B。
  2. 改讀 0x3500(曝光高位),記錄值。
  3. 練習「寫後回讀」驗證。
看完這單元你應該能說出:
  • I2C 位元級(START/ACK/16-bit 位址序)。
  • i2cdetect / i2cget 讀 OV5647 ID。
  • i2ctransfer 讀寫與回讀驗證。
  • OV5647 page/bank 機制與關鍵 register 群。
  • 安全準則。

進階真實情境 Worked Example:用 i2ctransfer 追蹤 OV5647 streaming 時的曝光暫存器

場景:OV5647 正在串流,你想即時觀察 AE 演算法如何調整曝光暫存器。需要在不中斷串流的情況下,每秒讀取一次曝光值。

即時監控曝光 register
#!/bin/bash
# 持續監控 OV5647 曝光 register(不中斷 streaming)
echo "timestamp,exp_hi,exp_mid,exp_lo,total_lines"
for i in $(seq 1 60); do
  TS=$(date +%s.%N)
  # 讀 0x3501(高)、0x3502(中)、0x3503(低)
  EH=$(sudo i2ctransfer -y 22 w2@0x36 0x35 0x01 r1 2>/dev/null | awk '{print $1}')
  EM=$(sudo i2ctransfer -y 22 w2@0x36 0x35 0x02 r1 2>/dev/null | awk '{print $1}')
  EL=$(sudo i2ctransfer -y 22 w2@0x36 0x35 0x03 r1 2>/dev/null | awk '{print $1}')
  # 組合成曝光行數(16-bit,高 byte 在前)
  TOTAL=$(( (16#${EH#0x} << 8) | (16#${EM#0x}) ))
  echo "$TS,$EH,$EM,$EL,$TOTAL"
  sleep 1
done > exposure_log.csv
為什麼選這條路徑:用 i2ctransfer 而非 libcamera controls 是因為你想看到感測器暫存器的實際值,而非 libcamera 的 µs 抽象。這能揭露「libcamera 設的值」與「感測器實際收到的值」之間的差異——例如 driver 的 gain map 導致非線性映射。注意:I2C 讀取會短暫佔用匯流排,若太頻繁可能影響 streaming,建議 ≤5Hz。

深入原理擴充:I2C 匯流排的电气限制與 RPi5 的多匯流排架構

RPi5 有兩條 Camera I2C 匯流排(i2c-22 對應 Camera 0、i2c-10 對應 Camera 1),各自獨立。但有幾個容易忽略的电气限制:

大家以為沒問題但其實是陷阱:很多人以為「i2cdetect 掃到 0x36 就代表 I2C 完全正常」,但 i2cdetect 是在靜止狀態下掃描的。當感測器 streaming 時,I2C 匯流排可能因 HS 訊號耦合而產生雜訊——症状是「i2c 讀值偶爾跳動」。這在排線品質差時尤其明顯,需要用示波器在 streaming 時觀察 SDA 波形。

診斷式疑難排解表

症狀可能原因解決方案
i2cdetect 掃到 0x36 但 i2cget 讀回 0xFF感測器 RESET 未釋放,I2C 雖通但暫存器不可存取確認 DT overlay 的 reset-gpios 正確;用 GPIO 工具手動 toggle reset 腳位
i2cget 讀值偶爾跳動(±1)匯流排雜訊或上拉不足導致上升緣緩慢降低 I2C 速度到 100kHz(i2cset -y 22 0x00 0x04 設控制暫存器);檢查上拉電阻
兩顆感測器都掃不到I2C 匯流排被其他 device 佔用或 SDA/SCL 接反用 GPIO 18/19(i2c-22)和 GPIO 0/1(i2c-10)的 pin mux 確認;排除其他 I2C 設備
i2ctransfer 寫入後回讀值不同暫存器是 write-only 或需要 delay 才生效查 datasheet 確認暫存器屬性;部分 OV暫存器需等待 VBlank 才更新
probe 成功但讀回的 ID 不是 0x5647I2C 位址 0x36 上接的是另一顆感測器讀完整的 0x300A/0x300B 確認型號;查板子原理圖確認感測器型號

進階挑戰題

  1. 寫一個 Shell 腳本,透過 I2C 動態修改 OV5647 的 PLL 暫存器(0x3034-0x3037),觀察 pixel clock 變化對幀率的影響。注意:修改 PLL 可能導致串流暫時中斷。
  2. RPi5 的 i2c-22 與 i2c-10 各自的最大可靠傳輸速率是多少?設計一個腳本逐步提升 I2C 速度,找出兩條匯流排的速率上限。
  3. 若要在同一條 I2C 匯流排上同時控制 OV5647(0x36)和一顆 EEPROM(假設 0x50),會遇到什麼問題?如何解決?

延伸閱讀

專案級端到端 Worked Example:感測器 I2C 探針工具箱專案

場景:你將長期處理多顆 OV 感測器。與其每次打指令,不如建立一套可重用的 I2C 探針腳本:讀寫、dump、監控、回讀驗證一次到位。本專案整合單元 3(I2C 位元級)、6(bring-up)知識。

建立 sensor_probe.sh 工具箱
#!/bin/bash
# 用法:. ./sensor_probe.sh 然後用函式
BUS=22; ADDR=0x36
sensor_read()  {  # $1 = 16-bit register
  sudo i2ctransfer -y $BUS w2@$ADDR 0x${1:0:2} 0x${1:2:2} r1
}
sensor_write() {  # $1 = register, $2 = value
  sudo i2ctransfer -y $BUS w3@$ADDR 0x${1:0:2} 0x${1:2:2} $2
}
sensor_id() {
  echo "ID = $(sensor_read 300A)$(sensor_read 300B)"  # 預期 0x5647
}
sensor_dump() { # 關鍵 register 群
  for r in 300A 300B 3034 3035 3036 3037 3501 3502 3503 350A 350B; do
    printf "%s: %s\n" "$r" "$(sensor_read $r)"
  done
}
sensor_monitor() { # 串流中監控曝光,每秒一筆
  echo "t,exp_hi,exp_lo,gain"
  for i in $(seq 1 30); do
    EH=$(sensor_read 3501); EL=$(sensor_read 3502)
    GA=$(sensor_read 350A)
    echo "$(date +%s),$EH,$EL,$GA"
    sleep 1
  done
}
驗證工具箱
. ./sensor_probe.sh
sensor_id          # 預期:ID = 0x5647
sensor_read 3500   # 讀曝光高位
sensor_write 3034 0x18; sensor_read 3034   # 寫後回讀:預期 0x18
sensor_dump        # 完整快照,記錄下來做 bring-up log
專案規模與跨單元整合:這套工具是「帶得走」的資產:sensor_dump 對應單元 6 的 register 快照、sensor_monitor 對應單元 3 的即時曝光監控、sensor_id 對應單元 6 的 bring-up 第一步。往後每顆新感測器只需改 BUS/ADDR,其餘全部重用。安全守則:只在非串流時寫 register。

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

  1. sudo i2cdetect -y 22 確認 0x36 回應。
  2. 讀 0x300A/0x300B 確認 ID = 0x5647。
  3. 執行「寫後回讀」:寫 0x3034=0x18,回讀確認一致。
  4. 在 streaming 時跑 sensor_monitor,確認讀值穩定(±1 內)。
  5. 檢查 dmesg 無 I2C timeout 錯誤。
  6. 記錄 bus 編號(Camera 0 → i2c-22,Camera 1 → i2c-10)。
判讀指標:回讀不一致 = 暫存器 write-only 或需延遲;streaming 時讀值跳動 = 匯流排雜訊/排線耦合;全回 0xFF = 上拉或 RESET 問題(回單元 6 決策樹)。

平台間對照:I2C 存取工具

面向RPi5Orange PiOrin NanoThor
Camera I2C busi2c-22 / i2c-10i2c-0~3(依 BSP)i2c-0 / i2c-30(依感測器槽)HSB 乙太感測器(非本地 I2C)
常用工具i2ctransfer/i2cdetecti2ctransfer(需裝)i2ctransfer / busyboxi2ctransfer(透過 carrier)
位址轉換7-bit(0x36)同(0x36/0x3c)同(0x36)同(register 相同)
上拉 1.8V 別亂接 3.3Vbus 編號常因 BSP 而變需先載入 NVIDIA driver感測器連在乙太上,工具鏈不同

互動式檢核清單


第 3 輪深度加深

① Register 位元級完整工作流:SCCB 設定 OV5647 曝光時間

步驟暫存器寫入值位元級說明
1. 設定 AEC 高位0x35000x00bit[3:0] = 曝光 bit[19:16]
2. 設定 AEC 中位0x35010x64bit[7:0] = 曝光 bit[15:8],行數 = 0x064 = 100 行
3. 設定 AEC 低位0x35020x00bit[7:4] = 曝光 bit[11:8] 的 4-bit 小數
4. 設定 AEC 控制0x35030x03bit[0]=1 manual AEC, bit[1]=1 manual AGC
SMBus vs SCCB:SCCB 是 OmniVision 私有協議,兼容 I2C 但多了三階段 write(phase2 = register, phase3 = data)。i2cset 可直接替代 SCCB。

② 多層疑難排解決策樹

決策樹 A:I2C 寫入成功但感測器無反應

I2C 寫入成功但感測器無反應
├─ 檢查 A:確認是 SCCB 還是標準 I2C
│  ├─ SCCB 3-phase → i2cset 自動處理
│  └─ 非標準 → 需自訂 i2c driver
├─ 檢查 B:PLL 設定是否正確(0x3034-0x3037)
│  ├─ PLL 未鎖定 → pixel clock 為 0 → 無輸出
│  └─ PLL 鎖定 → 繼續
├─ 檢查 C:data lane 是否啟用(0x3000-0x3002)
│  ├─ 未啟用 → 寫入 0x00 到 0x3000(stream on)
│  └─ 已啟用 → 繼續
└─ 檢查 D:MCLK 是否送達感測器
   ├─ 無 MCLK → 外部時鐘源問題
   └─ 有 MCLK → 感測器內部問題,嘗試 reset sequence

③ 量測驗證完整 SOP:I2C/SCCB 通訊品質

  1. 工具:邏輯分析儀(Saleae / Logic Pro 16)+ I2C decoder
  2. 接線:SCL/SDA 探棒接至 CSI connector 對應腳位
  3. 觸發:設定 SCL falling edge 觸發
  4. 量測:
    a. SCL 頻率:標準 100kHz,快速 400kHz
    b. SDA rise time:預期 <1000ns (100kHz) / <300ns (400kHz)
    c. ACK/NACK 比率:10 次 transaction 中 NACK 次數
    d. Start/Stop 條件完整性
  5. 判讀:NACK >1/10 = 通訊不穩;rise time 超標 = SDA pull-up 太弱
  6. 常見偏差:排線過長 → rise time 增加;pull-up 電阻太大 → 速率受限

④ 四平台終極對照

面向RPi5Orange PiOrin NanoThor推薦
I2C 控制器Broadcom BCM2712, 400kHzAllwinner, 400kHzTegra, 400kHzTegra, 1MHz+Thor 最快
SCCB 支援i2cset 直接支援i2csetNV 驅動內建NV 驅動內建RPi5 最直觀
I2C 多主機不原生支援有限支援支援Orin+ 最佳
debug 便利性i2c-tools 直接用i2c-toolsNV 自有工具NV + HoloscanRPi5 最易 debug

⑤ 完整 Bring-up 專案 Checklist