CMOS攝像頭(1):總體介紹和上電控制


  FPGA采集攝像頭數據,經過中間緩存,最后輸出到屏幕上,這個工程幾乎是所有FPGAer都要經歷的工程。曾聽人說過,如果能獨立的做出攝像頭顯示工程,那么就代表他的FPGA終於入門了。

  這次,我准備將目前市面上最常用的三款攝像頭——OV7670、OV7725、OV5640的開發全過程全部記錄下來,並且提供所有代碼,若日后需要做相關項目,也方便自己回顧,迅速撿起來。OV7670和OV7725都是30w像素級攝像頭,其典型輸出為640x480@30fps(VGA),各方面時序也完全一致,僅僅是攝像頭配置不同,OV7725可以說是OV7670的升級版,前者比后者的成像效果好很多。而OV5640為500w像素級攝像頭,最高支持 2592x1944@15fps(QSXGA)的圖像輸出。

一、硬件電路(by小梅哥AC620)

  如下是常用的CMOS硬件電路。

  共有20個引腳,其解釋如下所示:

  這里先說明一點,OV7725和OV5640 芯片的 DVP 接口本身擁有 10 位的數據線,可以輸出 10 位的 RAW 數據,但是在大多數情況下我們使用高 8 位數據即可,因此模組在設計時大多只涉及 D9~D2 這高 8 位,映射到模組上的OV_D7~OV_D0。注意:上述電路的 OV_SCL 和 OV_SDA 沒有連接物理上拉電阻,直接使用會有問題,必須在 Quartus II 軟件中對該 2 處引腳設置開啟 FPGA 的 IO 片上上拉電阻,功能才能有效。
  此外注意到,20個引腳中,有一個引腳是 NC 空引腳,一個引腳是 OV_STROBE 預留引腳,因此很多攝像頭電路會直接將這兩個引腳刪除,最終變成 18 個引腳,而且一般是會加上拉電阻的,這種攝像頭硬件電路圖如下所示:
  上述兩種接口基本適配市面上賣的OV7670、OV7725、OV5640等攝像頭,直接插上就行。

二、內部結構

  內部結構有些小復雜,直接說重點。攝像頭采集圖像,經過內部一系列的處理,最終通過端口輸出,輸出端口有幾種,如DVP、MIPI、LVDS、CSI等,我們一般用的是DVP接口,有些模塊的DVP是10位的,我們取高8位即可,舍棄掉了低2位。

 

三、上電配置時序分析

1、OV7670/OV7725

  OV7670 和 OV7725 的數據手冊中並沒有出現上電的時序圖,但是給出了一條信息:Setting time after software/hardware reset:1ms。所謂軟件復位說的是寄存器復位,攝像頭寄存器很多,有一個寄存器有復位功能,對其寫入復位操作可以達到軟件復位效果。而硬件復位指的就是硬件電路圖上的 RST 信號的復位操作。

  在一本名為《OV7670 照相模組硬件應用指南》的PDF文件中倒是給出了上電時序圖,但是感覺參數和原版的 datasheet 有些出入,所以我沒有采用。

  經過上板我得出一些結論:

(1)cmos_pwdn 信號直接賦 0 即可。

(2)cmos_rst_n 信號直接賦 1 即可。

(3)rst_n 賦 1 后,必須延時 1ms 后再進行 SCCB 配置。

2、OV5640

  OV5640的上電時序在其 datasheet 中明確給出了,如下所示:

  注意 DOVDD 和 AVDD 是 OV5640 器件內部就已經設計好的,不用自己設計。

  經過上板發現,cmos_pwdn 信號不延時直接賦 0 也是可以的。總結如下:

(1)cmos_pwdn直接賦0即可。

(2)cmos_rst_n 信號延時1ms后賦 1 即可。

(3)cmos_rst_n信號賦 1 后,延時 20ms 后才能再進行SCCB配置。

 3、時鍾Xclk

  OV7670、OV7725、OV5640的輸入時鍾Xclk,一般都建議為 24Mhz,用 FPGA 的 PLL 分頻到 24Mhz 給它就行,攝像頭內部有自己的 PLL,會按照內部設計供給其內部各個模塊使用,使得攝像頭能正常工作。關於Pclk,我們后面再說。

 

四、代碼展現

