草庐IT

基于FPGA的OV5640摄像头驱动

庭-学习ing 2023-10-12 原文

基于FPGA的OV5640摄像头驱动


一、OV5640的相关介绍

(1)野火的OV5640引脚图

(2)引脚介绍

(3)功能框图


<1>OV5640 的控制寄存器,它根据这些寄存器配置的参数来运行,而这些参数是由外部控制器通过 SIO_C 和 SIO_D 引脚写入的, SIO_C与 SIO_D 使用的通讯协议跟 I2C十分类似。

<2>OV5640的通信、控制信号及外部时钟,其中 PCLK、 HREF 及VSYNC分别是像素同步时钟、行同步信号以及帧同步信号,这与液晶屏控制中的信号是很类似的。 RESETB引脚为低电平时,用于复位整个传感器芯片,PWDN 用于控制芯片进入低功耗模式。注意最后的一个 XCLK引脚,它跟 PCLK
是完全不同的, XCLK 是用于驱动整个传感器芯片的时钟信号,是外部输入到OV5640的信号;而 PCLK是 OV5640输出数据时的同步信号,它是由 OV5640 输出的信号。 XCLK可以外接晶振或由外部控制器提供。

<3>感光矩阵,光信号在这里转化成电信号,经过各种处理,这些信号存储成由一个个像素点表示的数字图像。

<4>包含了 DSP 处理单元,它会根据控制寄存器的配置做一些基本的图像处理运算。这部分还包含了图像格式转换单元及压缩单元,转换出的数据最终通过Y0-Y9引脚输出,一般来说我们使用 8根据数据线来传输,这时仅使用 Y2-Y9 引脚。


<5>VCM 处理单元,他会通过图像分析来实现图像的自动对焦功能。要实现自动对焦还需要下载自动对焦固件到模组,后面摄像头实验详细介绍这个功能。

二、SCCB时序介绍------与IIC基本相似

注意:当主机向从机进行指令或数据的写入时,串行数据线SDA上的数据在串行时钟SCL
为高电平时写入从机设备,每次只写入一位数据;串行数据线SDA中的数据在串行时钟
SCL为低电平时进行数据更新,以保证在SCL为高电平时采集到SDA数据的稳定状态。

(1)上电时序------主要按照官方文档的时序图来写程序

代码(严格按照时序图完成)

module Power_ctrl
#(
    parameter    CNT_5MS         =       20'd250_000,
    parameter    CNT_1MS         =       20'd50_000,
    parameter    CNT_20MS        =       20'd1000_000
)
(
    input           wire                    sys_clk,
    input           wire                    rst,
    
    output          wire                    pwdn,
    output          wire                    resetb,
    output          wire                    sccb_en,
    output          wire                    clk_en
);

reg [20:0] cnt;

assign pwdn = (cnt >= CNT_5MS - 1'd1)?1'd0:1'd1;
assign resetb = (cnt >= CNT_5MS + CNT_1MS - 1'd1)?1'd1:1'd0;
assign sccb_en = (cnt >= (CNT_5MS + CNT_1MS + CNT_20MS - 1'd1))?1'd1:1'd0;
assign clk_en = (cnt >= CNT_5MS - 1'd1)?1'd1:1'd0;

always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt <= 1'd0;
    else if(cnt == (CNT_5MS + CNT_1MS + CNT_20MS - 1'd1))
        cnt <= cnt;
    else
        cnt <= cnt + 1'd1;

endmodule

(2)读

IIC:

SSCB:

读程序验证以读ID来实现:

时序图:

时序分析

Start:是用来启动读的标志位,为1时有效
statue:一共13个状态
cnt:这是一个用来统计scl周期个数的变量
cnt_stop_flag:在STOP状态下,需要先用到2个周期来完成STOP的操作,这时cnt_stop_flag为0,当完成后cnt_stop_flag置1
cnt_stop:用来对STOP状态下的计时
I2C_CLK_x:这是一个用来仿真验证的波形,它与scl相差135°的相位,在它的下降沿sda发生改变
cnt_clkx:统计I2C_CLK_x一个周期所需的计数值
cnt_clkx_flag:在cnt_clkx下降沿的前一个周期置一,下降沿处置零
I2C_SDA:sda信号线
Data:{ID,ADD},24位
I2C_CLK:就是SCL
data_flag:DATA状态下,在数据稳定的地方置1一个周期,用来判断什么时候读数据
data:读出的数据
cnt_clk:统计I2C_CLK一个周期所需的计数值
cnt_clkx_flag:在I2C_CLK下降沿的前一个周期置一,下降沿处置零
read_en:结束一次读操作的标志

由于在SSCB时序开始时是sda先产生下降沿,且sda的数据需在scl高电平时保持稳定,所以采用135°相位差的方法来完成,135不是固定的,可以进行一定的修改,sda数据在每个I2C_CLK_x下降沿处改变,在数据发送和接收状态时,每次发8个数据,然后就就进入下一状态,在这里有个问题就是ID号的第八位是省去的,实际的ID就是七位,因为之前没注意,导致第一次仿真无误后上板时出错,查阅资料发现IIC的应答阶段与SSCB不太一样,其实也差不多,就是SSCB的应答处理你可以不用判断是否应答成功,因为SSCB不一定为返回给你正确的应答值,本程序的x表示为应答状态,即不关心状态,在STOP状态时,先利用好两个周期的时间来完成STOP的操作,然后又是和开头的操作一样,重新开始,然后发读的ID,本程序中,sda在所有的x状态和DATA状态都为1’dz,接着开始读数据即可,然后进行NO ACK操作,sda主动置为高电平,完成后就再进行一次STOP操作,结束整个读操作。(注意:SSCB没有IIC的连续读和连续写)

开始位:

停止位:

上面两图第一个信号是scl,第二个是sda

程序

module SSCB_ID
(
    input       wire                sys_clk,
    input       wire                rst,
    input       wire                read_en,
    input       wire    [15:0]      addr,
    input       wire    [7:0]       ID,
    input       wire                sda_rd,
    
    output      reg             sda,
    output      reg             scl,
    output      reg             read_end,
	output	    reg     [7:0]   Data_reg,
    output      reg     [12:0]  statue,
    output      wire    [23:0]  Cmd
);

localparam
    IDLE            =           13'b0_0000_0000_0001,
    START_ID1       =           13'b0_0000_0000_0010,
    x_ack1          =           13'b0_0000_0000_0100,
    ADD_H           =           13'b0_0000_0000_1000,
    x_ack2          =           13'b0_0000_0001_0000,
    ADD_L           =           13'b0_0000_0010_0000,
    x_ack3          =           13'b0_0000_0100_0000,
    STOP            =           13'b0_0000_1000_0000,
    START_ID2       =           13'b0_0001_0000_0000,
    x_ack4          =           13'b0_0010_0000_0000,
    DATA            =           13'b0_0100_0000_0000,
    NO              =           13'b0_1000_0000_0000,
    END             =           13'b1_0000_0000_0000;
    
localparam
    CLOCK       =       30'd50_000_000,
    I2C_CLK     =       30'd400_000,
    START_MAX   =       (CLOCK/I2C_CLK)*3/4 - 1'd1,
    MAX         =       CLOCK/I2C_CLK - 1'd1,
    MAX_2       =       (CLOCK/I2C_CLK)/2 - 1'd1;
    
//wire [23:0] Cmd;

//reg sda;

assign Cmd = {ID,addr};

//reg         sda;

reg [20:0]  cnt;
reg [20:0]  cnt_stop;
reg         cnt_stop_flag;

reg         I2C_CLK_x;
reg [20:0]  cnt_CLKx;
reg         cnt_clkx_flag;

reg         data_flag;

reg [20:0]  cnt_clk;
reg         cnt_clk_flag;

//结束标志
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        read_end <= 1'd0;
    else if(statue == END && cnt_clk == MAX - 1'd1)
        read_end <= 1'd1;
    else
        read_end <= 1'd0;

//相移135°的I2C信号计数
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_CLKx <= 1'd0;
    else if(cnt_CLKx == MAX)
        cnt_CLKx <= 1'd0;
    else if(read_en == 1'd1 && (statue == STOP) && cnt_stop_flag == 1'd1)
        cnt_CLKx <= cnt_CLKx + 1'd1;
    else if(read_en == 1'd1 
            && (statue !=STOP) 
            && statue != NO && statue != END)
        cnt_CLKx <= cnt_CLKx + 1'd1;
    else
        cnt_CLKx <= 1'd0;

//相移135°的I2C信号周期更替标志    
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_clkx_flag <= 1'd0;
    else if(cnt_CLKx == MAX - 1'd1)
        cnt_clkx_flag <= 1'd1;
    else
        cnt_clkx_flag <= 1'd0;
        
//相移135°的I2C信号产生        
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        I2C_CLK_x <= 1'd1;
    else if((statue == STOP && cnt_stop_flag != 1'd1) 
            || (statue == x_ack3 && cnt_clkx_flag == 1'd1)
            || (statue == DATA && cnt == 6'd8 && cnt_clkx_flag == 1'd1)
            || (statue == NO && statue == END))
        I2C_CLK_x <= 1'd1;
    else if(cnt_CLKx <= MAX_2 -1'd1 && read_en == 1'd1)
        I2C_CLK_x <= 1'd0;
    else if(cnt_CLKx <= MAX - 1'd1 && read_en == 1'd1)
        I2C_CLK_x <= 1'd1;
    else if(read_en != 1'd1)
        I2C_CLK_x <= 1'd1;
        
//STOP状态时的计数停
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_stop_flag <= 1'd0;
    else if(statue == STOP && cnt == 2'd2 && cnt_stop == MAX)
        cnt_stop_flag <= 1'd1;
    else if(statue == START_ID2)
        cnt_stop_flag <= 1'd0;
        
//STOP状态下让I2C总线停止两个周期的计
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_stop <= 1'd0;
    else if(statue == STOP && cnt_stop == MAX)
        cnt_stop <= 1'd0;
    else if(statue == STOP && cnt_stop_flag != 1'd1)
        cnt_stop <= cnt_stop + 1'd1;
    else
        cnt_stop <= 1'd0;
        
//SCL时钟生成
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_clk <= 1'd0;
     else if(statue == STOP || statue == IDLE)
        cnt_clk <= 1'd0;
     else if(cnt_clk == MAX)
        cnt_clk <= 1'd0;
     else
        cnt_clk <= cnt_clk + 1'd1;
        
//SCL时钟生成
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        scl <= 1'd1;
    else if(statue == END && cnt_clk_flag == 1'd1)
        scl <= 1'd1;
    else if(statue == STOP || (statue == x_ack3 && cnt_clkx_flag == 1'd1))
        scl <= 1'd1;
    else if(statue == STOP && cnt_stop_flag == 1'd1 && cnt_clkx_flag == 1'd1)
        scl <= 1'd0;
    else if(statue == IDLE && cnt_CLKx == START_MAX)
        scl <= 1'd0;
    else if(cnt_clk <= MAX_2 && read_en == 1'd1 && statue != IDLE)
        scl <= 1'd0;
    else if(cnt_clk < MAX && read_en == 1'd1 && statue != IDLE)
        scl <= 1'd1;
    else if(cnt_clk == MAX && read_en == 1'd1 && statue != IDLE)
        scl <= 1'd0;
    else if(read_en != 1'd1)
        scl <= 1'd1;

//全局计数
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt <= 1'd0; 
    else if(statue == STOP)
        begin
            if(cnt_stop == MAX && cnt == 2'd2)
                cnt <= 1'd0;
             else if(cnt_stop == MAX)
                cnt <= cnt + 1'd1;
             else
                cnt <= cnt;
        end
     else if(statue == NO && statue == END)
        cnt <= 1'd0;
     else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
        cnt <= 1'd0;
     else if(cnt_clkx_flag == 1'd1)
        cnt <= cnt + 1'd1;
     else
        cnt <= cnt;
        
//I2C信号周期更替标志       
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_clk_flag <= 1'd0;
    else if(cnt_clk == MAX - 1'd1)
        cnt_clk_flag <= 1'd1;
    else
        cnt_clk_flag <= 1'd0;
        
//data数据记录标志
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        data_flag <= 1'd0;
    else if(statue == DATA && cnt_clk == START_MAX)
        data_flag <= 1'd1;
    else
        data_flag <= 1'd0;

//数据存储
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
         Data_reg <= 1'd0;
    else if(statue == DATA && data_flag == 1'd1)
        Data_reg <= {Data_reg[6:0],sda_rd};
    else
        Data_reg <= Data_reg;       
        
//状态机跳转      
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        statue <= IDLE;
    else case(statue)
        IDLE:
            if(read_en == 1'd1 && cnt_CLKx == START_MAX)
                statue <= START_ID1;
                
        START_ID1:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= x_ack1;
                
        x_ack1:
            if(cnt_clkx_flag == 1'd1)
                statue <= ADD_H;
                
        ADD_H:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= x_ack2;
                
        x_ack2:
            if(cnt_clkx_flag == 1'd1)
                statue <= ADD_L;
                
        ADD_L:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= x_ack3;
                
        x_ack3:
            if(cnt_clkx_flag == 1'd1)
                statue <= STOP;
                
        STOP:
            if(cnt_stop_flag == 1'd1 && cnt_CLKx == START_MAX)
                statue <= START_ID2;
                
        START_ID2:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= x_ack4;
                
        x_ack4:
            if(cnt_clkx_flag == 1'd1)
                statue <= DATA;
                
        DATA:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= NO;
                
        NO:
            if(cnt_clk_flag == 1'd1)
                statue <= END;
                
        END:     
            if(cnt_clk_flag == 1'd1)
                statue <= IDLE;
                
        default:statue <= IDLE;
    endcase 
    
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        sda <= 1'd1;
    else case(statue)
        IDLE:
            if(read_en == 1'd1)
                sda <= 1'd0;
            else
                sda <= 1'd1;
                
        START_ID1:
            if(cnt_clkx_flag == 1'd1 && cnt < 6'd7)
                sda <= Cmd[22 - cnt];
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd7)
                sda <= 1'd0;
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd0;
                
        x_ack1:
            if(cnt_clkx_flag == 1'd1)
                sda <= Cmd[15 - cnt];
                
        ADD_H:
            if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
                sda <= Cmd[15 - cnt];
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd0;
                
        x_ack2:
            if(cnt_clkx_flag == 1'd1)
                sda <= Cmd[7 - cnt];
                
        ADD_L:
            if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
                sda <= Cmd[7 - cnt];
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd0;
                
        x_ack3:
            if(cnt_clkx_flag == 1'd1)
                sda <= 1'd0;
                
        STOP:
            if(cnt_stop_flag != 1'd1 && cnt == 1'd0)
                sda <= 1'd0;
            else if(cnt_stop_flag == 1'd1)
                sda <= 1'd0;
            else
                sda <= 1'd1;
                
        START_ID2:
            if(cnt_clkx_flag == 1'd1 && cnt < 6'd7)
                sda <= Cmd[22 - cnt];
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd7)
                sda <= 1'd1;
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd0;
                
        x_ack4:
            sda <= 1'd0;     
                
        DATA:
            if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd1;
            else
                sda <= 1'd0;
                
        NO:     
            if(cnt_clk_flag == 1'd1)
                sda <= 1'd0;
            else
                sda <= 1'd1;
                
        END:
            if(cnt_clk_flag == 1'd1)
                sda <= 1'd1;
                
        default:sda <= 1'd1;
    endcase

/*   
assign sda_x = (statue == x_ack1 || statue == x_ack2 || statue == x_ack3 
                || statue == x_ack4 || (statue == DATA))?1'dz:sda;
*/

endmodule

结果演示:

(3) 写

IIC与SSCB通用
设置寄存器时会用到

SSCB写寄存器部分

module SSCB_WR
(
    input       wire                sys_clk,
    input       wire                rst,
    input       wire                WR_en,
    input       wire    [15:0]      addr,
    input       wire    [7:0]       ID,
    input       wire    [7:0]       data,
    
    output      reg             sda,
    output      reg             scl,
    output      reg             WR_end,
    output      reg     [9:0]   statue
);

localparam
    IDLE            =           10'b0000000001,
    START_ID1       =           10'b0000000010,
    x_ack1          =           10'b0000000100,
    ADD_H           =           10'b0000001000,
    x_ack2          =           10'b0000010000,
    ADD_L           =           10'b0000100000,
    x_ack3          =           10'b0001000000,
    DATA            =           10'b0010000000,
    x_ack4          =           10'b0100000000,
    END             =           10'b1000000000;
    
localparam
    CLOCK       =       30'd50_000_000,
    I2C_CLK     =       30'd400_000,
    START_MAX   =       (CLOCK/I2C_CLK)*3/4 - 1'd1,
    MAX         =       CLOCK/I2C_CLK - 1'd1,
    MAX_2       =       (CLOCK/I2C_CLK)/2 - 1'd1;
    
wire [23:0] Cmd;
//reg sda;

assign Cmd = {ID,addr};

reg [20:0]  cnt;

reg         I2C_CLK_x;
reg [20:0]  cnt_CLKx;
reg         cnt_clkx_flag;

reg [20:0]  cnt_clk;
reg         cnt_clk_flag;

//结束标志
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        WR_end <= 1'd0;
    else if(statue == END && cnt_clk == MAX - 1'd1)
        WR_end <= 1'd1;
    else
        WR_end <= 1'd0;

//相移135°的I2C信号计数
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_CLKx <= 1'd0;
    else if(cnt_CLKx == MAX)
        cnt_CLKx <= 1'd0;
    else if(WR_en == 1'd1 && statue != x_ack4 && statue != END)
        cnt_CLKx <= cnt_CLKx + 1'd1;
    else
        cnt_CLKx <= 1'd0;

//相移135°的I2C信号周期更替标志    
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_clkx_flag <= 1'd0;
    else if(cnt_CLKx == MAX - 1'd1)
        cnt_clkx_flag <= 1'd1;
    else
        cnt_clkx_flag <= 1'd0;
        
//相移135°的I2C信号产生        
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        I2C_CLK_x <= 1'd1;
    else if((statue == DATA && cnt == 6'd8 && cnt_clkx_flag == 1'd1)
            || (statue == x_ack4 && statue == END))
        I2C_CLK_x <= 1'd1;
    else if(cnt_CLKx <= MAX_2 -1'd1 && WR_en == 1'd1)
        I2C_CLK_x <= 1'd0;
    else if(cnt_CLKx <= MAX - 1'd1 && WR_en == 1'd1)
        I2C_CLK_x <= 1'd1;
    else if(WR_en != 1'd1)
        I2C_CLK_x <= 1'd1;
        
//SCL时钟生成
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_clk <= 1'd0;
     else if(statue == IDLE)
        cnt_clk <= 1'd0;
     else if(cnt_clk == MAX)
        cnt_clk <= 1'd0;
     else
        cnt_clk <= cnt_clk + 1'd1;
        
//SCL时钟生成
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        scl <= 1'd1;
    else if(statue == END && cnt_clk_flag == 1'd1)
        scl <= 1'd1;
    else if(statue == IDLE && cnt_CLKx == START_MAX)
        scl <= 1'd0;
    else if(cnt_clk <= MAX_2 && WR_en == 1'd1 && statue != IDLE)
        scl <= 1'd0;
    else if(cnt_clk < MAX && WR_en == 1'd1 && statue != IDLE)
        scl <= 1'd1;
    else if(cnt_clk == MAX && WR_en == 1'd1 && statue != IDLE)
        scl <= 1'd0;
    else if(WR_en != 1'd1)
        scl <= 1'd1;

//全局计数
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt <= 1'd0;
     else if(statue == x_ack4 && statue == END)
        cnt <= 1'd0;
     else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
        cnt <= 1'd0;
     else if(cnt_clkx_flag == 1'd1)
        cnt <= cnt + 1'd1;
     else
        cnt <= cnt;
        
//I2C信号周期更替标志       
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_clk_flag <= 1'd0;
    else if(cnt_clk == MAX - 1'd1)
        cnt_clk_flag <= 1'd1;
    else
        cnt_clk_flag <= 1'd0;     
        
//状态机跳转      
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        statue <= IDLE;
    else case(statue)
        IDLE:
            if(WR_en == 1'd1 && cnt_CLKx == START_MAX)
                statue <= START_ID1;
                
        START_ID1:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= x_ack1;
                
        x_ack1:
            if(cnt_clkx_flag == 1'd1)
                statue <= ADD_H;
                
        ADD_H:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= x_ack2;
                
        x_ack2:
            if(cnt_clkx_flag == 1'd1)
                statue <= ADD_L;
                
        ADD_L:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= x_ack3;
                
        x_ack3:
            if(cnt_clkx_flag == 1'd1)
                statue <= DATA;
                
        DATA:
            if(cnt == 8'd8 && cnt_clkx_flag == 1'd1)
                statue <= x_ack4;
                
        x_ack4:
            if(cnt_clk_flag == 1'd1)
                statue <= END;
                
        END:     
            if(cnt_clk_flag == 1'd1)
                statue <= IDLE;
                
        default:statue <= IDLE;
    endcase 
    
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        sda <= 1'd1;
    else case(statue)
        IDLE:
            if(WR_en == 1'd1)
                sda <= 1'd0;
            else
                sda <= 1'd1;
                
        START_ID1:
            if(cnt_clkx_flag == 1'd1 && cnt < 6'd7)
                sda <= Cmd[22 - cnt];
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd7)
                sda <= 1'd0;
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd0;
                
        x_ack1:
            if(cnt_clkx_flag == 1'd1)
                sda <= Cmd[15 - cnt];
                
        ADD_H:
            if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
                sda <= Cmd[15 - cnt];
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd0;
                
        x_ack2:
            if(cnt_clkx_flag == 1'd1)
                sda <= Cmd[7 - cnt];
                
        ADD_L:
            if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
                sda <= Cmd[7 - cnt];
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd0;
                
        x_ack3:
            if(cnt_clkx_flag == 1'd1)
                sda <= data[7 - cnt];     
                
        DATA:
            if(cnt_clkx_flag == 1'd1 && cnt <= 6'd7)
                sda <= data[7 - cnt];
            else if(cnt_clkx_flag == 1'd1 && cnt == 6'd8)
                sda <= 1'd0;
                
        x_ack4:     
            if(cnt_clk_flag == 1'd1)
                sda <= 1'd0;
            else
                sda <= 1'd0;
                
        END:
            if(cnt_clk_flag == 1'd1)
                sda <= 1'd1;
                
        default:sda <= 1'd1;
    endcase 

/*    
assign sda_x = (statue == x_ack1 || statue == x_ack2 || statue == x_ack3 
                || statue == x_ack4 || (statue == DATA))?1'dz:sda;
*/

endmodule

SSCB寄存器地址和数据来源(应用了野火的代码)

module OV5640_cfg
(
    input       wire                sys_clk,
    input       wire                rst,
    input       wire                start,
    input       wire                write_end,
    
    output      wire     [23:0]     cfg_data,
    output      reg                 WR_en,
    output      reg                 init_end
);

parameter   REG_NUM         =   8'd251      ;
wire    [23:0]  cfg_data_reg[REG_NUM-1:0]   ;

reg [15:0] cnt;
reg [8:0]  number;
reg cnt_en;

parameter delay = 16'd9999;

always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt_en <= 1'd0;
    else if(write_end == 1'd1 && init_end != 1'd1)
        cnt_en <= 1'd1;
    else if(cnt == delay)
        cnt_en <= 1'd0;
    else
        cnt_en <= cnt_en;

always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        init_end <= 1'd0;
    else if(number == REG_NUM && cnt == delay)
        init_end <= 1'd1;

always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        cnt <= 1'd0;
    else if(cnt == delay)
        cnt <= 1'd0;
    else if(WR_en != 1'd1 && start == 1'd1 && init_end != 1'd1 && cnt_en == 1'd1)
        cnt <= cnt + 1'd1;
    else
        cnt <= 1'd0;

always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        number <= 1'd0;
    else if(write_end == 1'd1 && number != REG_NUM)
        number <= number + 1'd1;
    else
        number <= number;
        
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        WR_en <= 1'd0;
    else if(write_end == 1'd1)
        WR_en <= 1'd0;
    else if(number != REG_NUM && cnt == delay)
        WR_en <= 1'd1;
    else if(number == 1'd0 && start == 1'd1)
        WR_en <= 1'd1;
    
assign cfg_data =  (number != REG_NUM)?cfg_data_reg[number]:1'd0;

//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'h07};
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'hfa};//Y起始地址
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'h06};//Y结束地址
assign  cfg_data_reg[222]  =       {16'h3807, 8'ha9};
assign  cfg_data_reg[223]  =       {16'h3808, 8'h03};//水平输出宽度
assign  cfg_data_reg[224]  =       {16'h3809, 8'h20};
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'h64};
assign  cfg_data_reg[229]  =       {16'h380e, 8'h02};//总垂直大小

assign  cfg_data_reg[230]  =       {16'h380f, 8'he4};
assign  cfg_data_reg[231]  =       {16'h3813, 8'h04};//垂直偏移
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

(4)顶层测试

顶层代码

module OV5640_Top
(
    input       wire                sys_clk,
    input       wire                rst,
    
    inout       wire                sda,
    output      wire                scl,
    output      wire                pwdn,
    output      wire                resetb,
    output      wire    [9:0]       statue_WR,
    output      wire    [12:0]      statue_RD
);

reg                 read_en;
wire    [15:0]      addr;
wire                read_end;
wire                sccb_en;
wire                clk_en;
wire                start;
wire    [7:0]       Data_reg;
wire    [23:0]      cfg_data;
wire                init_end;
wire				write_end;
wire				WR_en;

wire                scl_rd;
wire                scl_wr;

wire                sda_rd;
wire                sda_wr;
reg                 number;
/*
wire    [9:0]       statue_WR;
wire    [12:0]      statue_RD;
*/

wire         [7:0]       ID      =       8'h3C;
wire         [15:0]      rd_add  =       16'h3808;
wire         [23:0]      Cmd;

assign start = (sccb_en == 1'd1 && clk_en == 1'd1)?1'd1:1'd0;

assign sda = (init_end == 1'd1)?((statue_RD == 13'b0_0000_0000_0100
            || statue_RD == 13'b0_0000_0001_0000 
            || statue_RD == 13'b0_0000_0100_0000
            || statue_RD == 13'b0_0010_0000_0000
            || statue_RD == 13'b0_0100_0000_0000)?1'dz:sda_rd)
            :sda_wr;

assign scl = (init_end == 1'd1)?scl_rd:scl_wr;

always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        number <= 1'd1;
    else if(number == 1'd1 && read_end == 1'd1 && init_end == 1'd1)
        number <= 1'd0;
        
always@(posedge sys_clk or negedge rst)
    if(rst == 1'd0)
        read_en <= 1'd0;
    else if(read_end == 1'd1)
        read_en <= 1'd0;
    else if(init_end == 1'd1 && number == 1'd1)
        read_en <= 1'd1;

Power_ctrl 
#(
    .CNT_5MS(20'd250_000),
    .CNT_1MS(20'd50_000),
    .CNT_20MS(20'd1000_000)  
)
Power_ctrl_inst
(
    .sys_clk(sys_clk),
    .rst(rst),
    
    .pwdn(pwdn),
    .resetb(resetb),
    .sccb_en(sccb_en),
    .clk_en(clk_en)
);

OV5640_cfg OV5640_cfg_inst
(
    .sys_clk(sys_clk),
    .rst(rst),
    .start(start),
    .write_end(write_end),
    
    .cfg_data(cfg_data),
    .WR_en(WR_en),
    .init_end(init_end)
);

SSCB_WR SSCB_WR_inst
(
    .sys_clk(sys_clk),
    .rst(rst),
    .WR_en(WR_en),
    .addr(cfg_data[23:8]),
    .ID(ID),
    .data(cfg_data[7:0]),
    
    .sda(sda_wr),
    .scl(scl_wr),
    .WR_end(write_end),
    .statue(statue_WR)
);

SSCB_ID SSCB_ID_inst
(
    .sys_clk(sys_clk),
    .rst(rst),
    .read_en(read_en),
    .addr(rd_add),
    .ID(ID),
    .sda_rd(sda),
    
    .sda(sda_rd),
    .scl(scl_rd),
    .read_end(read_end),
    .Data_reg(Data_reg),
    .statue(statue_RD),
    .Cmd(Cmd)
);

endmodule

测试结果


该测试是将刚才写于的寄存器数据读出来判断是否一致(该寄存器地址为16‘h3808)

(5) 使用inout的注意事项

<1>inout只能用wire来赋值
<2>inout在赋值时,要使用三态逻辑门来赋值(也有其他的方法,但最好还是用这个),且赋值的内容必须包含高阻态。
<3>尽量不要在底层使用inout,不然会有一些警告。
<4>底层不要数据赋值为z
<5>规范使用inout,不然可能导致电路短路

以上几点,如果不遵循,可能编译通过了,但生成的电路会不满足你的设计要求。

三、摄像头数据读取

数据输出时序:

由于个人SDRAM设计原因,后续再补全

有关基于FPGA的OV5640摄像头驱动的更多相关文章

  1. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  2. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  3. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  6. ruby-on-rails - (Ruby,Rails) 基于角色的身份验证和用户管理...? - 2

    我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源

  7. ruby - 在 Rakefile 中动态生成 Rake 测试任务(基于现有的测试文件) - 2

    我正在根据Rakefile中的现有测试文件动态生成测试任务。假设您有各种以模式命名的单元测试文件test_.rb.所以我正在做的是创建一个以“测试”命名空间内的文件名命名的任务。使用下面的代码,我可以用raketest:调用所有测试require'rake/testtask'task:default=>'test:all'namespace:testdodesc"Runalltests"Rake::TestTask.new(:all)do|t|t.test_files=FileList['test_*.rb']endFileList['test_*.rb'].eachdo|task|n

  8. ruby - 如何使用 Ruby 基于字母数字字符串生成颜色? - 2

    我想要像“嘿那里”这样的东西变成,例如,#316583。我希望将任意长度的字符串“归结”为十六进制颜色。我不知道从哪里开始。我在想,每个字符串的MD5散列都是不同的-但如何将该散列转换为十六进制颜色数字? 最佳答案 你可以只取几位前几位:require'digest/md5'color=Digest::MD5.hexdigest('Mytext')[0..5] 关于ruby-如何使用Ruby基于字母数字字符串生成颜色?,我们在StackOverflow上找到一个类似的问题:

  9. 【自动驾驶环境感知项目】——基于Paddle3D的点云障碍物检测 - 2

    文章目录1.自动驾驶实战:基于Paddle3D的点云障碍物检测1.1环境信息1.2准备点云数据1.3安装Paddle3D1.4模型训练1.5模型评估1.6模型导出1.7模型部署效果附录show_lidar_pred_on_image.py1.自动驾驶实战:基于Paddle3D的点云障碍物检测项目地址——自动驾驶实战:基于Paddle3D的点云障碍物检测课程地址——自动驾驶感知系统揭秘1.1环境信息硬件信息CPU:2核AI加速卡:v100总显存:16GB总内存:16GB总硬盘:100GB环境配置Python:3.7.4框架信息框架版本:PaddlePaddle2.4.0(项目默认框架版本为2.3

  10. ruby - 运行测试时静音 Chrome 驱动程序控制台输出 - 2

    我使用的是最新版本的Chrome(32.0.1700.107)和Chrome驱动程序(V2.8)。但是当我在Ruby中使用以下代码运行示例测试时:require'selenium-webdriver'WAIT=Selenium::WebDriver::Wait.new(timeout:100)$driver=Selenium::WebDriver.for:chrome$driver.manage.window.maximize$driver.navigate.to'https://www.google.co.in'defapps_hoverele_hover=$driver.find_

随机推荐