CMOS攝像頭(2):SCCB配置寄存器


  上電之后要做的是通過 SCCB 協議對攝像頭的寄存器進行配置,在之前的博客中詳細介紹過 SCCB 協議的基本原理以及和 IIC 協議的區別。經過半年后,在正點原子的 SCCB 配置寄存器的基礎上,我進行了小小的改動,使得代碼更加簡潔易懂。

一、SCCB協議編寫

  SCCB協議的介紹見之前的博客《協議——SCCB與IIC的區別》,此處不再贅述。之前的 SCCB 協議需要在端口進行 8 位地址和16位地址的選擇,這次我把選擇開關刪了,改為內部代碼判斷,端口更加簡潔了。

1、端口例化

//SCCB寄存器配置
//---------------------------------------------------
sccb_ov5640_cfg u_sccb_ov5640_cfg
(
    .clk                    (sccb_dri_clk           ),
    .rst_n                  (rst_n                  ),
    .sccb_vld                (sccb_vld                ), //SCCB配置有效信號
    .sccb_done              (sccb_done              ), //SCCB配置一次結束
    .sccb_en                (sccb_en                ),
    .sccb_data              (sccb_data              ),
    .sccb_cfg_done          (sccb_cfg_done          )  //SCCB配置全部結束
);

//SCCB時序驅動
//---------------------------------------------------
sccb 
#(
    .DEVICE_ID              (8'h78                  ), //器件ID
    .CLK                    (26'd24_000_000         ), //24Mhz   
    .SCL                    (18'd250_000            )  //250Khz
)
u_sccb
(       
    .clk                    (cmos_xclk              ),   
    .rst_n                  (rst_n                  ),
    //SCCB input ------------------------------------
    .sccb_en                (sccb_en                ),
    .sccb_addr              (sccb_data[23:8]        ),  
    .sccb_data              (sccb_data[7:0]         ),
    //SCCB output -----------------------------------
    .sccb_done              (sccb_done              ),   
    .sccb_scl               (cmos_scl               ),
    .sccb_sda               (cmos_sda               ),
    //dri_clk ---------------------------------------
    .sccb_dri_clk           (sccb_dri_clk           )  //1Mhz
);

  OV7670 和 OV7725 的寫 ID 都是 8'h42,而 OV5640 的寫 ID 為 8'h78,這些都可以在各自的 datasheet 中查到,沒什么說的。

  SCCB協議的 SCL 要求不高於400Khz,我們設置為 250Khz,通過例化模塊的參數傳遞可以直接更改這些信息,對於 sccb 代碼內部不必再更改,方便移植。

  sccb_start信號為開始配置標志,在上一講博客中設計得到。sccb_en、sccb_data、sccb_done、sccb_dri_clk 都是和 sccb_cfg 模塊進行交互的信號,其中 sccb_dri_clk 是 sccb_cfg 模塊的時鍾驅動信號。而 cmos_scl 和 cmos_sda 則是要輸出到端口的信號。sccb_cfg_done 信號表示所有寄存器都配置完成,這個信號可以給之后的圖像獲取模塊 cmos_capture 使用。

 

2、sccb 代碼

//**************************************************************************
// *** 名稱 : sccb.v
// *** 作者 : xianyu_FPGA
// *** 博客 : https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2019-08-10
// *** 描述 : SCCB控制器,只支持寫
//**************************************************************************

module sccb
//========================< 參數 >==========================================
#(
parameter DEVICE_ID         = 8'b01010000           , //器件ID
parameter CLK               = 26'd50_000_000        , //本模塊的時鍾頻率
parameter SCL               = 18'd250_000             //輸出的SCL時鍾頻率
)
//========================< 端口 >==========================================
(
input   wire                clk                     , //時鍾
input   wire                rst_n                   , //復位,低電平有效
//SCCB input ---------------------------------------- 
input   wire                sccb_en                 , //SCCB觸發信號
input   wire    [15:0]      sccb_addr               , //SCCB器件內地址
input   wire    [ 7:0]      sccb_data               , //SCCB要寫的數據
//SCCB output --------------------------------------- 
output  reg                 sccb_done               , //SCCB一次操作完成
output  reg                 sccb_scl                , //SCCB的SCL時鍾信號
inout   wire                sccb_sda                , //SCCB的SDA數據信號
//dri_clk ------------------------------------------- 
output  reg                 sccb_dri_clk              //驅動SCCB操作的驅動時鍾,1Mhz
);
//========================< 狀態機參數 >====================================
localparam  IDLE            = 6'b00_0001            ; //空閑狀態
localparam  DEVICE          = 6'b00_0010            ; //寫器件地址
localparam  ADDR_16         = 6'b00_0100            ; //寫字地址高8位
localparam  ADDR_8          = 6'b00_1000            ; //寫字地址低8位
localparam  DATA            = 6'b01_0000            ; //寫數據
localparam  STOP            = 6'b10_0000            ; //結束
//========================< 信號 >==========================================
reg                         sda_dir                 ; //SCCB數據(SDA)方向控制
reg                         sda_out                 ; //SDA輸出信號
reg                         state_done              ; //狀態結束
reg    [ 6:0]               cnt                     ; //計數
reg    [ 7:0]               state_c                 ; //狀態機當前狀態
reg    [ 7:0]               state_n                 ; //狀態機下一狀態
reg    [15:0]               sccb_addr_t             ; //地址寄存
reg    [ 7:0]               sccb_data_t             ; //數據寄存
reg    [ 9:0]               clk_cnt                 ; //分頻時鍾計數
wire   [ 8:0]               clk_divide              ; //模塊驅動時鍾的分頻系數
//==========================================================================
//==    SDA數據輸出或高阻
//==========================================================================
assign  sccb_sda = sda_dir ?  sda_out : 1'bz;         
//==========================================================================
//==     生成SCL的4倍時鍾來驅動后面SCCB的操作,生成1Mhz的sccb_dri_clk
//==========================================================================
assign  clk_divide = (CLK/SCL) >> 3; //>>3即除以8

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        sccb_dri_clk <=  1'b1;
        clk_cnt <= 10'd0;
    end
    else if(clk_cnt == clk_divide - 1'd1) begin
        clk_cnt <= 10'd0;
        sccb_dri_clk <= ~sccb_dri_clk;
    end
    else
        clk_cnt <= clk_cnt + 1'b1;
end
//==========================================================================
//==    狀態機
//==========================================================================
always @(posedge sccb_dri_clk or negedge rst_n) begin
    if(!rst_n)
        state_c <= IDLE;
    else
        state_c <= state_n;
end

always @(*) begin
    case(state_c)
        IDLE: begin                             //空閑狀態
           if(sccb_en)
               state_n = DEVICE;
           else
               state_n = IDLE;
        end
        DEVICE: begin                           //寫器件ID
            if(state_done) begin
                if(sccb_addr[15:8]!=0)
                   state_n = ADDR_16;
                else if(sccb_addr[15:8]==0)
                   state_n = ADDR_8 ;
            end
            else
                state_n = DEVICE;
        end
        ADDR_16: begin                          //寫地址高8位
            if(state_done)
                state_n = ADDR_8;
            else
                state_n = ADDR_16;
        end
        ADDR_8: begin                           //寫地址低8位
            if(state_done)
                state_n = DATA;
            else
                state_n = ADDR_8;
        end
        DATA: begin                             //寫數據
            if(state_done)
                state_n = STOP;
            else
                state_n = DATA;
        end
        STOP: begin                             //結束
            if(state_done)
                state_n = IDLE;
            else
                state_n = STOP ;
        end
        default:state_n= IDLE;
    endcase
end
//==========================================================================
//==    設計各路信號
//==========================================================================
always @(posedge sccb_dri_clk or negedge rst_n) begin
    if(!rst_n) begin
        sccb_scl    <= 1'b1;
        sda_out     <= 1'b1;
        sda_dir     <= 1'b1;
        sccb_done   <= 1'b0;
        cnt         <= 1'b0;
        state_done  <= 1'b0;
        sccb_addr_t <= 1'b0;
        sccb_data_t <= 1'b0;
    end
    else begin
        state_done  <= 1'b0;
        cnt         <= cnt + 1'b1;
        case(state_c)
            //--------------------------------------------------- 空閑狀態
            IDLE: begin
                    sccb_scl  <= 1'b1;
                    sda_out   <= 1'b1;
                    sda_dir   <= 1'b1;
                    sccb_done <= 1'b0;
                    cnt       <= 7'b0;
                    if(sccb_en) begin
                        sccb_addr_t <= sccb_addr;
                        sccb_data_t <= sccb_data;
                    end
            end
            //--------------------------------------------------- 寫器件ID
            DEVICE: begin
                case(cnt)
                    7'd1 : sda_out  <= 1'b0;
                    7'd3 : sccb_scl <= 1'b0;
                    7'd4 : sda_out  <= DEVICE_ID[7];
                    7'd5 : sccb_scl <= 1'b1;
                    7'd7 : sccb_scl <= 1'b0;
                    7'd8 : sda_out  <= DEVICE_ID[6];
                    7'd9 : sccb_scl <= 1'b1;
                    7'd11: sccb_scl <= 1'b0;
                    7'd12: sda_out  <= DEVICE_ID[5];
                    7'd13: sccb_scl <= 1'b1;
                    7'd15: sccb_scl <= 1'b0;
                    7'd16: sda_out  <= DEVICE_ID[4];
                    7'd17: sccb_scl <= 1'b1;
                    7'd19: sccb_scl <= 1'b0;
                    7'd20: sda_out  <= DEVICE_ID[3];
                    7'd21: sccb_scl <= 1'b1;
                    7'd23: sccb_scl <= 1'b0;
                    7'd24: sda_out  <= DEVICE_ID[2];
                    7'd25: sccb_scl <= 1'b1;
                    7'd27: sccb_scl <= 1'b0;
                    7'd28: sda_out  <= DEVICE_ID[1];
                    7'd29: sccb_scl <= 1'b1;
                    7'd31: sccb_scl <= 1'b0;
                    7'd32: sda_out  <= DEVICE_ID[0];
                    7'd33: sccb_scl <= 1'b1;
                    7'd35: sccb_scl <= 1'b0;
                    7'd36: begin
                           sda_dir  <= 1'b0;    //從機應答
                           sda_out  <= 1'b1;
                    end
                    7'd37: sccb_scl <= 1'b1;
                    7'd38: state_done <= 1'b1;  //狀態結束
                    7'd39: begin
                           sccb_scl <= 1'b0;
                           cnt <= 1'b0;
                    end
                    default:;
                endcase
            end
            //--------------------------------------------------- 寫字地址高8位
            ADDR_16: begin
                case(cnt)
                    7'd0 : begin
                           sda_dir  <= 1'b1 ;
                           sda_out  <= sccb_addr_t[15];
                    end
                    7'd1 : sccb_scl <= 1'b1;
                    7'd3 : sccb_scl <= 1'b0;
                    7'd4 : sda_out  <= sccb_addr_t[14];
                    7'd5 : sccb_scl <= 1'b1;
                    7'd7 : sccb_scl <= 1'b0;
                    7'd8 : sda_out  <= sccb_addr_t[13];
                    7'd9 : sccb_scl <= 1'b1;
                    7'd11: sccb_scl <= 1'b0;
                    7'd12: sda_out  <= sccb_addr_t[12];
                    7'd13: sccb_scl <= 1'b1;
                    7'd15: sccb_scl <= 1'b0;
                    7'd16: sda_out  <= sccb_addr_t[11];
                    7'd17: sccb_scl <= 1'b1;
                    7'd19: sccb_scl <= 1'b0;
                    7'd20: sda_out  <= sccb_addr_t[10];
                    7'd21: sccb_scl <= 1'b1;
                    7'd23: sccb_scl <= 1'b0;
                    7'd24: sda_out  <= sccb_addr_t[9];
                    7'd25: sccb_scl <= 1'b1;
                    7'd27: sccb_scl <= 1'b0;
                    7'd28: sda_out  <= sccb_addr_t[8];
                    7'd29: sccb_scl <= 1'b1;
                    7'd31: sccb_scl <= 1'b0;
                    7'd32: begin
                           sda_dir  <= 1'b0;    //從機應答
                           sda_out  <= 1'b1;
                    end
                    7'd33: sccb_scl <= 1'b1;
                    7'd34: state_done <= 1'b1;  //狀態結束
                    7'd35: begin
                           sccb_scl <= 1'b0;
                           cnt <= 1'b0;
                    end
                    default:;
                endcase
            end
            //--------------------------------------------------- 寫字地址低8位
            ADDR_8: begin
                case(cnt)
                    7'd0: begin
                           sda_dir  <= 1'b1 ;
                           sda_out  <= sccb_addr_t[7];
                    end
                    7'd1 : sccb_scl <= 1'b1;
                    7'd3 : sccb_scl <= 1'b0;
                    7'd4 : sda_out  <= sccb_addr_t[6];
                    7'd5 : sccb_scl <= 1'b1;
                    7'd7 : sccb_scl <= 1'b0;
                    7'd8 : sda_out  <= sccb_addr_t[5];
                    7'd9 : sccb_scl <= 1'b1;
                    7'd11: sccb_scl <= 1'b0;
                    7'd12: sda_out  <= sccb_addr_t[4];
                    7'd13: sccb_scl <= 1'b1;
                    7'd15: sccb_scl <= 1'b0;
                    7'd16: sda_out  <= sccb_addr_t[3];
                    7'd17: sccb_scl <= 1'b1;
                    7'd19: sccb_scl <= 1'b0;
                    7'd20: sda_out  <= sccb_addr_t[2];
                    7'd21: sccb_scl <= 1'b1;
                    7'd23: sccb_scl <= 1'b0;
                    7'd24: sda_out  <= sccb_addr_t[1];
                    7'd25: sccb_scl <= 1'b1;
                    7'd27: sccb_scl <= 1'b0;
                    7'd28: sda_out  <= sccb_addr_t[0];
                    7'd29: sccb_scl <= 1'b1;
                    7'd31: sccb_scl <= 1'b0;
                    7'd32: begin
                           sda_dir  <= 1'b0;    //從機應答
                           sda_out  <= 1'b1;
                    end
                    7'd33: sccb_scl <= 1'b1;
                    7'd34: state_done <= 1'b1;  //狀態結束
                    7'd35: begin
                           sccb_scl <= 1'b0;
                           cnt <= 1'b0;
                    end
                    default:;
                endcase
            end
            //--------------------------------------------------- 寫數據
            DATA: begin
                case(cnt)
                    7'd0: begin
                           sda_out <= sccb_data_t[7];
                           sda_dir <= 1'b1;
                    end
                    7'd1 : sccb_scl <= 1'b1;
                    7'd3 : sccb_scl <= 1'b0;
                    7'd4 : sda_out  <= sccb_data_t[6];
                    7'd5 : sccb_scl <= 1'b1;
                    7'd7 : sccb_scl <= 1'b0;
                    7'd8 : sda_out  <= sccb_data_t[5];
                    7'd9 : sccb_scl <= 1'b1;
                    7'd11: sccb_scl <= 1'b0;
                    7'd12: sda_out  <= sccb_data_t[4];
                    7'd13: sccb_scl <= 1'b1;
                    7'd15: sccb_scl <= 1'b0;
                    7'd16: sda_out  <= sccb_data_t[3];
                    7'd17: sccb_scl <= 1'b1;
                    7'd19: sccb_scl <= 1'b0;
                    7'd20: sda_out  <= sccb_data_t[2];
                    7'd21: sccb_scl <= 1'b1;
                    7'd23: sccb_scl <= 1'b0;
                    7'd24: sda_out  <= sccb_data_t[1];
                    7'd25: sccb_scl <= 1'b1;
                    7'd27: sccb_scl <= 1'b0;
                    7'd28: sda_out  <= sccb_data_t[0];
                    7'd29: sccb_scl <= 1'b1;
                    7'd31: sccb_scl <= 1'b0;
                    7'd32: begin
                           sda_dir  <= 1'b0;    //從機應答
                           sda_out  <= 1'b1;
                    end
                    7'd33: sccb_scl <= 1'b1;
                    7'd34: state_done <= 1'b1;  //狀態結束
                    7'd35: begin
                           sccb_scl <= 1'b0;
                           cnt  <= 1'b0;
                    end
                    default:;
                endcase
            end
            //--------------------------------------------------- 結束
            STOP: begin
                case(cnt)
                    7'd0: begin
                           sda_dir  <= 1'b1;
                           sda_out  <= 1'b0;
                    end
                    7'd1 : sccb_scl <= 1'b1;
                    7'd3 : sda_out  <= 1'b1;
                    7'd15: state_done <= 1'b1;  //狀態結束
                    7'd16: begin
                           cnt <= 1'b0;
                           sccb_done <= 1'b1;   //sccb配置完成
                    end
                    default:;
                endcase
            end
        endcase
    end
end


endmodule

 

3、SCCB協議配置寄存器代碼

module sccb_ov5640_cfg
//========================< 參數 >==========================================
#(
parameter REG_NUM           = 240                   , //寄存器個數
parameter CMOS_H_PIXEL      = 24'd1024              , //CMOS水平方向像素個數
parameter CMOS_V_PIXEL      = 24'd768               , //CMOS垂直方向像素個數
parameter TOTAL_H_PIXEL     = CMOS_H_PIXEL+13'd1216 , //水平總像素大小
parameter TOTAL_V_PIXEL     = CMOS_V_PIXEL+13'd504    //垂直總像素大小
)
//========================< 端口 >==========================================
(
input   wire                clk                     , //時鍾,1Mhz
input   wire                rst_n                   , //復位,低電平有效
input   wire                sccb_vld                  , //SCCB配置有效信號
input   wire                sccb_done               , //SCCB寄存器配置完成信號
output  reg                 sccb_en                 , //SCCB觸發執行信號   
output  reg     [23:0]      sccb_data               , //SCCB要配置的地址與數據(高8位地址,低8位數據)
output  reg                 sccb_cfg_done             //SCCB全部寄存器配置完成信號
);
//========================< 信號 >==========================================
reg                         sccb_vld_r                ;
wire                        sccb_start                ;
reg    [7:0]                reg_cnt                 ; //寄存器配置個數計數器
//==========================================================================
//==    sccb_vld上升沿檢測
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        sccb_vld_r <= 1'b0;
    else
        sccb_vld_r <= sccb_vld;
end

assign sccb_start = sccb_vld && ~sccb_vld_r;
//==========================================================================
//==    sccb觸發執行信號 
//==========================================================================  
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        sccb_en <= 1'b0;
    else if(sccb_start)                     //開始配置寄存器
        sccb_en <= 1'b1;
    else if(sccb_done && reg_cnt < REG_NUM) //上一個配置完后立馬配置下一個
        sccb_en <= 1'b1;
    else
        sccb_en <= 1'b0;    
end 
//==========================================================================
//==    寄存器配置個數計數
//==========================================================================    
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        reg_cnt <= 8'd0;
    else if(sccb_en)   
        reg_cnt <= reg_cnt + 8'b1;
end
//==========================================================================
//==    所有寄存器全部配置完成信號
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        sccb_cfg_done <= 1'b0;
    else if((reg_cnt == REG_NUM) && sccb_done)
        sccb_cfg_done <= 1'b1;  
end        
//==========================================================================
//==    配置寄存器地址與數據,Xclk=24Mhz
//==========================================================================
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        sccb_data <= 24'b0;
    else begin
    case(reg_cnt)
    ......
            default:
        endcase
    end
end



endmodule

 上述代碼是改自正點原子的FPGA教程,我將軟件復位后的延時刪了,因為在前面我們已經進行了硬件復位,所以完全沒必要再進行寄存器軟件復位,而且就算進行軟件復位,不需要再延時也是OK的。

  ov7725、ov7670和ov5640在這部分也是幾乎一樣的,只不過是寄存器和寄存器的個數那需要改一下,相關注意事項請看下面的詳細介紹。

 

二、OV7670寄存器配置

  1 //**************************************************************************
  2 // *** 名稱 : sccb_ov7670_cfg.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2019-08-10
  6 // *** 描述 : SCCB配置OV7670寄存器
  7 //**************************************************************************
  8 
  9 module sccb_ov7670_cfg
 10 //========================< 參數 >==========================================
 11 #(
 12 parameter REG_NUM           = 123                     //寄存器個數
 13 )
 14 //========================< 端口 >==========================================
 15 (
 16 input   wire                clk                     , //時鍾,1Mhz
 17 input   wire                rst_n                   , //復位,低電平有效
 18 input   wire                sccb_vld                  , //SCCB配置有效信號
 19 input   wire                sccb_done               , //SCCB寄存器配置完成信號
 20 output  reg                 sccb_en                 , //SCCB觸發執行信號   
 21 output  reg     [15:0]      sccb_data               , //SCCB要配置的地址與數據(高8位地址,低8位數據)
 22 output  reg                 sccb_cfg_done             //SCCB全部寄存器配置完成信號
 23 );
 24 //========================< 信號 >==========================================
 25 reg                         sccb_vld_r                ;
 26 wire                        sccb_start                ;
 27 reg    [6:0]                reg_cnt                 ; //寄存器配置個數計數器
 28 //==========================================================================
 29 //==    sccb_vld上升沿檢測
 30 //==========================================================================
 31 always @(posedge clk or negedge rst_n) begin
 32     if(!rst_n)
 33         sccb_vld_r <= 1'b0;
 34     else
 35         sccb_vld_r <= sccb_vld;
 36 end
 37 
 38 assign sccb_start = sccb_vld && ~sccb_vld_r;
 39 //==========================================================================
 40 //==    sccb觸發執行信號 
 41 //==========================================================================  
 42 always @(posedge clk or negedge rst_n) begin
 43     if(!rst_n)
 44         sccb_en <= 1'b0;
 45     else if(sccb_start)                     //開始配置寄存器
 46         sccb_en <= 1'b1;
 47     else if(sccb_done && reg_cnt < REG_NUM) //上一個配置完后立馬配置下一個
 48         sccb_en <= 1'b1;
 49     else
 50         sccb_en <= 1'b0;    
 51 end 
 52 //==========================================================================
 53 //==    寄存器配置個數計數
 54 //==========================================================================    
 55 always @(posedge clk or negedge rst_n) begin
 56     if(!rst_n)
 57         reg_cnt <= 7'd0;
 58     else if(sccb_en)   
 59         reg_cnt <= reg_cnt + 7'b1;
 60 end
 61 //==========================================================================
 62 //==    所有寄存器全部配置完成信號
 63 //==========================================================================
 64 always @(posedge clk or negedge rst_n) begin
 65     if(!rst_n)
 66         sccb_cfg_done <= 1'b0;
 67     else if((reg_cnt == REG_NUM) && sccb_done)  
 68         sccb_cfg_done <= 1'b1;  
 69 end        
 70 //==========================================================================
 71 //==    配置寄存器地址與數據
 72 //==========================================================================
 73 always @(posedge clk or negedge rst_n) begin
 74     if(!rst_n)
 75         sccb_data <= 16'b0;
 76     else begin
 77         case(reg_cnt)                        //30fps,XCLK=24Mhz,PCLK=24Mhz,ID=0x42
 78 //像素格式 ----------------------------------------------------------------------------------
 79             7'd0   : sccb_data <= 16'h12_04; //COM7     寄存器復位:否,圖像選擇:00_YUV,04_RGB
 80             7'd1   : sccb_data <= 16'h40_d0; //COM15    RGB565,范圍00-FF(YUV為80,,范圍為01-FE)
 81                                              //                            YUYV YVYU UYVY VYUY
 82             7'd2   : sccb_data <= 16'h3a_04; //TSLB     與COM13配合,bit[3]:0    0    1    1
 83             7'd3   : sccb_data <= 16'h3d_88; //COM13    與TSLB 配合,bit[3]: 0    1    0    1
 84 //幀率控制(Xclk=24Mhz) ----------------------------------------------------------------------
 85                                              //fps      30     15    25    14.3
 86                                              //pclk     24     12    24     12                                        
 87             7'd4   : sccb_data <= 16'h11_80; //CLKRC    80     00    80     00
 88             7'd5   : sccb_data <= 16'h6b_0a; //DBLV     0a     0a    0a     0a
 89             7'd6   : sccb_data <= 16'h2a_00; //EXHCH    00     00    00     00
 90             7'd7   : sccb_data <= 16'h2b_00; //EXHCL    00     00    00     00
 91             7'd8   : sccb_data <= 16'h92_00; //DM_LNL   00     00    66     1a
 92             7'd9   : sccb_data <= 16'h93_00; //DM_LNH   00     00    00     00
 93             7'd10  : sccb_data <= 16'h3b_0a; //COM11    0a     0a    0a     0a
 94 //鏡像控制 ----------------------------------------------------------------------------------
 95             7'd17  : sccb_data <= 16'h1e_01; //鏡像翻轉 正常模式01  垂直翻轉11
 96                                              //         水平翻轉21  水平垂直翻轉31
 97 //測試圖案 ----------------------------------------------------------------------------------
 98                                              //         正常  彩條  細紋  漸變彩條
 99             7'd18  : sccb_data <= 16'h70_00; //測試圖案  00    00    80     80
