FPGA——OV5640摄像头初始化实现及仿真

一、设计思路

1、步骤

摄像头有一个上电复位信号(低有效)
摄像头在系统复位之后,摄像头上电复位信号要保持稳定的低电平至少1ms
1ms之后之后,拉高上电复位信号
拉高上电复位信号后20ms之后,摄像头才能进入工作模式
进入工作模式之后,通过SCCB将初始化的参数写入摄像头寄存器内
摄像头初始化完成
上电复位信号延迟计数器,参数ROM地址计数器

2、仿真方法

因为SCCB协议是由IIC协议移植过来的,而IIC有应答位,而SCCB是无关项,虽然板级验证是没有问题的,但是在仿真过程中IIC状态机是要接收ACK才可以正常工作的,所有要将IIC代码中的ACK信号改一下
sda == 0 改成 sda == 1

二、摄像头初始化代码

module camera_init(
   clk         ,
   rst_n       ,
   init_done   ,  //摄像头初始化完成标志信号
   camera_rst_n,
   camera_pwdn ,  //ov5640掉电控制信号
   scl         ,  //sccb 时钟信号
   sda            //sccb 串行数据信号
);

localparam RGB          =     0;
localparam JPEG         =     1;
localparam LUT_W        =     8;
localparam Q_W          =     24;

parameter IMAGE_FLIP    =     1'b0; //图像上下翻转,高有效
parameter IMAGE_MIRROR  =     1'b0; //图像左右镜像,高有效
parameter IMAGE_TYPE    =     RGB;  //输出图像类型
parameter IMAGE_WIDTH   =     800;  //图像宽度
parameter IMAGE_HEIGHT  =     480;  //图像高度

parameter DEVICE_ADDR   =     8'h78;//摄像头器件地址


//计数器参数
localparam CNTRST_N     =     1050000;
// localparam CNTRST_N     =     21'd1100000;
localparam CNTRST_W     =     21;
localparam UP_N         =     50000;//摄像头复位信号拉高时间点
// localparam UP_N         =     21'd100000;
localparam CNTADDR_W    =     LUT_W;

input                   clk;
input                   rst_n;

output                  init_done;
output                  camera_pwdn;
output                  camera_rst_n;
output                  scl;

inout                   sda;

reg                     init_done;
wire                    camera_pwdn;
reg                     camera_rst_n;   
wire                    scl;
wire                    sda;


//中间变量
wire     [LUT_W-1:0]    lut_max_addr;  //不同图像输出格式下查找表最大地址
reg                     work_flag;
wire     [Q_W-1:0]      q;
reg                     rst_done;

//计数器变量
reg      [CNTRST_W-1:0] cnt_camrst;
wire                    add_cnt_camrst;
wire                    end_cnt_camrst;

reg      [CNTADDR_W-1:0]cnt_addr;
wire                    add_cnt_addr;
wire                    end_cnt_addr;

//SCCB模块变量
wire                    sccb_done;
reg                     wr;