1、總體架構

  總體架構如上所示,解釋如下:

(1)pll:時鍾分頻模塊

(2)ov7725_top:攝像頭的頂層模塊

(3)sdram_top:SDRAM圖像緩存模塊

(4)TFT_driver:TFT屏顯示模塊

(5)SEG_driver:數碼管顯示幀率模塊

  本系列模塊只重點討論第二個 ov7725_top 模塊,其他模塊前面的博客都有說過,其實就是搭積木而已。

2、工程頂層代碼

  頂層模塊都差不多,就是端口和 pll ,其他的都是別的部分了。

  1 //**************************************************************************
  2 // *** 名稱 : top.v
  3 // *** 作者 : xianyu_FPGA
  4 // *** 博客 : https://www.cnblogs.com/xianyufpga/
  5 // *** 日期 : 2020-5-20
  6 // *** 工具 : Quartus 13.0
  7 // *** 芯片 : Cyclone IV E
  8 // *** 型號 : EP4CE10F17C8
  9 // *** 描述 : 工程的頂層模塊
 10 //**************************************************************************
 11 module top
 12 //========================< 端口 >==========================================
 13 (
 14 input                       clk                     ,
 15 input                       rst_n                   ,
 16 //ov7670 --------------------------------------------
 17 input                       cmos_pclk               ,
 18 output                      cmos_xclk               ,
 19 input                       cmos_vsync              ,
 20 input                       cmos_href               ,
 21 input       [ 7:0]          cmos_data               ,
 22 output                      cmos_pwdn               ,
 23 output                      cmos_rst_n              ,
 24 output                      cmos_scl                ,
 25 inout                       cmos_sda                ,
 26 //sdram ---------------------------------------------
 27 output                      sdram_clk               ,   
 28 output                      sdram_cke               ,   
 29 output                      sdram_cs_n              ,
 30 output                      sdram_we_n              ,
 31 output                      sdram_cas_n             ,
 32 output                      sdram_ras_n             ,
 33 output      [ 1:0]          sdram_dqm               ,
 34 output      [ 1:0]          sdram_ba                ,
 35 output      [12:0]          sdram_addr              ,
 36 inout       [15:0]          sdram_dq                ,
 37 //TFT -----------------------------------------------
 38 output                      TFT_clk                 ,
 39 output                      TFT_de                  ,
 40 output                      TFT_pwm                 ,
 41 output                      TFT_hsync               ,
 42 output                      TFT_vsync               ,
 43 output      [15:0]          TFT_data                ,
 44 //Segment -------------------------------------------
 45 output                      SH_CP                   ,
 46 output                      ST_CP                   ,
 47 output                      DS                       
 48 );
 49 //========================< 信號 >==========================================
 50 wire                        clk_100m                ;
 51 wire                        clk_100m_shift          ;
 52 wire                        clk_24m                 ;
 53 wire                        clk_10m                 ;
 54 //SDRAM ---------------------------------------------
 55 wire                        sdram_init_done         ;
 56 wire                        wr_en                   ;  //SDRAM 寫使能
 57 wire        [15:0]          wr_data                 ;  //SDRAM 寫數據
 58 wire                        rd_en                   ;  //SDRAM 讀使能
 59 wire        [15:0]          rd_data                 ;  //SDRAM 讀數據
 60 //Segment -------------------------------------------
 61 wire        [ 7:0]          fps_rate                ;
 62 //==========================================================================
 63 //==                        PLL
 64 //==========================================================================
 65 pll pll
 66 (
 67     .inclk0                 (clk                    ),
 68     .c0                     (clk_24m                ), //CMOS xclk
 69     .c1                     (clk_100m               ), //SDRAM
 70     .c2                     (clk_100m_shift         ), //SDRAM
 71     .c3                     (clk_10m                )  //TFT
 72 );
 73 //==========================================================================
 74 //==                ov7725,已裁剪為480x272
 75 //==========================================================================
 76 ov7725_top u_ov7725_top
 77 (
 78     .clk_24m                (clk_24m                ),
 79     .rst_n                  (rst_n & sdram_init_done), //SDRAM復位后
 80     //cmos ------------------------------------------
 81     .cmos_pclk              (cmos_pclk              ),
 82     .cmos_xclk              (cmos_xclk              ),
 83     .cmos_vsync             (cmos_vsync             ),
 84     .cmos_href              (cmos_href              ),
 85     .cmos_data              (cmos_data              ),
 86     .cmos_rst_n             (cmos_rst_n             ),
 87     .cmos_pwdn              (cmos_pwdn              ),
 88     .cmos_scl               (cmos_scl               ),
 89     .cmos_sda               (cmos_sda               ),
 90     //rgb565 ----------------------------------------
 91     .rgb_vld                (wr_en                  ), //rgb數據指示
 92     .rgb_data               (wr_data                ), //rgb數據
 93     .fps_rate               (fps_rate               )  //fps幀率
 94 );
 95 //==========================================================================
 96 //==                        SDRAM
 97 //==========================================================================
 98 sdram_top u_sdram_top
 99 (
100     .ref_clk                (clk_100m               ), //SDRAM 控制器參考時鍾
101     .out_clk                (clk_100m_shift         ), //給SDRAM器件的偏移時鍾
102     .rst_n                  (rst_n                  ), //系統復位
103     //用戶寫端口 ------------------------------------
104     .wr_clk                 (cmos_pclk              ), //寫端口FIFO: 寫時鍾
105     .wr_en                  (wr_en                  ), //寫端口FIFO: 寫使能
106     .wr_data                (wr_data                ), //寫端口FIFO: 寫數據
107     .wr_min_addr            (24'd0                  ), //寫SDRAM的起始地址
108     .wr_max_addr            (480*272                ), //寫SDRAM的結束地址
109     .wr_len                 (10'd512                ), //寫SDRAM時的數據突發長度
110     .wr_load                (~rst_n                 ), //寫端口復位: 復位寫地址,清空寫FIFO
111     //用戶讀端口 ------------------------------------
112     .rd_clk                 (clk_10m                ), //讀端口FIFO: 讀時鍾
113     .rd_en                  (rd_en                  ), //讀端口FIFO: 讀使能
114     .rd_data                (rd_data                ), //讀端口FIFO: 讀數據
115     .rd_min_addr            (24'd0                  ), //讀SDRAM的起始地址
116     .rd_max_addr            (480*272                ), //讀SDRAM的結束地址
117     .rd_len                 (10'd512                ), //從SDRAM中讀數據時的突發長度
118     .rd_load                (~rst_n                 ), //讀端口復位: 復位讀地址,清空讀FIFO
119     //用戶控制端口 ----------------------------------
120     .sdram_init_done        (sdram_init_done        ), //SDRAM 初始化完成標志
121     .sdram_pingpang_en      (1'b1                   ), //SDRAM 乒乓操作使能,1開0關
122     //SDRAM 芯片接口 --------------------------------
123     .sdram_clk              (sdram_clk              ), //SDRAM 芯片時鍾
124     .sdram_cke              (sdram_cke              ), //SDRAM 時鍾有效
125     .sdram_cs_n             (sdram_cs_n             ), //SDRAM 片選
126     .sdram_ras_n            (sdram_ras_n            ), //SDRAM 行有效
127     .sdram_cas_n            (sdram_cas_n            ), //SDRAM 列有效
128     .sdram_we_n             (sdram_we_n             ), //SDRAM 寫有效
129     .sdram_ba               (sdram_ba               ), //SDRAM Bank地址
130     .sdram_addr             (sdram_addr             ), //SDRAM 行/列地址
131     .sdram_dq               (sdram_dq               ), //SDRAM 數據
132     .sdram_dqm              (sdram_dqm              )  //SDRAM 數據掩碼
133 );
134 //==========================================================================
135 //==                        TFT
136 //==========================================================================
137 TFT_driver u_TFT_driver 
138 (
139     .clk                    (clk_10m                ), 
140     .rst_n                  (rst_n                  ),
141     .TFT_req                (rd_en                  ),
142     .TFT_x                  (                       ),
143     .TFT_y                  (                       ),
144     .TFT_din                (rd_data                ),
145     .TFT_clk                (TFT_clk                ),
146     .TFT_de                 (TFT_de                 ),
147     .TFT_pwm                (TFT_pwm                ),
148     .TFT_hsync              (TFT_hsync              ),
149     .TFT_vsync              (TFT_vsync              ),
150     .TFT_data               (TFT_data               )   
151 );
152 //==========================================================================
153 //==                        數碼管顯示幀率
154 //==========================================================================
155 SEG_driver u_SEG_driver
156 (
157     .clk                    (clk_100m               ),
158     .rst_n                  (rst_n                  ),
159     .en                     (1                      ),
160     .value                  (fps_rate               ), //fps幀率值
161     .SH_CP                  (SH_CP                  ),
162     .ST_CP                  (ST_CP                  ),
163     .DS                     (DS                     )
164 );
165 
166 
167 
168         
169 endmodule

  這里補充一個知識點:FPGA時鍾為50Mhz,攝像頭需要 24Mhz,SDRAM需要兩個100Mhz,而 TFT 屏的推薦時鍾是 9Mhz,但 pll 已經無法分出 9Mhz 了,因此我分了一個近視的 10Mhz 代替 9Mhz,最終顯示也沒有任何問題。

  給出的是 ov7725_top,其實 ov7670、ov5640的頂層例化也是完全一樣的。

  很多人的攝像頭工程喜歡把一些簡單的信號如 cmos_pwdn 和 cmos_rst_n 信號,在頂層模塊中就直接賦出去,這也是可以的。而我的本次設計中,工程頂層模塊中沒有任何代碼,不管信號復雜與否都嚴格分塊設計,全都寫進了內部模塊里,這樣生成的 rtl 視圖更簡潔,各個模塊的配合也更直觀。

3、攝像頭頂層代碼

(1)OV7670和OV7725

  前面說過多次,這兩貨是一樣的,cmos_pwdn 信號和 cmos_rst_n 信號都可以直接賦值,而 cmos_rst_n 信號拉高后,必須延時 1ms 后才能進行 SCCB 配置,代碼如下所示:

//==========================================================================
//==                        cmos簡單信號
//==========================================================================
assign cmos_xclk  = clk_24m; //24MHz CMOS XCLK output
assign cmos_pwdn  = 1'b0;    //非節電模式,即正常模式
assign cmos_rst_n = 1'b1;    //復位信號,可不用延時
/*
always @(posedge clk_24m or negedge rst_n) begin
    if(!rst_n)
        cmos_rst_n <= 1'b0;    
    else
        cmos_rst_n <= 1'b1;
end
*/
//==========================================================================
//==                        SCCB驅動和配置
//==========================================================================
//延時1ms再進行SCCB配置
//---------------------------------------------------
always @(posedge clk_24m or negedge rst_n) begin
    if(!rst_n)
        delay_cnt <= 'b0;
    else if(delay_cnt <= 24000)
        delay_cnt <= delay_cnt + 1'b1;                    
end

assign sccb_vld = delay_cnt == 24001;

 

(2)OV5640

  OV5640的 cmos_pwdn 信號可以直接賦值,cmos_rst_n 信號卻必須延時 1ms 后才能拉高,拉高后再延時 20ms 后才能進行 SCCB 配置,代碼如下所示:

//==========================================================================
//==                        cmos簡單信號
//==========================================================================
//24MHz CMOS XCLK output
//---------------------------------------------------
assign cmos_xclk  = clk_24m;

//非節電模式,即正常模式,不延時也有用
//---------------------------------------------------
assign cmos_pwdn  = 1'b0;

//延時計數器
//---------------------------------------------------
always @(posedge clk_24m or negedge rst_n) begin
    if(!rst_n)
        delay_cnt <= 'b0;
    else if(delay_cnt <= 504000)
        delay_cnt <= delay_cnt + 1'b1;                    
end

//復位信號,至少延時1ms
//---------------------------------------------------
always @(posedge clk_24m or negedge rst_n) begin
    if(!rst_n)
        cmos_rst_n <= 1'b0;       
    else if(delay_cnt==240000)
        cmos_rst_n <= 1'b1;
end
//==========================================================================
//==                        SCCB驅動和配置
//==========================================================================
//至少再延時20ms再進行SCCB配置
//---------------------------------------------------
assign sccb_vld = delay_cnt==504001

 

  OK,本篇博客就到這,下一篇講解 SCCB 配置是怎么回事。

 

參考資料:

[1]正點原子FPGA教程

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

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

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

 


免責聲明!

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



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