官方论坛
官方淘宝
官方博客
微信公众号
点击联系吴工 点击联系周老师
您的当前位置:主页 > FPGA行业资讯 >

基于FPGA的OV5640摄像头采集设计-明德扬科教(mdy-edu.com)

发布时间:2019-12-10   作者:admin 浏览量:


本文设计思想采用明德扬至简设计法。在做摄像头数据采集处理之前,需要配置OV5640传感器内部寄存器使其按要求正常工作,详细内容请参见《OV5640自动对焦照相模组应用指南》。首先要关注OV5640的上电时序:

主控制器控制RESET PWDN两个信号按上电时序要求变化,之后允许ov_config模块配置内部寄存器。这里始终将PWDN拉低。实验中将摄像头分辨率设置为720p,即1280*720 ,帧率为30fps,图像输出格式是RGB565。此时摄像头输入时钟XCLK频率24MHz,输出像素时钟PCLK84MHz。由于实验使用的是OV5640双目摄像头模组,且XCLK由外部24MHz晶振给出,故ov_config模块整体结构及端口定义如下:

setup模块构造上电时序,两个reg_config分别配置一个OV5640摄像头。SCCB_interface子模块负责SCCB协议读写寄存器数据。由于OV5640摄像头内部寄存器地址为16位,因此写寄存器地址阶段分高低字节两次写入。datasheet中给出了OV5640SCCB ID地址(写),故读ID地址为0X79

SCCB时序图及AC characteristics如下:

SCCB时钟SIOC支持最大频率为400KHz,一般选100KHz即可。以下是本人设计的SCCB接口读写时序状态机,写操作:IDLE START WRI_ID WRI_REG WRI_DATA STOP 对应三相写,读操作:IDLE START WRI_ID WRI_REG STOP      START WRI_ID RD_DATA STOP对应两相写和两相读。

上代码:

SCCB读写模块:

