• OV7670/OV7725/OV5640开发记录(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.

  • 相关阅读:
    【小工具】根据定义的白名单字段进行Bean的拷贝
    【Java】Java8的Lambda入门记录
    【Java】浅谈Java IO
    【工具】我的Git学习日志
    【Java】浅谈HashMap
    【Java】Java Queue的简介
    【ZooKeeper】ZooKeeper入门流水记
    【MQTT】Mosquitto的安装与使用流水记
    【数据结构】简单的数据结构图解
    【Java多线程】JDK1.5并发包API杂谈
  • 原文地址:https://www.cnblogs.com/xianyufpga/p/12275949.html
Copyright © 2020-2023  润新知