【camera】分散点学习

目录

[方向一:Sensor 上电时序](#方向一:Sensor 上电时序)

[IMX989 的完整启动流程](#IMX989 的完整启动流程)

为什么顺序这么重要

[Sensor 硬件层面(你看不到的部分)](#Sensor 硬件层面(你看不到的部分))

[IMX989 的 Standby/Restart](#IMX989 的 Standby/Restart)

[方向二、ADDR_BYTE 和 DATA_BYTE 的含义](#方向二、ADDR_BYTE 和 DATA_BYTE 的含义)

方向三、噪声特性

[为什么 sensor 驱动工程师要懂噪声](#为什么 sensor 驱动工程师要懂噪声)

三种主要噪声

[1. 光子散粒噪声(Photon Shot Noise)](#1. 光子散粒噪声(Photon Shot Noise))

[2. 读出噪声(Read Noise)](#2. 读出噪声(Read Noise))

[3. 暗电流噪声(Dark Current Noise)](#3. 暗电流噪声(Dark Current Noise))

噪声和增益的关系

噪声特性曲线

[ISP 降噪如何利用噪声特性](#ISP 降噪如何利用噪声特性)

噪声标定怎么做

你可以做的验证实验

噪声知识总结

一句话总结


方向一:Sensor 上电时序

IMX989 的完整启动流程

从代码中提取的完整时序:

cs 复制代码
时间线 →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→

Step 1: SOC 端 MIPI 时钟使能
  OT_MIPI_ENABLE_MIPI_CLOCK
  ↓
Step 2: SOC 端 MIPI RX 复位
  OT_MIPI_RESET_MIPI
  ↓
Step 3: SOC 端配置 combo_dev_attr + ext_data_type
  ↓
Step 4: SOC 端 MIPI RX 解复位(准备接收数据)
  OT_MIPI_UNRESET_MIPI
  ↓
Step 5: Sensor 端上电(sample_comm_vi_start_sensor)
  │
  ├── 5a: 使能 Sensor 时钟(MCLK)
  │     OT_MIPI_ENABLE_SENSOR_CLOCK
  │
  ├── 5b: 复位 Sensor(拉低 RESET 引脚)
  │     OT_MIPI_RESET_SENSOR
  │
  └── 5c: 解复位 Sensor(拉高 RESET 引脚)
        OT_MIPI_UNRESET_SENSOR
  ↓
Step 6: ISP 框架调用 modules989_init()
  │
  ├── 6a: 初始化 I2C(open /dev/i2c-X)
  │
  ├── 6b: 写初始化序列(imx989_common_init + 模式特定序列)
  │     → 几百个寄存器写入
  │
  ├── 6c: 写运行时寄存器(CIT/GAIN/DGAIN/VMAX)
  │     modules989_default_reg_init()
  │
  ├── 6d: 等待 10ms
  │     delay_ms(10)
  │
  └── 6e: 启动 Sensor 出流
        modules989_restart() → 写 0x0100 = 0x01
  ↓
Step 7: MIPI 开始传输数据 → ISP 开始处理 → 画面出现

为什么顺序这么重要

cs 复制代码
如果先启动 Sensor 再配置 SOC 端:
  → Sensor 已经开始发 MIPI 数据了
  → SOC 端 MIPI RX 还没准备好
  → 数据丢失,前几帧花屏

如果 MCLK 还没稳定就复位解复位:
  → Sensor 内部 PLL 没锁上
  → 寄存器写入可能失败
  → Sensor 不响应 I2C

如果 0x0100 = 0x01 写太早(寄存器还没配完):
  → Sensor 用默认参数出流
  → 帧率/分辨率可能不对

Sensor 硬件层面(你看不到的部分)

cs 复制代码
PCB 上电时(硬件工程师负责):
  │
  ├── VDD_IO(I/O 供电)= 1.8V  ← 最先上
  ├── VDD_CORE(核心供电)= 1.1V
  ├── VDD_ANALOG(模拟供电)= 2.8V
  ├── AVDD(MIPI 供电)= 1.2V
  │
  └── 所有电源稳定后 → 等待 MCLK

软件接管后(上面的 Step 5 开始):
  │
  ├── 给 MCLK(通常 24MHz 或 37.125MHz)
  ├── Sensor 内部 PLL 锁定 → 产生内部时钟
  │     内部时钟 = MCLK × PLL 倍频
  │     比如 24MHz × 62.5 = 1500MHz(MIPI 速率)
  │
  ├── RESET 拉高 → Sensor 开始执行内部初始化
  │
  └── I2C 可用(通常 MCLK 稳定后 1~10ms)

IMX989 的 Standby/Restart

cs 复制代码
// 暂停出流(不关闭电源)
modules989_standby() → 写 0x0100 = 0x00  // STANDBY 模式

// 恢复出流
modules989_restart() → 写 0x0100 = 0x01  // 恢复出流

这是最轻量的"开关"方式,不需要重新写整个初始化序列。

方向二、ADDR_BYTE 和 DATA_BYTE 的含义

不同的 sensor 有不同的地址/数据宽度组合:

cs 复制代码
┌──────────┬──────────┬─────────────┬─────────────────────┐
│ Sensor   │ADDR_BYTE │ DATA_BYTE   │ 一次传输的字节数      │
├──────────┼──────────┼─────────────┼─────────────────────┤
│ IMX989   │    2     │     1       │ [AH][AL][D]         │
│ OV 系列   │    2     │     1       │ [AH][AL][D]         │
│ SC 系列   │    2     │     1       │ [AH][AL][D]         │
│ 某些老 sensor│  1    │     1       │ [A][D]              │
│ 某些 16bit │   2     │     2       │ [AH][AL][DH][DL]    │
└──────────┴──────────┴─────────────┴─────────────────────┘

IMX989:
  #define MODULES989_ADDR_BYTE  2  ← 寄存器地址是 16bit(2 字节)
  #define MODULES989_DATA_BYTE  1  ← 每个寄存器值是 8bit(1 字节)

如果配错了:

cs 复制代码
ADDR_BYTE 写成 1(应该是 2):
  → 只发了低字节地址
  → sensor 收到错误的寄存器地址
  → 写到了别的地方 → 可能花屏或死机

DATA_BYTE 写成 2(应该是 1):
  → 多发了一个字节
  → sensor 把第二个字节当成下一条命令的地址
  → 后续通信全部错乱

方向三、噪声特性

为什么 sensor 驱动工程师要懂噪声

你以为的画面:

理想:每个像素值 = 真实光照强度

实际的画面:

每个像素值 = 真实光照 + 各种噪声

噪声决定了:

· 画面干不干净

· ISP 降噪模块的参数怎么调

· 增益上限设多少合适


三种主要噪声

1. 光子散粒噪声(Photon Shot Noise)

物理本质:光子到达是随机事件(泊松分布)

即使光照完全均匀,每个像素收到的光子数也不一样:

期望收到 100 个光子

实际可能收到:93, 107, 98, 102, 95, 110...

噪声 = √信号 = √100 = 10

信噪比 SNR = 信号/噪声 = 100/10 = 10

复制代码

信号越大 → 绝对噪声越大 → 但信噪比越好:

弱光(100 个光子):噪声 = 10,SNR = 10 → 画面有噪点

强光(10000 个光子):噪声 = 100,SNR = 100 → 画面干净

这就是为什么暗光下噪点多的根本原因!

这个噪声无法消除------它是光的物理特性,不是 sensor 的问题。


2. 读出噪声(Read Noise)

来源:sensor 内部的电路噪声

像素 → 放大器 → ADC → 输出

每一步电路都会引入噪声:

· 热噪声(温度越高越大)

· 1/f 噪声(低频噪声)

· 量化噪声(ADC 精度有限)

即使完全没有光(盖上镜头盖),像素值也不是 0:

→ 而是在某个值附近随机抖动

用代码中的数据来看:

#define MODULES989_BLACK_LEVEL 1032 // OB(Optical Black)值

cs 复制代码
OB 值的含义:

  盖上镜头盖(完全没有光)时,像素值 ≈ 1032
  
  但每个像素不是精确的 1032:
  
  像素1: 1030
  像素2: 1035
  像素3: 1028
  像素4: 1034
  ...
  
  平均值 ≈ 1032(这就是 Black Level)
  标准差 ≈ 2~5(这就是读出噪声的大小)

ISP 的 BLC(Black Level Correction)模块就是减去这个值:

cs 复制代码
原始像素值 = 1032 + 信号 + 噪声
BLC 后     = 0 + 信号 + 噪声  ← 去掉了直流偏置

3. 暗电流噪声(Dark Current Noise)

cs 复制代码
来源:即使没有光,像素也会因为热效应产生电子

  温度每升高 6~7°C → 暗电流翻倍
  
  室温(25°C):暗电流很小,可忽略
  高温(60°C):暗电流显著增大,画面出现"热噪点"
  
  长时间曝光时更明显:
  · 曝光 1ms:暗电流几乎看不到
  · 曝光 1s:暗电流积累很多,画面有明显的亮点

噪声和增益的关系

cs 复制代码
增益放大信号的同时,也放大了噪声!

  原始信号:S = 100,噪声 N = 10(散粒)+ 3(读出)= 13
  SNR = 100/13 ≈ 7.7

  增益 2x 后:
  信号 = 200,噪声 = 26
  SNR = 200/26 ≈ 7.7  ← SNR 不变!

  但!如果是在暗处,原始信号只有 10:
  原始:S = 10,N = 3.16(散粒)+ 3(读出)= 6.16
  SNR = 10/6.16 ≈ 1.6  ← 已经很差了
  
  增益 16x 后:
  信号 = 160,噪声 = 98.6
  SNR = 160/98.6 ≈ 1.6  ← 依然很差
  
  → 画面看起来就是"一片噪点"

这就是为什么高增益 = 高噪点,而且降噪怎么都救不回来。


噪声特性曲线

每个 sensor 都有一条这样的特性曲线:

cs 复制代码
噪声
 ↑
 │                                              ╱
 │                                           ╱
 │                                        ╱
 │                                     ╱
 │                                  ╱
 │                              ╱╱
 │                          ╱╱
 │                      ╱╱
 │                  ╱╱
 │              ╱╱
 │         ╱╱╱╱        ← 噪声 = √(散粒² + 读出² + 暗电流²)
 │    ╱╱╱╱                 高增益区域噪声急剧上升
 │╱╱╱╱
 └──────────────────────────────────→ 增益(ISO)
  1x    2x    4x    8x   16x   32x   64x

IMX989 的三个模式在这条曲线上的位置不同:

cs 复制代码
线性模式(10bit):
  · 增益范围 1x ~ 64x
  · 高增益时噪声明显,但能用

DCG 12bit(HCG:LCG = 4:1):
  · HCG 路径天生灵敏度高 4 倍
  · 相当于"起点就高",同样场景需要的增益更小
  · 所以暗光下噪点更少
  · 有效动态范围 ≈ 线性 + 12dB

DCG 14bit(HCG:LCG = 16:1):
  · HCG 灵敏度高 16 倍
  · 暗光性能最好
  · 但 VMAX 更小(2776),最大曝光时间更短
  · 亮处可能过曝

ISP 降噪如何利用噪声特性

你的代码里有一个关键参数:

isp_def->noise_calibration = &g_cmos_noise_calibration;

cs 复制代码
Noise Calibration(噪声标定):

  告诉 ISP 在不同 ISO(增益)下,噪声有多大
  
  ┌──────────────────────────────────┐
  │ ISO 100: 噪声 = 2.0              │
  │ ISO 200: 噪声 = 2.8              │
  │ ISO 400: 噪声 = 4.0              │
  │ ISO 800: 噪声 = 5.7              │
  │ ISO 1600: 噪声 = 8.0             │
  │ ISO 3200: 噪声 = 11.3            │
  └──────────────────────────────────┘
  
  ISP 的 Bayer NR 和 3DNR 模块根据这个表决定:
  · 降噪力度多大
  · 降噪力度太小 → 噪点残留
  · 降噪力度太大 → 细节丢失(涂抹感)

如果噪声标定不准:

cs 复制代码
标定值偏低(实际噪声 8,你告诉 ISP 是 4):
  → ISP 降噪力度不够
  → 画面噪点残留
  → 用户觉得"画面不干净"

标定值偏高(实际噪声 4,你告诉 ISP 是 8):
  → ISP 降噪过度
  → 画面细节被抹掉
  → 用户觉得"画面像水彩画"

噪声标定怎么做

cs 复制代码
标准方法(需要实验室环境):

1. 盖上镜头盖(全黑环境)
2. 拍 100 帧 RAW 图
3. 对每个像素计算标准差 → 这就是读出噪声
4. 在不同光照下重复 → 得到散粒噪声曲线
5. 在不同增益下重复 → 得到完整的噪声特性

实际操作:
  · Sony FAE 通常会提供一份噪声标定数据
  · 你也可以用 IQ 工具自己标定
  · 标定结果写入 g_cmos_noise_calibration

你可以做的验证实验

复制代码
# 在设备上 dump 一帧 RAW 图(盖上镜头盖)
# 然后用工具分析:

# 1. 平均值 ≈ Black Level(应该接近 1032)
# 2. 标准差 ≈ 读出噪声大小
# 3. 最大值 - 最小值 ≈ 噪声峰峰值

# 如果不盖镜头盖,对着均匀白墙拍:
# 1. 看画面的均匀性
# 2. 四角比中心暗 → 需要 LSC 校正
# 3. 局部有色偏 → 需要 CCM 校正

噪声知识总结

噪声类型 来源 能否消除 和增益的关系
光子散粒噪声 光的量子特性 ❌ 物理限制 信号越大,绝对噪声越大,但 SNR 越好
读出噪声 sensor 电路 ⚠️ 可通过降噪减弱 被增益等比放大
暗电流噪声 热效应 ⚠️ 可通过降温/减法减弱 温度越高越大,长曝光更明显

一句话总结

噪声是画面的"底线"------sensor 的物理噪声决定了画面质量的上限,ISP 降噪只能在这个上限之内做优化。理解噪声特性,你才能设合理的增益上限、调正确的降噪参数、解释为什么暗光下噪点多。