FPGA心得体会:相机传感器驱动和相关配置

用过了几款相机(OV5640,IMX等),对使用相机也有了一点心得,在此记录。

当你得到一款相机,你需要做的:

第一件事:在datasheet中阅读配置单,知道怎么配置、配置完输出来是什么。

  配置输出尺寸;传输模式:DDR?SDR?;传输格式:raw8/10/12?rgb?

  Dvp or Lvds?

  通常官方手册会有推荐配置的寄存器列表,按那个来做就好,很多东西自己摸索都不好掌握:比如OV5640的输出帧数;

第二件事:根据接口决定驱动模块的组成;

  核心要义就是解析同步头(或者叫做同步字、短包之类的),从而得到一帧一行的开始和结束。

  DVP接口知道同步头,大致时序后可以写解析驱动;

  Lvds需要先进行差分转单端、串并转换操作后方可进行解析;

第三件事:几个Lane?

  单个Lane自不用说,多个Lane的时候需要考虑几个要素:

  1.同步;通过字同步获得Offset解析出合适的数据以后(Lvds),通过分析信号到来的先后顺序完成Lane同步(类似MIPI模块里的操作);

  2.根据Lane的数量进行拼接,查阅手册知道输到Lane的方式,哪个前哪个后,完成拼接;

  3.拼接完以后按照恰当的时序输出完整的像素。
之后的得到几个输出:

  Vsync,Vaild,Data,就可以决定进一步做算法还是直写入DDR3 SDRAM中了。

IMX222:DVP相机+SPI配置

数据手册参考:

 

配置方案:1080P

配置时序:MAX 28MHZ

XSpiPs_SetClkPrescaler
16分频:

 

 

 主要含义和代码:

反转采集方式:从上到下。

{ 0x02, 0x01,0x01}, // vs revers

 显示模式:

{ 0X02, 0x02, 0xF }, //1080p

 输出速率:DR

{ 0X02, 0x11, 0x00 }, //6 data rate :double inclk sdr paralle

采集数据宽度:raw10

{ 0X02, 0x12, 0x82 }, //7 AD 12bi

 参考代码:

复制代码
{ 0x02, 0x00, 0x31 }, //standby mode
{ 0x02, 0x01,0x01}, // vs reverse
{ 0X02, 0x02, 0xF }, //1080p
{ 0X02, 0x03, 0x4c }, //2
{ 0X02, 0x04, 0x04 }, //3 hmax:1100
{ 0X02, 0x05, 0x65 }, //4
{ 0X02, 0x06, 0x04 }, //5 Vmax:1125
{ 0X02, 0x11, 0x00 }, //6 data rate :double inclk sdr parallel
{ 0X02, 0x12, 0x82 }, //7 AD 12bit
{ 0X02, 0x14, 0x00 }, //8 x_strat_addr[7:0]
{ 0X02, 0x15, 0x00 }, //9 x_strat_addr[11:8]
{ 0X02, 0x16, 0x3C }, //10 y_strat_addr[7:0]
{ 0X02, 0x17, 0x00 }, //11 y_strat_addr[11:8]
{ 0X02, 0x18, 0xC0 }, //12 cropping xsize [7:0]
{ 0X02, 0x19, 0x07 }, //13 cropping xsize [11:8]
{ 0X02, 0x1A, 0x51 }, //14 cropping ysize [7:0]
{ 0X02, 0x1B, 0x04 }, //15 cropping ysize [11:8]
{ 0X02, 0x1e, 0x20 }, //gain
{ 0X02, 0x20, 0xf0 }, //16 black level offset
{ 0X02, 0x21, 0x00 }, //16 H Sync width
{ 0X02, 0x2c, 0x00 }, //16 trigger master mode
{ 0X02, 0x2d, 0x42 }, //clk 1/2pai delay sdr
{ 0X02, 0x3b, 0xe1 }, //18 sync code parallel
{ 0X02, 0x9A, 0x26 }, //22
{ 0X02, 0x9B, 0x02 }, //23  Adjustment registers for each operation mode.1080p@30fps 12b = 226h
{ 0X02, 0xCE, 0x16 }, //24  16h
{ 0X02, 0xCF, 0x82 }, //25  82h
{ 0X02, 0xD0, 0x00 }, //26  both top
{ 0X02, 0x00, 0x00 }, //27  master mode  
View Code
复制代码

