3. Sensor驱动编写:sensor初始化流程、寄存器读写操作、电源管理序列、时钟配置

Sensor驱动是Camera驱动栈的最底层,直接与硬件传感器芯片交互。本章聚焦于驱动开发中最核心的四个环节:初始化流程编排、寄存器读写抽象、电源上电序列控制以及时钟树配置。掌握这些基础,才能为后续的图像格式转换与NEON优化提供稳定的数据源。

实战前提:本章所有代码示例基于Linux内核V4L2框架,Sensor型号以SONY IMX335为参考。实际开发中请替换为对应Sensor的Datasheet参数。

3.1 Sensor初始化流程

Sensor从上电到输出有效图像数据,需要经历一个严格的硬件状态机转换。典型的初始化流程包含以下阶段:

  1. 硬件复位:拉低XCLR引脚,保持至少10ms后释放。
  2. 电源上电:按照AVDD、DVDD、IOVDD的顺序依次供电,每步间隔5ms。
  3. 时钟输入:提供MCLK(主时钟),通常为24MHz或27MHz。
  4. I2C通信建立:等待Sensor内部PLL锁定,约需20ms。
  5. 寄存器初始化序列:写入一组预定义的寄存器配置表。
  6. 软件触发:设置Streaming寄存器,使Sensor开始输出帧。
Sensor初始化状态机流程图 上电复位 电源稳定 时钟就绪 I2C通信 寄存器配置 Streaming ⚠ 复位后需等待至少10ms 电源顺序:AVDD→DVDD→IOVDD ⚠ PLL锁定约需20ms I2C地址通常为0x1A或0x36
关键时序参数(IMX335典型值):
  • 复位低电平保持时间:≥10ms
  • 电源上升时间:≤100μs
  • MCLK稳定到I2C可通信:≥20ms
  • 寄存器配置到Streaming输出:≥50ms

3.2 寄存器读写操作

Sensor驱动通过I2C或SPI总线与硬件通信。寄存器读写是驱动中最频繁的操作,必须保证原子性和错误处理。以下是一个标准的内核I2C读写封装:

/* sensor_i2c.c - I2C读写抽象层 */
#include <linux/i2c.h>
#include <linux/delay.h>

#define SENSOR_I2C_RETRY_TIMES  3
#define SENSOR_I2C_WAIT_MS      10

/* 16位地址 + 16位数据 寄存器写入 */
int sensor_write_reg(struct i2c_client *client, u16 reg, u16 val)
{
    int ret, i;
    u8 buf[4];

    buf[0] = (reg >> 8) & 0xFF;
    buf[1] = reg & 0xFF;
    buf[2] = (val >> 8) & 0xFF;
    buf[3] = val & 0xFF;

    for (i = 0; i < SENSOR_I2C_RETRY_TIMES; i++) {
        ret = i2c_master_send(client, buf, 4);
        if (ret == 4)
            return 0;
        msleep(SENSOR_I2C_WAIT_MS);
    }
    dev_err(&client->dev, "write reg 0x%04x failed: %d\n", reg, ret);
    return -EIO;
}
EXPORT_SYMBOL(sensor_write_reg);

/* 16位地址 + 16位数据 寄存器读取 */
int sensor_read_reg(struct i2c_client *client, u16 reg, u16 *val)
{
    int ret, i;
    u8 addr_buf[2] = { (reg >> 8) & 0xFF, reg & 0xFF };
    u8 data_buf[2];

    for (i = 0; i < SENSOR_I2C_RETRY_TIMES; i++) {
        ret = i2c_master_send(client, addr_buf, 2);
        if (ret != 2)
            continue;
        ret = i2c_master_recv(client, data_buf, 2);
        if (ret == 2) {
            *val = (data_buf[0] << 8) | data_buf[1];
            return 0;
        }
        msleep(SENSOR_I2C_WAIT_MS);
    }
    dev_err(&client->dev, "read reg 0x%04x failed: %d\n", reg, ret);
    return -EIO;
}
EXPORT_SYMBOL(sensor_read_reg);
寄存器读写接口对比
接口参数重试机制返回值
sensor_write_regclient, reg, val3次重试,间隔10ms0成功,-EIO失败
sensor_read_regclient, reg, *val3次重试,间隔10ms0成功,-EIO失败
sensor_write_arrayclient, table, size逐条写入,失败即返回0成功,负值错误码
避坑指南:
  • 部分Sensor使用8位寄存器地址,需根据Datasheet调整buf长度。
  • I2C通信失败后必须重试,不能直接返回,否则可能导致初始化卡死。
  • 批量写入时建议使用I2C多字节写模式,减少总线开销。

3.3 电源管理序列

Sensor对电源时序极其敏感,错误的供电顺序可能导致芯片损坏或无法正常工作。标准的三路电源管理序列如下:

Sensor电源上电时序图 时间 (t) AVDD (2.8V) DVDD (1.2V) IOVDD (1.8V) t0 t0+5ms t0+10ms ⚠ 下电时序与上电相反:IOVDD→DVDD→AVDD,间隔同样≥5ms

在内核驱动中,电源管理通常通过regulator框架实现:

/* sensor_power.c - 电源管理序列实现 */
#include <linux/regulator/consumer.h>

struct sensor_power {
    struct regulator *avdd;
    struct regulator *dvdd;
    struct regulator *iovdd;
};

