2、图像传感器基础:CCD与CMOS传感器区别、Bayer阵列原理、Sensor驱动与初始化

好,咱们开始第二讲。这一章是ISP调试的基石——图像传感器。说白了,你后面所有调优工作,都是建立在传感器把光转成电信号这个第一步上的。如果这一步没搞明白,后面调得再好也是白搭。

我个人习惯,拿到一个新项目,第一件事就是看Sensor的datasheet。不看清楚它的底细,你连它输出的数据对不对都不敢保证。咱们今天就把CCD和CMOS的区别、Bayer阵列的原理、以及Sensor怎么驱动起来,一次性讲透。

2.1 CCD与CMOS传感器:两种主流技术路线

先聊聊传感器本身。目前市面上主流的图像传感器就两种:CCD和CMOS。你可能会问,现在手机、相机不都是CMOS吗?CCD是不是过时了?嗯,也不完全是。

2.1.1 CCD传感器

CCD,全称电荷耦合器件。它的工作方式很有意思——每个像素点收集到的光生电荷,需要像接力棒一样,一行一行地传递到输出端,最后统一转换成电压信号。

优点:

  • 噪声低,画质干净。这是它最大的优势。
  • 填充因子高,感光面积大。
  • 一致性非常好,每个像素的响应很均匀。

缺点:

  • 功耗高。我记得以前用CCD的相机,拍一会儿机身就发热。
  • 读取速度慢。因为要串行传输,帧率上不去。
  • 成本高,工艺复杂。
  • 不支持局部读取,灵活性差。

我的经验:CCD现在主要用在工业检测、天文摄影、医疗影像这些对画质要求极高、对速度不敏感的领域。消费电子领域基本被CMOS取代了。

2.1.2 CMOS传感器

CMOS,互补金属氧化物半导体。它跟CCD最大的不同在于——每个像素点旁边都自带一个放大器,直接把电荷转成电压信号输出。说白了,就是每个像素都有自己的“小喇叭”,不用排队等传递。

优点:

  • 功耗低,省电。
  • 读取速度快,轻松做到高帧率。
  • 成本低,工艺跟主流芯片制造兼容。
  • 支持窗口读取、Binning等灵活操作。

缺点:

  • 噪声相对较大。每个像素的放大器一致性不如CCD。
  • 填充因子低,部分面积被电路占用了。

避坑指南:我曾经在一个项目中,选了一款低成本的CMOS传感器,结果暗光下噪声大得离谱。后来才发现,它的像素尺寸太小,信噪比根本不够。所以选型时,像素尺寸和工艺制程一定要看仔细。

2.1.3 两者对比总结

特性 CCD CMOS
噪声 较高
功耗
读取速度
成本
灵活性
主流应用 工业、医疗、科研 手机、相机、安防、汽车

2.2 Bayer阵列原理:为什么传感器拍出来是彩色的?

好,传感器把光转成了电信号,但有个问题——它只能感知光的强度,分不清颜色。那彩色照片是怎么来的?这就得靠Bayer阵列了。

说白了,Bayer阵列就是在每个像素前面盖一层彩色滤光片。滤光片的排列方式很有讲究,最常见的是RGGB模式:

  • 50%的像素是绿色(G)
  • 25%的像素是红色(R)
  • 25%的像素是蓝色(B)

为什么绿色占一半?因为人眼对绿色最敏感。你想想看,自然界中绿色信息最多,所以多分配一些像素给绿色,能更好地还原细节。

关键点:每个像素只记录一种颜色。要得到完整的彩色图像,必须通过插值算法把缺失的颜色算出来。这个过程就叫去马赛克(Demosaic),是ISP pipeline中非常重要的一步。

我刚开始做ISP时,总觉得Bayer阵列很简单。直到有一次,我调试一个低光照场景,发现图像边缘全是彩色伪影。查了半天,原来是去马赛克算法没处理好。嗯,这里要注意,Bayer阵列的排列方向(是RGGB还是BGGR)必须跟Sensor的配置一致,否则颜色会完全错乱。

2.2.1 常见的Bayer排列模式

模式 排列方式 常见厂商
RGGB 第一行:R G R G...
第二行:G B G B...
Sony、OmniVision
BGGR 第一行:B G B G...
第二行:G R G R...
部分Samsung
GRBG 第一行:G R G R...
第二行:B G B G...
部分安防Sensor

