1. RK平台Sensor驱动框架概述:V4L2子系统、I2C总线、DTS设备树节点配置

在RK平台上开发Sensor驱动,首先需要理解其核心软件架构。Sensor驱动并非孤立存在,而是深度嵌入在Linux内核的V4L2(Video for Linux 2)框架中,通过I2C总线与硬件通信,并由设备树(Device Tree)完成硬件资源的静态描述。本章将拆解这三个核心要素,为后续的寄存器读写与性能优化打下坚实基础。

1.1 V4L2子系统:Sensor驱动的“操作系统”

V4L2是Linux内核中为视频设备(包括Camera Sensor)提供统一接口的框架。它将Sensor抽象为一个“视频设备节点”(通常是/dev/videoX),使得用户空间的应用(如Camera HAL、GStreamer)可以通过标准系统调用(open、ioctl、mmap)来控制Sensor。

在V4L2框架中,Sensor驱动主要扮演“子设备(subdev)”的角色。驱动需要实现一组预定义的回调函数,用于配置分辨率、帧率、曝光、增益等参数。下图展示了V4L2子设备在RK平台Camera软件栈中的位置:

RK平台 V4L2 Sensor 驱动软件架构 用户空间:Camera HAL / GStreamer / 应用 内核空间:V4L2 核心框架 (Video Device / Media Controller) V4L2 Subdev 层:Sensor 驱动核心 (sensor.c) 硬件抽象层:I2C 总线驱动 / MIPI CSI-2 控制器 硬件:Camera Sensor (OV5648 / IMX258 / ...)

驱动开发者需要重点关注V4L2 Subdev层的实现。核心工作包括:

  • 实现 struct v4l2_subdev_ops:提供 core、video、pad 等操作集。
  • 实现 struct v4l2_subdev_video_ops:关键函数如 s_stream()(开启/关闭流)、g_frame_interval()(获取帧率)。
  • 实现 struct v4l2_subdev_pad_ops:用于设置媒体总线格式(set_fmt)和裁剪(set_selection)。
  • 注册 Subdev:通过 v4l2_async_register_subdev() 将驱动注册到V4L2异步框架中。
调试提示: 在调试初期,可以通过 cat /sys/kernel/debug/v4l2/ 查看已注册的V4L2设备列表。使用 media-ctl 工具可以动态查看和修改Pipeline拓扑,是排查Sensor枚举问题的利器。

1.2 I2C总线:寄存器读写的“高速公路”

绝大多数Camera Sensor通过I2C总线与主控SoC(如RK3588、RK3568)通信。I2C是一种同步、多主从、半双工的串行通信协议,使用两根线:SCL(时钟线)和SDA(数据线)。Sensor驱动通过I2C读写Sensor内部的寄存器,以完成初始化、配置工作模式、获取状态等操作。

在Linux内核中,I2C驱动被分为两个层次:

层次 组件 职责 开发者关注点
I2C 总线驱动 i2c-rk3x.c (RK平台) 处理I2C控制器硬件,适配器算法,产生时序 通常由BSP提供,无需修改;需确认时钟频率、DMA支持
I2C 设备驱动 sensor.c (你的驱动) 通过I2C核心API与Sensor芯片通信 使用 i2c_transfer() 或 i2c_smbus_* 函数

Sensor驱动中最常用的I2C读写函数封装如下:

/* 单字节写寄存器 */
static int sensor_write_reg(struct i2c_client *client, u16 reg, u8 val)
{
    int ret;
    struct i2c_msg msg;
    u8 buf[3];

    buf[0] = (reg >> 8) & 0xFF;  // 寄存器高字节
    buf[1] = reg & 0xFF;         // 寄存器低字节
    buf[2] = val;                // 写入值

    msg.addr = client->addr;
    msg.flags = 0;               // 写标志
    msg.len = 3;
    msg.buf = buf;

    ret = i2c_transfer(client->adapter, &msg, 1);
    if (ret == 1)
        return 0;
    else
        return -EIO;
}