100             7'd19  : sccb_data <= 16'h71_01; //測試圖案  01    81    01     81
101 //行場時序(默認值) --------------------------------------------------------------------------
102             7'd20  : sccb_data <= 16'h17_11; //HSTART   行頻開始高8位
103             7'd21  : sccb_data <= 16'h18_61; //HSTOP    行頻結束高8位
104             7'd22  : sccb_data <= 16'h32_80; //HREF     bit[5:3]行頻開始低3位,bit[2:0]行頻結束低3位
105             7'd23  : sccb_data <= 16'h19_03; //VSTART   場頻開始高8位
106             7'd24  : sccb_data <= 16'h1a_7b; //VSTOP    場頻結束高8位
107             7'd25  : sccb_data <= 16'h03_00; //VREF     bit[3:2]場頻開始低2位,bit[1:0]場頻結束低2位
108 //其他 --------------------------------------------------------------------------------------
109             7'd26  : sccb_data <= 16'h3e_00; //COM14    PCLK分頻
110             7'd27  : sccb_data <= 16'h73_00; //SCALING  DSP縮放時鍾分頻,與COM14需一致,選擇不分頻
111             7'd28  : sccb_data <= 16'h0c_00;
112             7'd29  : sccb_data <= 16'h7a_20;
113             7'd30  : sccb_data <= 16'h7b_1c;
114             7'd31  : sccb_data <= 16'h7c_28;
115             7'd32  : sccb_data <= 16'h7d_3c;
116             7'd33  : sccb_data <= 16'h7e_55;
117             7'd34  : sccb_data <= 16'h7f_68;
118             7'd35  : sccb_data <= 16'h80_76;
119             7'd36  : sccb_data <= 16'h81_80;
120             7'd37  : sccb_data <= 16'h82_88;
121             7'd38  : sccb_data <= 16'h83_8f;
122             7'd39  : sccb_data <= 16'h84_96;
123             7'd40  : sccb_data <= 16'h85_a3;
124             7'd41  : sccb_data <= 16'h86_af;
125             7'd42  : sccb_data <= 16'h87_c4;
126             7'd43  : sccb_data <= 16'h88_d7;
127             7'd44  : sccb_data <= 16'h89_e8;
128             7'd45  : sccb_data <= 16'h00_00;
129             7'd46  : sccb_data <= 16'h10_00;
130             7'd47  : sccb_data <= 16'h0d_00;
131             7'd48  : sccb_data <= 16'h14_28;
132             7'd49  : sccb_data <= 16'h24_75;
133             7'd50  : sccb_data <= 16'h25_63;
134             7'd51  : sccb_data <= 16'h26_A5;
135             7'd52  : sccb_data <= 16'h9f_78;
136             7'd53  : sccb_data <= 16'ha0_68;
137             7'd54  : sccb_data <= 16'ha1_03;
138             7'd55  : sccb_data <= 16'ha6_df;
139             7'd56  : sccb_data <= 16'ha7_df;
140             7'd57  : sccb_data <= 16'ha8_f0;
141             7'd58  : sccb_data <= 16'ha9_90;
142             7'd59  : sccb_data <= 16'haa_94;
143             7'd60  : sccb_data <= 16'h13_ef;
144             7'd61  : sccb_data <= 16'h0e_61;
145             7'd62  : sccb_data <= 16'h72_11;
146             7'd63  : sccb_data <= 16'h16_02;
147             7'd64  : sccb_data <= 16'h0f_4b;
148             7'd65  : sccb_data <= 16'h21_02;
149             7'd66  : sccb_data <= 16'h22_91;
150             7'd67  : sccb_data <= 16'h29_07;
151             7'd68  : sccb_data <= 16'h33_0b;
152             7'd69  : sccb_data <= 16'h35_0b;
153             7'd70  : sccb_data <= 16'h37_1d;
154             7'd71  : sccb_data <= 16'h38_71;
155             7'd72  : sccb_data <= 16'h39_2a;
156             7'd73  : sccb_data <= 16'h3c_78;
157             7'd74  : sccb_data <= 16'h4d_40;
158             7'd75  : sccb_data <= 16'h4e_20;
159             7'd76  : sccb_data <= 16'h69_00;
160             7'd77  : sccb_data <= 16'ha2_02;
161             7'd78  : sccb_data <= 16'h74_19;
162             7'd79  : sccb_data <= 16'h8d_4f;
163             7'd80  : sccb_data <= 16'h8e_00;
164             7'd81  : sccb_data <= 16'h8f_00;
165             7'd82  : sccb_data <= 16'h90_00;
166             7'd83  : sccb_data <= 16'h91_00;
167             7'd84  : sccb_data <= 16'h96_00;
168             7'd85  : sccb_data <= 16'h9a_80;
169             7'd86  : sccb_data <= 16'hb0_84;
170             7'd87  : sccb_data <= 16'hb1_0c;
171             7'd88  : sccb_data <= 16'hb2_0e;
172             7'd89  : sccb_data <= 16'hb3_82;
173             7'd90  : sccb_data <= 16'hb8_0a;
174             7'd91  : sccb_data <= 16'h43_14;
175             7'd92  : sccb_data <= 16'h44_f0;
176             7'd93  : sccb_data <= 16'h45_34;
177             7'd94  : sccb_data <= 16'h46_58;
178             7'd95  : sccb_data <= 16'h47_28;
179             7'd96  : sccb_data <= 16'h48_3a;
180             7'd97  : sccb_data <= 16'h59_88;
181             7'd98  : sccb_data <= 16'h5a_88;
182             7'd99  : sccb_data <= 16'h5b_44;
183             7'd100 : sccb_data <= 16'h5c_67;
184             7'd101 : sccb_data <= 16'h5d_49;
185             7'd102 : sccb_data <= 16'h5e_0e;
186             7'd103 : sccb_data <= 16'h64_04;
187             7'd104 : sccb_data <= 16'h65_20;
188             7'd105 : sccb_data <= 16'h66_05;
189             7'd106 : sccb_data <= 16'h94_04;
190             7'd107 : sccb_data <= 16'h95_08;
191             7'd108 : sccb_data <= 16'h6c_0a;
192             7'd109 : sccb_data <= 16'h6d_55;
193             7'd110 : sccb_data <= 16'h4f_80;
194             7'd111 : sccb_data <= 16'h50_80;
195             7'd112 : sccb_data <= 16'h51_00;
196             7'd113 : sccb_data <= 16'h52_22;
197             7'd114 : sccb_data <= 16'h53_5e;
198             7'd115 : sccb_data <= 16'h54_80;
199             7'd116 : sccb_data <= 16'h09_03;
200             7'd117 : sccb_data <= 16'h6e_11;
201             7'd118 : sccb_data <= 16'h6f_9f;
202             7'd119 : sccb_data <= 16'h55_00;
203             7'd120 : sccb_data <= 16'h56_40;
204             7'd121 : sccb_data <= 16'h57_80;
205             7'd122 : sccb_data <= 16'h15_00;
206             default: sccb_data <= 16'h1c_7f; //MIDH 制造商ID 高8位
207         endcase
208     end
209 end
210 
211 endmodule

  我只是把重要的寄存器提前了,可以通過更改 pll 寄存器而改變幀率,也可以通過窗口相關的寄存器改變輸出的分辨率,但有些麻煩,我后面會介紹一種更好的辦法來獲取任意分辨率,以期適應我們的屏幕。

  這里輸出的分辨率為 640x480,Pclk 為 24Mhz,fps 是我通過測量得到的,那如何進行理論值計算呢?

  datasheet 中有這樣一張圖,我們來算一下。一幀圖像時間 = (2 x tPclk)x 510 x 784 = (2 x 510 x 784) / Pclk hz,一幀圖像時間的倒數即為幀率 fps,因此幀率 fps = Pclk /(2 x 510 x 784),Pclk為24000000hz,因此計算得到 fps = 24000000 /(2 x 510 x 784)≈ 30.012。計算結果和實際測量值一致。

 