`timescale 1ns / 1ps


module sccb_interface(
    input clk,
    input rst_n,

    input wr_en,
    input rd_en,
    input [8-1:0] id_addr,
    input [16-1:0] reg_addr,
    input [8-1:0] wr_data,
    output reg [8-1:0] rd_data,
    output reg rd_vld,
    output rdy,

    output reg sio_c,
    output reg sio_out_en,
    output reg sio_out,
    input sio_in
    );

    parameter CYC = 500;
    

localparam  IDLE = 0 ;
localparam  START = 1 ;
localparam  WRI_ID = 2 ;
localparam  WRI_REG = 3 ;
localparam  WRI_DATA = 4;
localparam  RD_DATA = 5;
localparam  STOP = 6 ;

//计数器
reg [ (9-1):0]  div_cnt     ;
wire        add_div_cnt ;
wire        end_div_cnt ;
reg [ (5-1):0]  bit_cnt     ;
wire        add_bit_cnt ;
wire        end_bit_cnt ;
reg [5-1:0] N;
(*DONT_TOUCH = "TRUE"*)reg [7-1:0] state_c,state_n;
wire idle2start,start2wri_id,wri_id2wri_reg,wri_id2rd_data, wri_reg2wri_data,wri_reg2stop,wri_data2stop,rd_data2stop,stop2start,stop2idle;
wire [18-1:0] regaddr;
reg [16-1:0] reg_addr_tmp;
reg [8-1:0] wr_data_tmp;
wire [9-1:0] idaddr_nc;
reg [8-1:0] id_addr_tmp;
reg rd_oper,rd_flag;
wire [9-1:0] wdata_nc;
wire [8-1:0] id_rwCtrl;


assign rdy = state_c == IDLE && !wr_en && !rd_en;

always @(posedge clk or negedge rst_n) begin 
    if (rst_n==0) begin
        div_cnt <= 0; 
    end
    else if(add_div_cnt) begin
        if(end_div_cnt)
            div_cnt <= 0; 
        else
            div_cnt <= div_cnt+1 ;
   end
end
assign add_div_cnt = (state_c != IDLE);
assign end_div_cnt = add_div_cnt  && div_cnt == (CYC)-1 ;//5000ns,200KHZ

always @(posedge clk or negedge rst_n) begin 
    if (rst_n==0) begin
        bit_cnt <= 0; 
    end
    else if(add_bit_cnt) begin
        if(end_bit_cnt)
            bit_cnt <= 0; 
        else
            bit_cnt <= bit_cnt+1 ;
   end
end
assign add_bit_cnt = (end_div_cnt);
assign end_bit_cnt = add_bit_cnt  && bit_cnt == (N)-1 ;

always@(*)begin
    case(state_c)
        START:                  N = 1;
        WRI_REG:                N = 18; //(8+1)*2 = 18
        WRI_ID,WRI_DATA,RD_DATA:N = 9;//8+1
        STOP:                   N = 2;
        default:;
    endcase
end

//FSM:IDLE START WRI_ID WRI_REG STOP
always @(posedge clk or negedge rst_n) begin 
    if (rst_n==0) begin
        state_c <= IDLE ;
    end
    else begin
        state_c <= state_n;
   end
end
//write:IDLE START WRI_ID WRI_REG WRI_DATA STOP IDLE...
//read: IDLE START WRI_ID WRI_REG STOP      START WRI_ID RD_DATA STOP IDLE...

always @(*) begin 
    case(state_c)  
        IDLE :begin                             //0
            if(idle2start) 
                state_n = START ;
            else 
                state_n = state_c ;
        end
        START :begin                            //1
            if(start2wri_id) 
                state_n = WRI_ID ;
            else 
                state_n = state_c ;
        end
        WRI_ID :begin                           //2
            if(wri_id2wri_reg) 
                state_n = WRI_REG ;
            else if(wri_id2rd_data)
                state_n = RD_DATA;
            else 
                state_n = state_c ;
        end
        WRI_REG :begin                          //3
            if(wri_reg2wri_data)
                state_n = WRI_DATA;
            else if(wri_reg2stop) 
                state_n = STOP ;
            else 
                state_n = state_c ;
        end
        WRI_DATA:begin                          //4
            if(wri_data2stop)
                state_n = STOP;
            else 
                state_n = state_c;
        end 
        RD_DATA:begin                           //5
            if(rd_data2stop)
                state_n = STOP;
            else
                state_n = state_c;
        end
        STOP :begin                             //6
            if(stop2start)
                state_n = START;
            else if(stop2idle) 
                state_n = IDLE ;
            else 
                state_n = state_c ;
        end
        default : state_n = IDLE ;
    endcase
end

assign idle2start           = state_c==IDLE         && (wr_en || rd_en);
assign start2wri_id         = state_c==START        && (end_bit_cnt);
assign wri_id2wri_reg       = state_c==WRI_ID       && (end_bit_cnt && !rd_oper);
assign wri_id2rd_data       = state_c==WRI_ID       && (end_bit_cnt && rd_oper);
assign wri_reg2wri_data     = state_c==WRI_REG      && (end_bit_cnt && !rd_flag);
assign wri_reg2stop         = state_c==WRI_REG      && (end_bit_cnt && rd_flag);
assign wri_data2stop        = state_c==WRI_DATA     && (end_bit_cnt);
assign rd_data2stop         = state_c==RD_DATA      && (end_bit_cnt);
assign stop2start           = state_c==STOP         && (end_bit_cnt && rd_flag && !rd_oper);
assign stop2idle            = state_c==STOP         && (end_bit_cnt && (!rd_flag || rd_oper));

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rd_oper <= 0;
    end
    else if(stop2start)begin
        rd_oper <= 1;
    end
    else if(rd_oper && stop2idle)
        rd_oper <= 0;
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rd_flag <= 0;
    end
    else if(idle2start && rd_en)begin
        rd_flag <= 1;
    end
    else if(stop2idle)
        rd_flag <= 0;
end

//SCCB时钟
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)
        sio_c <= 1'b1;
    else if(add_div_cnt && div_cnt == CYC/4-1)
        sio_c <= 1;
    else if(add_div_cnt && div_cnt == CYC/4+CYC/2-1)
        sio_c <= 0;
    else if(state_c == IDLE)
        sio_c <= 1;//空闲状态sioc为1
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        sio_out <= 1;
    end
    else if(state_c == START && div_cnt == CYC/2-1)
        sio_out <= 0;//开始条件
    else if(state_c == WRI_ID)
        sio_out <= idaddr_nc[9-1-bit_cnt];
    else if(wri_reg2stop || wri_data2stop || rd_data2stop)
        sio_out <= 0;
    else if(state_c == WRI_REG)
        sio_out <= regaddr[18-1-bit_cnt];
    else if(state_c == WRI_DATA)
        sio_out <= wdata_nc[9-1-bit_cnt];
    else if(state_c == RD_DATA && bit_cnt == 9-1)
        sio_out <= 1;//NACK
    else if(state_c == STOP && div_cnt == CYC/2-1 && bit_cnt == 0)
        sio_out <= 1;//结束条件
end

assign idaddr_nc = {id_rwCtrl,1'b1};
assign regaddr = {reg_addr_tmp[15:8],1'b1,reg_addr_tmp[7:0],1'b1};
assign wdata_nc = {wr_data_tmp,1'b1};

assign id_rwCtrl = rd_oper ? {id_addr[7:1],1'b1} : id_addr;

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        reg_addr_tmp <= 0;
        wr_data_tmp <= 0;
    end
    else if(wr_en || rd_en)begin
        reg_addr_tmp <= reg_addr;
        wr_data_tmp <= wr_data;
    end
end


always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        sio_out_en <= 0;
    end
    else begin
        case(state_c)
            START,STOP:begin
                sio_out_en <= 1;
            end
            WRI_ID,WRI_DATA:begin
                if(bit_cnt != 9-1)
                    sio_out_en <= 1;
                else
                    sio_out_en <= 0;
            end
            WRI_REG:begin
                if(bit_cnt != 9-1 && bit_cnt != 18-1)
                    sio_out_en <= 1;
                else
                    sio_out_en <= 0;
            end
            RD_DATA:begin
                if(bit_cnt == 9-1)
                    sio_out_en <= 1;
                else
                    sio_out_en <= 0;
            end 
            default:sio_out_en <= 0;
        endcase
    end
end

//read data
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rd_data <= 0;
    end
    else if(state_c == RD_DATA && bit_cnt != 9-1 && div_cnt == CYC/2-1)begin
        rd_data[8-1-bit_cnt] <= sio_in;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        rd_vld <= 0;
    end
    else if(rd_data2stop)begin
        rd_vld <= 1;
    end
    else 
        rd_vld <= 0;
end


endmodule

sccb_interface


寄存器配置模块:

reg_config`timescale 1ns / 1ps


