第四章 图像采集接口设计:从摄像头到FPGA的最后一公里

图像采集接口,说白了就是摄像头和FPGA之间的「翻译官」。摄像头拍到的光信号转成电信号,但FPGA不认识啊,得有个接口把数据规规矩矩地送进来。我这些年做图像加速项目,踩得最多的坑就在这——接口没搞定,后面算法再牛也白搭。

今天咱们聊三个主流方案:OV5640/MT9V034的I2C配置、DVP并行接口、以及MIPI CSI-2。嗯,一个一个来。

4.1 摄像头配置:I2C总线上的「悄悄话」

摄像头传感器上电后,默认配置通常不是我们想要的。比如OV5640默认输出是640x480,但你想跑1080p,怎么办?得通过I2C总线给它写寄存器。

I2C是个双线协议:SCL(时钟)和SDA(数据)。摄像头作为从设备,FPGA作为主设备。我个人习惯把I2C控制器做成一个状态机,简单可靠。

核心要点:OV5640的I2C地址是0x3C(写)/0x3D(读),MT9V034是0x90(写)/0x91(读)。别搞反了,我刚开始就犯过这错。

写寄存器的流程很简单:

  1. 发起始位 + 设备地址(写)
  2. 等ACK
  3. 发寄存器高8位地址
  4. 等ACK
  5. 发寄存器低8位地址
  6. 等ACK
  7. 发数据字节
  8. 等ACK
  9. 发停止位

代码实现?给你看个核心片段:

// I2C写字节状态机 - 我自己项目里一直在用的模板
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state <= IDLE;
        scl_out <= 1'b1;
        sda_out <= 1'b1;
    end else begin
        case (state)
            IDLE: begin
                if (i2c_start_req) begin
                    state <= START;
                    bit_cnt <= 4'd8;
                end
            end
            START: begin
                // 拉低SDA,再拉低SCL
                sda_out <= 1'b0;
                state <= SEND_ADDR;
            end
            // ... 后续状态
        endcase
    end
end

我的经验:I2C时钟频率别超过400kHz,OV5640对时序挺敏感的。我曾经为了赶速度把时钟提到1MHz,结果摄像头死活不响应,查了两天才发现是时序违规。

4.2 DVP接口:并行数据的「老黄牛」

DVP(Digital Video Port)是经典的并行接口。信号线包括:

  • PCLK:像素时钟,每个上升沿采一个像素
  • VSYNC:帧同步,高电平表示一帧开始
  • HREF:行同步,高电平表示一行有效数据
  • DATA[7:0]或DATA[9:0]:像素数据

时序其实很简单:VSYNC拉高后,HREF会一行一行地拉高,每拉高一次就传一行像素。PCLK每个上升沿采一个字节(8位)或一个字(10位)。

嗯,这里要注意:OV5640的DVP模式输出是10位数据,但很多项目只用高8位。我个人习惯把低2位丢掉,省事。

避坑指南:我曾经遇到过VSYNC和HREF的极性搞反的问题。有些摄像头是低电平有效,有些是高电平有效。一定要看数据手册的时序图,别想当然。

Verilog实现的核心是行缓存:

// DVP数据采集 - 我常用的写法
always @(posedge pclk or negedge rst_n) begin
    if (!rst_n) begin
        pixel_data <= 16'd0;
        pixel_valid <= 1'b0;
    end else begin
        if (href && !vsync) begin  // 行有效且不在帧消隐区
            // 拼接两个8位数据成一个16位像素
            if (pclk_cnt == 0) begin
                pixel_data[15:8] <= dvp_data;
                pclk_cnt <= 1;
            end else begin
                pixel_data[7:0] <= dvp_data;
                pixel_valid <= 1'b1;
                pclk_cnt <= 0;
            end
        end else begin
            pixel_valid <= 1'b0;
            pclk_cnt <= 0;
        end
    end
end

DVP的优点是简单,缺点是线太多。8位数据就要10根线(8数据+2同步),要是跑1080p@60fps,PCLK得跑到148.5MHz,信号完整性就开始头疼了。

4.3 MIPI CSI-2:高速串行的「新贵」

MIPI CSI-2是现在手机摄像头的主流接口。它用差分信号传输,数据线可以1条、2条或4条lane。每对差分线传输速度能到1Gbps以上。

物理层用的是Sub-LVDS(低压差分信号),电压摆幅只有200mV左右。FPGA接收的时候需要专门的硬核(比如Xilinx的ISERDES)或者外部接口芯片。

关键区别:DVP是并行总线,MIPI是串行差分。MIPI的优势是线少、速率高、抗干扰强。但实现复杂度也高了一个数量级。

MIPI CSI-2的协议层分三层:

  • PHY层:处理电气特性、时钟恢复、串并转换
  • Lane管理层:数据分发、字节对齐、ECC校验
  • 协议层:解析帧头帧尾、数据类型(RAW8/RAW10/YUV等)

说实话,在FPGA里完整实现MIPI CSI-2接收端是个大工程。我建议的做法是:

  1. 用厂商提供的IP核(Xilinx MIPI CSI-2 RX Subsystem)
  2. 或者用外部桥接芯片(比如TI的DS90UB系列)转成并行
  3. 实在要自己写,至少准备好眼图仪和高速示波器

我的建议:初学者先从DVP入手,把I2C配置和行场同步搞明白。MIPI等有高速设计经验了再碰。我当年第一次调MIPI,光眼图就调了两周。

4.4 知识体系总览

下面这张图是我自己总结的,把三个接口的关系和选择逻辑画清楚了:

图像采集接口知识体系 摄像头传感器 I2C配置接口 SCL/SDA双线协议 DVP并行接口 PCLK+VSYNC+HREF+DATA MIPI CSI-2 差分串行+Sub-LVDS 特性对比 • 复杂度:I2C < DVP < MIPI • 速率:I2C(400kbps) < DVP(150MHz) < MIPI(1Gbps/lane) • 线数:I2C(2线) < MIPI(4-10线) < DVP(10-20线) • 适用场景:I2C=配置专用 / DVP=低分辨率 / MIPI=高分辨率高速 选择建议:低分辨率用DVP,高分辨率用MIPI,I2C永远需要

4.5 实际项目中的选择策略

我做过几个图像加速项目,总结下来:

应用场景 推荐摄像头 接口方案 我的备注
工业检测(VGA级别) MT9V034 DVP 全局快门,适合拍运动物体
安防监控(1080p) OV5640 DVP或MIPI OV5640两种接口都支持
手机/车载(4K以上) IMX系列 MIPI CSI-2 必须用FPGA硬核或桥接芯片

重要提醒:不管选哪种接口,I2C配置都是第一步。摄像头不上电配置好,后面啥都干不了。我习惯先把I2C读写调通,再用逻辑分析仪抓一下寄存器回读,确认配置写进去了再往下走。

嗯,图像采集接口这块就聊到这儿。说白了就是根据你的分辨率和帧率需求,选一个合适的接口方案。DVP适合练手和低分辨率项目,MIPI是未来趋势但门槛高。我个人建议:先把DVP玩透,再碰MIPI,这样基础扎实。

可可精品资料库