使用 Ultrascal+ 高效實(shí)現(xiàn)具有圖像處理 IP 核的圖像處理流水線系統(tǒng)。
本項(xiàng)目用到的品臺(tái)
硬件組件
Digilent Genesys ZU ?×1(FPGA平臺(tái))Digilent PCAM5 ×1(MIPI攝像頭)
軟件組件
AMD-Xilinx Vivado 設(shè)計(jì)套件
介紹
從簡(jiǎn)單的嵌入式視覺到自動(dòng)駕駛汽車和無(wú)人機(jī),圖像處理是許多應(yīng)用的核心。Xilinx MPSoC 具有內(nèi)置的 DisplayPort(DP) 功能,并在可編程邏輯 IO 中支持 MIPI DPhy,從這兩方面看,這是一個(gè)出色的嵌入式視覺平臺(tái)。
同時(shí)使用人工智能和機(jī)器學(xué)習(xí)功能使我們能夠創(chuàng)建一個(gè)復(fù)雜的嵌入式視覺處理系統(tǒng)。
在這個(gè)項(xiàng)目中,我們將探索使用 PCAM(FMC擴(kuò)展板) 和 Display Port 建立和運(yùn)行圖像處理。然后,我們可以添加圖像處理 IP 內(nèi)核以進(jìn)一步展示FPGA的處理圖像的能力。
可編程邏輯設(shè)計(jì)(PL端設(shè)計(jì))
在我們的設(shè)計(jì)中,我們將包括以下 IP
Zynq MPSoC Processing System - 啟用 DisplayPort、I2C 和 GPIO EMIO
MIPI CSI2 RX Sub System - 從 PCAM 接收 MIPI CSI2 流。
Sensor Demosaic - 將 RAW 像素格式轉(zhuǎn)換為 RGB 像素格式。
Gamma LUT - gamma圖像校正
Video Timing Generator - 生成輸出視頻時(shí)序
AXIS to Video Out - 從 AXI 流轉(zhuǎn)換為視頻流
Clock Wizards - 用于生成視頻像素時(shí)鐘和 MIPI CSI2 參考時(shí)鐘
除了 IP,我們還需要考慮時(shí)鐘架構(gòu),對(duì)于這個(gè)解決方案,我們?cè)O(shè)計(jì)了以下時(shí)鐘樹:
AXI 時(shí)鐘 - 150 MHz - 這為 AXI Stream 和 AXI lite 接口提供時(shí)鐘
DPHY 參考時(shí)鐘 - 200 MHz - 由Clock Wizards生成
Pixel Clock - 74.25 Mhz - 用于 1280 x 720 60 FPS - 由Clock Wizards生成
完成的框圖應(yīng)如下所示,內(nèi)核配置如下所示。
為了控制和配置 PCAM5(FMC-MIPI擴(kuò)展板),我們使用 I2C 和 GPIO,GPIO 信號(hào)用于啟用 PCAM5 并為其供電。而 I2C 用于配置 PCAM5 本身。
我們可以使用EMIO GPIO將處理器 GPIO 擴(kuò)展到可編程邏輯。雖然 I2C 由 PS IIC0 提供,但是我們需要在應(yīng)用程序軟件中配置器啟用。
要啟用 MPSoC 中的顯示端口,我們需要在 I/O 配置頁(yè)面下配置 DisplayPort 外設(shè)。硬件上使用了 ?Bank 505 的MGT。
我們還需要配置通過 EMIO 連接到 PL 的輔助引腳。這意味著我們需要以不同于 PS 中的 MIO 的方式處理它們,輔助輸出使能引腳低電平有效,因此我們需要在使用 EMIO 時(shí)對(duì)信號(hào)使用反相器。
配置好 DisplayPort 后,我們就可以啟用 PL 的實(shí)時(shí)視頻輸入。這在 PS-PL 配置下可用。
最后配置是設(shè)置 GPIO 以提供一個(gè) 1 位的 EMIO,這樣我們就可以打開和關(guān)閉 PCAM5。
Sensor將通過 I2C 進(jìn)行配置,最后通過兩個(gè) MIPI 通道以 280 Mbps 的數(shù)據(jù)速率輸出 10 位 RAW 視頻。
因此,我們需要配置 MIPI CSI-2 RX 子系統(tǒng)
由于我們只有一個(gè) MIPI 接口,我們將配置 MIPI 內(nèi)核以包含所有共享邏輯。如果我們?cè)谕粋€(gè) bank 上有多個(gè) MIPI 接口,則下一個(gè) MIPI 接口將被配置為使用示例設(shè)計(jì)中的共享邏輯。
MIPI 接口的最終設(shè)置是為 MIPI 通道和時(shí)鐘選擇 IO bank 和引腳分配。正如我們?cè)谶@里定義的那樣,我們不需要將它們添加到定義引腳位置的 XDC 文件中。
創(chuàng)建可用圖像的下一步是轉(zhuǎn)換原始圖像,其中包含有關(guān)每個(gè)像素一個(gè)顏色通道的信息。
在包含紅色、綠色和藍(lán)色元素的圖像中,這稱為去拜耳化,由 Sensor Demosaic IP 塊實(shí)現(xiàn)。
對(duì)于我們的應(yīng)用程序,PCAM5 將以以下格式輸出像素 BGBG/GRGR
綠色是紅色和藍(lán)色的兩倍,因?yàn)槲覀兊难劬?duì)綠色更敏感,如果可見光譜位于中間,則綠色比位于兩端的紅色和藍(lán)色更敏感。
處理的下一步是實(shí)現(xiàn)Gamma校正 IP 內(nèi)核
最后一個(gè)階段是插入一個(gè)VDMA,這將使視頻數(shù)據(jù)能夠從 PS DDR 寫入到內(nèi)存中。
為了向顯示端口提供輸出視頻的時(shí)序,我們?cè)诎l(fā)生器模式下使用VTC。
最后階段是 AXIS 到視頻輸出 IP,這將采用視頻時(shí)序控制器、時(shí)序信號(hào)和 AXI 視頻流來(lái)創(chuàng)建具有正確時(shí)序的輸出視頻。
其輸出將連接到顯示端口視頻實(shí)時(shí)輸入。
這需要一點(diǎn)思考,實(shí)時(shí)視頻輸入具有 36 位視頻輸入,像素的每個(gè)元素為 12 位。我們正在使用每個(gè)元素 10 位,這使其整體為 30 位。
通過填充 LSB,到視頻輸出的 AXIS 流將按比例放大輸出塊,從每像素 30 位到每像素 36 位。
確保 AXIS 到 Video Out IP 內(nèi)核輸出的顏色通道與 DisplayPort 實(shí)時(shí)視頻輸入正確對(duì)齊。最簡(jiǎn)單的方法是在 AXI Stream 中使用 AXIS 子集轉(zhuǎn)換器根據(jù)需要切換顏色通道。
為了幫助調(diào)試和理解設(shè)計(jì),我還在設(shè)計(jì)中包含了幾個(gè)集成邏輯分析儀。
隨著設(shè)計(jì)的完成,我們現(xiàn)在可以構(gòu)建硬件設(shè)計(jì)并使用 SDK 實(shí)現(xiàn)軟件應(yīng)用程序。
軟件開發(fā)
完成硬件設(shè)計(jì)后,下一步是編寫將在可編程邏輯中配置 IP 的軟件。此配置將允許他們通過視頻,可以顯示在 DisplayPort 監(jiān)視器上。
因此,我們的應(yīng)用軟件將執(zhí)行以下步驟
配置 GPIO 并啟用 PCAM5 電源
將 Video Mode 1280 x 720 設(shè)置為 60 FPS 并配置VTC
3)配置VDMA幀大小和內(nèi)存存儲(chǔ)
配置 Senso Demosaic
配置伽瑪校正
使用 I2C 鏈路配置 PCAM5
啟用從 VDMA 讀取和寫入幀緩沖區(qū)
設(shè)置顯示端口
就像我們處理其他項(xiàng)目一樣,我們需要導(dǎo)入硬件規(guī)范、創(chuàng)建應(yīng)用程序和 BSP。
為了能夠在顯示端口上使用實(shí)時(shí)視頻源,我們需要首先更新 BSP 設(shè)置并重新生成它。
在 BSP 設(shè)置中將 avbuf 更改為 dppsu 并等待 BSP 重新生成。
正確配置 BSP 后,我們可以創(chuàng)建應(yīng)用程序代碼。BSP 將提供使用 PL 中的 IP 塊所需的所有 API 函數(shù)。
主要應(yīng)用如下
?
#include?#include?"platform.h" #include?"xil_printf.h" #include?"xvtc.h" #include?"vga_modes.h" #include?"xv_tpg.h" #include?"xvidc.h" #include?"xavbuf.h" #include?"xavbuf_clk.h" #include?"xvidc.h" #include?"xdpdma_video_example.h" #include?"xiicps.h" #include?"i2c.h" #include?"xaxivdma.h" #include?"xaxivdma_i.h" #include?"xgpiops.h" #include?"xv_demosaic.h" #include?"xv_gamma_lut.h" #include?"math.h" XVtc?VtcInst; XVtc_Config?*vtc_config?; XV_tpg?tpg; XV_tpg_Config?*tpg_config; XIicPs??iic_cam; XAxiVdma?vdma; XAxiVdma_DmaSetup?vdmaDMA; XAxiVdma_Config?*vdmaConfig; XGpioPs?gp_cam; VideoMode?video; XV_demosaic?cfa; XV_gamma_lut?gamma_inst; u8?SendBuffer?[10]; u8?RecvBuffer?[10]; u16?gamma_reg[1024]; #define?DEMO_MAX_FRAME?(720*1280) #define?DEMO_STRIDE?(1280*2) #define?DISPLAY_NUM_FRAMES?3 #define?IIC_cam????XPAR_XIICPS_0_DEVICE_ID #define?cam_gpio????XPAR_XGPIOPS_0_DEVICE_ID #define?CAM_ID??????????????0x78 #define?IIC_CAM_ADDR????0x3c #define?IIC_SCLK_RATE??100000 #define?MUX_ADDR???0x70 void?detect_camera(); int??Initial_setting_1?(?u32?*cfg_init?,?int?cfg_init_QTY??); void?read_camera(); void?gamma_calc(float?gamma_val); u32?frameBuf[DISPLAY_NUM_FRAMES][DEMO_MAX_FRAME]; u32?*pFrames[DISPLAY_NUM_FRAMES]; int?main() { XVtc_Timing?vtcTiming; XVtc_SourceSelect?SourceSelect; XGpioPs_Config?*GPIO_Config; int?Status; init_platform(); disable_caches(); print("Hello?World "); vtc_config?=?XVtc_LookupConfig(XPAR_VTC_0_DEVICE_ID); XVtc_CfgInitialize(&VtcInst,?vtc_config,?vtc_config->BaseAddress); GPIO_Config?=?XGpioPs_LookupConfig(cam_gpio); Status=?XGpioPs_CfgInitialize(&gp_cam,GPIO_Config,GPIO_Config->BaseAddr); XGpioPs_SetOutputEnablePin(&gp_cam,78,1); XGpioPs_SetDirectionPin(&gp_cam,78,1); XGpioPs_WritePin(&gp_cam,78,0x0); usleep(2000000); XGpioPs_WritePin(&gp_cam,78,0x1); video?=?VMODE_1280x720; vtcTiming.HActiveVideo?=?video.width;?/**BaseAddress); //video?=?VMODE_1280x720; vdmaDMA.FrameDelay?=?0; vdmaDMA.EnableCircularBuf?=?1; vdmaDMA.EnableSync?=?0; vdmaDMA.PointNum?=?0; vdmaDMA.EnableFrameCounter?=?0; vdmaDMA.VertSizeInput?=?video.height; vdmaDMA.HoriSizeInput?=?(video.width)*4; vdmaDMA.FixedFrameStoreAddr?=?0; vdmaDMA.FrameStoreStartAddr[0]?=?(u32)??pFrames[0]; vdmaDMA.Stride?=?(video.width)*4; XAxiVdma_DmaConfig(&vdma,?XAXIVDMA_WRITE,?&(vdmaDMA)); Status?=?XAxiVdma_DmaSetBufferAddr(&vdma,?XAXIVDMA_WRITE,vdmaDMA.FrameStoreStartAddr); XAxiVdma_DmaConfig(&vdma,?XAXIVDMA_READ,?&(vdmaDMA)); XAxiVdma_DmaSetBufferAddr(&vdma,?XAXIVDMA_READ,vdmaDMA.FrameStoreStartAddr); XV_demosaic_Initialize(&cfa,?XPAR_V_DEMOSAIC_0_DEVICE_ID); XV_demosaic_Set_HwReg_width(&cfa,?video.width); XV_demosaic_Set_HwReg_height(&cfa,?video.height); XV_demosaic_Set_HwReg_bayer_phase(&cfa,?0x03); XV_demosaic_EnableAutoRestart(&cfa); XV_demosaic_Start(&cfa); gamma_calc(1.6); XV_gamma_lut_Initialize(&gamma_inst,?XPAR_V_GAMMA_LUT_0_DEVICE_ID); XV_gamma_lut_Set_HwReg_width(&gamma_inst,?video.width); XV_gamma_lut_Set_HwReg_height(&gamma_inst,?video.height); XV_gamma_lut_Set_HwReg_video_format(&gamma_inst,?0x00); XV_gamma_lut_Write_HwReg_gamma_lut_0_Bytes(&gamma_inst,?0,(int?*)?gamma_reg,?2048); XV_gamma_lut_Write_HwReg_gamma_lut_1_Bytes(&gamma_inst,?0,(int?*)?gamma_reg,?2048); XV_gamma_lut_Write_HwReg_gamma_lut_2_Bytes(&gamma_inst,?0,(int?*)?gamma_reg,?2048); XV_gamma_lut_Start(&gamma_inst); XV_gamma_lut_EnableAutoRestart(&gamma_inst); detect_camera(); SendBuffer[0]=?0x31; SendBuffer[1]=?0x03; SendBuffer[2]=?0x11; Status?=?XIicPs_MasterSendPolled(&iic_cam,?SendBuffer,?3,?IIC_CAM_ADDR); //writeReg(0x3103,?0x11); //[7]=1?Software?reset;?[6]=0?Software?power?down;?Default=0x02 SendBuffer[0]=?0x30; SendBuffer[1]=?0x08; SendBuffer[2]=?0x82; Status?=?XIicPs_MasterSendPolled(&iic_cam,?SendBuffer,?3,?IIC_CAM_ADDR); //writeReg(0x3008,?0x82); usleep(1000000); Initial_setting_1?(?cfg_init?,?63??); Initial_setting_1?(?cfg_simple_awb,?19??); Initial_setting_1?(?cfg_720p_60fps?,?38??); xil_printf("Configuration?Complete "); Status?=?XAxiVdma_DmaStart(&vdma,?XAXIVDMA_WRITE); Status?=?XAxiVdma_StartParking(&vdma,?0,?XAXIVDMA_WRITE); XAxiVdma_DmaStart(&vdma,?XAXIVDMA_READ); XAxiVdma_StartParking(&vdma,?0,?XAXIVDMA_READ); run_dppsu(); while(1){ } cleanup_platform(); return?0; } void?gamma_calc(float?gamma_val) { int?i; for(i?=?0;?i<1024;?i++){ gamma_reg[i]?=?(pow((i?/?1024.0),?(1/gamma_val))?*?1024.0); } } void?setup_tpg() { u32?height,width,status; tpg_config?=?XV_tpg_LookupConfig(XPAR_XV_TPG_0_DEVICE_ID); XV_tpg_CfgInitialize(&tpg,?tpg_config,?tpg_config->BaseAddress); #ifdef?DEBUG status?=?XV_tpg_IsReady(&tpg); printf("TPG?Status?%u? ",?(unsigned?int)?status); #endif XV_tpg_Set_height(&tpg,?(u32)?video.height); XV_tpg_Set_width(&tpg,?(u32)?video.width); height?=?XV_tpg_Get_height(&tpg); width?=?XV_tpg_Get_width(&tpg); XV_tpg_Set_colorFormat(&tpg,XVIDC_CSF_RGB); XV_tpg_Set_maskId(&tpg,?0x0); XV_tpg_Set_motionSpeed(&tpg,?0x4); #ifdef?DEBUG printf("info?from?tpg?%u?%u? ",?(unsigned?int)height,?(unsigned?int)width); #endif XV_tpg_Set_bckgndId(&tpg,XTPG_BKGND_SOLID_BLUE);?//XTPG_BKGND_TARTAN_COLOR_BARS); #ifdef?DEBUG status?=?XV_tpg_Get_bckgndId(&tpg); printf("Status?%x? ",?(unsigned?int)?status); #endif XV_tpg_EnableAutoRestart(&tpg); XV_tpg_Start(&tpg); #ifdef?DEBUG status?=?XV_tpg_IsIdle(&tpg); printf("Status?%u? ",?(unsigned?int)?status); #endif } void?detect_camera() { XIicPs_Config?*iic_conf; u32?Status; iic_conf?=?XIicPs_LookupConfig(IIC_cam); XIicPs_CfgInitialize(&iic_cam,iic_conf,iic_conf->BaseAddress); XIicPs_SetSClk(&iic_cam,?IIC_SCLK_RATE); SendBuffer[0]=?0x01; SendBuffer[1]=?0x00; Status?=?XIicPs_MasterSendPolled(&iic_cam,?SendBuffer,?1,?MUX_ADDR); if?(Status?!=?XST_SUCCESS)?{ print("SW?I2C?write?error "); return?XST_FAILURE; } usleep(2000000); SendBuffer[0]=?0x31; SendBuffer[1]=?0x00; Status?=?XIicPs_MasterSendPolled(&iic_cam,?SendBuffer,?2,?IIC_CAM_ADDR); if?(Status?!=?XST_SUCCESS)?{ print("I2C?write?error "); return?XST_FAILURE; } Status?=?XIicPs_MasterRecvPolled(&iic_cam,?RecvBuffer,1,?IIC_CAM_ADDR); if?(Status?!=?XST_SUCCESS)?{ print("I2C?read?error "); return?XST_FAILURE; } if(RecvBuffer[0]?!=?CAM_ID?){ print("Camera?not?detected "); } else{ print("Camera?detected? "); } } void?read_camera() { XIicPs_Config?*iic_conf; u32?Status; iic_conf?=?XIicPs_LookupConfig(IIC_cam); XIicPs_CfgInitialize(&iic_cam,iic_conf,iic_conf->BaseAddress); XIicPs_SetSClk(&iic_cam,?IIC_SCLK_RATE); SendBuffer[0]=?0x30; SendBuffer[1]=?0x0e; Status?=?XIicPs_MasterSendPolled(&iic_cam,?SendBuffer,?2,?IIC_CAM_ADDR); if?(Status?!=?XST_SUCCESS)?{ print("I2C?write?error "); return?XST_FAILURE; } Status?=?XIicPs_MasterRecvPolled(&iic_cam,?RecvBuffer,1,?IIC_CAM_ADDR); if?(Status?!=?XST_SUCCESS)?{ print("I2C?read?error "); return?XST_FAILURE; } } int??Initial_setting_1?(?u32?*cfg_init?,?int?cfg_init_QTY??) { s32??Status?,?byte_count; int?i?; u8?SendBuffer[10]; for(i=0;i<(cfg_init_QTY*2);i+=2){ SendBuffer[1]=??*(cfg_init?+?i); SendBuffer[0]=??(*(cfg_init?+?i))>>8; SendBuffer[2]=??*(cfg_init?+?i?+?1); Status?=?XIicPs_MasterSendPolled(&iic_cam,?SendBuffer,?3,?IIC_CAM_ADDR); if?(Status?!=?XST_SUCCESS)?{ print("I2C?read?error "); return?XST_FAILURE; } usleep(1000); } return?XST_SUCCESS; }
?
具有 I2C 配置的文件
?
//config_word_t?const?cfg_init_[]?= static?u32??cfg_init?[][2]??= { //[7]=0?Software?reset;?[6]=1?Software?power?down;?Default=0x02 {0x3008,?0x42}, //[1]=1?System?input?clock?from?PLL;?Default?read?=?0x11 {0x3103,?0x03}, //[3:0]=0000?MD2P,MD2N,MCP,MCN?input;?Default=0x00 {0x3017,?0x00}, //[7:2]=000000?MD1P,MD1N,?D3:0?input;?Default=0x00 {0x3018,?0x00}, //[6:4]=001?PLL?charge?pump,?[3:0]=1000?MIPI?8-bit?mode {0x3034,?0x18}, //PLL1?configuration //[7:4]=0001?System?clock?divider?/1,?[3:0]=0001?Scale?divider?for?MIPI?/1 {0x3035,?0x11}, //[7:0]=56?PLL?multiplier {0x3036,?0x38}, //[4]=1?PLL?root?divider?/2,?[3:0]=1?PLL?pre-divider?/1 {0x3037,?0x11}, //[5:4]=00?PCLK?root?divider?/1,?[3:2]=00?SCLK2x?root?divider?/1,?[1:0]=01?SCLK?root?divider?/2 {0x3108,?0x01}, //PLL2?configuration //[5:4]=01?PRE_DIV_SP?/1.5,?[2]=1?R_DIV_SP?/1,?[1:0]=00?DIV12_SP?/1 {0x303D,?0x10}, //[4:0]=11001?PLL2?multiplier?DIV_CNT5B?=?25 {0x303B,?0x19}, {0x3630,?0x2e}, {0x3631,?0x0e}, {0x3632,?0xe2}, {0x3633,?0x23}, {0x3621,?0xe0}, {0x3704,?0xa0}, {0x3703,?0x5a}, {0x3715,?0x78}, {0x3717,?0x01}, {0x370b,?0x60}, {0x3705,?0x1a}, {0x3905,?0x02}, {0x3906,?0x10}, {0x3901,?0x0a}, {0x3731,?0x02}, //VCM?debug?mode {0x3600,?0x37}, {0x3601,?0x33}, //System?control?register?changing?not?recommended {0x302d,?0x60}, //?? {0x3620,?0x52}, {0x371b,?0x20}, //???DVP {0x471c,?0x50}, {0x3a13,?0x43}, {0x3a18,?0x00}, {0x3a19,?0xf8}, {0x3635,?0x13}, {0x3636,?0x06}, {0x3634,?0x44}, {0x3622,?0x01}, {0x3c01,?0x34}, {0x3c04,?0x28}, {0x3c05,?0x98}, {0x3c06,?0x00}, {0x3c07,?0x08}, {0x3c08,?0x00}, {0x3c09,?0x1c}, {0x3c0a,?0x9c}, {0x3c0b,?0x40}, //[7]=1?color?bar?enable,?[3:2]=00?eight?color?bar {0x503d,?0x00}, //[2]=1?ISP?vflip,?[1]=1?sensor?vflip {0x3820,?0x46}, //[7:5]=010?Two?lane?mode,?[4]=0?MIPI?HS?TX?no?power?down,?[3]=0?MIPI?LP?RX?no?power?down,?[2]=1?MIPI?enable,?[1:0]=10?Debug?mode;?Default=0x58 {0x300e,?0x45}, //[5]=0?Clock?free?running,?[4]=1?Send?line?short?packet,?[3]=0?Use?lane1?as?default,?[2]=1?MIPI?bus?LP11?when?no?packet;?Default=0x04 {0x4800,?0x14}, {0x302e,?0x08}, //[7:4]=0x3?YUV422,?[3:0]=0x0?YUYV //{0x4300,?0x30}, //[7:4]=0x6?RGB565,?[3:0]=0x0?{b[4:0],g[5:3],g[2:0],r[4:0]} {0x4300,?0x6f}, {0x501f,?0x01}, {0x4713,?0x03}, {0x4407,?0x04}, {0x440e,?0x00}, {0x460b,?0x35}, //[1]=0?DVP?PCLK?divider?manual?control?by?0x3824[4:0] {0x460c,?0x20}, //[4:0]=1?SCALE_DIV=INT(3824[4:0]/2) {0x3824,?0x01}, //MIPI?timing //??{0x4805,?0x10},?//LPX?global?timing?select=auto //??{0x4818,?0x00},?//hs_prepare?+?hs_zero_min?ns //??{0x4819,?0x96}, //??{0x482A,?0x00},?//hs_prepare?+?hs_zero_min?UI // //??{0x4824,?0x00},?//lpx_p_min?ns //??{0x4825,?0x32}, //??{0x4830,?0x00},?//lpx_p_min?UI // //??{0x4826,?0x00},?//hs_prepare_min?ns //??{0x4827,?0x32}, //??{0x4831,?0x00},?//hs_prepare_min?UI //[7]=1?LENC?correction?enabled,?[5]=1?RAW?gamma?enabled,?[2]=1?Black?pixel?cancellation?enabled,?[1]=1?White?pixel?cancellation?enabled,?[0]=1?Color?interpolation?enabled {0x5000,?0x07}, //[7]=0?Special?digital?effects,?[5]=0?scaling,?[2]=0?UV?average?disabled,?[1]=1?Color?matrix?enabled,?[0]=1?Auto?white?balance?enabled {0x5001,?0x03} }; static?u32??cfg_simple_awb[][2]?= { //?Disable?Advanced?AWB {0x518d?,0x00}, {0x518f?,0x20}, {0x518e?,0x00}, {0x5190?,0x20}, {0x518b?,0x00}, {0x518c?,0x00}, {0x5187?,0x10}, {0x5188?,0x10}, {0x5189?,0x40}, {0x518a?,0x40}, {0x5186?,0x10}, {0x5181?,0x58}, {0x5184?,0x25}, {0x5182?,0x11}, //?Enable?simple?AWB {0x3406?,0x00}, {0x5183?,0x80}, {0x5191?,0xff}, {0x5192?,0x00}, {0x5001?,0x03} }; static?u32??cfg_720p_60fps[][2]?= {//1280?x?720?binned,?RAW10,?MIPISCLK=280M,?SCLK=56Mz,?PCLK=56M //PLL1?configuration {0x3008,?0x42}, //[7:4]=0010?System?clock?divider?/2,?[3:0]=0001?Scale?divider?for?MIPI?/1 {0x3035,?0x21}, //[7:0]=70?PLL?multiplier {0x3036,?0x46}, //[4]=0?PLL?root?divider?/1,?[3:0]=5?PLL?pre-divider?/1.5 {0x3037,?0x05}, //[5:4]=01?PCLK?root?divider?/2,?[3:2]=00?SCLK2x?root?divider?/1,?[1:0]=01?SCLK?root?divider?/2 {0x3108,?0x11}, //[6:4]=001?PLL?charge?pump,?[3:0]=1010?MIPI?10-bit?mode {0x3034,?0x1A}, //[3:0]=0?X?address?start?high?byte {0x3800,?(0?>>?8)?&?0x0F}, //[7:0]=0?X?address?start?low?byte {0x3801,?0?&?0xFF}, //[2:0]=0?Y?address?start?high?byte {0x3802,?(8?>>?8)?&?0x07}, //[7:0]=0?Y?address?start?low?byte {0x3803,?8?&?0xFF}, //[3:0]?X?address?end?high?byte {0x3804,?(2619?>>?8)?&?0x0F}, //[7:0]?X?address?end?low?byte {0x3805,?2619?&?0xFF}, //[2:0]?Y?address?end?high?byte {0x3806,?(1947?>>?8)?&?0x07}, //[7:0]?Y?address?end?low?byte {0x3807,?1947?&?0xFF}, //[3:0]=0?timing?hoffset?high?byte {0x3810,?(0?>>?8)?&?0x0F}, //[7:0]=0?timing?hoffset?low?byte {0x3811,?0?&?0xFF}, //[2:0]=0?timing?voffset?high?byte {0x3812,?(0?>>?8)?&?0x07}, //[7:0]=0?timing?voffset?low?byte {0x3813,?0?&?0xFF}, //[3:0]?Output?horizontal?width?high?byte {0x3808,?(1280?>>?8)?&?0x0F}, //[7:0]?Output?horizontal?width?low?byte {0x3809,?1280?&?0xFF}, //[2:0]?Output?vertical?height?high?byte {0x380a,?(720?>>?8)?&?0x7F}, //[7:0]?Output?vertical?height?low?byte {0x380b,?720?&?0xFF}, //HTS?line?exposure?time?in?#?of?pixels {0x380c,?(1896?>>?8)?&?0x1F}, {0x380d,?1896?&?0xFF}, //VTS?frame?exposure?time?in?#?lines {0x380e,?(984?>>?8)?&?0xFF}, {0x380f,?984?&?0xFF}, //[7:4]=0x3?horizontal?odd?subsample?increment,?[3:0]=0x1?horizontal?even?subsample?increment {0x3814,?0x31}, //[7:4]=0x3?vertical?odd?subsample?increment,?[3:0]=0x1?vertical?even?subsample?increment {0x3815,?0x31}, //[2]=0?ISP?mirror,?[1]=0?sensor?mirror,?[0]=1?horizontal?binning {0x3821,?0x01}, //little?MIPI?shit:?global?timing?unit,?period?of?PCLK?in?ns?*?2(depends?on?#?of?lanes) {0x4837,?36},?//?1/56M*2 //Undocumented?anti-green?settings {0x3618,?0x00},?//?Removes?vertical?lines?appearing?under?bright?light {0x3612,?0x59}, {0x3708,?0x64}, {0x3709,?0x52}, {0x370c,?0x03}, //[7:4]=0x0?Formatter?RAW,?[3:0]=0x0?BGBG/GRGR {0x4300,?0x00}, //[2:0]=0x3?Format?select?ISP?RAW?(DPC) {0x501f,?0x03}, {0x3008,?0x02}, };
?
顯示端口配置文件
?
/*****************************?Include?Files?*********************************/ #include?"xil_exception.h" #include?"xil_printf.h" #include?"xil_cache.h" #include?"xdpdma_video_example.h" /**************************?Constant?Definitions?*****************************/ #define?DPPSU_DEVICE_ID??XPAR_PSU_DP_DEVICE_ID #define?AVBUF_DEVICE_ID??XPAR_PSU_DP_DEVICE_ID #define?DPDMA_DEVICE_ID??XPAR_XDPDMA_0_DEVICE_ID #define?DPPSU_INTR_ID??151 #define?DPDMA_INTR_ID??154 #define?INTC_DEVICE_ID??XPAR_SCUGIC_0_DEVICE_ID #define?DPPSU_BASEADDR??XPAR_PSU_DP_BASEADDR #define?AVBUF_BASEADDR??XPAR_PSU_DP_BASEADDR #define?DPDMA_BASEADDR??XPAR_PSU_DPDMA_BASEADDR #define?BUFFERSIZE???1280?*?720?*?4??/*?HTotal?*?VTotal?*?BPP?*/ #define?LINESIZE???1280?*?4???/*?HTotal?*?BPP?*/ #define?STRIDE????LINESIZE???/*?The?stride?value?should be?aligned?to?256*/ /**************************?Variable?Declarations?***************************/ u8?Frame[BUFFERSIZE]?__attribute__?((__aligned__(256))); XDpDma_FrameBuffer?FrameBuffer; /****************************?Type?Definitions?*******************************/ /*****************************************************************************/ /** * *?Main?function?to?call?the?DPDMA?Video?example. * *?@param?None * *?@return?XST_SUCCESS?if?successful,?otherwise?XST_FAILURE. * *?@note??None * ******************************************************************************/ int?run_dppsu() { int?Status; Xil_DCacheDisable(); Xil_ICacheDisable(); xil_printf("DPDMA?Generic?Video?Example?Test? "); Status?=?DpdmaVideoExample(&RunCfg); if?(Status?!=?XST_SUCCESS)?{ xil_printf("DPDMA?Video?Example?Test?Failed "); return?XST_FAILURE; } xil_printf("Successfully?ran?DPDMA?Video?Example?Test "); return?XST_SUCCESS; } /*****************************************************************************/ /** * *?The?purpose?of?this?function?is?to?illustrate?how?to?use?the?XDpDma?device *?driver?in?Graphics?overlay?mode. * *?@param?RunCfgPtr?is?a?pointer?to?the?application?configuration?structure. * *?@return?XST_SUCCESS?if?successful,?else?XST_FAILURE. * *?@note??None. * *****************************************************************************/ int?DpdmaVideoExample(Run_Config?*RunCfgPtr) { u32?Status; /*?Initialize?the?application?configuration?*/ InitRunConfig(RunCfgPtr); Status?=?InitDpDmaSubsystem(RunCfgPtr); if?(Status?!=?XST_SUCCESS)?{ return?XST_FAILURE; } SetupInterrupts(RunCfgPtr); //xil_printf("Generating?Overlay..... "); GraphicsOverlay(Frame,?RunCfgPtr); /*?Populate?the?FrameBuffer?structure?with?the?frame?attributes?*/ FrameBuffer.Address?=?(INTPTR)Frame; FrameBuffer.Stride?=?STRIDE; FrameBuffer.LineSize?=?LINESIZE; FrameBuffer.Size?=?BUFFERSIZE; XDpDma_DisplayGfxFrameBuffer(RunCfgPtr->DpDmaPtr,?&FrameBuffer); return?XST_SUCCESS; } /*****************************************************************************/ /** * *?The?purpose?of?this?function?is?to?initialize?the?application?configuration. * *?@param?RunCfgPtr?is?a?pointer?to?the?application?configuration?structure. * *?@return?None. * *?@note??None. * *****************************************************************************/ void?InitRunConfig(Run_Config?*RunCfgPtr) { /*?Initial?configuration?parameters.?*/ RunCfgPtr->DpPsuPtr???=?&DpPsu; RunCfgPtr->IntrPtr???=?&Intr; RunCfgPtr->AVBufPtr??=?&AVBuf; RunCfgPtr->DpDmaPtr??=?&DpDma; RunCfgPtr->VideoMode?=?XVIDC_VM_1280x720_60_P; RunCfgPtr->Bpc???=?XVIDC_BPC_12;//XVIDC_BPC_8; RunCfgPtr->ColorEncode???=?XDPPSU_CENC_RGB; RunCfgPtr->UseMaxCfgCaps??=?1; RunCfgPtr->LaneCount???=?LANE_COUNT_2; RunCfgPtr->LinkRate????=?LINK_RATE_540GBPS; RunCfgPtr->EnSynchClkMode??=?0; RunCfgPtr->UseMaxLaneCount??=?1; RunCfgPtr->UseMaxLinkRate??=?1; } /*****************************************************************************/ /** * *?The?purpose?of?this?function?is?to?initialize?the?DP?Subsystem?(XDpDma, *?XAVBuf,?XDpPsu) * *?@param?RunCfgPtr?is?a?pointer?to?the?application?configuration?structure. * *?@return?None. * *?@note??None. * *****************************************************************************/ int?InitDpDmaSubsystem(Run_Config?*RunCfgPtr) { u32?Status; XDpPsu??*DpPsuPtr?=?RunCfgPtr->DpPsuPtr; XDpPsu_Config?*DpPsuCfgPtr; XAVBuf??*AVBufPtr?=?RunCfgPtr->AVBufPtr; XDpDma_Config?*DpDmaCfgPtr; XDpDma??*DpDmaPtr?=?RunCfgPtr->DpDmaPtr; /*?Initialize?DisplayPort?driver.?*/ DpPsuCfgPtr?=?XDpPsu_LookupConfig(DPPSU_DEVICE_ID); XDpPsu_CfgInitialize(DpPsuPtr,?DpPsuCfgPtr,?DpPsuCfgPtr->BaseAddr); /*?Initialize?Video?Pipeline?driver?*/ XAVBuf_CfgInitialize(AVBufPtr,?DpPsuPtr->Config.BaseAddr,?AVBUF_DEVICE_ID); /*?Initialize?the?DPDMA?driver?*/ DpDmaCfgPtr?=?XDpDma_LookupConfig(DPDMA_DEVICE_ID); XDpDma_CfgInitialize(DpDmaPtr,DpDmaCfgPtr); /*?Initialize?the?DisplayPort?TX?core.?*/ Status?=?XDpPsu_InitializeTx(DpPsuPtr); if?(Status?!=?XST_SUCCESS)?{ return?XST_FAILURE; } /*?Set?the?format?graphics?frame?for?DPDMA*/ Status?=?XDpDma_SetGraphicsFormat(DpDmaPtr,?RGBA8888); if?(Status?!=?XST_SUCCESS)?{ return?XST_FAILURE; } /*?Set?the?format?graphics?frame?for?Video?Pipeline*/ Status?=?XAVBuf_SetInputNonLiveGraphicsFormat(AVBufPtr,?RGBA8888); if?(Status?!=?XST_SUCCESS)?{ return?XST_FAILURE; } /*?Set?the?QOS?for?Video?*/ XDpDma_SetQOS(RunCfgPtr->DpDmaPtr,?11); /*?Enable?the?Buffers?required?by?Graphics?Channel?*/ //XAVBuf_EnableGraphicsBuffers(RunCfgPtr->AVBufPtr,?1); XAVBuf_SetInputLiveVideoFormat(&AVBufPtr,?RGB_10BPC); XAVBuf_EnableVideoBuffers(&AVBufPtr,?1); /*?Set?the?output?Video?Format?*/ XAVBuf_SetOutputVideoFormat(AVBufPtr,?RGB_10BPC); /*?Select?the?Input?Video?Sources. *?Here?in?this?example?we?are?going?to?demonstrate *?graphics?overlay?over?the?TPG?video. */ XAVBuf_InputVideoSelect(AVBufPtr,?XAVBUF_VIDSTREAM1_LIVE,?XAVBUF_VIDSTREAM2_NONE); /*?Configure?Video?pipeline?for?graphics?channel?*/ //XAVBuf_ConfigureGraphicsPipeline(AVBufPtr); /*?Configure?the?output?video?pipeline?*/ XAVBuf_ConfigureOutputVideo(AVBufPtr); /*?Disable?the?global?alpha,?since?we?are?using?the?pixel?based?alpha?*/ XAVBuf_SetBlenderAlpha(AVBufPtr,?0,?0); /*?Set?the?clock?mode?*/ XDpPsu_CfgMsaEnSynchClkMode(DpPsuPtr,?RunCfgPtr->EnSynchClkMode); /*?Set?the?clock?source?depending?on?the?use?case. *?Here?for?simplicity?we?are?using?PS?clock?as?the?source*/ XAVBuf_SetAudioVideoClkSrc(AVBufPtr,?XAVBUF_PL_CLK,?XAVBUF_PS_CLK); /*?Issue?a?soft?reset?after?selecting?the?input?clock?sources?*/ XAVBuf_SoftReset(AVBufPtr); return?XST_SUCCESS; } /*****************************************************************************/ /** * *?The?purpose?of?this?function?is?to?setup?call?back?functions?for?the?DP *?controller?interrupts. * *?@param?RunCfgPtr?is?a?pointer?to?the?application?configuration?structure. * *?@return?None. * *?@note??None. * *****************************************************************************/ void?SetupInterrupts(Run_Config?*RunCfgPtr) { XDpPsu?*DpPsuPtr?=?RunCfgPtr->DpPsuPtr; XScuGic??*IntrPtr?=?RunCfgPtr->IntrPtr; XScuGic_Config?*IntrCfgPtr; u32??IntrMask?=?XDPPSU_INTR_HPD_IRQ_MASK?|?XDPPSU_INTR_HPD_EVENT_MASK; XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr,?XDPPSU_INTR_DIS,?0xFFFFFFFF); XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr,?XDPPSU_INTR_MASK,?0xFFFFFFFF); XDpPsu_SetHpdEventHandler(DpPsuPtr,?DpPsu_IsrHpdEvent,?RunCfgPtr); XDpPsu_SetHpdPulseHandler(DpPsuPtr,?DpPsu_IsrHpdPulse,?RunCfgPtr); /*?Initialize?interrupt?controller?driver.?*/ IntrCfgPtr?=?XScuGic_LookupConfig(INTC_DEVICE_ID); XScuGic_CfgInitialize(IntrPtr,?IntrCfgPtr,?IntrCfgPtr->CpuBaseAddress); /*?Register?ISRs.?*/ XScuGic_Connect(IntrPtr,?DPPSU_INTR_ID, (Xil_InterruptHandler)XDpPsu_HpdInterruptHandler,?RunCfgPtr->DpPsuPtr); /*?Trigger?DP?interrupts?on?rising?edge.?*/ XScuGic_SetPriorityTriggerType(IntrPtr,?DPPSU_INTR_ID,?0x0,?0x03); /*?Connect?DPDMA?Interrupt?*/ XScuGic_Connect(IntrPtr,?DPDMA_INTR_ID, (Xil_ExceptionHandler)XDpDma_InterruptHandler,?RunCfgPtr->DpDmaPtr); /*?Initialize?exceptions.?*/ Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT, (Xil_ExceptionHandler)XScuGic_DeviceInterruptHandler, INTC_DEVICE_ID); /*?Enable?exceptions?for?interrupts.?*/ Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); Xil_ExceptionEnable(); /*?Enable?DP?interrupts.?*/ XScuGic_Enable(IntrPtr,?DPPSU_INTR_ID); XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr,?XDPPSU_INTR_EN,?IntrMask); /*?Enable?DPDMA?Interrupts?*/ XScuGic_Enable(IntrPtr,?DPDMA_INTR_ID); XDpDma_InterruptEnable(RunCfgPtr->DpDmaPtr,?XDPDMA_IEN_VSYNC_INT_MASK); } /*****************************************************************************/ /** * *?The?purpose?of?this?function?is?to?generate?a?Graphics?frame?of?the?format *?RGBA8888?which?generates?an?overlay?on?1/2?of?the?bottom?of?the?screen. *?This?is?just?to?illustrate?the?functionality?of?the?graphics?overlay. * *?@param?RunCfgPtr?is?a?pointer?to?the?application?configuration?structure. *?@param?Frame?is?a?pointer?to?a?buffer?which?is?going?to?be?populated?with *????rendered?frame * *?@return?Returns?a?pointer?to?the?frame. * *?@note??None. * *****************************************************************************/ u8?*GraphicsOverlay(u8*?Frame,?Run_Config?*RunCfgPtr) { u64?Index; u32?*RGBA; RGBA?=?(u32?*)?Frame; /* *?Red?at?the?top?half *?Alpha?=?0x0F *?*/ for(Index?=?0;?Index?(BUFFERSIZE/4)?/2;?Index?++)?{ //RGBA[Index]?=?0x0F0000FF; } for(;?Index??
測(cè)試
應(yīng)用程序完成后,我們可以創(chuàng)建調(diào)試配置并下載位文件并運(yùn)行應(yīng)用程序軟件。
在監(jiān)視器上應(yīng)該能看到圖像,但是我們也可以檢查 ILA。這些顯示 ILA 將顯示從 MIPI CSI-2 IP 內(nèi)核和 VDMA 接收的輸入視頻流。
AXIS 使用邊帶信號(hào) Tuser 和 Tlast 傳輸視頻幀。Tuser 表示新幀的開始,而 Tlast 表示一行的結(jié)束。
第三個(gè) ILA 監(jiān)控 AXI Stream to Video Output,使我們能夠監(jiān)控輸出上的鎖定信號(hào)。
當(dāng)所有這些都運(yùn)行時(shí),將能夠看到圖像輸出:
解決方案的最終資源利用率如下圖所示,我們還有足夠的空間來(lái)實(shí)現(xiàn)有趣的算法
源代碼
審核編輯:劉清
評(píng)論
查看更多