第2章:Camera硬件基础
做Camera驱动开发,不懂硬件基础,就像开车不看路。我刚开始接触V4L2时,就吃过这个亏——软件调通了,图像就是出不来,最后发现是MIPI时钟配错了。所以这一章,咱们把Camera模组的硬件基础彻底搞明白。
2.1 Camera模组组成
一个典型的Camera模组,说白了就三大件:镜头、传感器、ISP。有些模组把ISP集成在传感器里,有些则外挂。我习惯把它们拆开来看,这样调试时思路更清晰。
2.1.1 镜头(Lens)
镜头负责把光线汇聚到传感器上。参数很多,但驱动开发最关心的是:
- 焦距:决定了视场角大小
- 光圈:影响进光量和景深
- 接口类型:M12、S mount等,决定了模组物理尺寸
嗯,这里有个坑。我曾经遇到一个项目,镜头和传感器不匹配,导致画面四角发暗。后来才发现是镜头的光学中心没对准传感器中心。所以选型时一定要看镜头和传感器的匹配表。
2.1.2 图像传感器(Image Sensor)
传感器是模组的心脏。它把光信号转成电信号。目前主流的有两种:
| 类型 | 优点 | 缺点 | 常见应用 |
|---|---|---|---|
| CCD | 画质好,噪声低 | 功耗高,成本高 | 工业相机、医疗设备 |
| CMOS | 功耗低,集成度高 | 噪声相对大 | 手机、安防、车载 |
我个人习惯用CMOS传感器做嵌入式开发。为什么?因为CMOS可以把ADC、时序控制、甚至部分ISP功能都集成在芯片里,外围电路简单很多。你想想看,一个手机摄像头模组就那么点空间,用CCD根本塞不进去。
关键参数:分辨率、帧率、像素大小、动态范围、信噪比。驱动开发时,这些参数决定了V4L2的格式配置和时钟计算。
2.1.3 ISP(图像信号处理器)
ISP负责把传感器输出的原始RAW数据,处理成我们肉眼看着舒服的图像。主要干这几件事:
- 去马赛克:把Bayer格式转成RGB
- 白平衡:校正色温偏差
- 降噪:减少传感器噪声
- 锐化:增强边缘细节
- Gamma校正:调整亮度曲线
我在项目中遇到过最头疼的事,就是ISP参数没调好,图像偏绿。折腾了两天,最后发现是白平衡增益寄存器写反了。所以调试ISP时,我建议先关掉所有处理,看RAW数据是否正常,再一步步打开功能。
小技巧:很多SoC内置了ISP,比如Rockchip的RKISP、Allwinner的ISP。驱动开发时,你只需要通过V4L2的subdev接口配置参数,ISP硬件会自动处理。
2.2 MIPI CSI-2接口协议
MIPI CSI-2是目前最主流的摄像头接口协议。说白了,它就是一套把图像数据从传感器传到SoC的通信标准。我刚开始接触时,被那些D-PHY、C-PHY、Lane、Clock的概念搞得晕头转向。其实没那么复杂。
2.2.1 物理层:D-PHY
D-PHY使用差分信号传输,一对线传时钟,一对或多对线传数据。每对数据线叫一个Lane。常见的配置有:
- 1-Lane:低分辨率,比如VGA摄像头
- 2-Lane:中等分辨率,比如720p
- 4-Lane:高分辨率,比如1080p以上
你想想看,为什么用差分信号?因为抗干扰能力强。嵌入式设备里电磁环境复杂,单端信号很容易被干扰。我有个项目,摄像头线缆长了点,图像就出现条纹,后来换成屏蔽线才解决。
2.2.2 协议层:CSI-2
CSI-2定义了数据怎么打包传输。数据包分为:
- 短包:传输控制信息,比如帧开始、帧结束
- 长包:传输图像数据,包含包头、数据、包尾
这里有个关键点:CSI-2支持多种数据类型,比如RAW8、RAW10、YUV422、RGB565等。驱动开发时,你需要在传感器端配置输出格式,同时在SoC端配置接收格式,两者必须匹配。
注意:MIPI时钟频率和Lane数决定了最大带宽。计算公式:带宽 = 时钟频率 × Lane数 × 2(DDR)。如果带宽不够,图像就会丢帧。我曾经因为时钟配低了,1080p@30fps死活跑不起来,后来把时钟从400MHz提到500MHz才搞定。
2.2.3 调试方法
调试MIPI接口,我一般用示波器看时钟和数据波形。但更实用的方法是:
- 检查SoC的MIPI接收端是否锁定时钟
- 读取传感器状态寄存器,看是否有CRC错误
- 用V4L2的
v4l2-ctl工具抓取原始数据
# 检查MIPI时钟锁定状态
cat /sys/kernel/debug/mipi_csi/status
# 抓取一帧RAW数据
v4l2-ctl --device=/dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=RG10 --stream-mmap --stream-to=frame.raw --stream-count=1
2.3 I2C控制总线
I2C是Camera模组最常用的控制总线。传感器、ISP、甚至镜头驱动(VCM)都通过I2C来配置寄存器。说白了,I2C就是一条两线制的串行总线:SCL(时钟)和SDA(数据)。
2.3.1 I2C协议要点
- 设备地址:每个I2C设备有唯一地址,通常是7位或10位
- 读写操作:主机发送起始信号、设备地址、读写位,然后传输数据
- 速率:标准模式100kHz,快速模式400kHz,高速模式3.4MHz
嗯,这里要注意。很多传感器支持多地址配置,比如OV5640的默认地址是0x3C,但可以通过引脚拉高拉低改成0x3E。我遇到过两个摄像头共用一条I2C总线,地址冲突导致其中一个无法识别。解决办法就是改地址或者用I2C MUX。
2.3.2 驱动中的I2C操作
在Linux驱动里,I2C读写通常用i2c_transfer或i2c_smbus接口。我个人习惯用i2c_smbus_write_byte_data,因为它封装好了寄存器地址和数据,代码更简洁。
#include <linux/i2c.h>
static int sensor_write_reg(struct i2c_client *client, u16 reg, u8 val)
{
int ret;
struct i2c_msg msg;
u8 buf[3];
buf[0] = (reg >> 8) & 0xFF; // 高字节地址
buf[1] = reg & 0xFF; // 低字节地址
buf[2] = val; // 数据
msg.addr = client->addr;
msg.flags = 0; // 写操作
msg.len = 3;
msg.buf = buf;
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret < 0) {
dev_err(&client->dev, "I2C write failed: %d\n", ret);
return ret;
}
return 0;
}
避坑指南:我曾经因为I2C时序问题,导致传感器寄存器写不进去。后来发现是SoC的I2C控制器时钟配错了,SCL频率太高,传感器跟不上。解决办法是把I2C时钟降到100kHz,问题就解决了。所以调试时,先用慢速模式,稳定后再提速。
2.3.3 常见问题
- 设备找不到:用
i2cdetect工具扫描总线,确认地址是否正确 - 读写超时:检查I2C上拉电阻,通常4.7kΩ比较合适
- 数据错误:用逻辑分析仪抓波形,看时序是否满足传感器要求
# 扫描I2C总线,查找设备
i2cdetect -y 0
# 读取传感器ID寄存器
i2cget -y 0 0x3c 0x00 w
好了,Camera硬件基础就讲到这里。下一章我们会深入V4L2框架,看看怎么把这些硬件抽象成设备节点。记住一句话:硬件是基础,软件是灵魂。搞懂了硬件,驱动开发就成功了一半。