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 → 数字处理 → 输出」这么一条流水线。
核心流程:
- 像素阵列:光电二极管把光转成电荷
- 模拟增益:放大微弱信号(AGC)
- ADC 转换:10-bit / 12-bit 量化
- ISP 处理:坏点校正、白平衡、去马赛克
- 格式输出:RAW / YUV / JPEG 等
你想想看,FPGA 接传感器,其实就是在「数字处理」和「格式输出」这两块做文章。前面的模拟部分,传感器内部已经帮你搞定了。
下面这张图,是我画的一个简化结构,方便你理解数据怎么从光信号变成 MIPI 差分信号。
你看,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 |
写寄存器的时序很简单:
- 主机发送起始信号
- 发送从机地址 + 写位(0)
- 等待从机应答
- 发送寄存器地址高字节
- 等待应答
- 发送寄存器地址低字节
- 等待应答
- 发送数据字节
- 等待应答
- 发送停止信号
注意: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 的基础知识,咱们就聊到这儿。记住一句话:传感器是「用寄存器说话的」。你把它寄存器摸透了,它就能乖乖给你出图。
可可精品资料库