
簡介本資源是一套基于FPGA EP4CE10平臺(tái)實(shí)現(xiàn)OV5640攝像頭圖像采集與HDMI實(shí)時(shí)顯示的完整Verilog HDL工程面向數(shù)字電路、嵌入式圖像系統(tǒng)及FPGA初學(xué)者與進(jìn)階開發(fā)者解決從傳感器驅(qū)動(dòng)、數(shù)據(jù)流控制到高清視頻輸出的一體化硬件設(shè)計(jì)難題。壓縮包共261個(gè)文件含56個(gè).cdb編譯數(shù)據(jù)庫、55個(gè).hdb層次化數(shù)據(jù)庫、28個(gè).v核心Verilog源碼涵蓋OV5640初始化、MIPI/DCMI時(shí)序控制、幀緩存、HDMI TMDS編碼與同步信號(hào)生成等模塊以及.qsf約束文件、.sof配置文件、.qpf工程文件和詳細(xì)readme說明總大小8.31MB。已有162人學(xué)習(xí)下載工程可直接在Quartus II中編譯燒錄無需額外適配讀者可獲得結(jié)構(gòu)清晰的多層級模塊劃分、I2C配置寄存器表、HDMI像素時(shí)鐘與色彩空間轉(zhuǎn)換邏輯、以及實(shí)測可用的軟硬件協(xié)同調(diào)試方案是掌握FPGA圖像采集系統(tǒng)開發(fā)的高復(fù)用性實(shí)踐范例。 前陣子整理工程歸檔時(shí)翻出這個(gè)項(xiàng)目挺典型的一塊EP4CE10入門級FPGA接一個(gè)OV5640攝像頭模組用Verilog HDL寫完整套邏輯最終把實(shí)時(shí)畫面通過HDMI輸出到顯示器上。項(xiàng)目不大但鏈路很長——從SCCB配置、DVP采集、跨時(shí)鐘域緩存到顯示時(shí)序生成幾乎把FPGA圖像處理的基礎(chǔ)環(huán)節(jié)都踩了一遍。這篇把整個(gè)方案、選型邏輯、代碼結(jié)構(gòu)和調(diào)試過程中踩過的坑都整理出來給準(zhǔn)備做同類項(xiàng)目的朋友一個(gè)參考。如果你正好想入門FPGA圖像處理千萬別一上來就碰Zynq、MPSOC這些大平臺(tái)。EP4CE10加一顆OV5640花兩三百塊錢就能把“攝像頭采集到HDMI顯示”這條完整通路跑起來。這里面每個(gè)模塊單獨(dú)拆出來都不難但組合在一起幾乎把FPGA開發(fā)中最核心的能力都練到了時(shí)序設(shè)計(jì)、跨時(shí)鐘域處理、狀態(tài)機(jī)、IP核使用、時(shí)序約束。1. 項(xiàng)目方案與關(guān)鍵選型為什么EP4CE10能扛下這套系統(tǒng)1.1 一套入門級FPGA能做到的極限先說清楚EP4CE10是什么貨色。這顆芯片是Altera現(xiàn)在叫Intel PSGCyclone IV系列里的低端型號(hào)邏輯單元大約10320個(gè)嵌入式RAM約414Kbit也就是50多KBPLL兩個(gè)沒有硬核處理器、沒有DDR控制器、沒有任何視頻專用接口。聽著很寒酸但做視頻通路恰恰夠用因?yàn)橐曨l處理絕大部分工作不是“算”而是“搬”。什么叫“搬”把攝像頭送來的像素?cái)?shù)據(jù)按節(jié)拍接住存進(jìn)FIFO再按顯示器的節(jié)奏送出去。這個(gè)過程里幾乎沒有乘加運(yùn)算不需要DSP不涉及復(fù)雜算法只考驗(yàn)?zāi)銓π盘?hào)的時(shí)序控制能力。OV5640這類DVP接口攝像頭輸出的是并行的8位或10位數(shù)據(jù)加PCLK、VSYNC、HREF三個(gè)同步信號(hào)數(shù)據(jù)流是單向的、連續(xù)的、節(jié)奏穩(wěn)定的非常適合用狀態(tài)機(jī)處理。我當(dāng)時(shí)選這個(gè)組合的邏輯很簡單一是為了避開MIPIOV5640雖然也支持MIPI但MIPI在FPGA上用起來麻煩需要高速串行接收EP4CE10不是干這個(gè)的料二是DVP并口邏輯清晰每個(gè)信號(hào)都能用示波器或邏輯分析儀去量對學(xué)生黨友好三是EP4CE10開發(fā)板便宜折騰壞了不心疼。如果你有正點(diǎn)原子、小梅哥、黑金這類常見的EP4CE10板子基本都能直接照著接。1.2 HDMI輸出的三條路線EP4CE10怎么選把圖像數(shù)據(jù)變成HDMI信號(hào)有三個(gè)層次的路線一定要先想清楚再動(dòng)手。第一條路線是接一顆RGB轉(zhuǎn)HDMI的發(fā)送芯片比如ADV7513、SiI9134、CH7034B。FPGA只負(fù)責(zé)輸出并行RGB數(shù)據(jù)、行場同步、數(shù)據(jù)使能DE和像素時(shí)鐘編碼和串行化全交給芯片。這種方式FPGA邏輯資源占用最少調(diào)試也簡單缺點(diǎn)是板子上要多一顆芯片涉及實(shí)際布線不適合純學(xué)習(xí)環(huán)境。第二條路線是FPC直接驅(qū)動(dòng)HDMI也就是用FPGA的LVDS差分IO去輸出TMDS信號(hào)自己實(shí)現(xiàn)DVI/HDMI的編碼。EP4CE10的差分IO標(biāo)稱速率約840Mbps720p的三條數(shù)據(jù)通道各742.5Mbps理論在范圍內(nèi)。但這條路有幾個(gè)坎引腳分配受限不是每個(gè)bank都支持LVDS標(biāo)準(zhǔn)需要外部電阻做差分端接PCB差分走線要等長、控阻抗還要自己寫8b/10b編碼器。難度直線上升但對理解HDMI協(xié)議幫助巨大。第三條是直接用帶HDMI硬核的SoC那就跟EP4CE10沒關(guān)系了不在討論范圍內(nèi)。我的意見很明確如果你目標(biāo)是“快速把完整視頻通路跑起來”選第一條用現(xiàn)成的RGB轉(zhuǎn)HDMI芯片把精力留給攝像頭采集鏈路如果你目標(biāo)是“研究HDMI協(xié)議本身”選第二條但要做好連續(xù)調(diào)試幾周的準(zhǔn)備。我下面的內(nèi)容以第一條路線為主因?yàn)閷P4CE10來說最現(xiàn)實(shí)也最適合大部分人一步一個(gè)腳印地學(xué)習(xí)。1.3 分辨率和緩存賬事前必須算清楚選分辨率不是拍腦袋要算帶寬和存儲(chǔ)。720p60的像素時(shí)鐘是74.25MHzRGB565每像素16bit數(shù)據(jù)率約1.19GbpsEP4CE10的普通IO跑74.25MHz綽綽有余。1080p60的像素時(shí)鐘是148.5MHz數(shù)據(jù)率約2.38GbpsEP4CE10勉強(qiáng)能跑但留給時(shí)序余量非常小入門階段不建議碰。更大的瓶頸是緩存。720p一幀RGB565是1280×720×21.8MBEP4CE10內(nèi)部只有約51KB壓根放不下一幀。所以必須用行緩存方案采集一行存進(jìn)FIFO再由顯示時(shí)序讀出來。這種方式在圖像快速運(yùn)動(dòng)時(shí)偶爾會(huì)出現(xiàn)撕裂但靜止或慢速場景下幾乎看不出來。要真正解決幀同步問題就得外掛SDRAM或DDR那是第二階段的事先別貪多。如果存儲(chǔ)資源不夠還有一種常見做法是降低輸出分辨率比如640×48060一幀只有614KB依然放不下但行緩存壓力小很多調(diào)試會(huì)更輕松。我建議第一次做這個(gè)項(xiàng)目從640×480起步跑通了再上720p。2. 硬件連接與引腳規(guī)劃讓FPGA、攝像頭和HDMI芯片“對齊”2.1 信號(hào)定義與引腳分配硬件連接是整件事里最“瑣碎但決定成敗”的部分。OV5640模組接口一般有這些信號(hào)SIO_CI2C時(shí)鐘、SIO_DI2C數(shù)據(jù)、VSYNC幀同步、HREF行有效、PCLK像素時(shí)鐘、D0~D7數(shù)據(jù)、MCLK主時(shí)鐘輸入、RESET、PWDN。RGB轉(zhuǎn)HDMI芯片一側(cè)則需要PCLK、DE、HSYNC、VSYNC和RGB數(shù)據(jù)線。我當(dāng)時(shí)的引腳分配參考如下注意不同芯片接口有差異但整體思路一致信號(hào)方向電平建議MCLK輸出到攝像頭3.3V由PLL輸出24MHzPCLK輸入從攝像頭1.8V/3.3V視模組而定串33ΩVSYNC/HREF輸入從攝像頭同PCLK與數(shù)據(jù)線同BankD[7:0]輸入從攝像頭同PCLK同一Bank盡量緊湊SIO_C/SIO_D雙向3.3V必須接上拉電阻2.2kΩ~4.7kΩHDMI_PCLK輸出到HDMI芯片3.3VPLL輸出75MHz或74.25MHzHDMI_DE/HS/VS輸出到HDMI芯片3.3V顯示時(shí)序模塊產(chǎn)生HDMI_RGB[15:0]輸出到HDMI芯片3.3V位寬按芯片支持選引腳分配的原則攝像頭的一組信號(hào)盡量放在同一個(gè)Bank避免跨Bank帶來的電平標(biāo)準(zhǔn)沖突PCLK這種高速時(shí)鐘輸入要挑靠近PLL的引腳I2C時(shí)鐘和數(shù)據(jù)不要在板子上繞太遠(yuǎn)。2.2 時(shí)鐘與復(fù)位策略O(shè)V5640需要一個(gè)主時(shí)鐘MCLK典型值24MHz可以由外部晶振直接給也可以由FPGA PLL分頻產(chǎn)生。用PLL的好處是可以完全由軟件控制。如果板上只有50MHz晶振PLL配置成M/N12/25就能得到24MHz。顯示側(cè)如果走RGB轉(zhuǎn)HDMI芯片需要給一個(gè)像素時(shí)鐘。720p60標(biāo)準(zhǔn)是74.25MHz這個(gè)頻率用整數(shù)分頻不好從50MHz得到最理想的方案是板上帶27MHz晶振PLL倍頻到74.25MHz27×11/4。如果沒有27MHz晶振用50MHz生成75MHz也可以因?yàn)?5MHz只是多了約1%很多顯示器能正常識(shí)別只是不符合標(biāo)準(zhǔn)。如果做720p顯示時(shí)序參數(shù)按75MHz重新算一遍即可。復(fù)位策略要分兩級。第一級是系統(tǒng)上電復(fù)位把PLL鎖定、把各模塊狀態(tài)機(jī)置初值。第二級是在PLL鎖定后給攝像頭一個(gè)上電復(fù)位時(shí)序拉低RESET一段時(shí)間釋放再延時(shí)至少20ms才開始SCCB配置。這個(gè)延時(shí)別省很多花屏問題就是配置前攝像頭沒徹底復(fù)位導(dǎo)致的。2.3 電源與硬件上的幾個(gè)坑OV5640模組的IO電平要特別注意。有的模組DVP信號(hào)電平是1.8V有的是2.8V拉到FPGA的3.3V Bank上就可能超壓。穩(wěn)妥的做法是串33Ω或100Ω電阻做隔離或者查清楚模組手冊再?zèng)Q定Bank電壓。如果模組自帶電平轉(zhuǎn)換那就放心接。HDMI發(fā)送芯片的電源一般有1.8V和3.3V兩路去耦電容要盡量靠近電源引腳每路至少一個(gè)10μF鉭電容加幾個(gè)0.1μF陶瓷電容。TMDS差分線如果在自制PCB上必須做100Ω差分阻抗控制等長走線如果是在開發(fā)板上用杜邦線飛線到HDMI芯片轉(zhuǎn)接板那就別指望太高分辨率了640×480一般還穩(wěn)720p就開始碰運(yùn)氣。另一個(gè)容易忽略的問題HDMI的5V電源和HPD檢測。HDMI座子的5V腳要接電源HPDHot Plug Detect要拉高不然顯示器根本不會(huì)識(shí)別到信號(hào)源。有些轉(zhuǎn)接板會(huì)把這部分電路做完但如果自己畫板一定檢查HPD的上下拉配置。3. Verilog核心模塊實(shí)現(xiàn)從SCCB配置到HDMI時(shí)序3.1 SCCB/I2C配置模塊OV5640的全部秘密在這OV5640的寄存器配置是通過SCCB總線完成的SCCB基本兼容I2C寫時(shí)序幾乎一樣。從地址一般是0x3C寫/0x3D讀有些模組是0x21具體看模組原理圖。這部分是整個(gè)項(xiàng)目最“磨人”的地方因?yàn)镺V5640要配的寄存器有好幾百個(gè)配錯(cuò)一個(gè)就可能導(dǎo)致黑屏、花屏、顏色不對。模塊設(shè)計(jì)分兩層底層是一個(gè)通用的I2C主機(jī)驅(qū)動(dòng)支持起始條件、停止條件、逐字節(jié)發(fā)送、ACK檢測上層是一個(gè)初始化狀態(tài)機(jī)從ROM或常量表里逐條取出寄存器地址和值調(diào)用底層驅(qū)動(dòng)寫入。I2C驅(qū)動(dòng)核心就是把握好SCL/SCK的時(shí)序用計(jì)數(shù)器產(chǎn)生符合時(shí)鐘頻率的時(shí)序。SCL頻率建議用100kHz低速穩(wěn)定別追求400kHz。下面是一個(gè)寫字節(jié)狀態(tài)機(jī)的核心框架// I2C寫一個(gè)字節(jié)返回ack信號(hào) // 狀態(tài)IDLE - START - SHIFT8 - ACK - STOP always (posedge clk_i2c or negedge rst_n) begin if (!rst_n) begin state IDLE; end else begin case (state) IDLE: if (start_req) begin scl 1b0; // 準(zhǔn)備發(fā)起起始 state START; end START: begin sda_out 1b0; // 起始條件SCL高時(shí)SDA拉低 state SHIFT8; end SHIFT8: begin // 每個(gè)bit先SDA賦值再SCL拉高再拉低 // 用bit_cnt控制8個(gè)bit end ACK: state STOP; STOP: state IDLE; endcase end end注意一點(diǎn)寫OV5640寄存器時(shí)每個(gè)寄存器寫完后最好加1ms左右延時(shí)等內(nèi)部生效。尤其PLL、分辨率相關(guān)的寄存器改完不能馬上接著寫下一個(gè)否則配置容易丟。初始化寄存器表網(wǎng)上有很多公開模板可以直接搜索“OV5640 register init value”或者參考OV5640的驅(qū)動(dòng)源碼。關(guān)鍵配置項(xiàng)包括輸出分辨率0x3800~0x3814區(qū)間的時(shí)序參數(shù)、輸出格式0x4300設(shè)為0x6F對應(yīng)RGB565、0x60對應(yīng)RGB888、PLL分頻0x3035/0x3036/0x3037、鏡像翻轉(zhuǎn)0x3820/0x3821、自動(dòng)曝光和自動(dòng)白平衡開關(guān)0x3212等。別自己憑感覺改對照datasheet的寄存器map逐個(gè)核對。3.2 DVP采集模塊VSYNC、HREF、PCLK三個(gè)信號(hào)怎么配合OV5640的DVP接口有三個(gè)關(guān)鍵信號(hào)VSYNC是幀同步一幀開始和結(jié)束的標(biāo)志HREF是行有效高電平期間每個(gè)PCLK上升沿對應(yīng)一個(gè)有效數(shù)據(jù)字節(jié)PCLK是像素時(shí)鐘采樣邊沿一般是上升沿。采集模塊的Verilog邏輯其實(shí)不復(fù)雜難點(diǎn)在于處理RGB565的字節(jié)拼接。OV5640在RGB565輸出模式下一個(gè)像素16bit分兩個(gè)PCLK周期輸出每個(gè)周期8bit。默認(rèn)順序通常是先高字節(jié)后低字節(jié)但具體順序取決于寄存器配置。// 采集模塊核心邏輯偽代碼 // 兩級觸發(fā)器打拍同步避免亞穩(wěn)態(tài) reg vsync_d1, vsync_d2; reg href_d1, href_d2; reg pclk_d1, pclk_d2; always (posedge clk) begin vsync_d1 vsync; vsync_d2 vsync_d1; href_d1 href; href_d2 href_d1; pclk_d1 pclk; pclk_d2 pclk_d1; end // PCLK上升沿采樣數(shù)據(jù) always (posedge pclk_d2 or negedge rst_n) begin if (!rst_n) begin byte_cnt 2d0; rgb_data 16d0; end else if (href_d2) begin case (byte_cnt) 2d0: begin rgb_data[15:8] din; byte_cnt 2d1; end 2d1: begin rgb_data[7:0] din; // 一個(gè)像素湊齊可以寫入FIFO fifo_wr_data {rgb_data[15:8], din}; fifo_wr_req 1b1; byte_cnt 2d0; end endcase end else begin byte_cnt 2d0; fifo_wr_req 1b0; end end這里有個(gè)很常見的坑data是8位還是10位。OV5640有10根數(shù)據(jù)線D[9:0]如果只用RGB565通常取D[9:2]作為8位數(shù)據(jù)但不同模組/配置可能不同要在實(shí)際調(diào)試時(shí)用邏輯分析儀確認(rèn)一下。如果發(fā)現(xiàn)圖像顏色間隔出現(xiàn)“很規(guī)律的雜點(diǎn)”優(yōu)先懷疑數(shù)據(jù)位取錯(cuò)或字節(jié)順序反了。VSYNC的作用是同步幀的啟停一般用下降沿開始計(jì)數(shù)在下一幀VSYNC前完成一行行采集。用ILA或者板載LED把VSYNC和HREF引出來觀察是很有用的調(diào)試手段。3.3 跨時(shí)鐘域與行緩存FIFO深度怎么算攝像頭輸出的PCLK和顯示器側(cè)的像素時(shí)鐘不是同一個(gè)域兩者頻率和相位都不同步直接用組合邏輯互相采必然出現(xiàn)亞穩(wěn)態(tài)和花屏。正確做法是異步FIFO隔離。FIFO的寫時(shí)鐘是PCLK讀時(shí)鐘是顯示像素時(shí)鐘數(shù)據(jù)寬度16bit深度要按“一行像素?cái)?shù)裕量”來算。以720p為例一行1280像素每像素16bit理論上深度2048就夠存一行。但考慮到FIFO讀寫同時(shí)進(jìn)行讀側(cè)可能有突發(fā)延遲建議深度取4096。EP4CE10內(nèi)部RAM只有414Kbit4096×16只有64Kbit完全放得下。// Quartus/Altera FIFO IP核例化 fifo_16x4096 u_fifo ( .wrclk(pclk_d2), // 寫時(shí)鐘來自攝像頭 .wrreq(fifo_wr_req), // HREF有效期間像素寫入請求 .data({hi_byte, lo_byte}), // 16bit RGB565 .rdclk(pixel_clk), // 讀時(shí)鐘來自顯示時(shí)序PLL .rdreq(fifo_rd_req), // DE有效時(shí)請求讀取 .q(display_rgb), // 讀出的像素?cái)?shù)據(jù) .wrfull(), .rdempty(rd_empty) );讀側(cè)的控制邏輯比較簡單顯示時(shí)序模塊在DE高電平期間每個(gè)像素時(shí)鐘讀一次FIFO讀到的數(shù)據(jù)直接送去HDMI芯片。但要注意FIFO空標(biāo)志如果DE期間讀空了就直接輸出黑色像素0x0000避免數(shù)據(jù)錯(cuò)位。這個(gè)方案的局限在于沒有真正的幀同步。寫側(cè)跟著攝像頭的VSYNC節(jié)奏讀側(cè)跟著顯示器時(shí)序兩者互相獨(dú)立可能出現(xiàn)“寫到一半被讀走”的情況畫面在某一幀會(huì)有一條水平的撕裂線。解決撕裂的辦法一般有用雙口RAM做兩行乒乓緩存或者干脆外掛SDRAM做多幀緩存。入門階段先接受這個(gè)缺陷重點(diǎn)是把圖像穩(wěn)定顯示出來。3.4 顯示時(shí)序與HDMI發(fā)送從DE到TMDS顯示時(shí)序生成是FPGA里最經(jīng)典的“數(shù)數(shù)”模塊沒有技術(shù)難點(diǎn)只有細(xì)心。以720p60為例標(biāo)準(zhǔn)時(shí)序參數(shù)如下參數(shù)值像素時(shí)鐘74.25MHz水平有效1280H前臺(tái)HFP110水平同步HSYNC40H后臺(tái)HBP220水平總數(shù)1650垂直有效720V前臺(tái)VFP5垂直同步VSYNC5V后臺(tái)VBP20垂直總數(shù)750用兩個(gè)計(jì)數(shù)器分別數(shù)行和場在有效區(qū)域內(nèi)拉高DE輸出RGB數(shù)據(jù)在同步區(qū)間輸出對應(yīng)的HSYNC/VSYNC電平代碼不復(fù)雜。// 行同步計(jì)數(shù)器示例 reg [10:0] h_cnt; reg [9:0] v_cnt; always (posedge pixel_clk or negedge rst_n) begin if (!rst_n) begin h_cnt 0; end else if (h_cnt H_TOTAL - 1) begin h_cnt 0; end else begin h_cnt h_cnt 1b1; end end assign hsync (h_cnt H_SYNC_START) (h_cnt H_SYNC_END); assign de (h_cnt H_ACTIVE_START) (h_cnt H_ACTIVE_END) (v_cnt V_ACTIVE_START) (v_cnt V_ACTIVE_END);如果用的是RGB轉(zhuǎn)HDMI芯片F(xiàn)PGA這邊的工作到這里就結(jié)束了直接把DE、HSYNC、VSYNC、RGB數(shù)據(jù)、PCLK接給芯片即可。芯片內(nèi)部會(huì)編碼成TMDS并串行輸出。如果是自己編碼TMDS需要實(shí)現(xiàn)DVI的編碼器每個(gè)通道把8bit數(shù)據(jù)經(jīng)過編碼變成10bit三個(gè)通道分別處理R、G、B再加上一個(gè)時(shí)鐘通道。編碼規(guī)則不復(fù)雜基于異或/異或非維護(hù)DC平衡但寫起來代碼量大而且需要把數(shù)據(jù)按10bit并行寬度經(jīng)OSERDES或DDR輸出寄存器轉(zhuǎn)成串行。EP4CE10沒有專門的OSERDES一般用兩個(gè)寄存器配合IO clock實(shí)現(xiàn)DDR輸出。這個(gè)方案我建議作為進(jìn)階挑戰(zhàn)別和主線混在一起做。4. 調(diào)試實(shí)錄與常見問題排查4.1 上電黑屏九成問題出在這三處拿到板子第一次上電顯示器沒畫面這是必然的別慌。按順序排查三處攝像頭有沒有正常工作、SCCB配置有沒有成功、HDMI側(cè)有沒有被識(shí)別。第一攝像頭是否工作。用示波器或邏輯分析儀看PCLK引腳有沒有波形正常配置后PCLK應(yīng)該有穩(wěn)定的時(shí)鐘輸出。如果PCLK沒有波形檢查MCLK有沒有送到RESET和PWDN的電平對不對。很多OV5640模組的PWDN引腳要拉低才工作拉高是掉電模式這個(gè)特別容易踩。第二SCCB配置是否成功。把FPGA的I2C驅(qū)動(dòng)中每個(gè)寫操作的ACK信號(hào)引出來如果連續(xù)出現(xiàn)NACK說明從機(jī)地址不對或者物理連接有問題。我曾遇到一個(gè)模組寫地址是0x21而不是0x3C結(jié)果怎么配都沒反應(yīng)。物理層方面SIO_D要接上拉電阻有些模組自帶上拉有些沒有需要自己補(bǔ)否則時(shí)鐘根本沒波形。第三HDMI是否被識(shí)別。用顯示器切換輸入源如果能看到“無信號(hào)”變成“信號(hào)輸入中”甚至花屏變綠說明HDMI鏈路已經(jīng)建立問題在數(shù)據(jù)端如果始終無信號(hào)檢查HPD引腳是否拉高、5V是否正常、TMDS時(shí)鐘通道有沒有輸出。這是黑屏問題里最容易混淆的一環(huán)很多人糾結(jié)半天攝像頭結(jié)果其實(shí)是HDMI芯片的電平配置不對。4.2 花屏、錯(cuò)位、撕裂的排查順序畫面已經(jīng)有了但“不對”這類問題比黑屏好定位得多通常是下面幾個(gè)原因。顏色偏色、整體發(fā)綠發(fā)紫先查RGB數(shù)據(jù)位接錯(cuò)或者字節(jié)順序拼反。比如RGB565的低字節(jié)和高字節(jié)交換圖像整體色調(diào)就會(huì)偏移。再查輸出格式寄存器是否真的配成了RGB565如果攝像頭實(shí)際輸出YUV422但按RGB565解讀顏色就會(huì)明顯不對。圖像錯(cuò)位、斜切面多半是行場同步時(shí)序參數(shù)和實(shí)際輸出分辨率不匹配。把顯示時(shí)序的H_TOTAL、V_TOTAL和有效區(qū)間參數(shù)逐項(xiàng)核對特別是HFP/HBP是否是0。HDMI芯片對各個(gè)時(shí)序區(qū)間有一定容錯(cuò)但DE與HSYNC的相對位置不對畫面就會(huì)沿對角線撕裂。橫向撕裂這是行緩存方案固有的問題攝像頭VSYNC和顯示器VSYNC不同步。想徹底消除只能上SDRAM幀緩存。臨時(shí)緩解辦法是降低幀率比如把攝像頭輸出調(diào)到30fps以下撕裂會(huì)不那么明顯。整屏雪花一般是TMDS數(shù)據(jù)鏈路問題。如果用轉(zhuǎn)接芯片查PCLK是否干凈、復(fù)位是否穩(wěn)定、RGB數(shù)據(jù)位寬和芯片配置是否一致。如果自己編碼TMDS優(yōu)先懷疑輸出IO沒有正確約束為差分標(biāo)準(zhǔn)或者時(shí)鐘通道沒有輸出。4.3 Quartus II實(shí)操細(xì)節(jié)Pin Planner與時(shí)序約束工程跑不通很多不是邏輯問題而是工程配置問題。幾個(gè)高頻坑點(diǎn)Pin Planner里必須把攝像頭的PCLK、FIFO讀時(shí)鐘、HDMI像素時(shí)鐘明確分配在可用的全局時(shí)鐘引腳或PLL輸入/輸出專用引腳上否則綜合時(shí)會(huì)報(bào)錯(cuò)或者時(shí)序收斂不了。添加時(shí)序約束是必須的。至少在Synopsys Design Constraints里寫create_clock -name clk_in -period 20.000 [get_ports {clk}] create_clock -name pclk -period 20.833 [get_ports {pclk}]PCLK來自攝像頭不是FPGA內(nèi)部時(shí)鐘但Quartus也需要知道它的周期才能對跨時(shí)鐘域FIFO的路徑做分析。如果你用SignalTap做在線調(diào)試探針信號(hào)不要跨太多時(shí)鐘域否則采樣數(shù)據(jù)亂飛很難分辨是邏輯錯(cuò)還是采樣錯(cuò)。還有一點(diǎn)Cyclone IV的PLL配置界面里M/N設(shè)置完之后看實(shí)際輸出頻率和相位確認(rèn)沒有超規(guī)格。MCLK給24MHz時(shí)最好用“時(shí)鐘使能”方式去做而不是拿一個(gè)高速PLL輸出直接當(dāng)MCLK否則攝像頭輸入時(shí)鐘毛刺會(huì)很重。4.4 經(jīng)驗(yàn)清單一次跑通的關(guān)鍵習(xí)慣項(xiàng)目做了幾輪之后我總結(jié)出幾個(gè)值得養(yǎng)成的習(xí)慣照做能省掉大量反復(fù)折騰的時(shí)間。先跑測試彩條再接攝像頭。把顯示時(shí)序模塊獨(dú)立驗(yàn)證一下用FPGA內(nèi)部產(chǎn)生彩條數(shù)據(jù)填到RGB輸出如果顯示器能顯示彩條說明HDMI鏈路和顯示時(shí)序沒問題問題范圍就縮小到采集側(cè)。這個(gè)步驟特別重要能直接把你從“全鏈路黑屏”里解放出來。攝像頭配置分階段驗(yàn)證。不要一次性寫入幾百條寄存器先把I2C驅(qū)動(dòng)跑通然后只寫基礎(chǔ)配置輸出格式、分辨率、時(shí)鐘看能不能出動(dòng)態(tài)畫面再逐步加AWB、曝光、飽和度等寄存器。這樣出了問題能快速定位是哪一組配置引起的。善用邏輯分析儀。如果板子沒有預(yù)留測試點(diǎn)可以用SignalTap把VSYNC、HREF、PCLK、FIFO寫請求、讀請求這些內(nèi)部信號(hào)引出來截圖保存。肉眼對著波形看比猜有效得多。我曾經(jīng)因?yàn)镕IFO的rdreq信號(hào)在DE期間偶爾沒拉高導(dǎo)致一行像素缺了十幾個(gè)點(diǎn)看著就是一條細(xì)豎線要不是看波形根本找不到。對EP4CE10這種資源緊張的芯片編譯設(shè)置上把優(yōu)化策略改成Area也能省不少資源。不過別過度優(yōu)化如果邏輯都快放不下了優(yōu)先考慮簡化設(shè)計(jì)比如把RGB888改成RGB565或者降低分辨率。做完這個(gè)項(xiàng)目之后我對“攝像頭采集HDMI顯示”這類系統(tǒng)心里就有底了再去看算法加速、目標(biāo)檢測這些進(jìn)階方向至少不會(huì)被數(shù)據(jù)通路卡住。我的建議是不要急著加各種圖像處理算法先把這條通路跑到穩(wěn)定真正理解每個(gè)信號(hào)為什么存在、每個(gè)時(shí)序?yàn)槭裁催@么定然后再去玩濾波、邊緣檢測、灰度轉(zhuǎn)換你會(huì)突然發(fā)現(xiàn)那些算法其實(shí)都不難。本文還有配套的精品資源點(diǎn)擊獲取