/* 多字节读寄存器 */
static int sensor_read_reg(struct i2c_client *client, u16 reg, u8 *val)
{
    int ret;
    struct i2c_msg msg[2];
    u8 reg_buf[2];

    reg_buf[0] = (reg >> 8) & 0xFF;
    reg_buf[1] = reg & 0xFF;

    msg[0].addr = client->addr;
    msg[0].flags = 0;            // 写寄存器地址
    msg[0].len = 2;
    msg[0].buf = reg_buf;

    msg[1].addr = client->addr;
    msg[1].flags = I2C_M_RD;     // 读数据
    msg[1].len = 1;
    msg[1].buf = val;

    ret = i2c_transfer(client->adapter, msg, 2);
    if (ret == 2)
        return 0;
    else
        return -EIO;
}
常见陷阱: 许多Sensor的寄存器地址是16位(如OV系列),但部分Sensor(如某些索尼IMX系列)使用8位地址。务必查阅Sensor数据手册确认地址宽度。地址长度不匹配是导致I2C通信失败的最常见原因之一。

I2C通信的时序与速率直接影响Sensor的初始化速度和配置灵活性。下图展示了Sensor驱动通过I2C总线进行寄存器读写的典型流程:

I2C 寄存器读写流程 驱动调用 i2c_transfer() 构造 struct i2c_msg 消息数组 I2C 控制器驱动处理 (i2c-rk3x) 硬件 I2C 总线传输 (SCL/SDA) 返回结果 (0 或 -EIO)

1.3 DTS设备树节点配置:硬件拓扑的“静态地图”

在RK平台(以及所有基于ARM的Linux系统)中,设备树(Device Tree Source, DTS)是描述硬件拓扑和资源分配的核心机制。对于Sensor驱动而言,DTS节点主要完成以下工作:

  • 绑定I2C总线:指定Sensor挂载在哪个I2C控制器上。
  • 指定I2C地址:Sensor的7位从机地址。
  • 配置电源与复位:定义AVDD、DVDD、IOVDD等电源,以及复位GPIO。
  • 关联时钟:指定MCLK(主时钟)的时钟源和频率。
  • 定义中断:如果Sensor支持中断(如VSYNC),则在此声明。

一个典型的RK3588平台Sensor DTS节点示例如下:

/* 在 i2c8 节点下添加 Sensor */
&i2c8 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c8m2_xfer>;

    ov5648: ov5648@36 {
        compatible = "ovti,ov5648";
        reg = <0x36>;                     // I2C 从机地址 (7位)
        clocks = <&cru CLK_MIPI_CAMARAOUT_M3>;
        clock-names = "xvclk";
        clock-frequency = <24000000>;     // MCLK 频率 24MHz

        // 电源配置
        avdd-supply = <&vcc_camera_avdd>;
        dovdd-supply = <&vcc_camera_dovdd>;
        dvdd-supply = <&vcc_camera_dvdd>;

        // 复位与电源控制 GPIO
        reset-gpios = <&gpio3 RK_PB6 GPIO_ACTIVE_LOW>;
        pwdn-gpios = <&gpio3 RK_PB7 GPIO_ACTIVE_HIGH>;

        // 端口连接 (绑定到 MIPI CSI 接口)
        port {
            ov5648_out: endpoint {
                remote-endpoint = <&mipi_in_ucam0>;
                data-lanes = <1 2>;       // 使用 2-lane MIPI
            };
        };
    };
};

DTS节点中的关键属性及其作用如下表所示:

属性名 类型 说明 调试要点
compatible 字符串 驱动匹配的关键标识,必须与驱动中 of_match_table 一致 拼写错误会导致驱动无法加载
reg u32 I2C从机地址(7位左移1位后的值) 使用 i2cdetect 工具确认实际地址
clock-frequency u32 Sensor主时钟频率,单位Hz 频率偏差过大会导致PLL锁定失败
reset-gpios GPIO句柄 复位引脚,通常为低电平有效 检查GPIO复用(pinctrl)是否正确
data-lanes u32数组 MIPI CSI数据通道映射 必须与硬件连线一致,否则无图像
调试技巧: 在系统启动后,通过 ls /sys/firmware/devicetree/base/ 可以查看实际生效的设备树。使用 dtc -I fs -O dts /sys/firmware/devicetree/base/ 可以反编译当前设备树,验证节点是否被正确解析。

下图展示了DTS节点如何将Sensor驱动与硬件资源关联起来:

DTS 节点与硬件资源绑定关系 DTS 节点 compatible = "ovti,ov5648" reg = <0x36> reset-gpios = ... avdd-supply = ... clocks = ... port { endpoint } I2C 控制器 (i2c8) GPIO 控制器 (pinctrl) PMIC / Regulator (电源) 内核在 probe 阶段解析 DTS,自动获取并申请上述资源