int sensor_power_on(struct sensor_power *pw)
{
    int ret;

    /* 1. AVDD先上电 */
    ret = regulator_enable(pw->avdd);
    if (ret) return ret;
    msleep(5);

    /* 2. DVDD */
    ret = regulator_enable(pw->dvdd);
    if (ret) goto err_dvdd;
    msleep(5);

    /* 3. IOVDD最后 */
    ret = regulator_enable(pw->iovdd);
    if (ret) goto err_iovdd;
    msleep(5);

    return 0;

err_iovdd:
    regulator_disable(pw->dvdd);
err_dvdd:
    regulator_disable(pw->avdd);
    return ret;
}

void sensor_power_off(struct sensor_power *pw)
{
    regulator_disable(pw->iovdd);
    msleep(5);
    regulator_disable(pw->dvdd);
    msleep(5);
    regulator_disable(pw->avdd);
}

3.4 时钟配置

Sensor需要外部主时钟(MCLK)驱动内部PLL,生成像素时钟、行时钟和帧时钟。时钟配置错误会导致输出图像花屏或无数据。典型的时钟树结构如下:

Sensor内部时钟树结构 MCLK (24MHz) PLL 倍频 分频器 PCLK 行/帧同步 ADC时钟

时钟配置的核心是PLL参数计算。以下是一个典型的PLL配置函数:

/* sensor_clk.c - PLL时钟配置 */
#define IMX335_MCLK       24000000  /* 24MHz */
#define IMX335_PCLK_TARGET 72000000 /* 目标像素时钟 72MHz */

struct pll_config {
    u16 pre_pll_div;   /* 预分频 */
    u16 pll_mult;      /* 倍频系数 */
    u16 post_div;      /* 后分频 */
};

int sensor_calc_pll(struct pll_config *cfg, u32 mclk, u32 target_pclk)
{
    u32 vco_freq;
    u32 best_diff = 0xFFFFFFFF;
    u16 best_mult = 0, best_post = 0;
    u16 m, p;

    /* 遍历倍频系数 100~300,后分频 1~8 */
    for (m = 100; m <= 300; m++) {
        for (p = 1; p <= 8; p++) {
            vco_freq = mclk * m / 2;  /* 预分频固定为2 */
            if (vco_freq < 400000000 || vco_freq > 800000000)
                continue;

            u32 pclk = vco_freq / p;
            u32 diff = abs((s32)(pclk - target_pclk));
            if (diff < best_diff) {
                best_diff = diff;
                best_mult = m;
                best_post = p;
            }
        }
    }

    if (best_diff > 1000000) {
        pr_err("sensor: cannot find valid PLL config\n");
        return -EINVAL;
    }

    cfg->pre_pll_div = 2;
    cfg->pll_mult = best_mult;
    cfg->post_div = best_post;

    pr_info("sensor: PLL config: pre=%d, mult=%d, post=%d, pclk=%u\n",
            cfg->pre_pll_div, cfg->pll_mult, cfg->post_div,
            mclk * best_mult / 2 / best_post);
    return 0;
}
常见Sensor时钟频率参考
Sensor型号MCLK (MHz)PCLK (MHz)帧率 (fps)
IMX335247230
OV5640248430
AR02302774.2560
IMX29037.12574.2560
调试技巧:如果Sensor输出图像异常,首先检查PCLK频率是否与预期一致。可以使用示波器测量Sensor的PCLK引脚,或通过读取Sensor内部PLL锁定状态寄存器确认。

3.5 完整初始化序列示例

将以上模块整合,形成完整的Sensor初始化函数:

/* sensor_init.c - 完整初始化序列 */
int sensor_init(struct i2c_client *client)
{
    struct sensor_power pw;
    struct pll_config pll;
    int ret;

    /* 1. 获取regulator句柄 */
    pw.avdd = regulator_get(&client->dev, "avdd");
    pw.dvdd = regulator_get(&client->dev, "dvdd");
    pw.iovdd = regulator_get(&client->dev, "iovdd");

    /* 2. 电源上电 */
    ret = sensor_power_on(&pw);
    if (ret) goto err_power;

    /* 3. 硬件复位 */
    gpio_set_value(client->irq, 0);
    msleep(20);
    gpio_set_value(client->irq, 1);
    msleep(20);

    /* 4. 配置PLL */
    ret = sensor_calc_pll(&pll, IMX335_MCLK, IMX335_PCLK_TARGET);
    if (ret) goto err_pll;

    /* 5. 写入PLL寄存器 */
    sensor_write_reg(client, 0x3000, pll.pre_pll_div);
    sensor_write_reg(client, 0x3002, pll.pll_mult);
    sensor_write_reg(client, 0x3004, pll.post_div);
    msleep(20);  /* 等待PLL锁定 */

    /* 6. 写入初始化寄存器表 */
    ret = sensor_write_array(client, imx335_init_regs,
                             ARRAY_SIZE(imx335_init_regs));
    if (ret) goto err_regs;

    /* 7. 启动Streaming */
    sensor_write_reg(client, 0x0100, 0x01);

    dev_info(&client->dev, "sensor initialized successfully\n");
    return 0;

err_regs:
err_pll:
    sensor_power_off(&pw);
err_power:
    regulator_put(pw.avdd);
    regulator_put(pw.dvdd);
    regulator_put(pw.iovdd);
    return ret;
}
初始化序列检查清单:
  • ✅ 电源顺序:AVDD → DVDD → IOVDD,间隔≥5ms
  • ✅ 复位时序:低电平保持≥10ms,释放后等待≥20ms
  • ✅ PLL锁定等待:≥20ms
  • ✅ 寄存器写入:逐条确认无I2C错误
  • ✅ Streaming使能:最后一步,使能后Sensor立即输出帧