3. Sensor驱动基础

驱动Sensor,说白了就是两件事:让它能动起来,让它按你想要的方式动。前者靠上电时序和初始化流程,后者靠I2C/SPI读写和PLL配置。这一章我把这些年踩过的坑和积累的经验都摊开来讲。

3.1 I2C/SPI寄存器读写——Sensor的“神经末梢”

Sensor内部有几百个寄存器,控制着曝光、增益、帧率、输出格式等等。读写这些寄存器,就是你和Sensor对话的唯一方式。

3.1.1 I2C vs SPI:怎么选?

特性 I2C SPI
引脚数 2线(SCL, SDA) 4线(SCLK, MOSI, MISO, CS)
速度 标准100kHz~400kHz,快模式可达1MHz 通常10MHz~50MHz,甚至更高
寻址方式 7位或10位设备地址 + 寄存器地址 片选信号选择设备,无地址冲突
多设备支持 总线挂载多个设备,地址不能冲突 每个设备独立CS,可挂载任意数量
典型应用 配置类Sensor(如OV系列) 高速配置或数据输出(如全局快门Sensor)
我的经验: 我个人习惯在原型阶段用I2C,因为示波器抓波形方便。但量产项目如果Sensor寄存器多、配置量大,我会优先选SPI——速度快,不容易被其他设备干扰。

3.1.2 I2C读写时序——别被ACK坑了

I2C的读写流程其实很固定,但有一个细节很多人忽略:写寄存器时,Sensor可能不会立即响应下一个操作。

// I2C写单个寄存器(标准流程)
uint8_t buf[3] = {reg_addr_high, reg_addr_low, data};
i2c_master_write(dev_addr, buf, 3);

// 读单个寄存器
uint8_t reg_addr[2] = {reg_addr_high, reg_addr_low};
i2c_master_write(dev_addr, reg_addr, 2);  // 先写地址
i2c_master_read(dev_addr, &data, 1);      // 再读数据
我曾经踩过的坑: 有一次调试IMX335,连续写寄存器时发现第三个寄存器写不进去。查了半天,发现是Sensor内部在写寄存器后需要一小段“消化时间”。解决方案很简单:每次写操作后加一个1ms的延时。后来我养成了习惯,所有I2C写操作后都加一个短延时,宁可慢一点,不能丢数据。

3.1.3 SPI读写——注意字节序和位宽

SPI比I2C快,但坑也不少。最常见的问题是字节序和位宽不匹配。

// SPI写寄存器(16位地址 + 8位数据)
uint16_t reg_addr = 0x300A;
uint8_t data = 0x55;

// 注意:有些Sensor要求先发地址高字节,有些要求低字节
spi_cs_low();
spi_transfer((reg_addr >> 8) & 0xFF);  // 地址高字节
spi_transfer(reg_addr & 0xFF);         // 地址低字节
spi_transfer(data);                    // 数据
spi_cs_high();
关键点: 拿到Sensor datasheet后,第一件事就是看SPI时序图。确认CPOL和CPHA模式(通常是Mode 0或Mode 3),确认地址和数据位宽。我见过有人把16位地址当8位发,结果Sensor完全不响应。

3.2 Sensor上电时序与初始化流程——一步错,步步错

Sensor上电不是简单的“给电就行”。不同电源轨的上电顺序、复位信号的时序、时钟的稳定时间,任何一个环节出问题,Sensor都可能无法正常工作。

3.2.1 典型上电时序

以常见的3.3V/1.8V/1.2V三路供电Sensor为例,上电顺序通常是:

  1. 先给模拟电源(AVDD)——通常是3.3V或2.8V
  2. 再给数字电源(DVDD)——通常是1.8V或1.2V
  3. 最后给IO电源(IOVDD)——通常是1.8V或3.3V
  4. 等待电源稳定——至少10ms
  5. 释放复位信号——拉高RST引脚
  6. 等待内部PLL锁定——通常需要5~20ms
  7. 开始I2C/SPI配置
注意: 不同Sensor的上电顺序可能不同。有些Sensor要求AVDD和DVDD同时上电,有些要求先DVDD后AVDD。一定要看datasheet里的Power-Up Sequence Timing Diagram。我曾经因为上电顺序反了,导致Sensor内部LDO损坏,直接烧了一片板子。