三、OV7725寄存器配置

  1 //**************************************************************************
  2 // *** 名稱 : sccb_cfg.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2019-08-10
  6 // *** 描述 : SCCB配置ov7725寄存器
  7 //**************************************************************************
  8 
  9 module sccb_ov7725_cfg
 10 //========================< 參數 >==========================================
 11 #(
 12 parameter REG_NUM           = 70                      //寄存器個數
 13 )
 14 //========================< 端口 >==========================================
 15 (
 16 input   wire                clk                     , //時鍾,1Mhz
 17 input   wire                rst_n                   , //復位,低電平有效
 18 input   wire                sccb_vld                  , //SCCB配置有效信號
 19 input   wire                sccb_done               , //SCCB寄存器配置完成信號
 20 output  reg                 sccb_en                 , //SCCB觸發執行信號   
 21 output  reg     [15:0]      sccb_data               , //SCCB要配置的地址與數據(高8位地址,低8位數據)
 22 output  reg                 sccb_cfg_done             //SCCB全部寄存器配置完成信號
 23 );
 24 //========================< 信號 >==========================================
 25 reg                         sccb_vld_r                ;
 26 wire                        sccb_start                ;
 27 reg    [6:0]                reg_cnt                 ; //寄存器配置個數計數器
 28 //==========================================================================
 29 //==    sccb_vld上升沿檢測
 30 //==========================================================================
 31 always @(posedge clk or negedge rst_n) begin
 32     if(!rst_n)
 33         sccb_vld_r <= 1'b0;
 34     else
 35         sccb_vld_r <= sccb_vld;
 36 end
 37 
 38 assign sccb_start = sccb_vld && ~sccb_vld_r;
 39 //==========================================================================
 40 //==    sccb觸發執行信號 
 41 //==========================================================================  
 42 always @(posedge clk or negedge rst_n) begin
 43     if(!rst_n)
 44         sccb_en <= 1'b0;
 45     else if(sccb_start)                     //開始配置寄存器
 46         sccb_en <= 1'b1;
 47     else if(sccb_done && reg_cnt < REG_NUM) //上一個配置完后立馬配置下一個
 48         sccb_en <= 1'b1;
 49     else
 50         sccb_en <= 1'b0;    
 51 end 
 52 //==========================================================================
 53 //==    寄存器配置個數計數
 54 //==========================================================================    
 55 always @(posedge clk or negedge rst_n) begin
 56     if(!rst_n)
 57         reg_cnt <= 7'd0;
 58     else if(sccb_en)   
 59         reg_cnt <= reg_cnt + 7'b1;
 60 end
 61 //==========================================================================
 62 //==    所有寄存器全部配置完成信號
 63 //==========================================================================
 64 always @(posedge clk or negedge rst_n) begin
 65     if(!rst_n)
 66         sccb_cfg_done <= 1'b0;
 67     else if((reg_cnt == REG_NUM) && sccb_done)  
 68         sccb_cfg_done <= 1'b1;  
 69 end        
 70 //==========================================================================
 71 //==    配置寄存器地址與數據
 72 //==========================================================================
 73 always @(posedge clk or negedge rst_n) begin
 74     if(!rst_n)
 75         sccb_data <= 16'b0;
 76     else begin
 77         case(reg_cnt)                            //30fps,XCLK=24Mhz,PCLK=24Mhz,ID=0x42
 78 //基本設置 ----------------------------------------------------------------------------
 79             7'd0  : sccb_data <= {8'h12, 8'h06}; //COM7 復位選擇:否;格式:VGA_RGB565 
 80             7'd1  : sccb_data <= {8'h0c, 8'h10}; //COM3 10正常模式; 90垂直翻轉
 81                                                  //         50水平翻轉; d0水平垂直翻轉
 82                                                  //Bit[0]   0正常模式;  1彩條測試
 83 //時序參數 ----------------------------------------------------------------------------
 84             7'd2  : sccb_data <= {8'h17, 8'h22}; //HSTART VGA:8'h22; QVGA:8'h3f
 85             7'd3  : sccb_data <= {8'h18, 8'ha4}; //HSIZE VGA:8'ha4; QVGA:8'h50
 86             7'd4  : sccb_data <= {8'h19, 8'h07}; //VSTART VGA:8'h07; QVGA:8'h03
 87             7'd5  : sccb_data <= {8'h1a, 8'hf0}; //VSIZE VGA:8'hf0; QVGA:8'h78
 88             7'd6  : sccb_data <= {8'h29, 8'ha0}; //HOutSize VGA:8'hA0; QVGA:8'hF0
 89             7'd7  : sccb_data <= {8'h2c, 8'hf0}; //VOutSize VGA:8'hF0; QVGA:8'h78
 90 //幀率PLL(Xclk=24Mhz) ---------------------------------------------------------------
 91                                                  //fps/pclk 30/24 15/12 25/24 14.3/12
 92             7'd8  : sccb_data <= {8'h11, 8'h01}; // 01 03 01 03
 93             7'd9  : sccb_data <= {8'h0d, 8'h41}; // 41 41 41 41
 94             7'd10 : sccb_data <= {8'h2a, 8'h00}; // 00 00 00 00
 95             7'd11 : sccb_data <= {8'h33, 8'h00}; // 00 00 66 1a
 96             7'd12 : sccb_data <= {8'h34, 8'h00}; // 00 00 00 00
 97             7'd13 : sccb_data <= {8'h2d, 8'h00}; // 00 00 00 00
 98             7'd14 : sccb_data <= {8'h2e, 8'h00}; // 00 00 00 00
 99             7'd15 : sccb_data <= {8'h0e, 8'h65}; // 65 65 65 65
