其他分享
首页 > 其他分享> > 基于DDR3的摄像头OV5640的VGA显示

基于DDR3的摄像头OV5640的VGA显示

作者:互联网

基于DDR3的摄像头OV5640的VGA显示

一.DDR3 SDRAM

1.基本介绍

DDR3 SDRAM 英 文 全 称 “ Double-Data-Rate Three Synchronous Dynamic Random Access Memory”,译为“第三代双倍速率同步动态随机存取内存”或“同步动态随机存储器”,是动态随机存储器(Dynamic Random Access Memory,简称 DRAM)家族的一份子。
同步、动态、随机是其性能特点的外在说明,也是其区别其他存储器的特色标签。这三个概念性的标签,我们要好好理解掌握。
双倍速率(Double-Data-Rate):DDR3 SDRAM 存储器与 SDRAM 有一个很大的不同,DDR3 SDRAM 或者说带 DDR 开头的。包括 DDR1、DDR2、DDR3、DDR4、DDR4,他们都有一个很大的相似点,就是双边沿输出输入数据,通俗来讲就是在一个周期内输出 2 次数据,上升沿输出一次数据,下降沿输出一次数据。而 SDRAM 则是在一个周期内的上升沿输出一次数据,下降沿不输出,所以同频率的 DDR3 SDRAM 与 SDRAM 相比速度快一倍。
同步(Synchronous):与通常的异步 DRAM 不同,DDR3 SDRAM 存在一个同步接口,其工作时钟的时钟频率与对应控制器(CPU/FPGA)的时钟频率相同,并且 SDRAM 内部的命令发送与数据传输均以此时钟为基准,实现指令或数据的同步操作;
动态(Dynamic):DDR3 SDRAM 需要不断的刷新来保证存储阵列内数据不丢失;
随机(Random):数据在 DDR3 SDRAM 中并不是按照线性依次存储,而是可以自由指定地址进行数据的读写。
空间存储量大、读写速度快以及价格相对便宜等优点使其在存储界屹立不倒、经久不衰,广泛应用在计算机中。

2.存储原理

简单来说,DDR3 SDRAM 内部可以理解为一个存储阵列,这是 DDR3 SDRAM 区别于管道式存储,实现随机地址存取的结构特点。为方便读者理解,我们将 DDR3 SDRAM 内部存储阵列类比于一张表格,表格中的每一个单元格可以类比为存储阵列的单个存储单元。若想要实现存储阵列中的某一存储单元的数据读写操作,我们要通过行地址(RowAddress)和列地址(Column Address)(先行后列)精确定位到这一存储单元,进而进行数据的
读写操作,这就是所谓的随机地址存取。DDR3 SDRAM 存储阵列类比图,如下图所示:
在这里插入图片描述DDR3 SDRAM 的基本存储单位是存储单元,而一个存储单元的容量为若干个 Bit,对于 DDR3 SDRAM 而言就是芯片的位宽,每个 Bit 存放于一个单独的存储体中,存储体是利用电容能够保持电荷以及可充放电的特性制成,主要由行选通三极管、列选通三极管、存储电容以及刷新放大器构成。电容所存储的电荷会随时间慢慢释放,这就需要不断刷新为电容充电,以保证存储数据可靠性。
如图所示:在这里插入图片描述将每个存储单元简化为单 Bit 的存储体,再将若干存储体排列为矩阵,同一行将行地址线相连,同一列将列地址线相连,就构成了一个存储阵列的简化模型。DDR3 SDRAM:内部存储阵列的简化模型,具体见图:
在这里插入图片描述

3.内部逻辑功能图

在这里插入图片描述
由图 可知,DDR3 SDRAM 内部包含一个逻辑控制单元,内部包含模式寄存器和命令解码器。外部通过 CS_N、RAC_N、CAS_N、WE_N 以及地址总线向逻辑控制单元输入命令,命令经过命令解码器进行译码后,将控制参数保存到模式寄存器中,逻辑控制单元进而控制逻辑运行。外部通过地址总线输入地址信息,地址信息在逻辑控制单元进行逻辑控制时起到辅助作用,除此之外,复用的地址总线与 Bank 控制逻辑、行地址复用器、列地址计数锁存器、列地址解码器等内部器件共同作用,精确选定存储阵列中与行列地址相对应的存储单元,进而进行数据存取操作。

4.AXI接口介绍

AXI 的英文全称是 Advancede Xtensible Interface,即高级可扩展接口。之所以要采用 AXI4 接口对 ddr 进行读写是因为 Xilinx 的 migddrIp 核,无论是 6 系列还是 7 系列还是最新的 FPGA,都集成了 AXI4 接口,为了采用 AXI4 接口进行读写,后续可以兼容 xilinx的其他 fpga,可复用性更强。
AXI 协议是一种高性能、高带宽、低延迟的片内总线,具有如下特点:
1、总线的地址/控制和数据通道是分离的;
2、支持不对齐的数据传输;
3、支持突发传输,突发传输过程中只需要首地址;
4、具有分离的读/写数据通道;
5、支持显著传输访问和乱序访问;
6、更加容易进行时序收敛。
在数字电路中只能传输二进制数 0 和 1,因此可能需要一组信号才能高效地传输信息,这一组信号就组成了接口。AXI4 协议支持以下三种类型的接口:
1、AXI4:高性能存储映射接口。
2、AXI4-Lite:简化版的 AXI4 接口,用于较少数据量的存储映射通信。
3、AXI4-Stream:用于高速数据流传输,非存储映射接口。
在这里我们首先解释一下存储映射(Meamory Map)这一概念。如果一个协议是存储映射的,那么主机所发出的会话(无论读或写)就会标明一个地址。这个地址对应于系统存储空间中的一个地址,表明是针对该存储空间的读写操作。AXI4 协议支持突发传输,主要用于处理器访问存储器等需要指定地址的高速数据传输场景。AXI-Lite 为外设提供单个数据传输,主要用于访问一些低速外设中的寄存器。而 AXI-Stream 接口则像 FIFO 一样,数据传输时不需要地址,在主从设备之间直接连续读写数据,主要用于如视频、高速AD、PCIe、DMA 接口等需要高速数据传输的场合。

二.OV5640摄像头

1.基本介绍