3.2.2 初始化流程——从reset到输出图像

初始化流程可以概括为以下几个步骤:

步骤 操作 说明
1 硬件复位 拉低RST引脚至少1ms,然后拉高
2 读取芯片ID 验证I2C/SPI通信是否正常
3 配置PLL时钟 设置输入时钟、倍频、分频
4 配置输出格式 设置分辨率、数据位宽、输出接口
5 配置曝光和增益 设置默认曝光时间和增益值
6 启动输出 设置寄存器使能视频输出
7 等待第一帧 检查VSYNC信号或状态寄存器
我的习惯: 初始化完成后,我会先读几个关键寄存器(如帧计数器、温度传感器),确认Sensor内部状态正常。这比直接等图像输出更可靠。

3.3 PLL时钟配置与帧率计算——让Sensor跑在目标频率上

PLL配置是Sensor驱动里最核心、也最容易出错的部分。配置错了,帧率不对、图像闪烁、甚至Sensor不工作都有可能。

3.3.1 PLL结构——从输入时钟到像素时钟

典型的Sensor PLL结构如下:

Sensor PLL时钟链路 输入时钟 (24MHz / 27MHz) 预分频 (/P) VCO (×M) Fvco = Fin/P × M 后分频 (/Q) PCLK 计算公式: Fpclk = Fin / P × M / Q 其中:Fin = 输入时钟频率,P = 预分频系数,M = 倍频系数,Q = 后分频系数 注意:VCO频率通常有范围限制(如 400MHz ~ 800MHz),配置时不能超出

3.3.2 帧率计算——从PCLK到FPS

有了像素时钟,帧率就可以算出来了。公式很简单:

帧率(FPS) = PCLK / (H_Total × V_Total)

其中:
- H_Total = 水平有效像素 + 水平消隐区
- V_Total = 垂直有效行数 + 垂直消隐区
举个例子: 假设PCLK = 72MHz,H_Total = 1920 + 280 = 2200,V_Total = 1080 + 45 = 1125。
帧率 = 72,000,000 / (2200 × 1125) ≈ 29.1 FPS

3.3.3 配置PLL的实战步骤

  1. 确定目标帧率——比如30FPS
  2. 确定分辨率——比如1920×1080
  3. 估算需要的PCLK——PCLK = FPS × H_Total × V_Total
  4. 根据输入时钟计算P、M、Q——确保VCO频率在允许范围内
  5. 写入寄存器——先写PLL配置,再触发PLL锁定
  6. 验证PLL锁定状态——读状态寄存器,确认锁定
我的经验: 配置PLL时,我习惯先写一个保守的配置(比如低帧率),确认Sensor能输出图像后,再逐步提高帧率。这样如果出问题,可以快速定位是PLL配置问题还是其他问题。

3.3.4 常见PLL配置错误

错误 现象 解决方法
VCO频率超出范围 PLL无法锁定,Sensor无输出 调整P、M、Q,使VCO在允许范围内
PCLK超过Sensor最大频率 图像出现噪点或花屏 降低帧率或增加消隐区
H_Total或V_Total设置错误 帧率不对,或图像偏移 参考datasheet中的时序参数表
PLL锁定时间不足 初始化后第一帧异常 增加PLL锁定等待时间
我曾经踩过的坑: 有一次配置IMX290,算出来的PCLK是74.25MHz,但Sensor最大只支持72MHz。结果图像输出正常,但每隔几帧就会出现一行花屏。查了两天才发现是PCLK超了。后来我养成了习惯:配置PLL前,先确认Sensor的PCLK上限。

3.4 本章小结

Sensor驱动开发,说白了就是三个核心:通信要稳(I2C/SPI读写可靠)、上电要对(时序和初始化流程正确)、时钟要准(PLL配置和帧率计算精确)。这三个环节任何一个出问题,Sensor都无法正常工作。

嗯,这一章的内容就到这里。记住:拿到新Sensor的第一件事,不是写代码,而是仔细看datasheet里的上电时序图和PLL配置表。省这一步,后面可能要花十倍的时间来填坑。