動(dòng)OV5640實(shí)現(xiàn)UDP視頻傳輸:從采集到網(wǎng)絡(luò)上傳的完整方案)
簡介面向 ZYNQ 7010 嵌入式開發(fā)者與 FPGA 工程師這是一份圍繞 OV5640 攝像頭視頻采集、UDP 網(wǎng)絡(luò)上傳的完整硬件驅(qū)動(dòng)資源可直接用于熟悉 SoC 軟硬件協(xié)同設(shè)計(jì)解決從傳感器接入到網(wǎng)絡(luò)輸出的鏈路搭建問題。壓縮包共 562 個(gè)文件約 37.88MB主要包含 VHDL/Verilog 驅(qū)動(dòng)源碼、XDC 約束文件、DCP 網(wǎng)表、RPT 綜合報(bào)告、Tcl 和 SH 工程腳本、文檔及日志類型覆蓋邏輯設(shè)計(jì)、綜合實(shí)現(xiàn)、仿真與上板調(diào)試各環(huán)節(jié)。目前已有 178 人學(xué)習(xí)下載。通過工程源碼與目錄結(jié)構(gòu)讀者可以理解 ARM 與 FPGA 的分工配合、OV5640 時(shí)序配置、VDMA 數(shù)據(jù)搬運(yùn)、UDP 打包發(fā)送等關(guān)鍵模塊并根據(jù)示例腳本快速復(fù)現(xiàn)實(shí)驗(yàn)、修改分辨率和網(wǎng)絡(luò)參數(shù)用于視頻監(jiān)控、圖像采集和遠(yuǎn)程傳輸類場景。1. ZYNQ 7010驅(qū)動(dòng)OV5640采集UDP通信上傳視頻一塊板子打通圖像采集與網(wǎng)絡(luò)傳輸做過圖像采集的工程師大多有過這種經(jīng)歷攝像頭采集端和網(wǎng)絡(luò)傳輸端各有一套邏輯調(diào)試時(shí)兩邊分開跑都正常一旦接起來就丟幀、花屏、帶寬上不去。ZYNQ 7010驅(qū)動(dòng)OV5640采集UDP通信上傳視頻這個(gè)項(xiàng)目本質(zhì)上是把“FPGA負(fù)責(zé)采集與緩存、ARM負(fù)責(zé)協(xié)議棧與網(wǎng)絡(luò)發(fā)送”這條經(jīng)典分工路徑走通。PL端用Verilog把OV5640輸出的DVP并行數(shù)據(jù)流收下來經(jīng)過幀同步、行緩沖后寫入DDR3PS端通過lwIP協(xié)議棧建立UDP socket把DDR3里的視頻幀按MTU切片發(fā)出。這里的關(guān)鍵不是“能不能通”而是“在7010這個(gè)資源受限的平臺(tái)上怎樣分配DMA通道和緩存策略才不丟幀”。對于剛接觸ZYNQ圖像傳輸?shù)拈_發(fā)者這套方案能幫你理解AXI總線上VDMA的帶寬占用模型對于有經(jīng)驗(yàn)的人參數(shù)設(shè)計(jì)和異常排查也有可借鑒的細(xì)節(jié)。2. 采集鏈路總體設(shè)計(jì)DVP接口、VDMA與DDR3緩存的映射關(guān)系2.1 為什么選擇DVP接口而非MIPIOV5640同時(shí)支持DVP和MIPI兩種輸出接口在ZYNQ 7010上絕大多數(shù)設(shè)計(jì)選用DVP。原因在于7010的PL端沒有原生MIPI CSI-2控制器實(shí)現(xiàn)MIPI接收需要額外的Lane管理邏輯和Byte對齊處理復(fù)雜度明顯上升。DVP接口的信號(hào)包括PCLK像素時(shí)鐘、HREF行有效、VSYNC幀同步、DATA[7:0]8位并行數(shù)據(jù)時(shí)序上更接近傳統(tǒng)CMOS sensor的工作方式直接用Verilog狀態(tài)機(jī)就能完成采集。圖像格式方面常見的配置是RGB565或YUV422分辨率支持從VGA到1080p。工程里最常用的組合是720p30fps RGB565原因是這個(gè)組合下數(shù)據(jù)率適中1280×720×2字節(jié)×30fps ≈ 55MB/sDDR3帶寬余量充足UDP也能在千兆網(wǎng)下不丟包地發(fā)出去。OV5640寄存器0x3035和0x3036用于設(shè)置PLL分頻0x3808~0x380B設(shè)定輸出尺寸這些都需要在上電初始化時(shí)通過I2C寫入。// OV5640 DVP接口信號(hào)連接示例XDC約束節(jié)選 set_property PACKAGE_PIN W13 [get_ports cam_pclk] // PCLK set_property IOSTANDARD LVCMOS33 [get_ports cam_pclk] set_property PACKAGE_PIN W12 [get_ports cam_vsync] // VSYNC set_property PACKAGE_PIN U12 [get_ports cam_href] // HREF set_property PACKAGE_PIN V12 [get_ports cam_data[0]] // DATA02.2 VDMA在數(shù)據(jù)傳輸中的角色VDMAVideo Direct Memory Access是Xilinx提供的視頻專用DMA IP核支持S2MMStream to Memory-Map和MM2SMemory-Map to Stream兩個(gè)方向。在采集鏈路中OV5640輸出的像素流經(jīng)過自定義Verilog邏輯轉(zhuǎn)換后接入VDMA的S2MM通道由VDMA負(fù)責(zé)把數(shù)據(jù)寫入DDR3指定地址。之所以不直接用普通DMA是因?yàn)閂DMA內(nèi)置了幀同步邏輯可以根據(jù)VSYNC信號(hào)自動(dòng)完成幀指針切換無需PS端軟件干預(yù)。VDMA參數(shù)配置中需要關(guān)注三個(gè)寄存器S2MM_FRMDLY_STRIDE幀延遲與行距、S2MM_FRMBUF_CNT幀緩沖數(shù)量和S2MM_HSIZE行像素?cái)?shù)。幀緩沖數(shù)量決定能緩存幾幀三緩沖最穩(wěn)妥——一幀在寫、一幀在讀、一幀在等即使UDP發(fā)送稍有抖動(dòng)也不容易覆蓋正在傳輸?shù)臄?shù)據(jù)。// VDMA S2MM寄存器配置示例PS端通過AXI-Lite寫入 #define VDMA_BASE 0x43000000 #define S2MM_HSIZE 0x0100 // 行像素?cái)?shù)720p 1280 #define S2MM_STRIDE 0x0104 // stride 行像素 × 2字節(jié) #define S2MM_FRMDLY_STRIDE 0x0108 // bit[13:0]幀延遲, bit[29:16]stride #define S2MM_START_ADDR 0x00AC // 起始地址寄存器 #define S2MM_FRMBUF_CNT 0x0120 // 幀緩沖數(shù)量 Xil_Out32(VDMA_BASE S2MM_HSIZE, 1280); Xil_Out32(VDMA_BASE S2MM_STRIDE, 1280 * 2); Xil_Out32(VDMA_BASE S2MM_FRMDLY_STRIDE, (720 16) | 0); Xil_Out32(VDMA_BASE S2MM_FRMBUF_CNT, 3); // 三緩沖 Xil_Out32(VDMA_BASE 0x0000, 0x0003); // S2MM復(fù)位后啟動(dòng)2.3 帶寬測算與DDR3分配策略ZYNQ 7010的DDR3總帶寬約4.2GB/s32位DDR3-1066但這是理論峰值。實(shí)際有效帶寬受刷新開銷、總線仲裁和訪問模式影響通常打六折。視頻鏈路占用情況需要分三塊看VDMA S2MM寫入、VDMA MM2S讀取如果PS端直接讀取DMA buffer也需要算入、PS CPU訪問DDR。分辨率與帶寬對應(yīng)關(guān)系如下分辨率格式幀率數(shù)據(jù)率DDR3占用率估算640×480RGB56530fps18.4MB/s0.4%1280×720RGB56530fps55.3MB/s1.3%1920×1080RGB56530fps124.4MB/s2.9%1920×1080YUV42230fps124.4MB/s2.9%帶寬不是瓶頸真正的瓶頸在于VDMA中斷頻率與UDP發(fā)送速率的匹配。720p30fps意味著每幀33msVDMA在幀完成時(shí)產(chǎn)生一次中斷PS端要在33ms內(nèi)把這一幀全部發(fā)出去。千兆以太網(wǎng)理論速率125MB/s扣掉UDP/IP頭開銷后實(shí)際可用約110MB/s發(fā)一幀720p大小的數(shù)據(jù)約1.8MB需要約16ms。這個(gè)時(shí)間小于幀間隔所以單看網(wǎng)絡(luò)也能跟上。但如果中斷響應(yīng)不及時(shí)或者lwIP的PBUF分配不當(dāng)就會(huì)出現(xiàn)畫面停頓。3. OV5640寄存器配置與Verilog采集邏輯的實(shí)現(xiàn)3.1 I2C初始化中的關(guān)鍵寄存器組合OV5640上電后默認(rèn)輸出SXGA1280×96015fps不會(huì)自動(dòng)輸出720p 30fps必須通過I2C配置。初始化序列通常包含幾十個(gè)寄存器寫入核心分為三組時(shí)鐘配置、輸出格式配置、時(shí)序配置。時(shí)鐘配置決定PCLK頻率輸出格式?jīng)Q定字節(jié)順序是RGB565還是YUV422時(shí)序配置決定HREF和VSYNC的相對位置直接影響采集邏輯的幀判斷。初始化代碼一般寫成數(shù)組表驅(qū)動(dòng)每條記錄包含寄存器地址和值。寫完后需要做一次軟件復(fù)位0x3103 0x03然后等待至少50ms讓sensor內(nèi)部PLL鎖定。判斷初始化是否成功的標(biāo)準(zhǔn)不是I2C寫操作無報(bào)錯(cuò)而是VSYNC信號(hào)是否有穩(wěn)定脈沖——用ILAIntegrated Logic Analyzer抓一下最直接。// OV5640初始化關(guān)鍵寄存器配置C語言數(shù)組節(jié)選 // 使用Xilinx IIC控制器IP地址0x3C7位地址 static unsigned char ov5640_init_regs[][2] { {0x3103, 0x11}, // 系統(tǒng)時(shí)鐘使能 {0x3008, 0x82}, // 軟復(fù)位需等待50ms {0x3103, 0x03}, // 解除復(fù)位 {0x3035, 0x21}, // PLL分頻 {0x3036, 0x46}, // PLL倍頻決定PCLK {0x3808, 0x05}, // 輸出高度高字節(jié) → 720p設(shè)置 {0x3809, 0xD0}, // 輸出高度低字節(jié) {0x380A, 0x02}, // 輸出寬度高字節(jié) {0x380B, 0x80}, // 輸出寬度低字節(jié) {0x4300, 0x30}, // RGB565格式RGB順序 {0x501F, 0x01}, // RGB輸出使能 };3.2 像素同步邏輯與行場標(biāo)志提取Verilog采集邏輯的核心是一個(gè)狀態(tài)機(jī)負(fù)責(zé)把PCLK驅(qū)動(dòng)的串行像素流整理成VDMA期望的AXI4-Stream格式。DVP時(shí)序中VSYNC下降沿表示一幀開始HREF高電平期間每個(gè)PCLK上升沿對應(yīng)一個(gè)有效像素。常見錯(cuò)誤是把VSYNC當(dāng)作幀有效信號(hào)直接用忽略了sensor在VSYNC前后會(huì)有若干行無效數(shù)據(jù)。我做這個(gè)邏輯時(shí)習(xí)慣的做法是先檢測VSYNC有效沿然后等待HREF第一次拉高后再開始計(jì)數(shù)這樣能跳過幀消隱區(qū)。另一個(gè)容易被忽略的細(xì)節(jié)是數(shù)據(jù)對齊。RGB565模式下OV5640輸出的是一個(gè)像素兩個(gè)字節(jié)但DVP是8位接口所以字節(jié)順序是低字節(jié)在前還是高字節(jié)在前由寄存器0x4300控制而Verilog側(cè)要做對應(yīng)的字節(jié)拼接。推薦的做法是在采集邏輯內(nèi)部先把兩個(gè)字節(jié)拼成16位再送入FIFO避免在VDMA側(cè)做字節(jié)交換。// OV5640數(shù)據(jù)采集Verilog邏輯核心代碼 reg [15:0] pixel_data; reg pixel_valid; reg [1:0] byte_cnt; always (posedge cam_pclk or negedge rst_n) begin if (!rst_n) begin byte_cnt 2d0; pixel_valid 1b0; end else if (cam_href byte_cnt 2d0) begin pixel_data[7:0] cam_data; // 第一個(gè)字節(jié)存低位 byte_cnt 2d1; pixel_valid 1b0; end else if (cam_href byte_cnt 2d1) begin pixel_data[15:8] cam_data; // 第二個(gè)字節(jié)存高位 byte_cnt 2d0; pixel_valid 1b1; // 16位像素拼好后有效 end else begin byte_cnt 2d0; pixel_valid 1b0; end end這段邏輯把兩個(gè)連續(xù)的8位數(shù)據(jù)拼成RGB565像素pixel_valid作為寫使能接入FIFO。byte_cnt在HREF為低時(shí)強(qiáng)制歸零確保每行從對齊位置開始拼接不會(huì)出現(xiàn)跨行錯(cuò)位。實(shí)際調(diào)試中如果圖像出現(xiàn)整體偏色或左右錯(cuò)位先檢查byte_cnt的復(fù)位邏輯是否綁定了HREF——這比檢查寄存器配置更常見。3.3 AXI4-Stream數(shù)據(jù)通路與FIFO深度選擇從像素拼接到VDMA之間需要插一個(gè)異步FIFO原因有二PCLK來自O(shè)V5640VDMA接口時(shí)鐘來自PL邏輯兩側(cè)頻率不同像素?cái)?shù)據(jù)是連續(xù)背靠背的而VDMA在空閑時(shí)會(huì)暫停需要一個(gè)緩沖吸收瞬時(shí)突發(fā)。FIFO深度選擇上720p一行1280個(gè)像素深度設(shè)2048合適不需要更大。深度過深會(huì)引入額外延遲而FIFO過半就會(huì)置位prog_full反而影響實(shí)時(shí)性。FIFO輸出側(cè)使用Xilinx的axis_data_fifo IP配置為標(biāo)準(zhǔn)AXI4-Stream接口數(shù)據(jù)寬度16位深度2048不啟用寄存器輸出。這樣VDMA的S2MM通道可以直接掛接不需要額外的AXI協(xié)議轉(zhuǎn)換邏輯。注意axis_data_fifo的tlast信號(hào)必須正確——VDMA用tlast判斷一行結(jié)束如果這個(gè)信號(hào)沒有在每行最后一個(gè)像素時(shí)拉高VDMA會(huì)一直在等數(shù)據(jù)導(dǎo)致幀完成中斷永遠(yuǎn)不產(chǎn)生。4. PS端UDP發(fā)送程序設(shè)計(jì)lwIP協(xié)議棧與VDMA中斷配合4.1 ZYNQ PS端以太網(wǎng)與lwIP的環(huán)境準(zhǔn)備PS端的UDP發(fā)送建立在雙核ARM Cortex-A9上常用的協(xié)議棧是lwIP。選擇lwIP而不是裸寫MAC驅(qū)動(dòng)是因?yàn)樗谔幚鞩P分片、校驗(yàn)和計(jì)算和PBUF內(nèi)存管理方面已經(jīng)有成熟實(shí)現(xiàn)雖然性能比不上帶TCP卸載引擎的方案但在視頻傳輸場景下UDP協(xié)議本身夠輕瓶頸不在協(xié)議處理而在內(nèi)存拷貝。Xilinx SDK里自帶lwIP庫模板選擇“l(fā)wIP Echo Server”模板后在此基礎(chǔ)上改造即可。帶寬相關(guān)參數(shù)需要調(diào)整lwIP的MEMP_NUM_PBUFPBUF數(shù)量、PBUF_POOL_SIZEPBUF池大小和UDP發(fā)送buffer。PBUF_POOL_SIZE太小時(shí)發(fā)送速度一快就會(huì)出現(xiàn)“No free pbufs”報(bào)錯(cuò)導(dǎo)致丟包。對于720p30fps實(shí)測PBUF_POOL_SIZE設(shè)為32以上比較穩(wěn)妥每個(gè)PBUF對應(yīng)一個(gè)UDP包1472字節(jié) payload16ms的發(fā)幀窗口內(nèi)需要約1300個(gè)包PBUF是復(fù)用還是新建會(huì)影響性能。// lwIP UDP發(fā)送初始化代碼基于Xilinx SDK的lwIP庫 struct udp_pcb *udp_pcb_inst; struct pbuf *p; err_t err; ip_addr_t remote_ip; IP4_ADDR(remote_ip, 192, 168, 1, 100); // 接收端IP udp_pcb_inst udp_new(); err udp_bind(udp_pcb_inst, IP_ADDR_ANY, 35000); // 本地端口 udp_connect(udp_pcb_inst, remote_ip, 35001); // 遠(yuǎn)端端口 // 幀發(fā)送函數(shù)在VDMA中斷回調(diào)中調(diào)用 void send_frame(u32 frame_addr, u32 frame_size) { u32 offset 0; u32 pkt_cnt; u32 i; // 按MTU切片發(fā)送1472字節(jié)對應(yīng)1500MTU pkt_cnt (frame_size 1471) / 1472; for (i 0; i pkt_cnt; i) { p pbuf_alloc(PBUF_TRANSPORT, 1472, PBUF_POOL); u32 copy_len (frame_size - offset) 1472 ? 1472 : (frame_size - offset); memcpy(p-payload, (void *)(frame_addr offset), copy_len); p-len copy_len; udp_send(udp_pcb_inst, p); pbuf_free(p); offset copy_len; } }這段代碼的關(guān)鍵是每一次udp_send調(diào)用都對應(yīng)一個(gè)UDP包包內(nèi)payload的數(shù)據(jù)是一幀圖像連續(xù)內(nèi)存的切片。每次發(fā)送前用pbuf_alloc分配新PBUF發(fā)送后立即釋放保證內(nèi)存池不被耗盡。如果接收端Wireshark抓包發(fā)現(xiàn)大量連續(xù)重復(fù)的包序號(hào)通常是這里的內(nèi)存復(fù)用出了問題。繼續(xù)看注意udp_send的返回值當(dāng)返回ERR_MEM時(shí)說明PBUF池不足需要降低分辨率和幀率或者增加PBUF池大小。4.2 VDMA中斷與幀指針回讀PS端程序的工作流程是初始化VDMA → 啟動(dòng)S2MM通道 → 注冊幀完成中斷處理器 → 在中斷回調(diào)中讀取幀地址并發(fā)送。VDMA的幀完成中斷會(huì)攜帶幀計(jì)數(shù)器可以據(jù)此判斷當(dāng)前是哪一幀完成。中斷回調(diào)函數(shù)內(nèi)要做的第一件事是清中斷標(biāo)志S2MM_INT_CLR寄存器寫入再通過S2MM_CURDESC寄存器讀取當(dāng)前寫指針。幀地址計(jì)算有一個(gè)容易出錯(cuò)的地方VDMA的三緩沖地址不是簡單的“基地址 幀大小×N”而是由S2MM_START_ADDR寄存器依次指定的。如果基地址設(shè)為0x10000000、幀大小為1280×720×2字節(jié)那么地址安排應(yīng)為0x10000000、0x10168000、0x102D0000也就是每幀1.4MB對齊。中斷回調(diào)中需要記錄當(dāng)前完成的幀序號(hào)下一次啟動(dòng)采集后應(yīng)沿用到下一個(gè)緩沖否則會(huì)讀重復(fù)幀。// VDMA幀完成中斷回調(diào) void vdma_isr_handler(void *callback_ref) { u32 intr_status; u32 cur_frame; intr_status Xil_In32(VDMA_BASE 0x0054); // S2MM_INT_STATUS if (intr_status 0x01) { // 幀完成標(biāo)志 Xil_Out32(VDMA_BASE 0x0058, 0x01); // 清中斷 cur_frame Xil_In32(VDMA_BASE 0x00AC) 28; // 幀指針 // 根據(jù)幀指針計(jì)算實(shí)際發(fā)送地址 send_frame(frame_base_addr[cur_frame], frame_size); } }4.3 幀率匹配發(fā)送速度和采集速度不同步怎么辦采集端固定30fps但UDP發(fā)送端因?yàn)榫W(wǎng)絡(luò)阻塞、PC接收端處理不過來等原因偶發(fā)發(fā)送減慢是常態(tài)。當(dāng)發(fā)送一幀耗時(shí)超過33ms就會(huì)遇到下一幀采集完成但上一幀還沒發(fā)完的情況。這時(shí)數(shù)據(jù)競爭不可避免。實(shí)際項(xiàng)目中在發(fā)送前先查詢VDMA的當(dāng)前寫指針位置如果發(fā)現(xiàn)“要發(fā)送的地址”已經(jīng)被新一輪寫入覆蓋就跳幀。這個(gè)機(jī)制實(shí)現(xiàn)簡單而且比加鎖等待更不容易死鎖——視頻數(shù)據(jù)晚一點(diǎn)沒關(guān)系中斷住系統(tǒng)才可怕。為解決網(wǎng)絡(luò)擁堵帶來的持續(xù)丟幀可以在發(fā)送邏輯中增加一個(gè)簡單的節(jié)流閥記錄上一幀的發(fā)送耗時(shí)如果連續(xù)三幀都超過40ms就主動(dòng)降低發(fā)送幀率——隔一幀發(fā)一幀。這個(gè)降幀是純PS端行為PL端始終在采集DDR3緩存保證最老幀被覆蓋是最安全的。實(shí)踐中還有一個(gè)細(xì)節(jié)PC接收端收到的幀序不要用UDP序號(hào)判斷因?yàn)閁DP本身不保證有序。幀數(shù)據(jù)包頭前4字節(jié)寫入幀計(jì)數(shù)接收端按幀計(jì)數(shù)重組。5. UDP協(xié)議棧參數(shù)調(diào)整MTU、端口緩沖與校驗(yàn)和策略5.1 MTU與UDP包大小如何影響傳輸效率OV5640的一幀720p RGB565數(shù)據(jù)量為1280×720×21843200字節(jié)在1500字節(jié)MTU下需要拆成約1252個(gè)UDP包。MTU大小直接影響效率——包越大有效載荷占比越高但超過1500會(huì)被IP層分片分片后任一片丟失會(huì)導(dǎo)致整包重組失敗在無線環(huán)境和高丟包網(wǎng)絡(luò)中建議將UDP payload直接設(shè)為1400字節(jié)以內(nèi)。這里給出不同MTU下的對比表MTUUDP payload上限一幀包數(shù)量有效載荷占比適用場景15001472125297.4%有線局域網(wǎng)9000巨型幀897220699.4%千兆直連14001372134496.3%跨網(wǎng)段傳輸巨型幀要確保交換機(jī)和接收端網(wǎng)卡都開啟支持否則靜默丟棄導(dǎo)致黑屏。建議默認(rèn)使用1500字節(jié)MTU這個(gè)參數(shù)在大多數(shù)網(wǎng)絡(luò)環(huán)境都穩(wěn)定。5.2 校驗(yàn)和增大吞吐還是保數(shù)據(jù)正確UDP的校驗(yàn)和計(jì)算量不大但lwIP默認(rèn)開啟校驗(yàn)和驗(yàn)證會(huì)占用CPU時(shí)間。對于視頻傳輸且接收端丟包不影響幀因?yàn)閬G包本身就會(huì)出現(xiàn)馬賽克部分方案會(huì)關(guān)閉發(fā)送端校驗(yàn)和實(shí)測吞吐量約提升8-10%。關(guān)閉方法是在lwIP選項(xiàng)CHECKSUM_GEN_UDP中設(shè)置編譯開關(guān)或者在UDP PCB創(chuàng)建后調(diào)用udp_set_flags設(shè)置UDP_FLAGS_NOCHKSUM。不建議接收端關(guān)閉校驗(yàn)和因?yàn)榻邮斩诵r?yàn)和能幫你確認(rèn)是不是網(wǎng)線質(zhì)量問題。5.3 接收端緩沖區(qū)與播放實(shí)時(shí)性UDP接收端PC的socket接收緩沖區(qū)也需要同步調(diào)整。Windows下默認(rèn)15872字節(jié)約12個(gè)UDP包這點(diǎn)緩沖根本撐不住視頻流的突發(fā)到達(dá)速度會(huì)導(dǎo)致大量丟包。網(wǎng)絡(luò)調(diào)試助手一般可以設(shè)置接收緩沖大小改到256KB以上。Wireshark抓包時(shí)如果發(fā)現(xiàn)接收端窗口滿了的提示優(yōu)先懷疑是接收緩沖配置問題。兩側(cè)IP和端口保持對應(yīng)關(guān)系接好網(wǎng)線后第一步ping通再接視頻。6. 排錯(cuò)維度從無畫面到花屏再到高延遲的檢查順序6.1 無畫面用ILA先看VSYNC再看HREF無畫面是最常見的現(xiàn)象排查順序比排查手法更重要。先抓VSYNC信號(hào)沒有脈沖就是OV5640沒工作檢查I2C配置和sensor供電VSYNC正常但沒有HREF多半是輸出分辨率配置和實(shí)際不一致導(dǎo)致時(shí)序狀態(tài)機(jī)沒觸發(fā)。HREF正常但沒有數(shù)據(jù)用ILA看PCLK和數(shù)據(jù)線的連接查看數(shù)據(jù)位序是否有誤。// ILA觸發(fā)電平設(shè)置在Vivado Hardware Manager中配置 // Mark VSYNC觸發(fā)條件Rising edge // 在sensor初始化后抓一次驗(yàn)證幀頭很多工程師遇到無畫面直接去查UDP代碼方向錯(cuò)了。采集鏈路有明確的前后級關(guān)系先保證PL側(cè)采集通、DDR3里有圖再排查PS側(cè)。6.2 花屏/橫紋/偏色分別意味著什么現(xiàn)象可能原因排查手段圖像整體偏綠/偏紅RGB565字節(jié)順序反了調(diào)整寄存器0x4300的值水平方向顏色錯(cuò)位byte_cnt未在行首復(fù)位修改Verilog邏輯花屏但顏色基本正常VDMA幀地址計(jì)算錯(cuò)誤核對幀大小和基地址周期性橫條紋DDR3帶寬不足或FIFO溢出加大FIFO深度/降分辨率偏色是最好排查的因?yàn)榧拇嫫髋渲镁湍菐讉€(gè)字節(jié)順序選項(xiàng)。花屏如果伴隨幀序號(hào)跳躍優(yōu)先查VDMA的三緩沖地址配置是否連續(xù)——很多初版代碼地址對齊到幀大小但沒對齊到DDR的32字節(jié)邊界導(dǎo)致地址遞減時(shí)錯(cuò)位。6.3 Wireshark抓包驗(yàn)證UDP上傳的實(shí)際效果Wireshark在這條鏈路上不只是抓包工具它可以實(shí)時(shí)驗(yàn)證鏈路通斷和數(shù)據(jù)內(nèi)容。先用“udp.port 35001”過濾出目標(biāo)流然后檢查三個(gè)指標(biāo)包速是否接近理論值720p30fps約為1252×3037560pps、包長是否都在1472字節(jié)附近、接收端是否收到連續(xù)的幀計(jì)數(shù)。幀計(jì)數(shù)在payload的前4字節(jié)Wireshark的“Follow UDP Stream”不能直接解析但可以通過“視圖→數(shù)據(jù)包字節(jié)”面板定位到自定義幀頭。想計(jì)算前后兩包的時(shí)間間隔Wireshark支持列自定義表達(dá)式添加frame.time_delta_displayed為列查看兩包間隔是否穩(wěn)定在26μs附近千兆網(wǎng)1472字節(jié)的線速發(fā)送間隔。幀與幀之間要留出足夠的時(shí)間間隔。全速發(fā)送狀態(tài)下兩幀之間只間隔約16ms發(fā)幀耗時(shí)接收端如果來不及處理就會(huì)在驅(qū)動(dòng)層丟棄表現(xiàn)為花屏但不丟UDP包。解決辦法是在PS端發(fā)送完一幀后加一個(gè)2ms延時(shí)再發(fā)下一幀不影響幀率觀感但能明顯降低接收端壓力。本文還有配套的精品資源點(diǎn)擊獲取