//根据图像参数生成对应的电路(节约FPGA电路资源)
generate
   //RGB输出格式
   if(IMAGE_TYPE == RGB)begin
      assign lut_max_addr = 252;
      case({IMAGE_FLIP,IMAGE_MIRROR})
         2'b00:
            begin
               ov5640_init_table_rgb ov5640_init_table_rgb(
                  .clk  (clk),
                  .addr (cnt_addr),
                  .q    (q)
               );
               //更改摄像头初始化查找表的初始化参数,图像不翻转,不镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
               defparam ov5640_init_table_rgb.IMAGE_WIDTH   = IMAGE_WIDTH;
               defparam ov5640_init_table_rgb.IMAGE_HEIGHT  = IMAGE_HEIGHT;
               defparam ov5640_init_table_rgb.IMAGE_FLIP    = 8'h40;
               defparam ov5640_init_table_rgb.IMAGE_MIRROR  = 4'h7;
            end
         2'b01:
            begin
               ov5640_init_table_rgb ov5640_init_table_rgb(
                  .clk  (clk),
                  .addr (cnt_addr),
                  .q    (q)
               );
               //更改摄像头初始化查找表的初始化参数,图像不翻转,镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
               defparam ov5640_init_table_rgb.IMAGE_WIDTH   = IMAGE_WIDTH;
               defparam ov5640_init_table_rgb.IMAGE_HEIGHT  = IMAGE_HEIGHT;
               defparam ov5640_init_table_rgb.IMAGE_FLIP    = 8'h40;
               defparam ov5640_init_table_rgb.IMAGE_MIRROR  = 4'h0;
            end
         2'b10:
            begin
               ov5640_init_table_rgb ov5640_init_table_rgb(
                  .clk  (clk),
                  .addr (cnt_addr),
                  .q    (q)
               );
               //更改摄像头初始化查找表的初始化参数,图像翻转,不镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
               defparam ov5640_init_table_rgb.IMAGE_WIDTH   = IMAGE_WIDTH;
               defparam ov5640_init_table_rgb.IMAGE_HEIGHT  = IMAGE_HEIGHT;
               defparam ov5640_init_table_rgb.IMAGE_FLIP    = 8'h47;
               defparam ov5640_init_table_rgb.IMAGE_MIRROR  = 4'h7;
            end
         2'b11:
            begin
               ov5640_init_table_rgb ov5640_init_table_rgb(
                  .clk  (clk),
                  .addr (cnt_addr),
                  .q    (q)
               );
               //更改摄像头初始化查找表的初始化参数,图像翻转,镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
               defparam ov5640_init_table_rgb.IMAGE_WIDTH   = IMAGE_WIDTH;
               defparam ov5640_init_table_rgb.IMAGE_HEIGHT  = IMAGE_HEIGHT;
               defparam ov5640_init_table_rgb.IMAGE_FLIP    = 8'h47;
               defparam ov5640_init_table_rgb.IMAGE_MIRROR  = 4'h0;
            end
      endcase
   end
   //JPEG输出格式
   else begin
      assign lut_max_addr = 250;
      case({IMAGE_FLIP,IMAGE_MIRROR})
         2'b00:
            begin
               ov5640_init_table_rgb ov5640_init_table_rgb(
                  .clk  (clk),
                  .addr (cnt_addr),
                  .q    (q)
               );
               //更改摄像头初始化查找表的初始化参数,图像不翻转,不镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
               defparam ov5640_init_table_rgb.IMAGE_WIDTH   = IMAGE_WIDTH;
               defparam ov5640_init_table_rgb.IMAGE_HEIGHT  = IMAGE_HEIGHT;
               defparam ov5640_init_table_rgb.IMAGE_FLIP    = 8'h40;
               defparam ov5640_init_table_rgb.IMAGE_MIRROR  = 4'h7;
            end
         2'b01:
            begin
               ov5640_init_table_rgb ov5640_init_table_rgb(
                  .clk  (clk),
                  .addr (cnt_addr),
                  .q    (q)
               );
               //更改摄像头初始化查找表的初始化参数,图像不翻转,镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
               defparam ov5640_init_table_rgb.IMAGE_WIDTH   = IMAGE_WIDTH;
               defparam ov5640_init_table_rgb.IMAGE_HEIGHT  = IMAGE_HEIGHT;
               defparam ov5640_init_table_rgb.IMAGE_FLIP    = 8'h40;
               defparam ov5640_init_table_rgb.IMAGE_MIRROR  = 4'h0;
            end
         2'b10:
            begin
               ov5640_init_table_rgb ov5640_init_table_rgb(
                  .clk  (clk),
                  .addr (cnt_addr),
                  .q    (q)
               );
               //更改摄像头初始化查找表的初始化参数,图像翻转,不镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
               defparam ov5640_init_table_rgb.IMAGE_WIDTH   = IMAGE_WIDTH;
               defparam ov5640_init_table_rgb.IMAGE_HEIGHT  = IMAGE_HEIGHT;
               defparam ov5640_init_table_rgb.IMAGE_FLIP    = 8'h47;
               defparam ov5640_init_table_rgb.IMAGE_MIRROR  = 4'h7;
            end
         2'b11:
            begin
               ov5640_init_table_rgb ov5640_init_table_rgb(
                  .clk  (clk),
                  .addr (cnt_addr),
                  .q    (q)
               );
               //更改摄像头初始化查找表的初始化参数,图像翻转,镜像,输出图像大小为IMAGE_WIDTH*IMAGE_HEIGHT
               defparam ov5640_init_table_rgb.IMAGE_WIDTH   = IMAGE_WIDTH;
               defparam ov5640_init_table_rgb.IMAGE_HEIGHT  = IMAGE_HEIGHT;
               defparam ov5640_init_table_rgb.IMAGE_FLIP    = 8'h47;
               defparam ov5640_init_table_rgb.IMAGE_MIRROR  = 4'h0;
            end
      endcase
   end