该摄像头主要由镜头、图像传感器、板载电路及下方的信号引脚组成。镜头部件包含一个镜头座和一个可旋转调节距离的凸透镜,通过旋转可以调节焦距,正常使用时,镜头座覆盖在电路板上遮光,光线只能经过镜头传输到正中央的图像传感器,它采集得的图像数据直接传输给 FPGA 芯片,FPGA 将接收到的图像数据缓存到 DDR3 SDRAM中,在 VGA 图像显示有效区域,VGA 驱动自 DDR3 SDRAM 读取图像数据在显示屏上进行显示。图像传感器是摄像头的核心部件,上述摄像头中的图像传感器是一款型号为
OV5640 的 CMOS 类型数字图像传感器。该传感器支持输出最大为 500 万像素的图像(2592x1944 分辨率),支持使用 VGA 时序输出图像数据,输出图像的数据格式支持YUV(422/420)、YCbCr422、 RGB565 以及 JPEG 格式,若直接输出 JPEG 格式的图像时可大大减少数据量,方便网络传输。它还可以对采集得的图像进行补偿,支持伽玛曲线、 白平衡、饱和度、色度等基础处理。根据不同的分辨率配置,传感器输出图像数据的帧率从 15-60 帧可调,工作时功率在 150mW-200mW 之间。
在这里插入图片描述

2.功能框图

在这里插入图片描述(1) 控制寄存器
标号1处的是 OV5640 的控制寄存器,它根据这些寄存器配置的参数来运行,而这些参数是由外部控制器通过 SIO_C 和 SIO_D 引脚写入的, SIO_C 与 SIO_D 使 用的通讯协议跟 I2C 十分类似。
(2) 通信、控制信号及时钟
标号2处包含了 OV5640 的通信、控制信号及外部时钟,其中 PCLK、 HREF 及VSYNC 分别是像素同步时钟、行同步信号以及帧同步信号,这与液晶屏控制中的信号是很类似的。 RESETB 引脚为低电平时,用于复位整个传感器芯片,PWDN 用于控制芯片进入低功耗模式。注意最后的一个 XCLK 引脚,它跟 PCLK是完全不同的, XCLK 是用于驱动整个传感器芯片的时钟信号,是外部输入到OV5640 的信号;而 PCLK 是 OV5640 输出数据时的同步信号,它是由 OV5640 输出的信号。 XCLK 可以外接晶振或由外部控制器提供。
(3) 感光矩阵
标号3处的是感光矩阵,光信号在这里转化成电信号,经过各种处理,这些信号存储成由一个个像素点表示的数字图像。
(4) 数据输出信号
标号4处包含了 DSP 处理单元,它会根据控制寄存器的配置做一些基本的图像处理运算。这部分还包含了图像格式转换单元及压缩单元,转换出的数据最终通过Y0-Y9 引脚输出,一般来说我们使用 8 根据数据线来传输。
(5) 数据输出信号
标号⑤处为 VCM 处理单元,他会通过图像分析来实现图像的自动对焦功能。要实现自动对焦还需要下载自动对焦固件到模组.(暂时没有加入这个功能)

三.代码设计

1.整体设计

在这里插入图片描述
各子模块功能介绍:
在这里插入图片描述由图表可知,OV5640-VGA 图像显示工程包含表格模块ov5640_vga_640x480 作为实验工程的顶层模块,内部实例化各子功能模块,连接对应信号,对外接收摄像头采集的图像数据,将处理后的数据存入 DDR3 SDRAM,在 VGA 显示器上显示出来;clk_gen 模块,调用 IP 核生成,产生整个实验工程的工作时钟;ov5640_top 模块,是 ov5640 部分各子功能模块的集合,实现 ov5640 摄像头的配置、图像采集与处理;DDR3 SDRAM_top 模块为 DDR3 SDRAM 读写控制器,存储处理后的图像数据;vga_ctrl 模块实现 VGA 显示器的驱动控制,读取 DDR3 SDRAM 存储的图像数据并在 VGA 显示屏上显示出来。

时钟生成模块直接调用PLL即可,AXI_DDR模块工程量较多,这里不做太多介绍,VGA驱动不需要介绍,这里仅详细介绍OV5640模块。
在这里插入图片描述各模块的功能:
在这里插入图片描述由上述图表可知,第一部分的 OV5640 相关模块包含 4 个子模块,首先是 ov5640_top模块,这一模块作为 ov5640 部分的顶层模块,内部实例化 3 个子功能模块,连接各子模块对应信号,外部对摄像头进行相关配置并接收摄像头采集的数据信息;ov5640_cfg 模块,是寄存器配置模块,内部包含对 ov5640 摄像头的配置信息;i2c_ctrl 模块,i2c 协议与SCCB 协议几乎无差别,使用 i2c 协议代替 SCCB 接口协议向 ov5640 摄像头写ov5640_cfg 模块内部包含的寄存器配置信息;ov5640_data 模块,是 ov5640 摄像头的图像采集模块,将摄像头传入的图像数据处理后写入 DDR3 SDRAM

2.OV5640各模块代码

ov5640_top模块

module  ov5640_top(

    input   wire            sys_clk         ,   //系统时钟
    input   wire            sys_rst_n       ,   //复位信号
    input   wire            sys_init_done   ,   //系统初始化完成(SDRAM + 摄像头)

    input   wire            ov5640_pclk     ,   //摄像头像素时钟
    input   wire            ov5640_href     ,   //摄像头行同步信号
    input   wire            ov5640_vsync    ,   //摄像头场同步信号
    input   wire    [ 7:0]  ov5640_data     ,   //摄像头图像数据

    output  wire            cfg_done        ,   //寄存器配置完成
    output  wire            sccb_scl        ,   //SCL
    inout   wire            sccb_sda        ,   //SDA
    output  wire            ov5640_wr_en    ,   //图像数据有效使能信号
    output  wire    [15:0]  ov5640_data_out     //图像数据
);


//parameter define
parameter    SLAVE_ADDR =  7'h3C   ; 	  // 器件地址(SLAVE_ADDR)
parameter    BIT_CTRL   =  1'b1         ; // 字地址位控制参数(16b/8b)
parameter    CLK_FREQ   = 26'd50_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter    I2C_FREQ   = 18'd250_000   ; // I2C的SCL时钟频率

//wire  define
wire            cfg_end     ;
wire            cfg_start   ;
wire    [23:0]  cfg_data    ;
wire            cfg_clk     ;


