4、Sensor驱动核心配置:分辨率切换与帧率控制、曝光与增益寄存器映射、HDR模式配置实战
好,咱们进入第四章节。前面几章我们把Sensor驱动的基础框架和I2C通信搭好了,现在要动真格的了——配置Sensor的核心参数。
说白了,Sensor驱动最核心的工作就三件事:让Sensor输出你想要的图像尺寸和帧率、让Sensor能正确曝光和增益、让Sensor工作在HDR模式。这三件事搞不定,后面的3A算法全是空中楼阁。
我个人习惯把这三块称为Sensor驱动的“三驾马车”。咱们一个一个来拆。
4.1 分辨率切换与帧率控制
先说说分辨率切换。你想想看,一个Sensor通常支持多种输出尺寸:比如400万像素的Sensor,可能支持2688x1520、1920x1080、1280x720等等。每次切换分辨率,本质上是在修改Sensor内部的行有效像素数和帧有效行数。
瑞芯平台的做法是,在驱动里维护一个struct sensor_mode数组,每个元素对应一种分辨率模式。我见过不少新手直接把所有寄存器值写死在代码里,结果换一个Sensor型号就得重写一遍——这其实是不对的。
来看一个典型的配置结构:
// sensor_drv.c
static struct sensor_mode g_sensor_modes[] = {
{
.width = 2688,
.height = 1520,
.fps = 30,
.reg_list = {
{0x0100, 0x00}, // 先关流
{0x0300, 0x04}, // PLL配置
{0x0301, 0x00},
{0x0302, 0x2C},
{0x0303, 0x01},
{0x0340, 0x05}, // 帧长 = 0x05DC = 1500
{0x0341, 0xDC},
{0x0342, 0x0B}, // 行长 = 0x0BB8 = 3000
{0x0343, 0xB8},
{0x0100, 0x01}, // 开流
{REG_NULL, 0x00},
},
},
// ... 其他模式
};
这里有个坑,我踩过好几次:切换分辨率前必须先关流(写0x0100=0x00),等所有寄存器配置完成后再开流。有些Sensor不关流也能切,但图像会花掉几帧——这在安防场景下是不能接受的。
帧率控制这块,我个人习惯在驱动里提供一个接口,允许上层动态调整帧率。比如从30fps降到25fps,只需要增加VBlank的值。但要注意:VBlank不能无限大,否则帧率太低会导致图像闪烁。
4.2 曝光与增益寄存器映射
接下来是曝光和增益。这是3A算法和Sensor驱动之间的“翻译官”。
3A算法算出来的曝光时间单位是微秒(us),增益是倍率(1x, 2x, 4x...)。但Sensor寄存器只认自己的值——比如曝光寄存器存的是行数,增益寄存器存的是模拟增益的code值。
所以驱动要做一层映射。我把它分成三步:
- 时间转行数:曝光行数 = 曝光时间(us) / 一行时间(us)。一行时间 = 1 / (PCLK / HTS)。
- 增益转code:不同Sensor的增益映射表不同,有的线性,有的分段。需要查datasheet。
- 写寄存器:把计算出的行数和code值写入对应的寄存器地址。
来看一个实际的映射函数:
// sensor_exposure.c
int sensor_set_exposure(struct sensor_dev *dev, uint32_t exp_us, uint32_t gain_x100)
{
uint16_t line_length = dev->cur_mode->line_length; // 从当前模式获取
uint32_t exp_lines;
uint16_t gain_code;
// 1. 曝光时间转行数
exp_lines = exp_us * 1000 / (line_length * 1000 / dev->pclk_khz);
// 简化后:exp_lines = exp_us * dev->pclk_khz / (line_length * 1000);
// 注意:exp_lines 不能超过 VTS - 4(留余量)
// 2. 增益转code(假设线性映射)
// gain_x100 = 100 表示1x, 200表示2x
if (gain_x100 <= 100) {
gain_code = 0; // 最小增益
} else if (gain_x100 <= 200) {
gain_code = (gain_x100 - 100) * 16 / 100; // 1x~2x映射到0~16
} else {
gain_code = 16 + (gain_x100 - 200) * 8 / 100; // 2x以上斜率变缓
}
// 3. 写寄存器
sensor_write_reg(dev, 0x0202, (exp_lines >> 8) & 0xFF); // 曝光高字节
sensor_write_reg(dev, 0x0203, exp_lines & 0xFF); // 曝光低字节
sensor_write_reg(dev, 0x0204, (gain_code >> 8) & 0xFF); // 增益高字节
sensor_write_reg(dev, 0x0205, gain_code & 0xFF); // 增益低字节
return 0;
}
增益映射这块,不同厂家差异很大。OV的Sensor喜欢用分段线性,SONY的Sensor很多用dB为单位。我建议在驱动初始化时,从Sensor的OTP里读取增益校准表,而不是硬编码。
4.3 HDR模式配置实战
HDR模式,说白了就是让Sensor同时输出亮部和暗部的细节。瑞芯微平台支持多种HDR方案:DOL( Digital Overlap)、Staggered HDR、Line Interleaved等。
我个人最常用的是DOL HDR,因为它对运动物体友好,不会出现鬼影。配置DOL HDR的核心是设置长短帧的曝光时间和增益。
来看一个DOL HDR的配置流程:
| 步骤 | 操作 | 寄存器示例 | 说明 |
|---|---|---|---|
| 1 | 使能HDR模式 | 0x0101 = 0x02 | 切换到HDR工作模式 |
| 2 | 配置长帧曝光 | 0x0202~0x0203 | 长帧曝光行数(通常为短帧的4~16倍) |
| 3 | 配置短帧曝光 | 0x0222~0x0223 | 短帧曝光行数 |
| 4 | 配置长帧增益 | 0x0204~0x0205 | 长帧模拟增益 |
| 5 | 配置短帧增益 | 0x0224~0x0225 | 短帧模拟增益 |
| 6 | 配置HDR合成比例 | 0x0400~0x0401 | 长短帧融合权重 |
嗯,这里要注意:HDR模式下帧率会下降。因为Sensor需要输出两帧(长帧+短帧)才能合成一帧HDR图像。比如你配置30fps的HDR,Sensor实际内部帧率是60fps。
另外,HDR模式的增益配置有个技巧:长帧增益尽量小,短帧增益尽量大。因为长帧曝光时间长,本身信号就强,再加增益容易饱和。短帧曝光时间短,信号弱,需要高增益来提亮暗部。
最后,我建议在驱动里提供一个HDR参数调试接口,允许在运行时动态调整长短帧的曝光比和增益比。这样3A算法可以根据场景亮度自适应调节HDR强度。
好了,这一章的内容就这些。分辨率切换、曝光增益映射、HDR配置,这三块是Sensor驱动的核心。你把这些搞透了,后面3A算法调优就是水到渠成的事。