参数:

像素时钟和约束:

上图可得:HD1080P@30FPS模式下DVP模式的时钟发送速率为74.25MHZ

数值:74.25MHZ

约束手段:输入到PLL中再相移以后输出;

 

如果PLL吃紧也可以参考:

create_clock -period 13.468 -name camera_pixel_clk-waveform {0.000 6.734} [get_ports camera_pixel_clk]

但是这个方法计算起来不太容易!

OV5640 MIPI模式:LVDS相机+I2C配置

OV5640:寄存器 自用-CSDN博客

配置时序

I2C < 400K

50_000 / 128 = 391 Khz

参考配置:开源骚客原创

一些细节:

ov5640的dvp模式输出1280p也存在难度,但是mipi模式可以轻易输出1920@30fps

配置代码来自凯文的博客,感谢凯文。

复制代码
// *********************************************************************************
// Project Name : OSXXXX
// Author       : dengkanwen
// Email        : dengkanwen@163.com
// Website      : http://www.opensoc.cn/
// Create Time  : 2017/5/11 22:31:05
// File Name    : .v
// Module Name  : 
// Called By    :
// Abstract     :
//
// CopyRight(c) 2016, OpenSoc Studio.. 
// All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date         By              Version                 Change Description
// -----------------------------------------------------------------------
// 2017/5/11    Kevin           1.0                     Original
//  
// *********************************************************************************
`timescale      1ns/1ns

module  ov5640_cfg(
        // system signals
        input                   sclk                    ,       // IIC_SCL x2
        input                   s_rst_n                 ,       
        // IIC Interfaces
        output  wire            iic_scl                 ,
        inout                   iic_sda                 ,
        // Debug Interfaces
        output  wire            cfg_done
);

//========================================================================\
// =========== Define Parameter and Internal signals =========== 
//========================================================================/
localparam      ANUM            =       122                     ;
localparam      DELAY_200US     =       10000                   ;

wire    [31:0]                  cfg_array[ANUM-1:0]             ;
reg     [ 8:0]                  cfg_index                       ;
reg                             start                           ;       
// wire                            cfg_done                        ;       
reg     [15:0]                  cnt_200us                       ;
wire                            busy_neg                        ;       
reg     [ 2:0]                  busy_arr                        ;
wire                            busy                            ;       

wire                            iic_start                       ;
wire    [31:0]                  iic_wdata                       ;
//=============================================================================
//**************    Main Code   **************
//input clock = 12MHZ
//
//=============================================================================
//                                ID    REG_ADDR  REG_VAL
assign  cfg_array[000]  =       {8'h78, 16'h3103, 8'h11};
assign  cfg_array[001]  =       {8'h78, 16'h3008, 8'h82};
assign  cfg_array[002]  =       {8'h78, 16'h3008, 8'h42};
assign  cfg_array[003]  =       {8'h78, 16'h3103, 8'h03};
assign  cfg_array[004]  =       {8'h78, 16'h3017, 8'h00};
assign  cfg_array[005]  =       {8'h78, 16'h3018, 8'h00};
assign  cfg_array[006]  =       {8'h78, 16'h3034, 8'h18};
assign  cfg_array[007]  =       {8'h78, 16'h3035, 8'h11}; // PLL root divider, bit[4], PLL pre-divider, bit[3:0]
assign  cfg_array[008]  =       {8'h78, 16'h3036, 8'h38}; // PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2] // SCLK root divider, bit[1:0] 
assign  cfg_array[009]  =       {8'h78, 16'h3037, 8'h11};
assign  cfg_array[010]  =       {8'h78, 16'h3108, 8'h01};
assign  cfg_array[011]  =       {8'h78, 16'h303D, 8'h10};
assign  cfg_array[012]  =       {8'h78, 16'h303B, 8'h19};
assign  cfg_array[013]  =       {8'h78, 16'h3630, 8'h2e};
assign  cfg_array[014]  =       {8'h78, 16'h3631, 8'h0e};
assign  cfg_array[015]  =       {8'h78, 16'h3632, 8'he2};
assign  cfg_array[016]  =       {8'h78, 16'h3633, 8'h23};
assign  cfg_array[017]  =       {8'h78, 16'h3621, 8'he0};
assign  cfg_array[018]  =       {8'h78, 16'h3704, 8'ha0};
assign  cfg_array[019]  =       {8'h78, 16'h3703, 8'h5a};
assign  cfg_array[020]  =       {8'h78, 16'h3715, 8'h78};
assign  cfg_array[021]  =       {8'h78, 16'h3717, 8'h01};
assign  cfg_array[022]  =       {8'h78, 16'h370b, 8'h60};
assign  cfg_array[023]  =       {8'h78, 16'h3705, 8'h1a};
assign  cfg_array[024]  =       {8'h78, 16'h3905, 8'h02};
assign  cfg_array[025]  =       {8'h78, 16'h3906, 8'h10};
assign  cfg_array[026]  =       {8'h78, 16'h3901, 8'h0a};
assign  cfg_array[027]  =       {8'h78, 16'h3731, 8'h02};
assign  cfg_array[028]  =       {8'h78, 16'h3600, 8'h37};
assign  cfg_array[029]  =       {8'h78, 16'h3601, 8'h33};
assign  cfg_array[030]  =       {8'h78, 16'h302d, 8'h60};
assign  cfg_array[031]  =       {8'h78, 16'h3620, 8'h52};
assign  cfg_array[032]  =       {8'h78, 16'h371b, 8'h20};
assign  cfg_array[033]  =       {8'h78, 16'h471c, 8'h50};
assign  cfg_array[034]  =       {8'h78, 16'h3a13, 8'h43};
assign  cfg_array[035]  =       {8'h78, 16'h3a18, 8'h00};
assign  cfg_array[036]  =       {8'h78, 16'h3a19, 8'hf8};
assign  cfg_array[037]  =       {8'h78, 16'h3635, 8'h13};
assign  cfg_array[038]  =       {8'h78, 16'h3636, 8'h06};
assign  cfg_array[039]  =       {8'h78, 16'h3634, 8'h44};     
assign  cfg_array[040]  =       {8'h78, 16'h3622, 8'h01};
assign  cfg_array[041]  =       {8'h78, 16'h3c01, 8'h34};
assign  cfg_array[042]  =       {8'h78, 16'h3c04, 8'h28};
assign  cfg_array[043]  =       {8'h78, 16'h3c05, 8'h98};
assign  cfg_array[044]  =       {8'h78, 16'h3c06, 8'h00};
assign  cfg_array[045]  =       {8'h78, 16'h3c07, 8'h08};
assign  cfg_array[046]  =       {8'h78, 16'h3c08, 8'h00};
assign  cfg_array[047]  =       {8'h78, 16'h3c09, 8'h1c};
assign  cfg_array[048]  =       {8'h78, 16'h3c0a, 8'h9c};
assign  cfg_array[049]  =       {8'h78, 16'h3c0b, 8'h40};
// assign  cfg_array[050]  =       {8'h78, 16'h503d, 8'h00}; // 8'h80 : standard eight color bar,  8'h00: normal
assign  cfg_array[050]  =       {8'h78, 16'h503d, 8'h00}; // 8'h80 : standard eight color bar,  8'h00: normal
assign  cfg_array[051]  =       {8'h78, 16'h3820, 8'h46};
assign  cfg_array[052]  =       {8'h78, 16'h300e, 8'h45};
assign  cfg_array[053]  =       {8'h78, 16'h4800, 8'h14};
assign  cfg_array[054]  =       {8'h78, 16'h302e, 8'h08};
assign  cfg_array[055]  =       {8'h78, 16'h4300, 8'h6f};
assign  cfg_array[056]  =       {8'h78, 16'h501f, 8'h01};
assign  cfg_array[057]  =       {8'h78, 16'h4713, 8'h03};
assign  cfg_array[058]  =       {8'h78, 16'h4407, 8'h04};
assign  cfg_array[059]  =       {8'h78, 16'h440e, 8'h00};    
assign  cfg_array[060]  =       {8'h78, 16'h460b, 8'h35};
assign  cfg_array[061]  =       {8'h78, 16'h460c, 8'h20};
assign  cfg_array[062]  =       {8'h78, 16'h3824, 8'h01};
assign  cfg_array[063]  =       {8'h78, 16'h5000, 8'h07};
assign  cfg_array[064]  =       {8'h78, 16'h5001, 8'h03};
assign  cfg_array[065]  =       {8'h78, 16'h3008, 8'h42};
assign  cfg_array[066]  =       {8'h78, 16'h3035, 8'h21}; //11,21:30 41:60
assign  cfg_array[067]  =       {8'h78, 16'h3036, 8'h46}; //70 multi
assign  cfg_array[068]  =       {8'h78, 16'h3037, 8'h05};
assign  cfg_array[069]  =       {8'h78, 16'h3108, 8'h11};  
assign  cfg_array[070]  =       {8'h78, 16'h3034, 8'h1A};
assign  cfg_array[071]  =       {8'h78, 16'h3800, 8'h01}; // X address start high byte
assign  cfg_array[072]  =       {8'h78, 16'h3801, 8'h50}; // X address start low byte
assign  cfg_array[073]  =       {8'h78, 16'h3802, 8'h01}; // Y address start high byte
assign  cfg_array[074]  =       {8'h78, 16'h3803, 8'hAA};
assign  cfg_array[075]  =       {8'h78, 16'h3804, 8'h08}; // 
assign  cfg_array[076]  =       {8'h78, 16'h3805, 8'hEF}; // 
assign  cfg_array[077]  =       {8'h78, 16'h3806, 8'h05};
assign  cfg_array[078]  =       {8'h78, 16'h3807, 8'hF9};
assign  cfg_array[079]  =       {8'h78, 16'h3810, 8'h00};    
assign  cfg_array[080]  =       {8'h78, 16'h3811, 8'h10}; // 
assign  cfg_array[081]  =       {8'h78, 16'h3812, 8'h00};
assign  cfg_array[082]  =       {8'h78, 16'h3813, 8'h0C};
assign  cfg_array[083]  =       {8'h78, 16'h3808, 8'h07}; // X_OUTPUT_SIZE:16'h0780 -> 1920
assign  cfg_array[084]  =       {8'h78, 16'h3809, 8'h80}; 
assign  cfg_array[085]  =       {8'h78, 16'h380a, 8'h04}; // Y_OUTPUT_SIZE:16'h0438 -> 1080
assign  cfg_array[086]  =       {8'h78, 16'h380b, 8'h38};       
assign  cfg_array[087]  =       {8'h78, 16'h380c, 8'h09};
assign  cfg_array[088]  =       {8'h78, 16'h380d, 8'hC4};
assign  cfg_array[089]  =       {8'h78, 16'h380e, 8'h04};  
assign  cfg_array[090]  =       {8'h78, 16'h380f, 8'h60};
assign  cfg_array[091]  =       {8'h78, 16'h3814, 8'h11};
assign  cfg_array[092]  =       {8'h78, 16'h3815, 8'h11};
assign  cfg_array[093]  =       {8'h78, 16'h3821, 8'h00};
assign  cfg_array[094]  =       {8'h78, 16'h4837, 8'h24};
assign  cfg_array[095]  =       {8'h78, 16'h3618, 8'h00};
assign  cfg_array[096]  =       {8'h78, 16'h3612, 8'h59};
assign  cfg_array[097]  =       {8'h78, 16'h3708, 8'h64};
assign  cfg_array[098]  =       {8'h78, 16'h3709, 8'h52};
assign  cfg_array[099]  =       {8'h78, 16'h370c, 8'h03};     
assign  cfg_array[100]  =       {8'h78, 16'h4300, 8'h00}; // Format RAW, [3:0]=0x0 BGBG/GRGR
assign  cfg_array[101]  =       {8'h78, 16'h501f, 8'h03}; // Format select ISP RAW (DPC)
assign  cfg_array[102]  =       {8'h78, 16'h3406, 8'h00};
assign  cfg_array[103]  =       {8'h78, 16'h5192, 8'h04};
assign  cfg_array[104]  =       {8'h78, 16'h5191, 8'hf8};
assign  cfg_array[105]  =       {8'h78, 16'h518d, 8'h26};
assign  cfg_array[106]  =       {8'h78, 16'h518f, 8'h42};
assign  cfg_array[107]  =       {8'h78, 16'h518e, 8'h2b};
assign  cfg_array[108]  =       {8'h78, 16'h5190, 8'h42};
assign  cfg_array[109]  =       {8'h78, 16'h518b, 8'hd0};       
assign  cfg_array[110]  =       {8'h78, 16'h518c, 8'hbd};
assign  cfg_array[111]  =       {8'h78, 16'h5187, 8'h18};
assign  cfg_array[112]  =       {8'h78, 16'h5188, 8'h18};
assign  cfg_array[113]  =       {8'h78, 16'h5189, 8'h56};
assign  cfg_array[114]  =       {8'h78, 16'h518a, 8'h5c};
assign  cfg_array[115]  =       {8'h78, 16'h5186, 8'h1c};
assign  cfg_array[116]  =       {8'h78, 16'h5181, 8'h50};
assign  cfg_array[117]  =       {8'h78, 16'h5184, 8'h20};
assign  cfg_array[118]  =       {8'h78, 16'h5182, 8'h11};
assign  cfg_array[119]  =       {8'h78, 16'h5183, 8'h00};        
assign  cfg_array[120]  =       {8'h78, 16'h5001, 8'h03};
assign  cfg_array[121]  =       {8'h78, 16'h3008, 8'h02};

//-----------------------------------------------------------------------------

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                start   <=      1'b0;
        else if(cfg_index == 'd0 && start == 1'b0)
                start   <=      1'b1;
        else if(busy_neg == 1'b1 && cfg_index < ANUM)
                start   <=      1'b1;
        else
                start   <=      1'b0;
end

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                cfg_index       <=      'd0;
        else if(cfg_index >= ANUM)
                cfg_index       <=      ANUM;
        else if(start == 1'b1)
                cfg_index       <=      cfg_index + 1'b1;
end

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                cnt_200us       <=      'd0;
        else if(cfg_index >= ANUM && cfg_done == 1'b0)
                cnt_200us       <=      cnt_200us + 1'b1;
end

always  @(posedge sclk or negedge s_rst_n) begin
        if(s_rst_n == 1'b0)
                busy_arr        <=      'd0;
        else
                busy_arr        <=      {busy_arr[1:0], busy}; 
end

assign  busy_neg        =       busy_arr[2] & ~busy_arr[1];
assign  iic_start       =       (cfg_done == 1'b1) ? 1'b0 : start;
assign  iic_wdata       =       (cfg_done == 1'b1) ? 'd0 : cfg_array[cfg_index];
assign  cfg_done        =       (cnt_200us >= DELAY_200US) ? 1'b1 : 1'b0;

ov5640_iic     ov5640_iic_inst(
        // system signals
        .sclk                   (sclk                   ),
        .s_rst_n                (s_rst_n                ),
        // IIC
        .iic_scl                (iic_scl                ),
        .iic_sda                (iic_sda                ),
        // Othesr
        .start                  (iic_start              ),
        .wdata                  (iic_wdata              ),
        .riic_data              (riic_data              ),
        .busy                   (busy                   )
);

endmodule
View Code
复制代码

 

posted @   NoNounknow  阅读(137)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示