100             7'd16 : sccb_data <= {8'h2b, 8'h00}; //60hz/50hz 默認00/60hz,有燈光干擾,fps如上
101                                                  //          改為9e/50hz,無燈光干擾,fps會變
102 //DSP 控制 ----------------------------------------------------------------------------
103             7'd17 : sccb_data <= {8'h42, 8'h7f}; //TGT_B 黑電平校准藍色通道目標值
104             7'd18 : sccb_data <= {8'h4d, 8'h09}; //FixGain 模擬增益放大器
105             7'd19 : sccb_data <= {8'h63, 8'hf0}; //AWB_Ctrl0 自動白平衡控制字節0
106             7'd20 : sccb_data <= {8'h64, 8'hff}; //DSP_Ctrl1 DSP控制字節1
107             7'd21 : sccb_data <= {8'h65, 8'h00}; //DSP_Ctrl2 DSP控制字節2
108             7'd22 : sccb_data <= {8'h66, 8'h00}; //DSP_Ctrl3 DSP控制字節3
109             7'd23 : sccb_data <= {8'h67, 8'h00}; //DSP_Ctrl4 DSP控制字節4 
110 //AGC AEC AWB ------------------------------------------------------------------------
111             7'd24 : sccb_data <= {8'h13, 8'hff}; //COM8 自動增益/白平衡/曝光
112             7'd25 : sccb_data <= {8'h0f, 8'hc5}; //COM6
113             7'd26 : sccb_data <= {8'h14, 8'h11};  
114             7'd27 : sccb_data <= {8'h22, 8'h98}; 
115             7'd28 : sccb_data <= {8'h23, 8'h03};  
116             7'd29 : sccb_data <= {8'h24, 8'h40}; 
117             7'd30 : sccb_data <= {8'h25, 8'h30};  
118             7'd31 : sccb_data <= {8'h26, 8'ha1};      
119             7'd32 : sccb_data <= {8'h6b, 8'haa}; 
120             7'd33 : sccb_data <= {8'h13, 8'hff}; 
121 //參數調整 ---------------------------------------------------------------------------
122             7'd34 : sccb_data <= {8'h90, 8'h0a}; //EDGE1 邊緣增強控制1
123             7'd35 : sccb_data <= {8'h91, 8'h01}; //DNSOff 閾值下限
124             7'd36 : sccb_data <= {8'h92, 8'h01}; //EDGE2 銳度(邊緣增強)強度上限
125             7'd37 : sccb_data <= {8'h93, 8'h01}; //EDGE3 銳度(邊緣增強)強度下限
126             7'd38 : sccb_data <= {8'h94, 8'h5f}; //MTX1 矩陣系數1
127             7'd39 : sccb_data <= {8'h95, 8'h53}; //MTX2 矩陣系數2
128             7'd40 : sccb_data <= {8'h96, 8'h11}; //MTX3 矩陣系數3
129             7'd41 : sccb_data <= {8'h97, 8'h1a}; //MTX4 矩陣系數4
130             7'd42 : sccb_data <= {8'h98, 8'h3d}; //MTX5 矩陣系數5
131             7'd43 : sccb_data <= {8'h99, 8'h5a}; //MTX6 矩陣系數6
132             7'd44 : sccb_data <= {8'h9a, 8'h1e}; //MTX_Ctrl 矩陣控制
133             7'd45 : sccb_data <= {8'h9b, 8'h3f}; //BRIGHT 亮度
134             7'd46 : sccb_data <= {8'h9c, 8'h25}; //CNST 對比度            
135             7'd47 : sccb_data <= {8'h9e, 8'h81}; //UV/ADJ0 紫外線調控
136             7'd48 : sccb_data <= {8'ha6, 8'h06}; //SDE 特殊數字效果控制
137             7'd49 : sccb_data <= {8'ha7, 8'h65}; //USAT "U"飽和增益
138             7'd50 : sccb_data <= {8'ha8, 8'h65}; //VSAT "V"飽和增益            
139             7'd51 : sccb_data <= {8'ha9, 8'h80}; //HUECOS cos值   
140             7'd52 : sccb_data <= {8'haa, 8'h80}; //HUESIN sin值
141 //伽馬控制 -------------------------------------------------------------------------
142             7'd53 : sccb_data <= {8'h7e, 8'h0c}; 
143             7'd54 : sccb_data <= {8'h7f, 8'h16}; 
144             7'd55 : sccb_data <= {8'h80, 8'h2a}; 
145             7'd56 : sccb_data <= {8'h81, 8'h4e}; 
146             7'd57 : sccb_data <= {8'h82, 8'h61}; 
147             7'd58 : sccb_data <= {8'h83, 8'h6f}; 
148             7'd59 : sccb_data <= {8'h84, 8'h7b}; 
149             7'd60 : sccb_data <= {8'h85, 8'h86};   
150             7'd61 : sccb_data <= {8'h86, 8'h8e}; 
151             7'd62 : sccb_data <= {8'h87, 8'h97}; 
152             7'd63 : sccb_data <= {8'h88, 8'ha4}; 
153             7'd64 : sccb_data <= {8'h89, 8'haf}; 
154             7'd65 : sccb_data <= {8'h8a, 8'hc5}; 
155             7'd66 : sccb_data <= {8'h8b, 8'hd7}; 
156             7'd67 : sccb_data <= {8'h8c, 8'he8}; 
157             7'd68 : sccb_data <= {8'h8d, 8'h20}; 
158 //others --------------------------------------------------------------------------
159             7'd69 : sccb_data <= {8'h3d, 8'h03}; //模擬過程的直流偏移
160         endcase
161     end
162 end
163 
164 endmodule

  和 OV7670 差不多,注意一下第 13 個寄存器 8'h2b,根據韓彬的書《FPGA設計技巧與案例開發詳解》介紹,假設我們原本寄存器配置的是 640x480@30fps,此時8'h2b的值為00,大陸的白熾燈頻率為50hz,會導致圖像效果出現條紋干擾。當把 8'h2b寄存器的值改為 9e 時,就可以避免該條紋干擾,但幀率卻會由 30fps 變成 25fps。

 

