2、摄像头传感器基础:OV5640/IMX219传感器介绍、寄存器配置、I2C控制接口

好,咱们今天聊聊摄像头传感器。做视频采集,传感器是绕不开的第一关。我个人习惯把传感器比作「相机的底片」——它决定了你画面的底子好不好。

市面上常见的传感器很多,但做嵌入式视频处理,OV5640 和 IMX219 这两颗芯片,你迟早会碰到。为什么?因为性价比高、资料多、而且都能跑到 500 万像素以上。我在项目中用过不下十种摄像头模组,这两颗是最「皮实」的。

2.1 OV5640 与 IMX219 核心参数对比

先看一张对比表,心里有个数。

参数 OV5640 IMX219
分辨率 最高 2592x1944 (5MP) 最高 3280x2464 (8MP)
像素尺寸 1.4 µm 1.12 µm
输出接口 DVP / MIPI CSI-2 (1-2 lane) MIPI CSI-2 (2 lane)
帧率 1080p@30fps / 720p@60fps 1080p@30fps / 720p@60fps
控制接口 I2C (SCCB 兼容) I2C
工作电压 核心 1.5V / IO 1.8-3.3V 核心 1.2V / IO 1.8V
功耗 约 150mW (1080p) 约 140mW (1080p)

嗯,这里要注意。OV5640 支持 DVP 并行接口,这对老一些的 FPGA 开发板很友好。IMX219 只走 MIPI,信号速率高,但布线要求也高。我刚开始用 IMX219 时,就因为差分对没等长,画面全是雪花——折腾了两天才发现是 PCB 走线的问题。

2.2 传感器内部结构

这两颗传感器内部逻辑其实差不多。说白了,就是「感光 → 模拟处理 → ADC → 数字处理 → 输出」这么一条流水线。

核心流程:

  1. 像素阵列:光电二极管把光转成电荷
  2. 模拟增益:放大微弱信号(AGC)
  3. ADC 转换:10-bit / 12-bit 量化
  4. ISP 处理:坏点校正、白平衡、去马赛克
  5. 格式输出:RAW / YUV / JPEG 等

你想想看,FPGA 接传感器,其实就是在「数字处理」和「格式输出」这两块做文章。前面的模拟部分,传感器内部已经帮你搞定了。

下面这张图,是我画的一个简化结构,方便你理解数据怎么从光信号变成 MIPI 差分信号。

像素阵列 5MP / 8MP 模拟处理 增益 + 相关双采样 ADC 10-bit / 12-bit ISP + 输出 MIPI / DVP I2C 控制总线(寄存器读写) 外部时钟 XCLK

你看,I2C 总线就像一条「指挥链」,贯穿了模拟、ADC、ISP 各个模块。你通过 I2C 写寄存器,就能控制增益、曝光、输出格式——说白了,传感器怎么干活,全看你寄存器怎么配。

2.3 寄存器配置要点

传感器的寄存器,少则几百个,多则上千。你不可能全记住,也没必要。我一般只关注这几类:

  • 系统控制:软复位、时钟分频、PLL 配置
  • 输出格式:分辨率、帧率、数据格式(RAW/YUV/JPEG)
  • 模拟控制:增益、曝光时间、黑电平校准
  • ISP 控制:白平衡、色彩矩阵、Gamma 校正

我的习惯:拿到一颗新传感器,先配好 PLL 和输出格式,让传感器能「出图」。然后再慢慢调图像质量。别一上来就调白平衡——画面都出不来,调了也白调。

举个例子,OV5640 初始化时,需要先软复位(寄存器 0x3008 = 0x80),然后配置 PLL。下面是一段典型的初始化序列:

// OV5640 初始化片段(I2C 写入)
// 1. 软复位
i2c_write(0x3008, 0x80);
delay_ms(10);

