4. Sensor驱动初始化流程:从Probe到ID验证
Sensor驱动初始化,是整个Camera驱动栈的起点。说白了,就是让主控芯片和Sensor芯片互相认识一下。我见过不少工程师,一上来就追着ISP的bug跑,结果查了半天,发现Sensor压根就没正常工作。嗯,这章我们就来把初始化流程彻底捋一遍。
4.1 Sensor Probe函数调用栈
Probe函数,是驱动加载的入口。Kernel在I2C总线上发现设备后,就会调用你注册的Probe回调。我个人习惯,拿到一个新Sensor驱动,第一件事就是看Probe的调用栈。
一个典型的Probe调用栈长这样:
sensor_probe()
├── 解析dts配置 (of_property_read_xxx)
├── 分配私有数据结构 (kzalloc)
├── 初始化I2C客户端 (i2c_check_functionality)
├── 上电序列 (sensor_power_up)
├── 初始化MCLK/PCLK (clk_prepare_enable)
├── 读取Sensor ID (sensor_read_id)
├── 注册V4L2子设备 (v4l2_i2c_subdev_init)
└── 注册中断/其他 (request_threaded_irq)
这里有个坑,我踩过好几次。有些Sensor的Probe里,上电和读ID之间没有加延时。结果ID读出来是0xFF或者0x00。为什么?因为Sensor内部PLL还没稳定。我曾经在一个项目里,加了20ms的延时,问题就解决了。
4.2 Power On序列与GPIO控制
Power On序列,是Sensor初始化的重中之重。每个Sensor都有自己的上电时序要求,比如AVDD先上,然后IOVDD,最后Reset拉高。时序不对,Sensor可能无法正常工作,甚至烧坏。
我建议,把Power On序列做成一个独立函数,方便调试和复用。下面是一个典型的Power On序列:
static int sensor_power_up(struct sensor_device *sensor)
{
// 1. 配置GPIO为输出,初始为低
gpio_direction_output(sensor->reset_gpio, 0);
gpio_direction_output(sensor->pwdn_gpio, 1); // 先进入Power Down
// 2. 按顺序上电
regulator_enable(sensor->avdd); // 模拟电压 2.8V
mdelay(1); // 等待1ms
regulator_enable(sensor->iovdd); // IO电压 1.8V
mdelay(1);
regulator_enable(sensor->dvdd); // 数字电压 1.2V
mdelay(5); // 等待电压稳定
// 3. 释放Reset
gpio_set_value(sensor->pwdn_gpio, 0); // 退出Power Down
mdelay(1);
gpio_set_value(sensor->reset_gpio, 1); // Reset拉高
mdelay(20); // 等待内部初始化
return 0;
}
你想想看,如果AVDD和IOVDD的顺序搞反了,会发生什么?有些Sensor内部有ESD保护二极管,顺序反了会导致电流倒灌,轻则工作异常,重则烧毁Sensor。我曾经在一个项目中,因为时序问题,连续烧了3块Sensor板子。后来查datasheet才发现,AVDD必须比IOVDD先上电至少1ms。
4.3 MCLK与PCLK频率配置
MCLK是Sensor的主时钟,PCLK是像素时钟。这两个时钟的频率,直接决定了Sensor的输出帧率和数据带宽。
MCLK通常由主控提供,常见频率有24MHz、27MHz、48MHz等。PCLK则由Sensor内部PLL生成,和MCLK有固定的倍数关系。
配置MCLK时,要注意以下几点:
- 频率精度:MCLK的精度要在±1%以内,否则Sensor内部PLL可能失锁。
- 占空比:通常要求45%~55%,否则影响时钟质量。
- 抖动:时钟抖动过大会导致图像出现条纹噪声。
下面是一个MCLK配置的示例:
static int sensor_init_clk(struct sensor_device *sensor)
{
int ret;
// 获取MCLK句柄
sensor->mclk = devm_clk_get(sensor->dev, "mclk");
if (IS_ERR(sensor->mclk)) {
dev_err(sensor->dev, "Failed to get MCLK\n");
return PTR_ERR(sensor->mclk);
}
// 设置频率为24MHz
ret = clk_set_rate(sensor->mclk, 24000000);
if (ret) {
dev_err(sensor->dev, "Failed to set MCLK rate\n");
return ret;
}
// 使能时钟
ret = clk_prepare_enable(sensor->mclk);
if (ret) {
dev_err(sensor->dev, "Failed to enable MCLK\n");
return ret;
}
// 等待时钟稳定
usleep_range(1000, 2000);
return 0;
}
PCLK的配置,通常在Sensor的初始化寄存器序列里完成。比如,通过设置PLL倍频系数,让PCLK达到目标频率。我建议,在调试阶段,用示波器实测一下MCLK和PCLK的波形。有一次,我发现MCLK的波形有毛刺,查了半天,原来是PCB走线太长,加了终端匹配电阻就好了。
4.4 Register Dump与ID读取验证
Register Dump,是验证Sensor是否正常工作的最直接手段。通过I2C读取Sensor的寄存器值,和datasheet对比,就能判断I2C通信是否正常,Sensor内部逻辑是否正常。
ID读取,是Register Dump的一个特例。每个Sensor都有一个唯一的ID寄存器,比如0x0000地址存放芯片ID。Probe函数里,必须读取这个ID,验证是否和预期一致。
下面是一个ID读取的示例:
static int sensor_check_id(struct sensor_device *sensor)
{
u16 chip_id;
int ret;
// 读取芯片ID寄存器
ret = sensor_i2c_read(sensor, 0x0000, &chip_id);
if (ret) {
dev_err(sensor->dev, "Failed to read chip ID\n");
return ret;
}
dev_info(sensor->dev, "Chip ID: 0x%04x\n", chip_id);
// 验证ID
if (chip_id != SENSOR_EXPECTED_ID) {
dev_err(sensor->dev, "Chip ID mismatch: expected 0x%04x, got 0x%04x\n",
SENSOR_EXPECTED_ID, chip_id);
return -ENODEV;
}
return 0;
}
如果ID读出来不对,怎么办?我一般按以下步骤排查:
- 检查I2C地址:Sensor的I2C地址是否正确?有些Sensor有多个地址,通过引脚电平选择。
- 检查I2C通信:用示波器抓I2C波形,看是否有ACK信号。
- 检查电源:用万用表量一下各电源引脚,看电压是否正常。
- 检查时钟:用示波器看MCLK是否有波形。
- 检查Reset:Reset引脚的电平是否正确?
4.5 知识结构总览
下面这张图,把Sensor初始化流程的核心逻辑串起来了。你可以把它当作调试时的路线图。
4.6 常见问题与排查思路
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| Probe函数未调用 | dts配置错误、I2C地址不匹配 | 检查dts文件,核对I2C地址 |
| ID读取为0xFF | I2C通信失败、Sensor未上电 | 用示波器抓I2C波形,量电源电压 |
| ID读取为0x00 | Sensor处于复位状态、时钟未稳定 | 检查Reset引脚电平,增加延时 |
| MCLK无波形 | 时钟配置错误、GPIO复用冲突 | 检查clk_set_rate返回值,检查pinmux |
| 图像有条纹 | MCLK抖动过大、电源纹波过大 | 用示波器测时钟抖动,加滤波电容 |
嗯,Sensor初始化这块,说难不难,说简单也不简单。关键是要把每一步都验证到位。我见过太多人,跳过ID验证直接调图像,结果浪费了好几天。记住,基础不牢,地动山摇。