四、OV5640寄存器配置

  1 //**************************************************************************
  2 // *** 名稱 : sccb_ov5640_cfg.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2019-08-10
  6 // *** 描述 : SCCB配置ov5640寄存器
  7 //**************************************************************************
  8 
  9 module sccb_ov5640_cfg
 10 //========================< 參數 >==========================================
 11 #(
 12 parameter REG_NUM           = 240                   , //寄存器個數
 13 parameter CMOS_H_PIXEL      = 24'd1024              , //CMOS水平方向像素個數
 14 parameter CMOS_V_PIXEL      = 24'd768               , //CMOS垂直方向像素個數
 15 parameter TOTAL_H_PIXEL     = CMOS_H_PIXEL+13'd1216 , //水平總像素大小
 16 parameter TOTAL_V_PIXEL     = CMOS_V_PIXEL+13'd504    //垂直總像素大小
 17 )
 18 //========================< 端口 >==========================================
 19 (
 20 input   wire                clk                     , //時鍾,1Mhz
 21 input   wire                rst_n                   , //復位,低電平有效
 22 input   wire                sccb_vld                , //SCCB配置有效信號
 23 input   wire                sccb_done               , //SCCB寄存器配置完成信號
 24 output  reg                 sccb_en                 , //SCCB觸發執行信號   
 25 output  reg     [23:0]      sccb_data               , //SCCB要配置的地址與數據(高8位地址,低8位數據)
 26 output  reg                 sccb_cfg_done             //SCCB全部寄存器配置完成信號
 27 );
 28 //========================< 信號 >==========================================
 29 reg                         sccb_vld_r                ;
 30 wire                        sccb_start                ;
 31 reg    [7:0]                reg_cnt                   ; //寄存器配置個數計數器
 32 //==========================================================================
 33 //==    sccb_vld上升沿檢測
 34 //==========================================================================
 35 always @(posedge clk or negedge rst_n) begin
 36     if(!rst_n)
 37         sccb_vld_r <= 1'b0;
 38     else
 39         sccb_vld_r <= sccb_vld;
 40 end
 41 
 42 assign sccb_start = sccb_vld && ~sccb_vld_r;
 43 //==========================================================================
 44 //==    sccb觸發執行信號 
 45 //==========================================================================  
 46 always @(posedge clk or negedge rst_n) begin
 47     if(!rst_n)
 48         sccb_en <= 1'b0;
 49     else if(sccb_start)                     //開始配置寄存器
 50         sccb_en <= 1'b1;
 51     else if(sccb_done && reg_cnt < REG_NUM) //上一個配置完后立馬配置下一個
 52         sccb_en <= 1'b1;
 53     else
 54         sccb_en <= 1'b0;    
 55 end 
 56 //==========================================================================
 57 //==    寄存器配置個數計數
 58 //==========================================================================    
 59 always @(posedge clk or negedge rst_n) begin
 60     if(!rst_n)
 61         reg_cnt <= 8'd0;
 62     else if(sccb_en)   
 63         reg_cnt <= reg_cnt + 8'b1;
 64 end
 65 //==========================================================================
 66 //==    所有寄存器全部配置完成信號
 67 //==========================================================================
 68 always @(posedge clk or negedge rst_n) begin
 69     if(!rst_n)
 70         sccb_cfg_done <= 1'b0;
 71     else if((reg_cnt == REG_NUM) && sccb_done)
 72         sccb_cfg_done <= 1'b1;  
 73 end        
 74 //==========================================================================
 75 //==    配置寄存器地址與數據,Xclk=24Mhz
 76 //==========================================================================
 77 always @(posedge clk or negedge rst_n) begin
 78     if(!rst_n)
 79         sccb_data <= 24'b0;
 80     else begin
 81     case(reg_cnt)                                  //Xclk=24Mhz FPS=30fps Pclk=24Mhz(不解)
 82 //基本配置 ------------------------------------------------------------------------------------
 83             8'd0  : sccb_data <= {16'h3008,8'h02}; //復位休眠 Bit[7]:復位 Bit[6]:休眠
 84             8'd1  : sccb_data <= {16'h300e,8'h58}; //DVP 使能 DVP enable
 85             8'd2  : sccb_data <= {16'h4300,8'h61}; //格式控制 RGB565
 86             8'd3  : sccb_data <= {16'h503d,8'h00}; //測試圖案 00正常 80彩條 81混亂 82棋盤
 87 //PLL(11_23為20fps) ---------------------------------------------------------------------------
 88             8'd4  : sccb_data <= {16'h3035,8'h21}; //PLL分頻 11/1x 21/2x 32/3x
 89             8'd5  : sccb_data <= {16'h3036,8'h69}; //PLL倍頻 23/1÷ 46/2÷ 69/3÷
 90             8'd6  : sccb_data <= {16'h3037,8'h03}; //PLL分頻 bit[4]:0/1 bypass/÷2
 91 //ISP(VGA模式) --------------------------------------------------------------------------------
 92             8'd7  : sccb_data <= {16'h3800,8'h00};
 93             8'd8  : sccb_data <= {16'h3801,8'h00};
 94             8'd9  : sccb_data <= {16'h3802,8'h00};
 95             8'd10 : sccb_data <= {16'h3803,8'h04};
 96             8'd11 : sccb_data <= {16'h3804,8'h0a};
 97             8'd12 : sccb_data <= {16'h3805,8'h3f};
 98             8'd13 : sccb_data <= {16'h3806,8'h07};
 99             8'd14 : sccb_data <= {16'h3807,8'h9b};
