3、摄像头驱动与初始化:理解摄像头(如OV5640)的I2C配置协议,编写初始化序列,通过逻辑分析仪验证波形。

摄像头驱动,说白了就是让摄像头“活过来”的第一步。很多同学一上来就急着读图像数据,结果发现画面全是雪花。嗯,我当年也干过这事。后来才明白,摄像头初始化没做好,后面全是白费功夫。

今天我们就拿OV5640这个经典传感器开刀。它支持500万像素,I2C接口配置,市面上绝大多数FPGA图像项目都在用。我个人习惯,拿到一个新摄像头,第一件事不是看数据手册,而是先搞清楚它的I2C协议——这是所有配置的基础。

3.1 I2C协议基础回顾

I2C是个两线制协议:SCL(时钟)和SDA(数据)。主设备(FPGA)控制时钟,从设备(摄像头)响应地址。OV5640的I2C地址是0x78(写)和0x79(读)。

为什么是这两个地址?因为芯片的ADDR引脚电平决定了地址最低位。OV5640默认拉低,所以7位地址是0x3C,左移一位加上读写位,就成了0x78和0x79。这个细节,我刚开始做的时候踩过坑——地址写错了,逻辑分析仪抓出来全是NACK。

核心要点: I2C写操作流程:START -> 设备地址(写) -> ACK -> 寄存器地址高字节 -> ACK -> 寄存器地址低字节 -> ACK -> 数据 -> ACK -> STOP

OV5640的寄存器是16位地址,8位数据。这和很多8位地址的传感器不一样。你想想看,如果按8位地址去发,摄像头根本不会理你。

3.2 初始化序列的编写思路

OV5640的初始化序列,说白了就是往一堆寄存器里写特定值。数据手册里有个“初始化序列表”,几百行。但别慌,我们不需要全部照搬。

我个人习惯,把初始化分成三个阶段:

  1. 芯片复位与时钟配置——先让摄像头“醒过来”
  2. 输出格式设置——告诉它你要RGB565还是JPEG
  3. 分辨率与帧率配置——比如640x480 @ 30fps

我曾经在项目里遇到一个问题:摄像头输出图像颜色不对。折腾了两天,最后发现是初始化序列里漏写了一行——设置色彩矩阵的寄存器。从那以后,我每次写初始化代码,都会对照数据手册逐行核对。

3.3 代码实现:FPGA端的I2C控制器

下面是一个简化的I2C写操作Verilog代码。注意,这里只展示了单次写操作,实际初始化需要循环调用。

module i2c_wr #(
    parameter CLK_DIV = 250  // 50MHz -> 200kHz I2C时钟
)(
    input  clk, rst_n,
    input  start,            // 启动信号
    input  [15:0] reg_addr,  // 16位寄存器地址
    input  [7:0]  reg_data,  // 8位数据
    output reg done,         // 完成标志
    inout  sda,              // I2C数据线
    output reg scl           // I2C时钟线
);

    // 状态机定义
    localparam IDLE   = 4'd0;
    localparam START  = 4'd1;
    localparam ADDR   = 4'd2;  // 发送设备地址
    localparam ACK1   = 4'd3;
    localparam REG_H  = 4'd4;  // 寄存器高字节
    localparam ACK2   = 4'd5;
    localparam REG_L  = 4'd6;  // 寄存器低字节
    localparam ACK3   = 4'd7;
    localparam DATA   = 4'd8;  // 发送数据
    localparam ACK4   = 4'd9;
    localparam STOP   = 4'd10;

    reg [3:0] state, next_state;
    reg [7:0] clk_cnt;
    reg sda_out, sda_oe;  // sda输出和输出使能

    // 双向SDA控制
    assign sda = sda_oe ? sda_out : 1'bz;

    // 时钟分频与状态机跳转(略去完整实现,重点看结构)
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state <= IDLE;
            scl <= 1;
            sda_oe <= 0;
            done <= 0;
        end else begin
            case (state)
                IDLE: begin
                    if (start) begin
                        state <= START;
                        clk_cnt <= 0;
                    end
                end
                // ... 其他状态跳转
                STOP: begin
                    // 产生STOP条件:SCL高时SDA从低变高
                    state <= IDLE;
                    done <= 1;
                end
            endcase
        end
    end