//------------- i2c_ctrl_inst -------------
i2c_ctrl
#(
    .DEVICE_ADDR    (SLAVE_ADDR ), //i2c设备器件地址
    .SYS_CLK_FREQ   (CLK_FREQ   ), //i2c_ctrl模块系统时钟频率
    .SCL_FREQ       (I2C_FREQ   )  //i2c的SCL时钟频率
)
i2c_ctrl_inst
(
    .sys_clk     (sys_clk       ),   //输入系统时钟,50MHz
    .sys_rst_n   (sys_rst_n     ),   //输入复位信号,低电平有效
    .wr_en       (1'b1          ),   //输入写使能信号
    .rd_en       (              ),   //输入读使能信号
    .i2c_start   (cfg_start     ),   //输入i2c触发信号
    .addr_num    (BIT_CTRL      ),   //输入i2c字节地址字节数
    .byte_addr   (cfg_data[23:8]),   //输入i2c字节地址
    .wr_data     (cfg_data[7:0] ),   //输入i2c设备数据

    .rd_data     (              ),   //输出i2c设备读取数据
    .i2c_end     (cfg_end       ),   //i2c一次读/写操作完成
    .i2c_clk     (cfg_clk       ),   //i2c驱动时钟
    .i2c_scl     (sccb_scl      ),   //输出至i2c设备的串行时钟信号scl
    .i2c_sda     (sccb_sda      )    //输出至i2c设备的串行数据信号sda
);

//------------- ov5640_cfg_inst -------------
ov5640_cfg  ov5640_cfg_inst(

    .sys_clk        (cfg_clk    ),   //系统时钟,由iic模块传入
    .sys_rst_n      (sys_rst_n  ),   //系统复位,低有效
    .cfg_end        (cfg_end    ),   //单个寄存器配置完成

    .cfg_start      (cfg_start  ),   //单个寄存器配置触发信号
    .cfg_data       (cfg_data   ),   //ID,REG_ADDR,REG_VAL
    .cfg_done       (cfg_done   )    //寄存器配置完成
);

//------------- ov5640_data_inst -------------
ov5640_data ov5640_data_inst(

    .sys_rst_n          (sys_rst_n & sys_init_done  ),  //复位信号
    .ov5640_pclk        (ov5640_pclk    ),   //摄像头像素时钟
    .ov5640_href        (ov5640_href    ),   //摄像头行同步信号
    .ov5640_vsync       (ov5640_vsync   ),   //摄像头场同步信号
    .ov5640_data        (ov5640_data    ),   //摄像头图像数据

    .ov5640_wr_en       (ov5640_wr_en   ),   //图像数据有效使能信号
    .ov5640_data_out    (ov5640_data_out)    //图像数据
);

endmodule

i2c_ctrl模块

// An highlighted block
module  i2c_ctrl
#(
    parameter   DEVICE_ADDR     =   7'b1010_000     ,   //i2c设备地址
    parameter   SYS_CLK_FREQ    =   26'd50_000_000  ,   //输入系统时钟频率
    parameter   SCL_FREQ        =   18'd250_000         //i2c设备scl时钟频率
)
(
    input   wire            sys_clk     ,   //输入系统时钟,50MHz
    input   wire            sys_rst_n   ,   //输入复位信号,低电平有效
    input   wire            wr_en       ,   //输入写使能信号
    input   wire            rd_en       ,   //输入读使能信号
    input   wire            i2c_start   ,   //输入i2c触发信号
    input   wire            addr_num    ,   //输入i2c字节地址字节数
    input   wire    [15:0]  byte_addr   ,   //输入i2c字节地址
    input   wire    [7:0]   wr_data     ,   //输入i2c设备数据

    output  reg             i2c_clk     ,   //i2c驱动时钟
    output  reg             i2c_end     ,   //i2c一次读/写操作完成
    output  reg     [7:0]   rd_data     ,   //输出i2c设备读取数据
    output  reg             i2c_scl     ,   //输出至i2c设备的串行时钟信号scl
    inout   wire            i2c_sda         //输出至i2c设备的串行数据信号sda
);

// parameter define
parameter   CNT_CLK_MAX     =   (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3   ;   //cnt_clk计数器计数最大值

parameter   CNT_START_MAX   =   8'd100; //cnt_start计数器计数最大值

parameter   IDLE            =   4'd00,  //初始状态
            START_1         =   4'd01,  //开始状态1
            SEND_D_ADDR     =   4'd02,  //设备地址写入状态 + 控制写
            ACK_1           =   4'd03,  //应答状态1
            SEND_B_ADDR_H   =   4'd04,  //字节地址高八位写入状态
            ACK_2           =   4'd05,  //应答状态2
            SEND_B_ADDR_L   =   4'd06,  //字节地址低八位写入状态
            ACK_3           =   4'd07,  //应答状态3
            WR_DATA         =   4'd08,  //写数据状态
            ACK_4           =   4'd09,  //应答状态4
            START_2         =   4'd10,  //开始状态2
            SEND_RD_ADDR    =   4'd11,  //设备地址写入状态 + 控制读
            ACK_5           =   4'd12,  //应答状态5
            RD_DATA         =   4'd13,  //读数据状态
            N_ACK           =   4'd14,  //非应答状态
            STOP            =   4'd15;  //结束状态

// wire  define
wire            sda_in          ;   //sda输入数据寄存
wire            sda_en          ;   //sda数据写入使能信号

// reg   define
reg     [7:0]   cnt_clk         ;   //系统时钟计数器,控制生成clk_i2c时钟信号
reg     [3:0]   state           ;   //状态机状态
reg             cnt_i2c_clk_en  ;   //cnt_i2c_clk计数器使能信号
reg     [1:0]   cnt_i2c_clk     ;   //clk_i2c时钟计数器,控制生成cnt_bit信号
reg     [2:0]   cnt_bit         ;   //sda比特计数器
reg             ack             ;   //应答信号
reg             i2c_sda_reg     ;   //sda数据缓存
reg     [7:0]   rd_data_reg     ;   //自i2c设备读出数据


// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <=  8'd0;
    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)
        cnt_clk <=  8'd0;
    else
        cnt_clk <=  cnt_clk + 1'b1;

// i2c_clk:i2c驱动时钟
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_clk <=  1'b1;
    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)
        i2c_clk <=  ~i2c_clk;

// cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk_en  <=  1'b0;
    else    if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
        cnt_i2c_clk_en  <=  1'b0;
    else    if(i2c_start == 1'b1)
        cnt_i2c_clk_en  <=  1'b1;

// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk <=  2'd0;
    else    if(cnt_i2c_clk_en == 1'b1)
        cnt_i2c_clk <=  cnt_i2c_clk + 1'b1;

// cnt_bit:sda比特计数器
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if((state == IDLE) || (state == START_1) || (state == START_2)
                || (state == ACK_1) || (state == ACK_2) || (state == ACK_3)
                || (state == ACK_4) || (state == ACK_5) || (state == N_ACK))
        cnt_bit <=  3'd0;
    else    if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        cnt_bit <=  3'd0;
    else    if((cnt_i2c_clk == 2'd3) && (state != IDLE))
        cnt_bit <=  cnt_bit + 1'b1;

// state:状态机状态跳转
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else    case(state)
        IDLE:
            if(i2c_start == 1'b1)
                state   <=  START_1;
            else
                state   <=  state;
        START_1:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_D_ADDR;
            else
                state   <=  state;
        SEND_D_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_1;
            else
                state   <=  state;
        ACK_1:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                    if(addr_num == 1'b1)
                        state   <=  SEND_B_ADDR_H;
                    else
                        state   <=  SEND_B_ADDR_L;
                end
             else
                state   <=  state;
        SEND_B_ADDR_H:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_2;
            else
                state   <=  state;
        ACK_2:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  SEND_B_ADDR_L;
            else
                state   <=  state;
        SEND_B_ADDR_L:
            if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
                state   <=  ACK_3;
            else
                state   <=  state;
        ACK_3:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                    if(wr_en == 1'b1)
                        state   <=  WR_DATA;
                    else    if(rd_en == 1'b1)
                        state   <=  START_2;
                    else
                        state   <=  state;
                end
             else
                state   <=  state;
        WR_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_4;
            else
                state   <=  state;
        ACK_4:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  STOP;
            else
                state   <=  state;
        START_2:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_RD_ADDR;
            else
                state   <=  state;
        SEND_RD_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_5;
            else
                state   <=  state;
        ACK_5:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  RD_DATA;
            else
                state   <=  state;
        RD_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  N_ACK;
            else
                state   <=  state;
        N_ACK:
            if(cnt_i2c_clk == 3)
                state   <=  STOP;
            else
                state   <=  state;
        STOP:
            if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
                state   <=  IDLE;
            else
                state   <=  state;
        default:    state   <=  IDLE;
    endcase

// ack:应答信号
always@(*)
    case    (state)
        IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
        WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
            ack <=  1'b1;
        ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
            if(cnt_i2c_clk == 2'd0)
                ack <=  sda_in;//1'b0;//
            else
                ack <=  ack;
        default:    ack <=  1'b1;
    endcase

// i2c_scl:输出至i2c设备的串行时钟信号scl
always@(*)
    case    (state)
        IDLE:
            i2c_scl <=  1'b1;
        START_1:
            if(cnt_i2c_clk == 2'd3)
                i2c_scl <=  1'b0;
            else
                i2c_scl <=  1'b1;
        SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L,
        ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK:
            if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2))
                i2c_scl <=  1'b1;
            else
                i2c_scl <=  1'b0;
        STOP:
            if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0))
                i2c_scl <=  1'b0;
            else
                i2c_scl <=  1'b1;
        default:    i2c_scl <=  1'b1;
    endcase

// i2c_sda_reg:sda数据缓存
always@(*)
    case    (state)
        IDLE:
            begin
                i2c_sda_reg <=  1'b1;
                rd_data_reg <=  8'd0;
            end
        START_1:
            if(cnt_i2c_clk <= 2'd0)
                i2c_sda_reg <=  1'b1;
            else
                i2c_sda_reg <=  1'b0;
        SEND_D_ADDR:
            if(cnt_bit <= 3'd6)
                i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];
            else
                i2c_sda_reg <=  1'b0;
        ACK_1:
            i2c_sda_reg <=  1'b1;
        SEND_B_ADDR_H:
            i2c_sda_reg <=  byte_addr[15 - cnt_bit];
        ACK_2:
            i2c_sda_reg <=  1'b1;
        SEND_B_ADDR_L:
            i2c_sda_reg <=  byte_addr[7 - cnt_bit];
        ACK_3:
            i2c_sda_reg <=  1'b1;
        WR_DATA:
            i2c_sda_reg <=  wr_data[7 - cnt_bit];
        ACK_4:
            i2c_sda_reg <=  1'b1;
        START_2:
            if(cnt_i2c_clk <= 2'd1)
                i2c_sda_reg <=  1'b1;
            else
                i2c_sda_reg <=  1'b0;
        SEND_RD_ADDR:
            if(cnt_bit <= 3'd6)
                i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];
            else
                i2c_sda_reg <=  1'b1;
        ACK_5:
            i2c_sda_reg <=  1'b1;
        RD_DATA:
            if(cnt_i2c_clk  == 2'd2)
                rd_data_reg[7 - cnt_bit]    <=  sda_in;
            else
                rd_data_reg <=  rd_data_reg;
        N_ACK:
            i2c_sda_reg <=  1'b1;
        STOP:
            if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
                i2c_sda_reg <=  1'b0;
            else
                i2c_sda_reg <=  1'b1;
        default:
            begin
                i2c_sda_reg <=  1'b1;
                rd_data_reg <=  rd_data_reg;
            end
    endcase

// rd_data:自i2c设备读出数据
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_data <=  8'd0;
    else    if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        rd_data <=  rd_data_reg;

// i2c_end:一次读/写结束信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_end <=  1'b0;
    else    if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
        i2c_end <=  1'b1;
    else
        i2c_end <=  1'b0;

// sda_in:sda输入数据寄存
assign  sda_in = i2c_sda;
// sda_en:sda数据写入使能信号
assign  sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2)
                    || (state == ACK_3) || (state == ACK_4) || (state == ACK_5))
                    ? 1'b0 : 1'b1;
// i2c_sda:输出至i2c设备的串行数据信号sda
assign  i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;

endmodule


ov5640_cfg模块(寄存器配置模块)

module  ov5640_cfg
(
    input   wire            sys_clk     ,   //系统时钟,由iic模块传入
    input   wire            sys_rst_n   ,   //系统复位,低有效
    input   wire            cfg_end     ,   //单个寄存器配置完成

    output  reg             cfg_start   ,   //单个寄存器配置触发信号
    output  wire    [23:0]  cfg_data    ,   //ID,REG_ADDR,REG_VAL
    output  reg             cfg_done        //寄存器配置完成
);



//parameter define
parameter   REG_NUM         =   8'd251      ;   //总共需要配置的寄存器个数
parameter   CNT_WAIT_MAX    =   15'd20000   ;   //寄存器配置等待计数最大值

//wire  define
wire    [23:0]  cfg_data_reg[REG_NUM-1:0]   ;   //寄存器配置数据暂存

//reg   define
reg     [14:0]  cnt_wait    ;   //寄存器配置等待计数器
reg     [7:0]   reg_num     ;   //配置寄存器个数



//cnt_wait:寄存器配置等待计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_wait    <=  15'd0;
    else    if(cnt_wait < CNT_WAIT_MAX)
        cnt_wait    <=  cnt_wait + 1'b1;

//reg_num:配置寄存器个数
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        reg_num <=  8'd0;
    else    if(cfg_end == 1'b1)
        reg_num <=  reg_num + 1'b1;

//cfg_start:单个寄存器配置触发信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cfg_start   <=  1'b0;
    else    if(cnt_wait == (CNT_WAIT_MAX - 1'b1))
        cfg_start   <=  1'b1;
    else    if((cfg_end == 1'b1) && (reg_num < REG_NUM))
        cfg_start   <=  1'b1;
    else
        cfg_start   <=  1'b0;

//cfg_done:寄存器配置完成
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cfg_done    <=  1'b0;
    else    if((reg_num == REG_NUM) && (cfg_end == 1'b1))
        cfg_done    <=  1'b1;

//cfg_data:ID,REG_ADDR,REG_VAL
assign  cfg_data = (cfg_done == 1'b1) ? 24'b0 : cfg_data_reg[reg_num];

//----------------------------------------------------
//cfg_data_reg:寄存器配置数据暂存  ID   REG_ADDR REG_VAL
assign  cfg_data_reg[000]  =       {16'h3103, 8'h11};
assign  cfg_data_reg[001]  =       {16'h3008, 8'h82};
assign  cfg_data_reg[002]  =       {16'h3008, 8'h42};
assign  cfg_data_reg[003]  =       {16'h3103, 8'h03};
assign  cfg_data_reg[004]  =       {16'h3017, 8'hff};
assign  cfg_data_reg[005]  =       {16'h3018, 8'hff};
assign  cfg_data_reg[006]  =       {16'h3034, 8'h1A};
assign  cfg_data_reg[007]  =       {16'h3037, 8'h13};
assign  cfg_data_reg[008]  =       {16'h3108, 8'h01};
assign  cfg_data_reg[009]  =       {16'h3630, 8'h36};

assign  cfg_data_reg[010]  =       {16'h3631, 8'h0e};
assign  cfg_data_reg[011]  =       {16'h3632, 8'he2};
assign  cfg_data_reg[012]  =       {16'h3633, 8'h12};
assign  cfg_data_reg[013]  =       {16'h3621, 8'he0};
assign  cfg_data_reg[014]  =       {16'h3704, 8'ha0};
assign  cfg_data_reg[015]  =       {16'h3703, 8'h5a};
assign  cfg_data_reg[016]  =       {16'h3715, 8'h78};
assign  cfg_data_reg[017]  =       {16'h3717, 8'h01};
assign  cfg_data_reg[018]  =       {16'h370b, 8'h60};
assign  cfg_data_reg[019]  =       {16'h3705, 8'h1a};

assign  cfg_data_reg[020]  =       {16'h3905, 8'h02};
assign  cfg_data_reg[021]  =       {16'h3906, 8'h10};
assign  cfg_data_reg[022]  =       {16'h3901, 8'h0a};
assign  cfg_data_reg[023]  =       {16'h3731, 8'h12};
assign  cfg_data_reg[024]  =       {16'h3600, 8'h08};
assign  cfg_data_reg[025]  =       {16'h3601, 8'h33};
assign  cfg_data_reg[026]  =       {16'h302d, 8'h60};
assign  cfg_data_reg[027]  =       {16'h3620, 8'h52};
assign  cfg_data_reg[028]  =       {16'h371b, 8'h20};
assign  cfg_data_reg[029]  =       {16'h471c, 8'h50};

assign  cfg_data_reg[030]  =       {16'h3a13, 8'h43};
assign  cfg_data_reg[031]  =       {16'h3a18, 8'h00};
assign  cfg_data_reg[032]  =       {16'h3a19, 8'hf8};
assign  cfg_data_reg[033]  =       {16'h3635, 8'h13};
assign  cfg_data_reg[034]  =       {16'h3636, 8'h03};
assign  cfg_data_reg[035]  =       {16'h3634, 8'h40};
assign  cfg_data_reg[036]  =       {16'h3622, 8'h01};
assign  cfg_data_reg[037]  =       {16'h3c01, 8'h34};
assign  cfg_data_reg[038]  =       {16'h3c04, 8'h28};
assign  cfg_data_reg[039]  =       {16'h3c05, 8'h98};

assign  cfg_data_reg[040]  =       {16'h3c06, 8'h00};
assign  cfg_data_reg[041]  =       {16'h3c07, 8'h08};
assign  cfg_data_reg[042]  =       {16'h3c08, 8'h00};
assign  cfg_data_reg[043]  =       {16'h3c09, 8'h1c};
assign  cfg_data_reg[044]  =       {16'h3c0a, 8'h9c};
assign  cfg_data_reg[045]  =       {16'h3c0b, 8'h40};
assign  cfg_data_reg[046]  =       {16'h3810, 8'h00};
assign  cfg_data_reg[047]  =       {16'h3811, 8'h10};
assign  cfg_data_reg[048]  =       {16'h3812, 8'h00};
assign  cfg_data_reg[049]  =       {16'h3708, 8'h64};

assign  cfg_data_reg[050]  =       {16'h4001, 8'h02};
assign  cfg_data_reg[051]  =       {16'h4005, 8'h1a};
assign  cfg_data_reg[052]  =       {16'h3000, 8'h00};
assign  cfg_data_reg[053]  =       {16'h3004, 8'hff};
assign  cfg_data_reg[054]  =       {16'h300e, 8'h58};
assign  cfg_data_reg[055]  =       {16'h302e, 8'h00};
assign  cfg_data_reg[056]  =       {16'h4300, 8'h61};
assign  cfg_data_reg[057]  =       {16'h501f, 8'h01};
assign  cfg_data_reg[058]  =       {16'h440e, 8'h00};
assign  cfg_data_reg[059]  =       {16'h5000, 8'ha7};

assign  cfg_data_reg[060]  =       {16'h3a0f, 8'h30};
assign  cfg_data_reg[061]  =       {16'h3a10, 8'h28};
assign  cfg_data_reg[062]  =       {16'h3a1b, 8'h30};
assign  cfg_data_reg[063]  =       {16'h3a1e, 8'h26};
assign  cfg_data_reg[064]  =       {16'h3a11, 8'h60};
assign  cfg_data_reg[065]  =       {16'h3a1f, 8'h14};
assign  cfg_data_reg[066]  =       {16'h5800, 8'h23};
assign  cfg_data_reg[067]  =       {16'h5801, 8'h14};
assign  cfg_data_reg[068]  =       {16'h5802, 8'h0f};
assign  cfg_data_reg[069]  =       {16'h5803, 8'h0f};

assign  cfg_data_reg[070]  =       {16'h5804, 8'h12};
assign  cfg_data_reg[071]  =       {16'h5805, 8'h26};
assign  cfg_data_reg[072]  =       {16'h5806, 8'h0c};
assign  cfg_data_reg[073]  =       {16'h5807, 8'h08};
assign  cfg_data_reg[074]  =       {16'h5808, 8'h05};
assign  cfg_data_reg[075]  =       {16'h5809, 8'h05};
assign  cfg_data_reg[076]  =       {16'h580a, 8'h08};
assign  cfg_data_reg[077]  =       {16'h580b, 8'h0d};
assign  cfg_data_reg[078]  =       {16'h580c, 8'h08};
assign  cfg_data_reg[079]  =       {16'h580d, 8'h03};

assign  cfg_data_reg[080]  =       {16'h580e, 8'h00};
assign  cfg_data_reg[081]  =       {16'h580f, 8'h00};
assign  cfg_data_reg[082]  =       {16'h5810, 8'h03};
assign  cfg_data_reg[083]  =       {16'h5811, 8'h09};
assign  cfg_data_reg[084]  =       {16'h5812, 8'h07};
assign  cfg_data_reg[085]  =       {16'h5813, 8'h03};
assign  cfg_data_reg[086]  =       {16'h5814, 8'h00};
assign  cfg_data_reg[087]  =       {16'h5815, 8'h01};
assign  cfg_data_reg[088]  =       {16'h5816, 8'h03};
assign  cfg_data_reg[089]  =       {16'h5817, 8'h08};

assign  cfg_data_reg[090]  =       {16'h5818, 8'h0d};
assign  cfg_data_reg[091]  =       {16'h5819, 8'h08};
assign  cfg_data_reg[092]  =       {16'h581a, 8'h05};
assign  cfg_data_reg[093]  =       {16'h581b, 8'h06};
assign  cfg_data_reg[094]  =       {16'h581c, 8'h08};
assign  cfg_data_reg[095]  =       {16'h581d, 8'h0e};
assign  cfg_data_reg[096]  =       {16'h581e, 8'h29};
assign  cfg_data_reg[097]  =       {16'h581f, 8'h17};
assign  cfg_data_reg[098]  =       {16'h5820, 8'h11};
assign  cfg_data_reg[099]  =       {16'h5821, 8'h11};

assign  cfg_data_reg[100]  =       {16'h5822, 8'h15};
assign  cfg_data_reg[101]  =       {16'h5823, 8'h28};
assign  cfg_data_reg[102]  =       {16'h5824, 8'h46};
assign  cfg_data_reg[103]  =       {16'h5825, 8'h26};
assign  cfg_data_reg[104]  =       {16'h5826, 8'h08};
assign  cfg_data_reg[105]  =       {16'h5827, 8'h26};
assign  cfg_data_reg[106]  =       {16'h5828, 8'h64};
assign  cfg_data_reg[107]  =       {16'h5829, 8'h26};
assign  cfg_data_reg[108]  =       {16'h582a, 8'h24};
assign  cfg_data_reg[109]  =       {16'h582b, 8'h22};

assign  cfg_data_reg[110]  =       {16'h582c, 8'h24};
assign  cfg_data_reg[111]  =       {16'h582d, 8'h24};
assign  cfg_data_reg[112]  =       {16'h582e, 8'h06};
assign  cfg_data_reg[113]  =       {16'h582f, 8'h22};
assign  cfg_data_reg[114]  =       {16'h5830, 8'h40};
assign  cfg_data_reg[115]  =       {16'h5831, 8'h42};
assign  cfg_data_reg[116]  =       {16'h5832, 8'h24};
assign  cfg_data_reg[117]  =       {16'h5833, 8'h26};
assign  cfg_data_reg[118]  =       {16'h5834, 8'h24};
assign  cfg_data_reg[119]  =       {16'h5835, 8'h22};

assign  cfg_data_reg[120]  =       {16'h5836, 8'h22};
assign  cfg_data_reg[121]  =       {16'h5837, 8'h26};
assign  cfg_data_reg[122]  =       {16'h5838, 8'h44};
assign  cfg_data_reg[123]  =       {16'h5839, 8'h24};
assign  cfg_data_reg[124]  =       {16'h583a, 8'h26};
assign  cfg_data_reg[125]  =       {16'h583b, 8'h28};
assign  cfg_data_reg[126]  =       {16'h583c, 8'h42};
assign  cfg_data_reg[127]  =       {16'h583d, 8'hce};
assign  cfg_data_reg[128]  =       {16'h5180, 8'hff};
assign  cfg_data_reg[129]  =       {16'h5181, 8'hf2};

assign  cfg_data_reg[130]  =       {16'h5182, 8'h00};
assign  cfg_data_reg[131]  =       {16'h5183, 8'h14};
assign  cfg_data_reg[132]  =       {16'h5184, 8'h25};
assign  cfg_data_reg[133]  =       {16'h5185, 8'h24};
assign  cfg_data_reg[134]  =       {16'h5186, 8'h09};
assign  cfg_data_reg[135]  =       {16'h5187, 8'h09};
assign  cfg_data_reg[136]  =       {16'h5188, 8'h09};
assign  cfg_data_reg[137]  =       {16'h5189, 8'h75};
assign  cfg_data_reg[138]  =       {16'h518a, 8'h54};
assign  cfg_data_reg[139]  =       {16'h518b, 8'he0};

assign  cfg_data_reg[140]  =       {16'h518c, 8'hb2};
assign  cfg_data_reg[141]  =       {16'h518d, 8'h42};
assign  cfg_data_reg[142]  =       {16'h518e, 8'h3d};
assign  cfg_data_reg[143]  =       {16'h518f, 8'h56};
assign  cfg_data_reg[144]  =       {16'h5190, 8'h46};
assign  cfg_data_reg[145]  =       {16'h5191, 8'hf8};
assign  cfg_data_reg[146]  =       {16'h5192, 8'h04};
assign  cfg_data_reg[147]  =       {16'h5193, 8'h70};
assign  cfg_data_reg[148]  =       {16'h5194, 8'hf0};
assign  cfg_data_reg[149]  =       {16'h5195, 8'hf0};

assign  cfg_data_reg[150]  =       {16'h5196, 8'h03};
assign  cfg_data_reg[151]  =       {16'h5197, 8'h01};
assign  cfg_data_reg[152]  =       {16'h5198, 8'h04};
assign  cfg_data_reg[153]  =       {16'h5199, 8'h12};
assign  cfg_data_reg[154]  =       {16'h519a, 8'h04};
assign  cfg_data_reg[155]  =       {16'h519b, 8'h00};
assign  cfg_data_reg[156]  =       {16'h519c, 8'h06};
assign  cfg_data_reg[157]  =       {16'h519d, 8'h82};
assign  cfg_data_reg[158]  =       {16'h519e, 8'h38};
assign  cfg_data_reg[159]  =       {16'h5480, 8'h01};

assign  cfg_data_reg[160]  =       {16'h5481, 8'h08};
assign  cfg_data_reg[161]  =       {16'h5482, 8'h14};
assign  cfg_data_reg[162]  =       {16'h5483, 8'h28};
assign  cfg_data_reg[163]  =       {16'h5484, 8'h51};
assign  cfg_data_reg[164]  =       {16'h5485, 8'h65};
assign  cfg_data_reg[165]  =       {16'h5486, 8'h71};
assign  cfg_data_reg[166]  =       {16'h5487, 8'h7d};
assign  cfg_data_reg[167]  =       {16'h5488, 8'h87};
assign  cfg_data_reg[168]  =       {16'h5489, 8'h91};
assign  cfg_data_reg[169]  =       {16'h548a, 8'h9a};

assign  cfg_data_reg[170]  =       {16'h548b, 8'haa};
assign  cfg_data_reg[171]  =       {16'h548c, 8'hb8};
assign  cfg_data_reg[172]  =       {16'h548d, 8'hcd};
assign  cfg_data_reg[173]  =       {16'h548e, 8'hdd};
assign  cfg_data_reg[174]  =       {16'h548f, 8'hea};
assign  cfg_data_reg[175]  =       {16'h5490, 8'h1d};
assign  cfg_data_reg[176]  =       {16'h5381, 8'h1e};
assign  cfg_data_reg[177]  =       {16'h5382, 8'h5b};
assign  cfg_data_reg[178]  =       {16'h5383, 8'h08};
assign  cfg_data_reg[179]  =       {16'h5384, 8'h0a};

assign  cfg_data_reg[180]  =       {16'h5385, 8'h7e};
assign  cfg_data_reg[181]  =       {16'h5386, 8'h88};
assign  cfg_data_reg[182]  =       {16'h5387, 8'h7c};
assign  cfg_data_reg[183]  =       {16'h5388, 8'h6c};
assign  cfg_data_reg[184]  =       {16'h5389, 8'h10};
assign  cfg_data_reg[185]  =       {16'h538a, 8'h01};
assign  cfg_data_reg[186]  =       {16'h538b, 8'h98};
assign  cfg_data_reg[187]  =       {16'h5580, 8'h06};
assign  cfg_data_reg[188]  =       {16'h5583, 8'h40};
assign  cfg_data_reg[189]  =       {16'h5584, 8'h10};

assign  cfg_data_reg[190]  =       {16'h5589, 8'h10};
assign  cfg_data_reg[191]  =       {16'h558a, 8'h00};
assign  cfg_data_reg[192]  =       {16'h558b, 8'hf8};
assign  cfg_data_reg[193]  =       {16'h501d, 8'h40};
assign  cfg_data_reg[194]  =       {16'h5300, 8'h08};
assign  cfg_data_reg[195]  =       {16'h5301, 8'h30};
assign  cfg_data_reg[196]  =       {16'h5302, 8'h10};
assign  cfg_data_reg[197]  =       {16'h5303, 8'h00};
assign  cfg_data_reg[198]  =       {16'h5304, 8'h08};
assign  cfg_data_reg[199]  =       {16'h5305, 8'h30};

assign  cfg_data_reg[200]  =       {16'h5306, 8'h08};
assign  cfg_data_reg[201]  =       {16'h5307, 8'h16};
assign  cfg_data_reg[202]  =       {16'h5309, 8'h08};
assign  cfg_data_reg[203]  =       {16'h530a, 8'h30};
assign  cfg_data_reg[204]  =       {16'h530b, 8'h04};
assign  cfg_data_reg[205]  =       {16'h530c, 8'h06};
assign  cfg_data_reg[206]  =       {16'h5025, 8'h00};
assign  cfg_data_reg[207]  =       {16'h3008, 8'h02};
assign  cfg_data_reg[208]  =       {16'h3035, 8'h11};
assign  cfg_data_reg[209]  =       {16'h3036, 8'h46};

assign  cfg_data_reg[210]  =       {16'h3c07, 8'h08};
assign  cfg_data_reg[211]  =       {16'h3820, 8'h47};
assign  cfg_data_reg[212]  =       {16'h3821, 8'h00};
assign  cfg_data_reg[213]  =       {16'h3814, 8'h31};
assign  cfg_data_reg[214]  =       {16'h3815, 8'h31};
assign  cfg_data_reg[215]  =       {16'h3800, 8'h00};
assign  cfg_data_reg[216]  =       {16'h3801, 8'h00};
assign  cfg_data_reg[217]  =       {16'h3802, 8'h00};
assign  cfg_data_reg[218]  =       {16'h3803, 8'h04};
assign  cfg_data_reg[219]  =       {16'h3804, 8'h0a};

assign  cfg_data_reg[220]  =       {16'h3805, 8'h3f};
assign  cfg_data_reg[221]  =       {16'h3806, 8'h07};
assign  cfg_data_reg[222]  =       {16'h3807, 8'h9b};
assign  cfg_data_reg[223]  =       {16'h3808, 8'h02};
assign  cfg_data_reg[224]  =       {16'h3809, 8'h80};
assign  cfg_data_reg[225]  =       {16'h380a, 8'h01};
assign  cfg_data_reg[226]  =       {16'h380b, 8'he0};
assign  cfg_data_reg[227]  =       {16'h380c, 8'h07};
assign  cfg_data_reg[228]  =       {16'h380d, 8'h68};
assign  cfg_data_reg[229]  =       {16'h380e, 8'h03};

assign  cfg_data_reg[230]  =       {16'h380f, 8'hd8};
assign  cfg_data_reg[231]  =       {16'h3813, 8'h06};
assign  cfg_data_reg[232]  =       {16'h3618, 8'h00};
assign  cfg_data_reg[233]  =       {16'h3612, 8'h29};
assign  cfg_data_reg[234]  =       {16'h3709, 8'h52};
assign  cfg_data_reg[235]  =       {16'h370c, 8'h03};
assign  cfg_data_reg[236]  =       {16'h3a02, 8'h17};
assign  cfg_data_reg[237]  =       {16'h3a03, 8'h10};
assign  cfg_data_reg[238]  =       {16'h3a14, 8'h17};
assign  cfg_data_reg[239]  =       {16'h3a15, 8'h10};

assign  cfg_data_reg[240]  =       {16'h4004, 8'h02};
assign  cfg_data_reg[241]  =       {16'h3002, 8'h1c};
assign  cfg_data_reg[242]  =       {16'h3006, 8'hc3};
assign  cfg_data_reg[243]  =       {16'h4713, 8'h03};
assign  cfg_data_reg[244]  =       {16'h4407, 8'h04};
assign  cfg_data_reg[245]  =       {16'h460b, 8'h35};
assign  cfg_data_reg[246]  =       {16'h460c, 8'h22};
assign  cfg_data_reg[247]  =       {16'h4837, 8'h22};
assign  cfg_data_reg[248]  =       {16'h3824, 8'h02};
assign  cfg_data_reg[249]  =       {16'h5001, 8'ha3};

assign  cfg_data_reg[250]  =       {16'h3503, 8'h00};

//-------------------------------------------------------

endmodule


ov5640_data模块(将摄像头采集到的数据发送给DDR3)

module ov5640_data(

	input   wire 			sys_rst_n,
//OV5640	
	input 	wire 			ov5640_pclk,
	input 	wire 			ov5640_href,
	input	wire 			ov5640_vsync,
	input 	wire 	[7:0]	ov5640_data,
//写FIFO
	output 	wire 			ov5640_wr_en,
	output  wire 	[15:0]	ov5640_data_out
    );

parameter 		PIC_WAIT	= 4'd10;//图像稳定前等待帧图像个数
	
wire 			pic_flag	;		//帧图像标志信号,每拉高一次,表示一帧完整图像
	
reg 			ov5640_vsync_dly	;//摄像头输入长信号打拍
reg 	[3:0]	cnt_pic				;//图像帧数计数器
reg 			pic_valid			;//帧有效标志信号
reg 	[7:0]	pic_data_reg		;//输入8位图像数据缓存
reg 	[15:0]	data_out_reg		;//输出16位图像数据缓存
reg 			data_flag			;//输入8位图像数据缓存
reg 			data_flag_dly1		;//图像数据拼接标志信号打拍

//ov5640_vsync_dly:摄像头输入长信号打拍
always@(posedge ov5640_pclk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		ov5640_vsync_dly <= 1'b0;
	else 	
		ov5640_vsync_dly <= ov5640_vsync;
end

//pic_flag:帧图像标志信号,每拉高一次,表示一帧完整图像
assign	pic_flag = ((ov5640_vsync_dly == 1'b0) && 
					(ov5640_vsync == 1'b1)) ? 1'b1: 1'b0;

//cnt_pic:图像帧计数器
always@(posedge ov5640_pclk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		cnt_pic <= 4'd0;
	else if(cnt_pic < PIC_WAIT)
		cnt_pic <= cnt_pic + 1'b1;
	else 
		cnt_pic <= cnt_pic;
end

//pic_valid:真有效信号
always@(posedge ov5640_pclk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		pic_valid <= 1'b0;
	else if((cnt_pic == PIC_WAIT) && (pic_flag == 1'b1))
		pic_valid <= 1'b1;
	else 
		pic_valid <= pic_valid;
end

//data_out_reg,pic_data_reg,data_flag:输出16位图像数据缓存
//输入8位图像数据缓存
always@(posedge ov5640_pclk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		begin
			data_out_reg <= 16'd0;
			pic_data_reg <= 8'd0;
			data_flag	 <= 1'b0;
		end
	else if(ov5640_href == 1'b1)
		begin
			data_out_reg <= data_out_reg ;
			pic_data_reg <= ov5640_data;
			data_flag	 <= ~data_flag;
		if(data_flag == 1'b1)
			data_out_reg <= {pic_data_reg,ov5640_data};
		else 	
			data_out_reg <= data_out_reg;
		end
	else 
		begin
			data_out_reg <= data_out_reg;
			pic_data_reg <= 8'd0;
			data_flag	 <= 1'b0;
		end
end

//data_flag_dly1:图片数据缓存打拍
always@(posedge ov5640_pclk or negedge sys_rst_n)
begin
	if(!sys_rst_n)
		data_flag_dly1 <= 1'b0;
	else 	
		data_flag_dly1 <= data_flag;
end

//ov5640_data_out :输出16位图像数据
assign ov5640_data_out = (pic_valid == 1'b1) ? data_out_reg: 16'd0;
//ov5640_wr_en:输出16位图片数据使能
assign 	ov5640_wr_en = pic_valid ?data_flag_dly1 : 1'b1;



endmodule

四.上板验证结果

在这里插入图片描述由于VGA是调用之前写过的640*480分辨率的模块,所以四周会有一些显示问题,后期更改一下分辨率和时钟即可。

由于DDR3实现的流程和代码都比较复杂,所以没有展示DDR3的代码。如有需要整个工程的文件,欢迎邮箱留言,看到后就会给大家分享!

初学入门,分享学习笔记和心得,如有指教,感激不尽!

标签:16,DDR3,VGA,assign,ov5640,cfg,OV5640,data,reg
来源: https://blog.csdn.net/weixin_46628093/article/details/115558116