100 //輸出窗口設置 --------------------------------------------------------------------------------
101             8'd15 : sccb_data <= {16'h3808,{4'd0,CMOS_H_PIXEL[11:8]  }}; //水平像素點數高4位
102             8'd16 : sccb_data <= {16'h3809,      CMOS_H_PIXEL[ 7:0]   }; //水平像素點數低8位
103             8'd17 : sccb_data <= {16'h380a,{5'd0,CMOS_V_PIXEL[10:8]  }}; //垂直像素點數高3位
104             8'd18 : sccb_data <= {16'h380b,      CMOS_V_PIXEL[ 7:0]   }; //垂直像素點數低8位
105             8'd19 : sccb_data <= {16'h380c,{3'd0,TOTAL_H_PIXEL[12:8] }}; //水平總像素大小高5位
106             8'd20 : sccb_data <= {16'h380d,      TOTAL_H_PIXEL[ 7:0]  }; //水平總像素大小低8位
107             8'd21 : sccb_data <= {16'h380e,{3'd0,TOTAL_V_PIXEL[12:8] }}; //垂直總像素大小高5位 
108             8'd22 : sccb_data <= {16'h380f,      TOTAL_V_PIXEL[ 7:0]  }; //垂直總像素大小低8位
109 //預縮放 --------------------------------------------------------------------------------------
110             8'd23 : sccb_data <= {16'h3810,8'h00}; //Timing Hoffset[11:8]
111             8'd24 : sccb_data <= {16'h3811,8'h10}; //Timing Hoffset[7:0]
112             8'd25 : sccb_data <= {16'h3812,8'h00}; //Timing Voffset[10:8]
113             8'd26 : sccb_data <= {16'h3813,8'h06}; //Timing Voffset[7:0]
114             8'd27 : sccb_data <= {16'h3814,8'h31}; //Timing X INC
115             8'd28 : sccb_data <= {16'h3815,8'h31}; //Timing Y INC
116             8'd29 : sccb_data <= {16'h3820,8'h40}; //上下翻轉:40/46
117             8'd30 : sccb_data <= {16'h3821,8'h07}; //左右翻轉:01/07
118 //SCCB ----------------------------------------------------------------------------------------
119             8'd31 : sccb_data <= {16'h3103,8'h02}; //Bit[1]:1 PLL Clock
120             8'd32 : sccb_data <= {16'h3108,8'h01}; //系統分頻
121 //VCM -----------------------------------------------------------------------------------------
122             8'd33 : sccb_data <= {16'h3600,8'h08}; //VCM控制,用於自動聚焦
123             8'd34 : sccb_data <= {16'h3601,8'h33}; //VCM控制,用於自動聚焦
124 //AEC/AGC -------------------------------------------------------------------------------------
125             8'd35 : sccb_data <= {16'h3a02,8'h17}; //60Hz max exposure
126             8'd36 : sccb_data <= {16'h3a03,8'h10}; //60Hz max exposure
127             8'd37 : sccb_data <= {16'h3a0f,8'h30}; //AEC控制;stable range in high
128             8'd38 : sccb_data <= {16'h3a10,8'h28}; //AEC控制;stable range in low
129             8'd39 : sccb_data <= {16'h3a11,8'h60}; //AEC控制; fast zone high
130             8'd40 : sccb_data <= {16'h3a13,8'h43}; //AEC(自動曝光控制)
131             8'd41 : sccb_data <= {16'h3a14,8'h17}; //50Hz max exposure
132             8'd42 : sccb_data <= {16'h3a15,8'h10}; //50Hz max exposure
133             8'd43 : sccb_data <= {16'h3a18,8'h00}; //AEC 增益上限
134             8'd44 : sccb_data <= {16'h3a19,8'hf8}; //AEC 增益上限
135             8'd45 : sccb_data <= {16'h3a1b,8'h30}; //AEC控制;stable range out high
136             8'd46 : sccb_data <= {16'h3a1e,8'h26}; //AEC控制;stable range out low
137             8'd47 : sccb_data <= {16'h3a1f,8'h14}; //AEC控制; fast zone low
138 //5060Hz --------------------------------------------------------------------------------------
139             8'd48 : sccb_data <= {16'h3c01,8'h34};
140             8'd49 : sccb_data <= {16'h3c04,8'h28};
141             8'd50 : sccb_data <= {16'h3c05,8'h98};
142             8'd51 : sccb_data <= {16'h3c06,8'h00}; //light meter 1 閾值[15:8]
143             8'd52 : sccb_data <= {16'h3c07,8'h08}; //light meter 1 閾值[7:0]
144             8'd53 : sccb_data <= {16'h3c08,8'h00}; //light meter 2 閾值[15:8]
145             8'd54 : sccb_data <= {16'h3c09,8'h1c}; //light meter 2 閾值[7:0]
146             8'd55 : sccb_data <= {16'h3c0a,8'h9c}; //sample number[15:8]
147             8'd56 : sccb_data <= {16'h3c0b,8'h40}; //sample number[7:0]
148 //BLC -----------------------------------------------------------------------------------------
149             8'd57 : sccb_data <= {16'h4001,8'h02}; //BLC(黑電平校准)補償起始行號
150             8'd58 : sccb_data <= {16'h4004,8'h02}; //BLC(背光) 2 lines
151             8'd59 : sccb_data <= {16'h4005,8'h1a}; //BLC(黑電平校准)補償始終更新
152 //ISP -----------------------------------------------------------------------------------------
153             8'd60 : sccb_data <= {16'h5000,8'ha7}; //ISP 控制
154             8'd61 : sccb_data <= {16'h5001,8'ha3}; //ISP 控制
155             8'd62 : sccb_data <= {16'h501d,8'h40}; //ISP 控制
156             8'd63 : sccb_data <= {16'h501f,8'h01}; //ISP RGB
157 //LENC(鏡頭校正)控制 16'h5800~16'h583d --------------------------------------------------------
158             8'd64 : sccb_data <= {16'h5800,8'h23};
159             8'd65 : sccb_data <= {16'h5801,8'h14};
160             8'd66 : sccb_data <= {16'h5802,8'h0f};
161             8'd67 : sccb_data <= {16'h5803,8'h0f};
162             8'd68 : sccb_data <= {16'h5804,8'h12};
163             8'd69 : sccb_data <= {16'h5805,8'h26};
164             8'd70 : sccb_data <= {16'h5806,8'h0c};
165             8'd71 : sccb_data <= {16'h5807,8'h08};
166             8'd72 : sccb_data <= {16'h5808,8'h05};
167             8'd73 : sccb_data <= {16'h5809,8'h05};
168             8'd74 : sccb_data <= {16'h580a,8'h08};
169             8'd75 : sccb_data <= {16'h580b,8'h0d};
170             8'd76 : sccb_data <= {16'h580c,8'h08};
171             8'd77 : sccb_data <= {16'h580d,8'h03};
172             8'd78 : sccb_data <= {16'h580e,8'h00};
173             8'd79 : sccb_data <= {16'h580f,8'h00};
174             8'd80 : sccb_data <= {16'h5810,8'h03};
175             8'd81 : sccb_data <= {16'h5811,8'h09};
176             8'd82 : sccb_data <= {16'h5812,8'h07};
177             8'd83 : sccb_data <= {16'h5813,8'h03};
178             8'd84 : sccb_data <= {16'h5814,8'h00};
179             8'd85 : sccb_data <= {16'h5815,8'h01};
180             8'd86 : sccb_data <= {16'h5816,8'h03};
181             8'd87 : sccb_data <= {16'h5817,8'h08};
182             8'd88 : sccb_data <= {16'h5818,8'h0d};
183             8'd89 : sccb_data <= {16'h5819,8'h08};
184             8'd90 : sccb_data <= {16'h581a,8'h05};
185             8'd91 : sccb_data <= {16'h581b,8'h06};
186             8'd92 : sccb_data <= {16'h581c,8'h08};
187             8'd93 : sccb_data <= {16'h581d,8'h0e};
188             8'd94 : sccb_data <= {16'h581e,8'h29};
189             8'd95 : sccb_data <= {16'h581f,8'h17};
190             8'd96 : sccb_data <= {16'h5820,8'h11};
191             8'd97 : sccb_data <= {16'h5821,8'h11};
192             8'd98 : sccb_data <= {16'h5822,8'h15};
193             8'd99 : sccb_data <= {16'h5823,8'h28};
194             8'd100: sccb_data <= {16'h5824,8'h46};
195             8'd101: sccb_data <= {16'h5825,8'h26};
196             8'd102: sccb_data <= {16'h5826,8'h08};
197             8'd103: sccb_data <= {16'h5827,8'h26};
198             8'd104: sccb_data <= {16'h5828,8'h64};
199             8'd105: sccb_data <= {16'h5829,8'h26};
200             8'd106: sccb_data <= {16'h582a,8'h24};
201             8'd107: sccb_data <= {16'h582b,8'h22};
202             8'd108: sccb_data <= {16'h582c,8'h24};
203             8'd109: sccb_data <= {16'h582d,8'h24};
204             8'd110: sccb_data <= {16'h582e,8'h06};
205             8'd111: sccb_data <= {16'h582f,8'h22};
206             8'd112: sccb_data <= {16'h5830,8'h40};
207             8'd113: sccb_data <= {16'h5831,8'h42};
208             8'd114: sccb_data <= {16'h5832,8'h24};
209             8'd115: sccb_data <= {16'h5833,8'h26};
210             8'd116: sccb_data <= {16'h5834,8'h24};
211             8'd117: sccb_data <= {16'h5835,8'h22};
212             8'd118: sccb_data <= {16'h5836,8'h22};
213             8'd119: sccb_data <= {16'h5837,8'h26};
214             8'd120: sccb_data <= {16'h5838,8'h44};
215             8'd121: sccb_data <= {16'h5839,8'h24};
216             8'd122: sccb_data <= {16'h583a,8'h26};
217             8'd123: sccb_data <= {16'h583b,8'h28};
218             8'd124: sccb_data <= {16'h583c,8'h42};
219             8'd125: sccb_data <= {16'h583d,8'hce};
220 //AWB(自動白平衡控制) 16'h5180~16'h519e -------------------------------------------------------
221             8'd126: sccb_data <= {16'h5180,8'hff};
222             8'd127: sccb_data <= {16'h5181,8'hf2};
223             8'd128: sccb_data <= {16'h5182,8'h00};
224             8'd129: sccb_data <= {16'h5183,8'h14};
225             8'd130: sccb_data <= {16'h5184,8'h25};
226             8'd131: sccb_data <= {16'h5185,8'h24};
227             8'd132: sccb_data <= {16'h5186,8'h09};
228             8'd133: sccb_data <= {16'h5187,8'h09};
229             8'd134: sccb_data <= {16'h5188,8'h09};
230             8'd135: sccb_data <= {16'h5189,8'h75};
231             8'd136: sccb_data <= {16'h518a,8'h54};
232             8'd137: sccb_data <= {16'h518b,8'he0};
233             8'd138: sccb_data <= {16'h518c,8'hb2};
234             8'd139: sccb_data <= {16'h518d,8'h42};
235             8'd140: sccb_data <= {16'h518e,8'h3d};
236             8'd141: sccb_data <= {16'h518f,8'h56};
237             8'd142: sccb_data <= {16'h5190,8'h46};
238             8'd143: sccb_data <= {16'h5191,8'hf8};
239             8'd144: sccb_data <= {16'h5192,8'h04};
240             8'd145: sccb_data <= {16'h5193,8'h70};
241             8'd146: sccb_data <= {16'h5194,8'hf0};
242             8'd147: sccb_data <= {16'h5195,8'hf0};
243             8'd148: sccb_data <= {16'h5196,8'h03};
244             8'd149: sccb_data <= {16'h5197,8'h01};
245             8'd150: sccb_data <= {16'h5198,8'h04};
246             8'd151: sccb_data <= {16'h5199,8'h12};
247             8'd152: sccb_data <= {16'h519a,8'h04};
248             8'd153: sccb_data <= {16'h519b,8'h00};
249             8'd154: sccb_data <= {16'h519c,8'h06};
250             8'd155: sccb_data <= {16'h519d,8'h82};
251             8'd156: sccb_data <= {16'h519e,8'h38};
252 //Gamma(伽馬)控制 16'h5480~16'h5490 -----------------------------------------------------------
253             8'd157: sccb_data <= {16'h5480,8'h01};
254             8'd158: sccb_data <= {16'h5481,8'h08};
255             8'd159: sccb_data <= {16'h5482,8'h14};
256             8'd160: sccb_data <= {16'h5483,8'h28};
257             8'd161: sccb_data <= {16'h5484,8'h51};
258             8'd162: sccb_data <= {16'h5485,8'h65};
259             8'd163: sccb_data <= {16'h5486,8'h71};
260             8'd164: sccb_data <= {16'h5487,8'h7d};
261             8'd165: sccb_data <= {16'h5488,8'h87};
262             8'd166: sccb_data <= {16'h5489,8'h91};
263             8'd167: sccb_data <= {16'h548a,8'h9a};
264             8'd168: sccb_data <= {16'h548b,8'haa};
265             8'd169: sccb_data <= {16'h548c,8'hb8};
266             8'd170: sccb_data <= {16'h548d,8'hcd};
267             8'd171: sccb_data <= {16'h548e,8'hdd};
268             8'd172: sccb_data <= {16'h548f,8'hea};
269             8'd173: sccb_data <= {16'h5490,8'h1d};
270 //CMX(彩色矩陣控制) 16'h5381~16'h538b ---------------------------------------------------------
271             8'd174: sccb_data <= {16'h5381,8'h1e};
272             8'd175: sccb_data <= {16'h5382,8'h5b};
273             8'd176: sccb_data <= {16'h5383,8'h08};
274             8'd177: sccb_data <= {16'h5384,8'h0a};
275             8'd178: sccb_data <= {16'h5385,8'h7e};
276             8'd179: sccb_data <= {16'h5386,8'h88};
277             8'd180: sccb_data <= {16'h5387,8'h7c};
278             8'd181: sccb_data <= {16'h5388,8'h6c};
279             8'd182: sccb_data <= {16'h5389,8'h10};
280             8'd183: sccb_data <= {16'h538a,8'h01};
281             8'd184: sccb_data <= {16'h538b,8'h98};
282 //SDE(特殊數碼效果)控制 16'h5580~16'h558b -----------------------------------------------------
283             8'd185: sccb_data <= {16'h5580,8'h06};
284             8'd186: sccb_data <= {16'h5583,8'h40};
285             8'd187: sccb_data <= {16'h5584,8'h10};
286             8'd188: sccb_data <= {16'h5589,8'h10};
287             8'd189: sccb_data <= {16'h558a,8'h00};
288             8'd190: sccb_data <= {16'h558b,8'hf8};
289 //CIP(顏色插值)控制 (16'h5300~16'h530c) -------------------------------------------------------
290             8'd191: sccb_data <= {16'h5300,8'h08};
291             8'd192: sccb_data <= {16'h5301,8'h30};
292             8'd193: sccb_data <= {16'h5302,8'h10};
293             8'd194: sccb_data <= {16'h5303,8'h00};
294             8'd195: sccb_data <= {16'h5304,8'h08};
295             8'd196: sccb_data <= {16'h5305,8'h30};
296             8'd197: sccb_data <= {16'h5306,8'h08};
297             8'd198: sccb_data <= {16'h5307,8'h16};
298             8'd199: sccb_data <= {16'h5309,8'h08};
299             8'd200: sccb_data <= {16'h530a,8'h30};
300             8'd201: sccb_data <= {16'h530b,8'h04};
301             8'd202: sccb_data <= {16'h530c,8'h06};
302 //測試閃光燈功能 ------------------------------------------------------------------------------
303             8'd203: sccb_data <= {16'h3000,8'h00}; //系統塊復位控制
304             8'd204: sccb_data <= {16'h3004,8'hff}; //時鍾使能控制
305             8'd205: sccb_data <= {16'h3017,8'hff}; //I/O控制[3:0] 00:input ff:output 
306             8'd206: sccb_data <= {16'h3018,8'hff}; //I/O控制[7:2] 00:input ff:output
307             8'd207: sccb_data <= {16'h3016,8'h02};
308             8'd208: sccb_data <= {16'h301c,8'h02};
309             8'd209: sccb_data <= {16'h3019,8'h02}; //打開閃光燈
310             8'd210: sccb_data <= {16'h3019,8'h00}; //關閉閃光燈
311 //others --------------------------------------------------------------------------------------
312             8'd211: sccb_data <= {16'h3612,8'h29};
313             8'd212: sccb_data <= {16'h3618,8'h00};
314             8'd213: sccb_data <= {16'h3620,8'h52};
315             8'd214: sccb_data <= {16'h3621,8'he0};
316             8'd215: sccb_data <= {16'h3622,8'h01};
317             8'd216: sccb_data <= {16'h302d,8'h60}; //系統控制
318             8'd217: sccb_data <= {16'h3630,8'h36};
319             8'd218: sccb_data <= {16'h3631,8'h0e};
320             8'd219: sccb_data <= {16'h3632,8'he2};
321             8'd220: sccb_data <= {16'h3633,8'h12};
322             8'd221: sccb_data <= {16'h3634,8'h40};
323             8'd222: sccb_data <= {16'h3635,8'h13};
324             8'd223: sccb_data <= {16'h3636,8'h03};
325             8'd224: sccb_data <= {16'h3703,8'h5a};
326             8'd225: sccb_data <= {16'h3704,8'ha0};
327             8'd226: sccb_data <= {16'h3705,8'h1a};
328             8'd227: sccb_data <= {16'h3708,8'h64};
329             8'd228: sccb_data <= {16'h3709,8'h52};
330             8'd229: sccb_data <= {16'h370b,8'h60};
331             8'd230: sccb_data <= {16'h370c,8'h03};
332             8'd231: sccb_data <= {16'h3715,8'h78};
333             8'd232: sccb_data <= {16'h3717,8'h01};
334             8'd233: sccb_data <= {16'h371b,8'h20};
335             8'd234: sccb_data <= {16'h3731,8'h12};
336             8'd235: sccb_data <= {16'h3901,8'h0a};
337             8'd236: sccb_data <= {16'h3905,8'h02};
338             8'd237: sccb_data <= {16'h3906,8'h10};
339             8'd238: sccb_data <= {16'h3b07,8'h0a}; //幀曝光模式
340             8'd239: sccb_data <= {16'h4407,8'h04}; //量化標度
341             default:sccb_data <= {16'h300a,8'h00}; //器件ID高8位
342         endcase
343     end
344 end
345 
346 
347 
348 endmodule

   ov5640的寄存器配置如上所示,可以配出 1024x768@30fps 的圖像,該寄存器配置表同樣來自正點原子的改編。注意一下,OV5640的寄存器地址是 16 位的,加上數據,sccb_data 的值為 24 位,這點和OV7670、OV7725不一樣。