// 2. 配置 PLL 为 24MHz 输入,输出 84MHz
i2c_write(0x3034, 0x1A);  // PLL 分频
i2c_write(0x3035, 0x21);  // PLL 倍频
i2c_write(0x3036, 0x46);  // PLL 控制

// 3. 设置输出为 640x480 @ 30fps
i2c_write(0x3808, 0x02);  // 宽度高字节
i2c_write(0x3809, 0x80);  // 宽度低字节 (640)
i2c_write(0x380A, 0x01);  // 高度高字节
i2c_write(0x380B, 0xE0);  // 高度低字节 (480)

// 4. 选择输出格式 YUV422
i2c_write(0x4300, 0x30);  // 格式选择

嗯,这里要注意。不同批次、不同模组的传感器,PLL 参数可能略有差异。我曾经在 OV5640 上踩过一个坑:同一份寄存器配置,在 A 模组上跑 1080p@30fps 没问题,换到 B 模组就丢帧。后来发现是晶振精度不一样,微调了 PLL 分频比才解决。

2.4 I2C 控制接口详解

I2C 是传感器控制的核心。说白了,就是两根线:SCL(时钟)和 SDA(数据)。FPGA 作为主机,传感器作为从机。

OV5640 和 IMX219 的 I2C 地址不同:

传感器 7-bit 地址 写地址 读地址
OV5640 0x3C 0x78 0x79
IMX219 0x10 0x20 0x21

写寄存器的时序很简单:

  1. 主机发送起始信号
  2. 发送从机地址 + 写位(0)
  3. 等待从机应答
  4. 发送寄存器地址高字节
  5. 等待应答
  6. 发送寄存器地址低字节
  7. 等待应答
  8. 发送数据字节
  9. 等待应答
  10. 发送停止信号

注意:OV5640 的寄存器地址是 16-bit,所以需要发两个字节。IMX219 的寄存器地址是 8-bit,只发一个字节。很多新手在这里搞混,写进去的寄存器值对不上号。

读寄存器稍微复杂一点,需要「伪写」再「重读」:

// IMX219 读寄存器示例(伪代码)
// 1. 伪写:告诉传感器要读哪个寄存器
i2c_start();
i2c_write(0x20);       // 从机地址 + 写
i2c_wait_ack();
i2c_write(0x30);       // 寄存器地址 0x30
i2c_wait_ack();
i2c_stop();

// 2. 重读:从该地址读取数据
i2c_start();
i2c_write(0x21);       // 从机地址 + 读
i2c_wait_ack();
data = i2c_read();     // 读取一个字节
i2c_send_nack();       // 主机发送非应答
i2c_stop();

我在 FPGA 上实现 I2C 控制器时,习惯用状态机来做。SCL 频率一般设在 100kHz-400kHz。别跑太快,传感器不一定跟得上。我曾经为了省时间把 I2C 频率调到 1MHz,结果传感器直接不响应——嗯,老老实实降回 400kHz 就好了。

2.5 避坑指南

做传感器驱动,有几个坑我反复踩过,分享给你:

  • 上电时序:传感器对电源上电顺序有要求。比如 OV5640 要求 AVDD 先于 DOVDD 上电。顺序错了,传感器可能无法初始化。
  • 复位时序:硬件复位后,需要等待至少 1ms 才能开始 I2C 通信。别一上电就急着写寄存器。
  • I2C 应答检查:每次写寄存器后,检查从机是否应答。如果连续 NAK,说明传感器没准备好或者地址错了。
  • 寄存器备份:调试时,先把传感器的默认寄存器读出来备份。万一调乱了,还能恢复。

一个小技巧:调试 I2C 时,用逻辑分析仪抓波形。看起始条件、地址、应答位对不对。很多时候,代码看着没问题,但波形就是不对——这时候别猜,直接看波形。

好了,关于 OV5640 和 IMX219 的基础知识,咱们就聊到这儿。记住一句话:传感器是「用寄存器说话的」。你把它寄存器摸透了,它就能乖乖给你出图。

可可精品资料库