module reg_config(
    input           clk,
    input           rst_n,

    input           en,
    output          finish,

    inout           sio_d,
    output          sio_c
    );

localparam WR_ID = 8'h78;
localparam RW_CTRL = 2'b11;//读
wire sio_out_en;
wire sio_out;
wire sio_in;
reg [9-1:0] reg_cnt;
wire add_reg_cnt,end_reg_cnt;
reg config_flag;
reg [26-1:0] op_reg_data;
wire rdy;
reg wr_en;
reg [16-1:0] reg_addr;
reg [8-1:0] wr_data;
reg config_done;
reg [ (2-1):0]  rw_cnt     ;
wire        add_rw_cnt ;
wire        end_rw_cnt ;
reg rd_en;
(*DONT_TOUCH = "TRUE"*)wire [8-1:0] rd_data;
(*DONT_TOUCH = "TRUE"*)wire rd_vld;

sccb_interface sccb_interface(
    .clk    (clk) ,
    .rst_n     (rst_n) ,
    .wr_en     (wr_en) ,
    .rd_en     (rd_en),
    .id_addr   (WR_ID) ,
    .reg_addr  (reg_addr) ,
    .wr_data   (wr_data) ,
    .rd_data   (rd_data),
    .rd_vld    (rd_vld),
    .rdy       (rdy) ,
    .sio_c     (sio_c) ,
    .sio_out_en(sio_out_en) ,
    .sio_out   (sio_out) ,
    .sio_in    (sio_in) 
    );

    assign sio_d = sio_out_en ? sio_out : 1'bz;
    assign sio_in = sio_d;

    always @(posedge clk or negedge rst_n) begin 
        if (rst_n==0) begin
            rw_cnt <= 0; 
        end
        else if(add_rw_cnt) begin
            if(end_rw_cnt)
                rw_cnt <= 0; 
            else
                rw_cnt <= rw_cnt+1 ;
        end
    end
    assign add_rw_cnt = (config_flag && rdy);
    assign end_rw_cnt = add_rw_cnt  && rw_cnt == (2)-1 ;//0 write 1 read

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            reg_cnt <= 0;
        end
        else if(add_reg_cnt)begin
            if(end_reg_cnt)
                reg_cnt <= 0;
            else
                reg_cnt <= reg_cnt + 1;
        end
    end

    assign add_reg_cnt = end_rw_cnt;       
    assign end_reg_cnt = add_reg_cnt && reg_cnt == 261-1;   

    //配置指令
    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            wr_en <= 0;
            reg_addr <= 0;
            wr_data <= 0;
        end
        else if(add_rw_cnt && rw_cnt == 0)begin
            wr_en    <= op_reg_data[25];
            reg_addr <= op_reg_data[23:8];
            wr_data  <= op_reg_data[7:0];
        end
        else if(end_rw_cnt)begin
            rd_en     <= op_reg_data[24];
            reg_addr  <= op_reg_data[23:8];
        end
        else begin
            wr_en <= 0;
            rd_en <= 0;
        end
    end


    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            config_flag <= 0;
        end
        else if(en && !config_flag && !config_done)begin
            config_flag <= 1;
        end
        else if(end_reg_cnt)
            config_flag <= 0;
    end

    always  @(posedge clk or negedge rst_n)begin
        if(rst_n==1'b0)begin
            config_done <= 0;
        end
        else if(end_reg_cnt)begin
            config_done <= 1;
        end
    end

    assign finish = config_done && rdy;

always@(*)   
 begin//op_reg_data [25] wr     [24] rd     [23:8] reg_addr     [7:0] wr_data     
    case(reg_cnt)
     //15fps VGA YUV output  // 24MHz input clock, 24MHz PCLK
     0:op_reg_data=   {RW_CTRL, 24'h310311};// system clock from pad, bit[1]
     1:op_reg_data=   {RW_CTRL, 24'h300882};// software reset, bit[7]// delay 5ms 
     2:op_reg_data=   {RW_CTRL, 24'h300842};// software power down, bit[6]
     3:op_reg_data=   {RW_CTRL, 24'h310303};// system clock from PLL, bit[1]
     4:op_reg_data=   {RW_CTRL, 24'h3017ff};// FREX, Vsync, HREF, PCLK, D[9:6] output enable
     5:op_reg_data=   {RW_CTRL, 24'h3018ff};// D[5:0], GPIO[1:0] output enable
     6:op_reg_data=   {RW_CTRL, 24'h30341A};// MIPI 10-bit
     7:op_reg_data=   {RW_CTRL, 24'h303713};// PLL root divider, bit[4], PLL pre-divider, bit[3:0]
     8:op_reg_data=   {RW_CTRL, 24'h310801};// PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2] // SCLK root divider, bit[1:0] 
     9:op_reg_data=   {RW_CTRL, 24'h363036};
     10:op_reg_data=  {RW_CTRL, 24'h36310e};
     11:op_reg_data=  {RW_CTRL, 24'h3632e2};
     12:op_reg_data=  {RW_CTRL, 24'h363312};
     13:op_reg_data=  {RW_CTRL, 24'h3621e0};
     14:op_reg_data=  {RW_CTRL, 24'h3704a0};
     15:op_reg_data=  {RW_CTRL, 24'h37035a};
     16:op_reg_data=  {RW_CTRL, 24'h371578};
     17:op_reg_data=  {RW_CTRL, 24'h371701};
     18:op_reg_data=  {RW_CTRL, 24'h370b60};
     19:op_reg_data=  {RW_CTRL, 24'h37051a};
     20:op_reg_data=  {RW_CTRL, 24'h390502};
     21:op_reg_data=  {RW_CTRL, 24'h390610};
     22:op_reg_data=  {RW_CTRL, 24'h39010a};
     23:op_reg_data=  {RW_CTRL, 24'h373112};
     24:op_reg_data=  {RW_CTRL, 24'h360008};// VCM control
     25:op_reg_data=  {RW_CTRL, 24'h360133};// VCM control
     26:op_reg_data=  {RW_CTRL, 24'h302d60};// system control
     27:op_reg_data=  {RW_CTRL, 24'h362052};
     28:op_reg_data=  {RW_CTRL, 24'h371b20};
     29:op_reg_data=  {RW_CTRL, 24'h471c50};
     30:op_reg_data=  {RW_CTRL, 24'h3a1343};// pre-gain = 1.047x
     31:op_reg_data=  {RW_CTRL, 24'h3a1800};// gain ceiling
     32:op_reg_data=  {RW_CTRL, 24'h3a19f8};// gain ceiling = 15.5x
     33:op_reg_data=  {RW_CTRL, 24'h363513};
     34:op_reg_data=  {RW_CTRL, 24'h363603};
     35:op_reg_data=  {RW_CTRL, 24'h363440};
     36:op_reg_data=  {RW_CTRL, 24'h362201}; // 50/60Hz detection     50/60Hz 灯光条纹过滤
     37:op_reg_data=  {RW_CTRL, 24'h3c0134};// Band auto, bit[7]
     38:op_reg_data=  {RW_CTRL, 24'h3c0428};// threshold low sum     
     39:op_reg_data=  {RW_CTRL, 24'h3c0598};// threshold high sum
     40:op_reg_data=  {RW_CTRL, 24'h3c0600};// light meter 1 threshold[15:8]
     41:op_reg_data=  {RW_CTRL, 24'h3c0708};// light meter 1 threshold[7:0]
     42:op_reg_data=  {RW_CTRL, 24'h3c0800};// light meter 2 threshold[15:8]
     43:op_reg_data=  {RW_CTRL, 24'h3c091c};// light meter 2 threshold[7:0]
     44:op_reg_data=  {RW_CTRL, 24'h3c0a9c};// sample number[15:8]
     45:op_reg_data=  {RW_CTRL, 24'h3c0b40};// sample number[7:0]
     46:op_reg_data=  {RW_CTRL, 24'h381000};// Timing Hoffset[11:8]
     47:op_reg_data=  {RW_CTRL, 24'h381110};// Timing Hoffset[7:0]
     48:op_reg_data=  {RW_CTRL, 24'h381200};// Timing Voffset[10:8] 
     49:op_reg_data=  {RW_CTRL, 24'h370864};
     50:op_reg_data=  {RW_CTRL, 24'h400102};// BLC start from line 2
     51:op_reg_data=  {RW_CTRL, 24'h40051a};// BLC always update
     52:op_reg_data=  {RW_CTRL, 24'h300000};// enable blocks
     53:op_reg_data=  {RW_CTRL, 24'h3004ff};// enable clocks 
     54:op_reg_data=  {RW_CTRL, 24'h300e58};// MIPI power down, DVP enable
     55:op_reg_data=  {RW_CTRL, 24'h302e00};
     56:op_reg_data=  {RW_CTRL, 24'h430060};// RGB565
     57:op_reg_data=  {RW_CTRL, 24'h501f01};// ISP RGB 
     58:op_reg_data=  {RW_CTRL, 24'h440e00};
     59:op_reg_data=  {RW_CTRL, 24'h5000a7}; // Lenc on, raw gamma on, BPC on, WPC on, CIP on // AEC target    自动曝光控制
     60:op_reg_data=  {RW_CTRL, 24'h3a0f30};// stable range in high
     61:op_reg_data=  {RW_CTRL, 24'h3a1028};// stable range in low
     62:op_reg_data=  {RW_CTRL, 24'h3a1b30};// stable range out high
     63:op_reg_data=  {RW_CTRL, 24'h3a1e26};// stable range out low
     64:op_reg_data=  {RW_CTRL, 24'h3a1160};// fast zone high
     65:op_reg_data=  {RW_CTRL, 24'h3a1f14};// fast zone low// Lens correction for ?   镜头补偿
     66:op_reg_data=  {RW_CTRL, 24'h580023};
     67:op_reg_data=  {RW_CTRL, 24'h580114};
     68:op_reg_data=  {RW_CTRL, 24'h58020f};
     69:op_reg_data=  {RW_CTRL, 24'h58030f};
     70:op_reg_data=  {RW_CTRL, 24'h580412};
     71:op_reg_data=  {RW_CTRL, 24'h580526};
     72:op_reg_data=  {RW_CTRL, 24'h58060c};
     73:op_reg_data=  {RW_CTRL, 24'h580708};
     74:op_reg_data=  {RW_CTRL, 24'h580805};
     75:op_reg_data=  {RW_CTRL, 24'h580905};
     76:op_reg_data=  {RW_CTRL, 24'h580a08};
     77:op_reg_data=  {RW_CTRL, 24'h580b0d};
     78:op_reg_data=  {RW_CTRL, 24'h580c08};
     79:op_reg_data=  {RW_CTRL, 24'h580d03};
     80:op_reg_data=  {RW_CTRL, 24'h580e00};
     81:op_reg_data=  {RW_CTRL, 24'h580f00};
     82:op_reg_data=  {RW_CTRL, 24'h581003};
     83:op_reg_data=  {RW_CTRL, 24'h581109};
     84:op_reg_data=  {RW_CTRL, 24'h581207};
     85:op_reg_data=  {RW_CTRL, 24'h581303};
     86:op_reg_data=  {RW_CTRL, 24'h581400};
     87:op_reg_data=  {RW_CTRL, 24'h581501};
     88:op_reg_data=  {RW_CTRL, 24'h581603};
     89:op_reg_data=  {RW_CTRL, 24'h581708};
     90:op_reg_data=  {RW_CTRL, 24'h58180d};
     91:op_reg_data=  {RW_CTRL, 24'h581908};
     92:op_reg_data=  {RW_CTRL, 24'h581a05};
     93:op_reg_data=  {RW_CTRL, 24'h581b06};
     94:op_reg_data=  {RW_CTRL, 24'h581c08};
     95:op_reg_data=  {RW_CTRL, 24'h581d0e};
     96:op_reg_data=  {RW_CTRL, 24'h581e29};
     97:op_reg_data=  {RW_CTRL, 24'h581f17};
     98:op_reg_data=  {RW_CTRL, 24'h582011};
     99:op_reg_data=  {RW_CTRL, 24'h582111};
     100:op_reg_data= {RW_CTRL, 24'h582215};
     101:op_reg_data= {RW_CTRL, 24'h582328};
     102:op_reg_data= {RW_CTRL, 24'h582446};
     103:op_reg_data= {RW_CTRL, 24'h582526};
     104:op_reg_data= {RW_CTRL, 24'h582608};
     105:op_reg_data= {RW_CTRL, 24'h582726};
     106:op_reg_data= {RW_CTRL, 24'h582864};
     107:op_reg_data= {RW_CTRL, 24'h582926};
     108:op_reg_data= {RW_CTRL, 24'h582a24};
     109:op_reg_data= {RW_CTRL, 24'h582b22};
     110:op_reg_data= {RW_CTRL, 24'h582c24};
     111:op_reg_data= {RW_CTRL, 24'h582d24};
     112:op_reg_data= {RW_CTRL, 24'h582e06};
     113:op_reg_data= {RW_CTRL, 24'h582f22};
     114:op_reg_data= {RW_CTRL, 24'h583040};
     115:op_reg_data= {RW_CTRL, 24'h583142};
     116:op_reg_data= {RW_CTRL, 24'h583224};
     117:op_reg_data= {RW_CTRL, 24'h583326};
     118:op_reg_data= {RW_CTRL, 24'h583424};
     119:op_reg_data= {RW_CTRL, 24'h583522};
     120:op_reg_data= {RW_CTRL, 24'h583622};
     121:op_reg_data= {RW_CTRL, 24'h583726};
     122:op_reg_data= {RW_CTRL, 24'h583844};
     123:op_reg_data= {RW_CTRL, 24'h583924};
     124:op_reg_data= {RW_CTRL, 24'h583a26};
     125:op_reg_data= {RW_CTRL, 24'h583b28};
     126:op_reg_data= {RW_CTRL, 24'h583c42};
     127:op_reg_data= {RW_CTRL, 24'h583dce};// lenc BR offset // AWB   自动白平衡
     128:op_reg_data= {RW_CTRL, 24'h5180ff};// AWB B block
     129:op_reg_data= {RW_CTRL, 24'h5181f2};// AWB control 
     130:op_reg_data= {RW_CTRL, 24'h518200};// [7:4] max local counter, [3:0] max fast counter
     131:op_reg_data= {RW_CTRL, 24'h518314};// AWB advanced 
     132:op_reg_data= {RW_CTRL, 24'h518425};
     133:op_reg_data= {RW_CTRL, 24'h518524};
     134:op_reg_data= {RW_CTRL, 24'h518609};
     135:op_reg_data= {RW_CTRL, 24'h518709};
     136:op_reg_data= {RW_CTRL, 24'h518809};
     137:op_reg_data= {RW_CTRL, 24'h518975};
     138:op_reg_data= {RW_CTRL, 24'h518a54};
     139:op_reg_data= {RW_CTRL, 24'h518be0};
     140:op_reg_data= {RW_CTRL, 24'h518cb2};
     141:op_reg_data= {RW_CTRL, 24'h518d42};
     142:op_reg_data= {RW_CTRL, 24'h518e3d};
     143:op_reg_data= {RW_CTRL, 24'h518f56};
     144:op_reg_data= {RW_CTRL, 24'h519046};
     145:op_reg_data= {RW_CTRL, 24'h5191f8};// AWB top limit
     146:op_reg_data= {RW_CTRL, 24'h519204};// AWB bottom limit
     147:op_reg_data= {RW_CTRL, 24'h519370};// red limit
     148:op_reg_data= {RW_CTRL, 24'h5194f0};// green limit
     149:op_reg_data= {RW_CTRL, 24'h5195f0};// blue limit
     150:op_reg_data= {RW_CTRL, 24'h519603};// AWB control
     151:op_reg_data= {RW_CTRL, 24'h519701};// local limit 
     152:op_reg_data= {RW_CTRL, 24'h519804};
     153:op_reg_data= {RW_CTRL, 24'h519912};
     154:op_reg_data= {RW_CTRL, 24'h519a04};
     155:op_reg_data= {RW_CTRL, 24'h519b00};
     156:op_reg_data= {RW_CTRL, 24'h519c06};
     157:op_reg_data= {RW_CTRL, 24'h519d82};
     158:op_reg_data= {RW_CTRL, 24'h519e38};// AWB control // Gamma    伽玛曲线
     159:op_reg_data= {RW_CTRL, 24'h548001};// Gamma bias plus on, bit[0] 
     160:op_reg_data= {RW_CTRL, 24'h548108};
     161:op_reg_data= {RW_CTRL, 24'h548214};
     162:op_reg_data= {RW_CTRL, 24'h548328};
     163:op_reg_data= {RW_CTRL, 24'h548451};
     164:op_reg_data= {RW_CTRL, 24'h548565};
     165:op_reg_data= {RW_CTRL, 24'h548671};
     166:op_reg_data= {RW_CTRL, 24'h54877d};
     167:op_reg_data= {RW_CTRL, 24'h548887};
     168:op_reg_data= {RW_CTRL, 24'h548991};
     169:op_reg_data= {RW_CTRL, 24'h548a9a};
     170:op_reg_data= {RW_CTRL, 24'h548baa};
     171:op_reg_data= {RW_CTRL, 24'h548cb8};
     172:op_reg_data= {RW_CTRL, 24'h548dcd};
     173:op_reg_data= {RW_CTRL, 24'h548edd};
     174:op_reg_data= {RW_CTRL, 24'h548fea};
     175:op_reg_data= {RW_CTRL, 24'h54901d};// color matrix   色彩矩阵
     176:op_reg_data= {RW_CTRL, 24'h53811e};// CMX1 for Y
     177:op_reg_data= {RW_CTRL, 24'h53825b};// CMX2 for Y
     178:op_reg_data= {RW_CTRL, 24'h538308};// CMX3 for Y
     179:op_reg_data= {RW_CTRL, 24'h53840a};// CMX4 for U
     180:op_reg_data= {RW_CTRL, 24'h53857e};// CMX5 for U
     181:op_reg_data= {RW_CTRL, 24'h538688};// CMX6 for U
     182:op_reg_data= {RW_CTRL, 24'h53877c};// CMX7 for V
     183:op_reg_data= {RW_CTRL, 24'h53886c};// CMX8 for V
     184:op_reg_data= {RW_CTRL, 24'h538910};// CMX9 for V
     185:op_reg_data= {RW_CTRL, 24'h538a01};// sign[9]
     186:op_reg_data= {RW_CTRL, 24'h538b98}; // sign[8:1] // UV adjust   UV色彩饱和度调整
     187:op_reg_data= {RW_CTRL, 24'h558006};// saturation on, bit[1]
     188:op_reg_data= {RW_CTRL, 24'h558340};
     189:op_reg_data= {RW_CTRL, 24'h558410};
     190:op_reg_data= {RW_CTRL, 24'h558910};
     191:op_reg_data= {RW_CTRL, 24'h558a00};
     192:op_reg_data= {RW_CTRL, 24'h558bf8};
     193:op_reg_data= {RW_CTRL, 24'h501d40};// enable manual offset of contrast// CIP  锐化和降噪 
     194:op_reg_data= {RW_CTRL, 24'h530008};// CIP sharpen MT threshold 1
     195:op_reg_data= {RW_CTRL, 24'h530130};// CIP sharpen MT threshold 2
     196:op_reg_data= {RW_CTRL, 24'h530210};// CIP sharpen MT offset 1
     197:op_reg_data= {RW_CTRL, 24'h530300};// CIP sharpen MT offset 2
     198:op_reg_data= {RW_CTRL, 24'h530408};// CIP DNS threshold 1
     199:op_reg_data= {RW_CTRL, 24'h530530};// CIP DNS threshold 2
     200:op_reg_data= {RW_CTRL, 24'h530608};// CIP DNS offset 1
     201:op_reg_data= {RW_CTRL, 24'h530716};// CIP DNS offset 2 
     202:op_reg_data= {RW_CTRL, 24'h530908};// CIP sharpen TH threshold 1
     203:op_reg_data= {RW_CTRL, 24'h530a30};// CIP sharpen TH threshold 2
     204:op_reg_data= {RW_CTRL, 24'h530b04};// CIP sharpen TH offset 1
     205:op_reg_data= {RW_CTRL, 24'h530c06};// CIP sharpen TH offset 2
     206:op_reg_data= {RW_CTRL, 24'h502500};
     207:op_reg_data= {RW_CTRL, 24'h300802}; // wake up from standby, bit[6]
     
     //set OV5640 to video mode 720p 
     208:op_reg_data=  {RW_CTRL, 24'h303521};// PLL     input clock =24Mhz, PCLK =84Mhz
     209:op_reg_data=  {RW_CTRL, 24'h303669};// PLL
     210:op_reg_data=  {RW_CTRL, 24'h3c0707}; // lightmeter 1 threshold[7:0]
     211:op_reg_data=  {RW_CTRL, 24'h382047}; // flip
     212:op_reg_data=  {RW_CTRL, 24'h382100}; // mirror
     213:op_reg_data=  {RW_CTRL, 24'h381431}; // timing X inc
     214:op_reg_data=  {RW_CTRL, 24'h381531}; // timing Y inc
     215:op_reg_data=  {RW_CTRL, 24'h380000}; // HS
     216:op_reg_data=  {RW_CTRL, 24'h380100}; // HS
     217:op_reg_data=  {RW_CTRL, 24'h380200}; // VS
     218:op_reg_data=  {RW_CTRL, 24'h3803fa}; // VS
     219:op_reg_data=  {RW_CTRL, 24'h38040a}; // HW (HE)
     220:op_reg_data=  {RW_CTRL, 24'h38053f}; // HW (HE)
     221:op_reg_data=  {RW_CTRL, 24'h380606}; // VH (VE)
     222:op_reg_data=  {RW_CTRL, 24'h3807a9}; // VH (VE)
     223:op_reg_data=  {RW_CTRL, 24'h380805}; // DVPHO     (1280)
     224:op_reg_data=  {RW_CTRL, 24'h380900}; // DVPHO     (1280)
     225:op_reg_data=  {RW_CTRL, 24'h380a02}; // DVPVO     (720)->
     226:op_reg_data=  {RW_CTRL, 24'h380bd0}; // DVPVO     (720)->
     227:op_reg_data=  {RW_CTRL, 24'h380c07}; // HTS
     228:op_reg_data=  {RW_CTRL, 24'h380d64}; // HTS
     229:op_reg_data=  {RW_CTRL, 24'h380e02}; // VTS
     230:op_reg_data=  {RW_CTRL, 24'h380fe4}; // VTS
     231:op_reg_data=  {RW_CTRL, 24'h381304}; // timing V offset
     232:op_reg_data=  {RW_CTRL, 24'h361800};
     233:op_reg_data=  {RW_CTRL, 24'h361229};
     234:op_reg_data=  {RW_CTRL, 24'h370952};
     235:op_reg_data=  {RW_CTRL, 24'h370c03};
     236:op_reg_data=  {RW_CTRL, 24'h3a0202}; // 60Hz max exposure
     237:op_reg_data=  {RW_CTRL, 24'h3a03e0}; // 60Hz max exposure
     238:op_reg_data=  {RW_CTRL, 24'h3a0800}; // B50 step
     239:op_reg_data=  {RW_CTRL, 24'h3a096f}; // B50 step
     240:op_reg_data=  {RW_CTRL, 24'h3a0a00}; // B60 step
     241:op_reg_data=  {RW_CTRL, 24'h3a0b5c}; // B60 step
     242:op_reg_data=  {RW_CTRL, 24'h3a0e06}; // 50Hz max band
     243:op_reg_data=  {RW_CTRL, 24'h3a0d08}; // 60Hz max band
     244:op_reg_data=  {RW_CTRL, 24'h3a1402}; // 50Hz max exposure
     245:op_reg_data=  {RW_CTRL, 24'h3a15e0}; // 50Hz max exposure
     246:op_reg_data=  {RW_CTRL, 24'h400402}; // BLC line number
     247:op_reg_data=  {RW_CTRL, 24'h30021c}; // reset JFIFO, SFIFO, JPG
     248:op_reg_data=  {RW_CTRL, 24'h3006c3}; // disable clock of JPEG2x, JPEG
     249:op_reg_data=  {RW_CTRL, 24'h471303}; // JPEG mode 3
     250:op_reg_data=  {RW_CTRL, 24'h440704}; // Quantization sacle
     251:op_reg_data=  {RW_CTRL, 24'h460b37};
     252:op_reg_data=  {RW_CTRL, 24'h460c20};
     253:op_reg_data=  {RW_CTRL, 24'h483716}; // MIPI global timing
     254:op_reg_data=  {RW_CTRL, 24'h382404}; // PCLK manual divider
     255:op_reg_data=  {RW_CTRL, 24'h5001a3}; // SDE on, CMX on, AWB on, scale on
     256:op_reg_data=  {RW_CTRL, 24'h350300}; // AEC/AGC on 
     257:op_reg_data=  {RW_CTRL, 24'h301602}; //Strobe output enable
     258:op_reg_data=  {RW_CTRL, 24'h3b070a}; //FREX strobe mode1    
     //strobe flash and frame exposure      
     259:op_reg_data={RW_CTRL, 24'h3b0083};              //STROBE CTRL: strobe request ON, Strobe mode: LED3 
     260:op_reg_data={RW_CTRL, 24'h3b0000};              //STROBE CTRL: strobe request OFF 
     
     default:op_reg_data={RW_CTRL, 24'h000000};
    endcase      
end     


endmodule

reg_config

上电时序模块:

setup`timescale 1ns / 1ps


module setup(
    input clk,//50MHZ
    input rst_n,
    output reg init_en,

    output reg ov_pwdn1,
    output reg ov_rst_n1,
    output reg ov_pwdn2,
    output reg ov_rst_n2
    );

parameter MS_CYC = 50_000;

reg [16-1:0] ms_cnt;
wire add_ms_cnt;
wire end_ms_cnt;
reg [ (5-1):0]  wait_cnt     ;
wire        add_wait_cnt ;
wire        end_wait_cnt ;
reg [5-1:0] N;
reg [ (2-1):0]  phase_cnt     ;
wire        add_phase_cnt ;
wire        end_phase_cnt ;
reg setup_done;


    //ms计数
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        ms_cnt <= 0;
    end
    else if(add_ms_cnt)begin
        if(end_ms_cnt)
            ms_cnt <= 0;
        else
            ms_cnt <= ms_cnt + 1;
    end
end

assign add_ms_cnt = !setup_done;       
assign end_ms_cnt = add_ms_cnt && ms_cnt== MS_CYC-1; //1_000_000/20 = 50_000  

always @(posedge clk or negedge rst_n) begin 
    if (rst_n==0) begin
        wait_cnt <= 0; 
    end
    else if(add_wait_cnt) begin
        if(end_wait_cnt)
            wait_cnt <= 0; 
        else
            wait_cnt <= wait_cnt+1 ;
   end
end
assign add_wait_cnt = (end_ms_cnt);
assign end_wait_cnt = add_wait_cnt  && wait_cnt == (N)-1 ;

always @(posedge clk or negedge rst_n) begin 
    if (rst_n==0) begin
        phase_cnt <= 0; 
    end
    else if(add_phase_cnt) begin
        if(end_phase_cnt)
            phase_cnt <= 0; 
        else
            phase_cnt <= phase_cnt+1 ;
   end
end
assign add_phase_cnt = (end_wait_cnt);
assign end_phase_cnt = add_phase_cnt  && phase_cnt == (3)-1 ;

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        setup_done <= 0;
    end
    else if(end_phase_cnt)begin
        setup_done <= 1;
    end
end


always@(*)begin
    case(phase_cnt)
        0:N = 5;
        1:N = 2;
        2:N = 21;
        default:;
    endcase
end

//信号逻辑
always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        ov_pwdn1 <= 1;
        ov_pwdn2 <= 1;
    end
    else if(add_phase_cnt && phase_cnt == 0)begin
        ov_pwdn1 <= 0;
        ov_pwdn2 <= 0;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        ov_rst_n1 <= 0;
        ov_rst_n2 <= 0;
    end
    else if(add_phase_cnt && phase_cnt == 1)begin
        ov_rst_n1 <= 1;
        ov_rst_n2 <= 1;
    end
end

always  @(posedge clk or negedge rst_n)begin
    if(rst_n==1'b0)begin
        init_en <= 0;
    end
    else if(end_phase_cnt)begin
        init_en <= 1;
    end
end


endmodule

setup

以下是生成原理图结构及仿真波形:

下面完成最重要的板级调试,先测试SCCB读,再测试SCCB写。

读状态下SIOD信号有响应,并非所有寄存器数据均为0,说明SCCB_interface模块将摄像头内部寄存器初始值读出。接下来把寄存器配置模块WR_CTRL设置为2'b11,检测读取数据是否与写入一致,从而进一步验证SCCB读写时序。

写入数据状态下,第9bit SIO_D信号被从机拉低,从机收到完整字节数据。这再次说明了SCCB的本质就是IIC。读取数据与写入数据基本一致,说明SCCB读写时序无误。现在在是PCLK时钟域下抓取VSYNC HREF DATA波形,ILA可以稳定启动并捕获到信号与DVP接口时序一致,此时摄像头正常工作。

配置完成后开始采集RGB数据。根据RGB565时序,每两个字节数据组成一个像素数据,因此采集输出过程中只需将PCLK时钟上升沿得到的两个数据拼接为16bit像素数据即可。



  •   
  •   
  •   
  •  
  • FPGA教育领域第一品牌
  • 咨询热线:020-39002701
  • 技术交流Q群:544453837