警告:配置Bayer模式时,一定要跟Sensor的datasheet核对。我曾经见过一个团队,因为搞错了Bayer顺序,整个项目的图像颜色都是反的,排查了整整两天。

2.3 Sensor驱动与初始化:让传感器开始工作

好了,理论讲完了,咱们来点实际的。怎么让Sensor开始干活?

Sensor驱动和初始化,说白了就是通过I2C或SPI总线,往Sensor的寄存器里写配置值。每个寄存器控制着Sensor的一个功能,比如曝光时间、增益、帧率、输出格式等等。

2.3.1 初始化流程

我一般按这个步骤来:

  1. 上电时序:先给Sensor供电,然后提供主时钟(MCLK),最后拉高复位引脚。顺序不能乱,否则Sensor可能无法正常启动。
  2. 读取ID:通过I2C读取Sensor的芯片ID,确认通信正常。这一步很重要,能快速排查硬件连接问题。
  3. 写入初始配置:按照datasheet推荐的初始化序列,写入一组寄存器值。包括输出分辨率、帧率、增益模式、Bayer排列等。
  4. 等待稳定:配置完成后,等待几帧时间,让Sensor内部PLL锁定,输出稳定的图像数据。
  5. 检查输出:通过MIPI或DVP接口抓取一帧数据,验证图像是否正常。

我的习惯:我会把初始化序列写成一个数组,每个元素包含寄存器地址和值。这样方便调试时增删改查。另外,我会在初始化完成后,回读几个关键寄存器,确认写入成功。

2.3.2 代码示例:I2C初始化Sensor

下面是一个简化的初始化代码示例,用伪代码表示:

// Sensor初始化函数
int sensor_init(void) {
    // 1. 上电时序
    sensor_power_on();
    delay_ms(10);
    
    // 2. 读取芯片ID
    uint16_t chip_id = i2c_read(0x0000);
    if (chip_id != 0x1234) {
        printf("Error: Sensor ID mismatch!\n");
        return -1;
    }
    
    // 3. 写入初始化序列
    const reg_config_t init_seq[] = {
        {0x0100, 0x00},  // 软件复位
        {0x0103, 0x01},  // 进入配置模式
        {0x0340, 0x02},  // 输出分辨率高字节
        {0x0341, 0xD0},  // 输出分辨率低字节 (720p)
        {0x0202, 0x00},  // 曝光时间高字节
        {0x0203, 0x64},  // 曝光时间低字节 (100行)
        {0x0204, 0x01},  // 增益高字节
        {0x0205, 0x00},  // 增益低字节 (1x)
        {0x0100, 0x01},  // 退出配置模式,开始输出
    };
    
    for (int i = 0; i < sizeof(init_seq)/sizeof(init_seq[0]); i++) {
        i2c_write(init_seq[i].addr, init_seq[i].value);
        delay_ms(1);
    }
    
    // 4. 等待稳定
    delay_ms(100);
    
    // 5. 回读验证
    uint16_t check = i2c_read(0x0100);
    if (check != 0x01) {
        printf("Warning: Sensor not in streaming mode!\n");
        return -2;
    }
    
    return 0;
}

注意:不同Sensor的初始化序列差异很大。有些Sensor需要先写入PLL配置,再设置分辨率。有些则必须先设置输出格式。一定要严格按照datasheet的时序要求来,否则Sensor可能输出花屏或者根本不工作。

2.3.3 常见问题与调试技巧

  • I2C通信失败:检查上拉电阻、电压电平是否匹配。我遇到过好几次,因为电平不匹配导致通信不稳定。
  • 输出花屏:大概率是MIPI lane配置不对,或者时钟频率不匹配。用示波器量一下MCLK和MIPI clock。
  • 图像全黑或全白:检查曝光时间和增益是否设置合理。全黑可能是曝光时间太短,全白可能是增益太大。
  • 颜色不对:检查Bayer排列模式是否配置正确。这个前面强调过了。

我的经验:调试Sensor时,我习惯先让它输出一个简单的测试图案(很多Sensor内置测试图案生成器)。如果测试图案正常,说明硬件和驱动没问题。然后再切换到正常模式,逐步排查图像质量问题。这样能快速定位是Sensor问题还是ISP问题。

好了,这一章的内容就到这里。CCD和CMOS的区别、Bayer阵列的原理、Sensor驱动初始化,这三块是ISP调试的入门必修课。下一章咱们会深入聊聊ISP pipeline的各个模块,从黑电平校正开始。到时候见。