Linux内核Camera子系统原理与实战详解 - 第4节
4.1 Camera Sensor 驱动模型概述
在 Linux 内核中,Camera Sensor 驱动位于 V4L2 子系统的底层,负责与物理图像传感器(如 OV5640、IMX219、AR0234 等)直接交互。Sensor 驱动通过 I2C 或 SPI 总线与主机通信,完成寄存器配置、曝光控制、增益调整、白平衡校正等核心功能。本章将深入剖析 Sensor 驱动的软件架构、控制逻辑与实战要点。
4.1.1 Sensor 驱动在 V4L2 框架中的位置
下图展示了 Camera Sensor 驱动在 V4L2 整体架构中的层级关系:
4.2 I2C/SPI Sensor 驱动框架
Camera Sensor 驱动通常采用 I2C 或 SPI 总线与主机通信。I2C 适用于低速控制(寄存器读写),SPI 适用于高速数据流场景。驱动框架遵循 Linux 内核设备模型,包含 probe、remove、电源管理、V4L2 子设备注册等标准流程。
4.2.1 驱动核心数据结构
每个 Sensor 驱动都需要定义以下核心结构体:
| 结构体 | 用途 | 关键字段 |
|---|---|---|
struct i2c_driver | I2C 总线驱动注册 | probe, remove, id_table |
struct v4l2_subdev | V4L2 子设备抽象 | ops, ctrl_handler, entity |
struct v4l2_subdev_ops | 子设备操作函数集 | core, video, pad, sensor |
struct v4l2_ctrl_handler | 控制句柄管理 | ctrl, lock, error |
struct sensor_priv | 驱动私有数据 | 寄存器缓存、电源状态、模式配置 |
4.2.2 驱动 probe 流程
Sensor 驱动的 probe 函数是初始化的核心,典型流程如下:
- I2C/SPI 设备检测: 读取 Sensor 的 Chip ID 寄存器,验证硬件是否存在。
- 电源与时钟初始化: 使能 AVDD、DVDD、IOVDD 电源,配置 MCLK 时钟。
- V4L2 子设备注册: 初始化
v4l2_subdev,设置ops和ctrl_handler。 - 控制句柄创建: 注册曝光、增益、白平衡等 V4L2 控制项。
- 媒体实体初始化: 设置 pad、link 等媒体拓扑信息。
- 默认寄存器序列加载: 写入 Sensor 初始化寄存器表,使其进入工作状态。
4.2.3 寄存器读写封装
I2C Sensor 驱动通常封装寄存器读写函数,支持 8 位/16 位地址和 8 位/16 位数据宽度:
/* 8位地址 + 8位数据 */
static int sensor_write_reg(struct i2c_client *client, u8 reg, u8 val)
{
struct i2c_msg msg;
u8 buf[2] = {reg, val};
msg.addr = client->addr;
msg.flags = 0;
msg.len = 2;
msg.buf = buf;
return i2c_transfer(client->adapter, &msg, 1);
}
/* 16位地址 + 16位数据 */
static int sensor_write_reg16(struct i2c_client *client, u16 reg, u16 val)
{
u8 buf[4];
buf[0] = (reg >> 8) & 0xFF;
buf[1] = reg & 0xFF;
buf[2] = (val >> 8) & 0xFF;
buf[3] = val & 0xFF;
return i2c_master_send(client, buf, 4);
}
4.3 Sensor 寄存器配置
Camera Sensor 的寄存器配置是驱动开发中最繁琐但最关键的部分。每个 Sensor 厂商(OmniVision、Sony、ON Semiconductor 等)都有独特的寄存器映射表。
4.3.1 寄存器配置表结构
典型的寄存器配置表以数组形式组织,包含寄存器地址和对应的值:
struct sensor_reg {
u16 addr;
u8 val;
};
static const struct sensor_reg ov5640_init_regs[] = {
{0x3103, 0x11}, /* 系统时钟配置 */
{0x3008, 0x82}, /* 软件复位 */
{0x3008, 0x42}, /* 启动 */
{0x3102, 0x03}, /* PLL 配置 */
{0x3035, 0x11}, /* 模拟电路配置 */
{0x3036, 0x46}, /* PLL 倍频系数 */
{0x3037, 0x12}, /* PLL 分频系数 */
{0x3017, 0x00}, /* IO 配置 */
{0x3018, 0x00}, /* IO 配置 */
/* ... 更多寄存器配置 ... */
{0x4740, 0x21}, /* 输出格式 YUV */
{0x501F, 0x01}, /* ISP 使能 */
{0xFFFF, 0xFF}, /* 结束标记 */
};
4.3.2 分辨率切换配置
不同分辨率对应不同的寄存器配置集合,驱动需要支持动态切换:
| 分辨率 | PLL 配置 | 窗口裁剪 | 输出尺寸 | 帧率 |
|---|---|---|---|---|
| 1920x1080 | 0x3035=0x11, 0x3036=0x46 | 0x3800=0x00, 0x3802=0x00 | 0x3808=0x07, 0x380A=0x04 | 30fps |
| 1280x720 | 0x3035=0x11, 0x3036=0x3C | 0x3800=0x00, 0x3802=0x00 | 0x3808=0x05, 0x380A=0x02 | 60fps |
| 640x480 | 0x3035=0x11, 0x3036=0x2D | 0x3800=0x00, 0x3802=0x00 | 0x3808=0x02, 0x380A=0x01 | 90fps |
4.4 控制逻辑:曝光、增益、白平衡
曝光、增益和白平衡是 Camera Sensor 最核心的三个控制参数,直接影响图像质量。V4L2 通过 v4l2_ctrl 框架统一管理这些控制项。
4.4.1 曝光控制
曝光控制通过调整 Sensor 的积分时间(行数)来实现。曝光时间越长,感光量越大。
/* 曝光控制实现示例 */
static int sensor_set_exposure(struct v4l2_subdev *sd, int exposure)
{
struct sensor_priv *priv = to_sensor_priv(sd);
struct i2c_client *client = priv->client;
int ret;
/* 曝光值范围:1 ~ 0xFFFF 行 */
exposure = clamp(exposure, 1, 0xFFFF);
/* 写入曝光寄存器(16位) */
ret = sensor_write_reg16(client, 0x3500, exposure);
if (ret < 0)
return ret;
priv->exposure = exposure;
return 0;
}
4.4.2 增益控制
增益控制分为模拟增益和数字增益。模拟增益在像素读出前放大信号,数字增益在 ADC 转换后处理。
| 增益类型 | 寄存器 | 范围 | 步长 | 说明 |
|---|---|---|---|---|
| 模拟增益 | 0x3508 | 0x00 ~ 0xFF | 0.125 dB | 低噪声,优先使用 |
| 数字增益 | 0x350A | 0x00 ~ 0xFF | 0.0625 dB | 高增益时噪声明显 |
| 全局增益 | 0x350B | 0x00 ~ 0x7F | 1 dB | 组合增益控制 |
/* 增益控制实现示例 */
static int sensor_set_gain(struct v4l2_subdev *sd, int gain)
{
struct sensor_priv *priv = to_sensor_priv(sd);
struct i2c_client *client = priv->client;
int analog_gain, digital_gain;
/* 增益值范围:0 ~ 0x7FFF */
gain = clamp(gain, 0, 0x7FFF);
/* 优先使用模拟增益,超出部分用数字增益补偿 */
analog_gain = min(gain, 0xFF);
digital_gain = gain - analog_gain;
sensor_write_reg(client, 0x3508, analog_gain);
sensor_write_reg(client, 0x350A, digital_gain);
priv->gain = gain;
return 0;
}
4.4.3 白平衡控制
白平衡通过调整 R、G、B 通道的增益来校正色温。V4L2 提供红、蓝平衡两个控制项,绿色通道通常作为基准。
/* 白平衡控制实现示例 */
static int sensor_set_white_balance(struct v4l2_subdev *sd,
int red_gain, int blue_gain)
{
struct sensor_priv *priv = to_sensor_priv(sd);
struct i2c_client *client = priv->client;
/* 红色通道增益寄存器 0x3400 ~ 0x3401 */
sensor_write_reg16(client, 0x3400, red_gain);
/* 蓝色通道增益寄存器 0x3402 ~ 0x3403 */
sensor_write_reg16(client, 0x3402, blue_gain);
/* 绿色通道固定为基准值 0x100 */
sensor_write_reg16(client, 0x3404, 0x100);
priv->red_gain = red_gain;
priv->blue_gain = blue_gain;
return 0;
}
4.4.4 V4L2 控制句柄注册
将上述控制逻辑注册到 V4L2 控制框架中:
static const struct v4l2_ctrl_ops sensor_ctrl_ops = {
.s_ctrl = sensor_s_ctrl,
};
static int sensor_init_controls(struct sensor_priv *priv)
{
struct v4l2_ctrl_handler *hdl = &priv->ctrl_handler;
v4l2_ctrl_handler_init(hdl, 5);
/* 曝光控制:类型为整数,范围 1~65535,步长 1 */
v4l2_ctrl_new_std(hdl, &sensor_ctrl_ops,
V4L2_CID_EXPOSURE, 1, 65535, 1, 1000);
/* 增益控制:类型为整数,范围 0~32767,步长 1 */
v4l2_ctrl_new_std(hdl, &sensor_ctrl_ops,
V4L2_CID_GAIN, 0, 32767, 1, 100);
/* 红平衡控制 */
v4l2_ctrl_new_std(hdl, &sensor_ctrl_ops,
V4L2_CID_RED_BALANCE, 0, 4095, 1, 256);
/* 蓝平衡控制 */
v4l2_ctrl_new_std(hdl, &sensor_ctrl_ops,
V4L2_CID_BLUE_BALANCE, 0, 4095, 1, 256);
if (hdl->error) {
dev_err(priv->dev, "control init failed: %d\n", hdl->error);
return hdl->error;
}
priv->sd.ctrl_handler = hdl;
return 0;
}
4.5 控制逻辑流程图
下图展示了用户空间通过 V4L2 接口设置曝光、增益、白平衡时,驱动内部的控制流程:
4.6 实战要点与避坑指南
- 寄存器配置顺序: 严格遵循数据手册,特别是 PLL 配置必须在分辨率设置之前完成。
- 曝光与帧率关系: 曝光时间不能超过一帧的总行数,否则会导致帧率下降。最大曝光行数 = VTS(垂直总行数)- 4。
- 增益优先级: 优先使用模拟增益,模拟增益达到上限后再使用数字增益,以降低噪声。
- 白平衡校准: 不同色温下需要不同的 R/G/B 增益比例,建议在驱动中预置多种色温模式。
- I2C 速率: Sensor 的 I2C 速率通常为 100kHz ~ 400kHz,高速模式可能导致通信失败。
i2cget 和 i2cset 工具可以直接在用户空间读写 Sensor 寄存器,快速验证寄存器配置是否正确。例如:i2cset -y 1 0x3c 0x3008 0x42。