1、窗口輸出的疑惑

其窗口輸出分三個部分:

(1)ISP 輸入窗口設置( ISP input size):0x3800~0x3807

(2)預縮放窗口設置( pre-scaling size) :0X3810~0X3821

(3)輸出窗口設置( data output size) :0X3808~0X380B

   按照很多開發板提供的寄存器參考表確實能得到圖像,但是很奇怪。首先 ISP 部分的寄存器。盡管輸出分辨率很大(例如1024x768),但是很多人的ISP寄存器部分都是參照的 VGA(640x480)模式下的值,最后竟然也能輸出。其次是預縮放窗口的寄存器,這個沒有深究,僅僅試了下上下翻轉和左右翻轉。最后是輸出窗口的寄存器,正點原子的還加上了0x380C~0x380F,而且和0x3808~0x380B是配合的。如果不改0x380C~0x380F,那還沒什么問題,當我試着更改這些值時,有時會不出圖像,有時圖像會縮小或放大。

2、Pclk的疑惑

  OV7670 和 OV7725 的 Pclk 很容易通過 PLL 的調配得到,但是 OV5640 卻不一樣。首先,OV5640 的 PLL 有三個,如下所示:

8'd4  : sccb_data <= {16'h3035,8'h21}; //PLL分頻 11/1÷ 21/2÷ 32/3÷
8'd5  : sccb_data <= {16'h3036,8'h69}; //PLL倍頻 23/1x 46/2x 69/3x
8'd6  : sccb_data <= {16'h3037,8'h03}; //PLL分頻 bit[4]:0/1 bypass/÷2

   先說寄存器 16'h3037寄存器,該寄存器默認值是03(也有很多人設為13),bit[4]的意思是旁路 PLL(即不管)或 2 分頻,而 bit[3:0] 在 datasheet 中也有分頻的意義,可卻沒有說明怎么回事。我這里設置 3037=03,即旁路pll,以下的說明都以這個為前提。

  然后是16'h3035寄存器,該pll寄存器比較簡單,其[7:4]的值代表着分頻系數。

  最后是寄存器16'h3036,這個寄存器的值就奇怪了,很多人寫的是69,我研究發現 69 應該是 x3 的意思,相應的 23 是 x1,46 是 x2,當 3035 為 11 而 3036 為 23 時,fps為20,以此為基准就可以對這兩個寄存器配置不同的值達到想要的幀率了,如下是我實驗測得的不同配置下的 fps 值。(如何測量 fps 值在下一講會整理)

  雖然 3036 寄存器和 3037 寄存器沒有完全搞懂,但是用最傻瓜的辦法還是得到了一些規律,至少可以得到一些想要的 fps 的值了。但是 Pclk 為多少呢?完全沒辦法知道!首先是這些測量結果和《ov5640寄存器參考列表》等PDF的值有出入,可能是設置窗口的數據和那些手冊不完全一致。其次按道理說,fps 增大那么 Pclk 也會增大,但是我測量 Pclk 的結果卻很詭異,根本不是那么回事。關於 Pclk 的相關資料很少,我實在搞不懂,如果有人知道,煩請指教一二。

  多說一句,OV7725 和 OV5640 的成像效果都很好,而 OV7670 的效果則非常差,我試了很多人的配置表,都是一樣的效果,非常差勁。而且很奇怪的是,OV7670 的圖像效果是旋轉了 90 度的,非常別扭,我查看寄存器列表也沒有發現改變旋轉的寄存器配置。所以說,如果嫌 OV5640 貴,那可以用 OV7725,千萬別為了便宜去買 OV7670,這個攝像頭就是個垃圾,誰用誰惡心!

 

  至此,我們完成了 SCCB 的配置,下一講的內容是 cmos_capture_rgb565,即 DVP 窗口輸出的設置。

 

 參考資料:

[1]正點原子FPGA教程

[2]小梅哥《OV5640圖像采集從原理到應用》

[3]開源騷客《SDRAM那些事兒》

[4]韓彬, 於瀟宇, 張雷鳴. FPGA設計技巧與案例開發詳解[M]. 電子工業出版社, 2014.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM