基于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设计原因,后续再补全

  • 8
    点赞
  • 61
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于 FPGAOV5640 摄像头显示例程可以分为以下几个步骤: 1. 硬件连接 将 OV5640 摄像头连接到 FPGA 板上的相应接口(一般为 MIPI CSI 接口),并将 FPGA 板连接到显示器上。 2. 寄存器配置 配置 OV5640 摄像头的寄存器,使其能够输出图像数据。可以使用 I2C 总线与 OV5640 摄像头通信,通过写入寄存器来配置摄像头。具体的寄存器配置可以参考 OV5640 数据手册。 3. 数据传输 将 OV5640 摄像头输出的数据传输到 FPGA 板上。一般来说,数据传输方式有两种: - 并行传输:将 OV5640 摄像头输出的像素数据通过并行接口传输到 FPGA 板上。这种传输方式需要使用大量的 FPGA 引脚,因此不太常用。 - MIPI CSI-2 串行传输:将 OV5640 摄像头输出的像素数据通过 MIPI CSI-2 串行接口传输到 FPGA 板上。这种传输方式需要使用较少的引脚,因此比较常用。 4. 图像处理 将传输到 FPGA 板上的图像数据进行处理,以便在显示器上显示。具体的图像处理算法根据需求而定,可以包括缩放、旋转、滤波等操作。 5. 显示器输出 将处理后的图像数据输出到显示器上进行显示。可以使用 VGA、HDMI 等接口将 FPGA 板连接到显示器上。 需要注意的是,OV5640 摄像头驱动和图像处理需要使用 FPGA 开发板上的软件进行实现。具体的软件实现方式要根据 FPGA 开发板和摄像头的具体情况来定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值