endmodule

这段代码的核心是状态机。每个状态对应I2C协议的一个阶段。注意看,我用了sda_oe来控制SDA方向——这是FPGA做I2C的关键。很多新手直接assign sda = sda_out,结果SDA被拉死,永远出不了数据。

调试技巧: 写I2C控制器时,先不要急着连摄像头。用逻辑分析仪抓SCL和SDA,看波形对不对。我一般会先发一个固定的寄存器地址和数据,比如0x3008写0x80(软复位),确认波形完美了再往下走。

3.4 逻辑分析仪验证波形

波形验证是联调中最重要的一环。我个人习惯用Saleae逻辑分析仪,采样率设到24MHz以上。为什么?因为I2C时钟200kHz,每个bit约5us,24MHz采样能保证每个bit有120个采样点,波形还原度很高。

抓波形时,重点关注以下几点:

  • START条件: SCL高时,SDA从高变低。这个沿必须干净,不能有毛刺。
  • 地址字节: 0x78(写)后面必须跟ACK(SDA被拉低)。如果看到NACK,检查地址对不对。
  • 寄存器地址: 16位,先高后低。比如0x3008,先发0x30,再发0x08。
  • 数据字节: 写完后看ACK。如果摄像头没响应,可能是寄存器地址越界了。
  • STOP条件: SCL高时,SDA从低变高。这个沿也要干净。

下面是我用SVG画的一个典型I2C写操作时序图,帮你直观理解每个阶段:

OV5640 I2C写操作时序(逻辑分析仪视角) SCL SDA START 设备地址 0x78 ACK RegH 0x30 ACK RegL 0x08 ACK 数据 0x80 ACK STOP 每个字节后跟一个ACK位(从设备拉低SDA表示应答) 寄存器地址16位,先发高字节,再发低字节 控制信号 地址 应答 数据
常见坑点:
  • SCL频率不能超过400kHz(OV5640标准模式),我一般用200kHz,稳定可靠。
  • SDA在SCL低电平时才能变化,高电平时必须稳定。逻辑分析仪上如果看到SDA在SCL高时跳变,那肯定是协议错误。
  • 有些FPGA开发板的I2C上拉电阻没焊,导致波形上升沿很缓。我遇到过,补焊4.7kΩ上拉电阻就好了。

3.5 初始化序列的验证流程

写完了I2C控制器和初始化序列,怎么验证?我一般按这个步骤来:

  1. 单步测试: 先发一个寄存器,比如0x3008写0x80(软复位)。用逻辑分析仪抓波形,确认START、地址、数据、STOP都正确。
  2. 回读验证: OV5640支持读操作。写完后读同一个寄存器,看返回值是不是你写的值。如果读回来是0xFF,说明摄像头没响应——检查硬件连接。
  3. 批量初始化: 把初始化序列表里的寄存器逐条写入。注意,有些寄存器需要延时等待(比如软复位后要等10ms)。
  4. 输出检查: 初始化完成后,用示波器看摄像头的VSYNC和HREF引脚。如果有脉冲输出,说明摄像头已经开始工作了。

我记得有一次,初始化序列写完了,逻辑分析仪波形也完美,但就是没图像。折腾了半天,发现是初始化序列里有个寄存器地址写错了——0x4740写成了0x4741。差一个bit,摄像头直接罢工。从那以后,我每次都会用回读功能验证关键寄存器。

我的习惯: 在FPGA内部做一个I2C回读测试模块。初始化完成后,自动读取几个关键寄存器(比如芯片ID寄存器0x300A和0x300B),如果读回来是0x56和0x40,说明摄像头正常。这个自检逻辑,能帮你省下大量调试时间。

好了,摄像头驱动与初始化这部分就讲到这里。记住,I2C协议是基础,波形验证是手段,初始化序列是核心。把这三点吃透了,OV5640就能乖乖听你指挥。

可可精品资料库