endgenerate



//pwdn为低电平时,器件才能开始工作
assign camera_pwdn = 0;

//摄像头复位信号计时计数器
always @(posedge clk or negedge rst_n)begin
   if(rst_n)
      cnt_camrst <= 0;
   else if(add_cnt_camrst)begin
      if(end_cnt_camrst)
         cnt_camrst <= 0;
      else
         cnt_camrst <= cnt_camrst + 1'b1;
   end
end
assign add_cnt_camrst = !work_flag && !rst_done;
assign end_cnt_camrst = add_cnt_camrst && cnt_camrst == CNTRST_N - 1;

//初始化配置查找表地址计数器
always @(posedge clk or negedge rst_n)begin
   if(rst_n)
      cnt_addr <= 0;
   else if(add_cnt_addr)begin
      if(end_cnt_addr)
         cnt_addr <= 0;
      else
         cnt_addr <= cnt_addr + 1'b1;
   end
end
assign add_cnt_addr = work_flag && sccb_done;
assign end_cnt_addr = add_cnt_addr && cnt_addr == lut_max_addr - 1;


//在上电之后1ms拉高摄像头复位信号
always @(posedge clk or negedge rst_n)begin
   if(rst_n)
      camera_rst_n <= 0;
   else if(add_cnt_camrst && cnt_camrst == UP_N - 1)
      camera_rst_n <= 1;
end

//初始化工作标志信号
always @(posedge clk or negedge rst_n)begin
   if(rst_n)
      work_flag <= 0;
   else if(end_cnt_camrst)
      work_flag <= 1;
   else if(end_cnt_addr)
      work_flag <= 0;
end

//摄像头复位信号初始化完成标志信号
always @(posedge clk or negedge rst_n)begin
   if(rst_n)
      rst_done <= 0;
   else if(end_cnt_camrst)
      rst_done <= 1;
end

always @(posedge clk or negedge rst_n)begin
   if(rst_n)
      wr <= 0;
   else if((end_cnt_camrst || add_cnt_addr) && !end_cnt_addr)
      wr <= 1;
   else
      wr <= 0;
end


always @(posedge clk or negedge rst_n)begin
   if(rst_n)
      init_done <= 0;
   else if(end_cnt_addr)
      init_done <= 1;
   // else
   //    init_done <= 1;
end

iic_module iic_module(
   .rst_n       ((!rst_n)), 
   .clk         (clk),
   .waddr_num   (2'd2),
   .device_addr (DEVICE_ADDR),
   .word_addr   (q[23:8]),
   .wr          (wr),
   .wr_data     (q[7:0]),
   .rd          (1'b0),
   .rd_data     (),
   .done        (sccb_done),
   .scl         (scl),
   .sda         (sda),
   .wr_data_vld (),
   .rd_data_vld ()
);

endmodule
posted @ 2021-02-18 15:47  AdriftCore  阅读(1467)  评论(0编辑  收藏  举报