學習內容
本文使用帶有HDMI接口的顯示器,構建圖像視頻顯示的測試工程,利用VDMA進行傳輸圖像視頻數據,進行彩條顯示的測試。
開發環境
vivado 18.3&SDK,PYNQ-Z2開發板。
準備工作
學習內容
本文使用帶有HDMI接口的顯示器,構建圖像視頻顯示的測試工程,利用VDMA進行傳輸圖像視頻數據,進行彩條顯示的測試。
開發環境
vivado 18.3&SDK,PYNQ-Z2開發板。
準備工作
所用到的IP:VDMA、video out IP、Video Timing Controller、動態時鐘配置 IP和DVI IP。
詳細介紹參考前文:
ZYNQ-Video out IP和Video Timing Controller IP簡介
系統框圖
通過控制器(MCU)把彩條數據寫入DDR,緩存到VDMA。然后通過AXI-stream to video out IP和VTC IP將視頻數據轉換為普通的視頻接口的時序信號,然后通過GP0控制視頻時序輸出,,由顯示器顯示輸出彩條圖片。
硬件平臺搭建
新建工程,創建 block design。
配置ZYNQ7
添加ZYNQ7 IP,對zynq進行初始化配置,
勾選M_AXI_GP0和S_AXI_HP0,
并添加時鐘和復位信號。
完成配置后點擊OK,配置后如下圖:
配置VDMA IP
并添加VDMA ip核,如下圖:
雙擊vdma ip核打開,進行配置ip核的相關信息。因為這里僅僅做顯示測試,所以不需要進行幀緩存和調用寫通道的資源,對于傳輸數據的位寬等信息根據自己將要顯示圖像大家進行合理配置。
配置Video out IP
添加 AXI4-Stream to Video Out IP,雙擊ip核打開,進行配置ip核的相關信息。按圖示配置即可,
配置VTC(Video Timing Controller ) IP
添加 Video Timing Controller IP,勾選AXI-Lite ,取消勾選監視器。
配置根據使用的顯示器進行顯示分辨率的設計,這里我就配置默認生成是1080p的
添加動態時鐘配置 IP和DVI IP
迪芝倫公司提供的IP,IP 鏈接,用于動態生成視頻時序信號的時鐘。
連接數據通路
完成視頻時序信號和視頻信號的連接,
完成連接FCLK_CLK1信號連接,
完成動態時鐘輸出的視頻時鐘的連接,
完成連接后點擊自動連接,勾選全部即可。
完成連接最終系統設計
完成連接最終系統設計如下:
然后我們進行generate output product 然后生成HDL封裝。接著就對應引腳進行引腳約束即可(PYNQ的粉色開發板可以直接引用這個約束):
set_property -dict { PACKAGE_PIN L17 IOSTANDARD TMDS_33 } [get_ports { tmds_tmds_clk_n }]; #IO_L11N_T1_SRCC_35 Sch=hdmi_tx_clk_n
set_property -dict { PACKAGE_PIN L16 IOSTANDARD TMDS_33 } [get_ports { tmds_tmds_clk_p }]; #IO_L11P_T1_SRCC_35 Sch=hdmi_tx_clk_p
set_property -dict { PACKAGE_PIN K18 IOSTANDARD TMDS_33 } [get_ports { tmds_tmds_data_n[0] }]; #IO_L12N_T1_MRCC_35 Sch=hdmi_tx_d_n[0]
set_property -dict { PACKAGE_PIN K17 IOSTANDARD TMDS_33 } [get_ports { tmds_tmds_data_p[0] }]; #IO_L12P_T1_MRCC_35 Sch=hdmi_tx_d_p[0]
set_property -dict { PACKAGE_PIN J19 IOSTANDARD TMDS_33 } [get_ports { tmds_tmds_data_n[1] }]; #IO_L10N_T1_AD11N_35 Sch=hdmi_tx_d_n[1]
set_property -dict { PACKAGE_PIN K19 IOSTANDARD TMDS_33 } [get_ports { tmds_tmds_data_p[1] }]; #IO_L10P_T1_AD11P_35 Sch=hdmi_tx_d_p[1]
set_property -dict { PACKAGE_PIN H18 IOSTANDARD TMDS_33 } [get_ports { tmds_tmds_data_n[2] }]; #IO_L14N_T2_AD4N_SRCC_35 Sch=hdmi_tx_d_n[2]
set_property -dict { PACKAGE_PIN J18 IOSTANDARD TMDS_33 } [get_ports { tmds_tmds_data_p[2] }]; #IO_L14P_T2_AD4P_SRCC_35 Sch=hdmi_tx_d_p[2]
set_property -dict { PACKAGE_PIN R19 IOSTANDARD LVCMOS33 } [get_ports { tmds_oen }]; #IO_0_34 Sch=hdmi_tx_hpdn
完成約束后進行綜合布局布線,等待生成bit流文件。bit文件生成后在FILE處,點擊導出硬件資源(包含bit流文件),接著launch SDK。
SDK軟件部分
打開SDK后,新建application project。首先導入相關的IP的驅動文件
導入VDMA API驅動
這里打開system.mss,點擊axi_vdma這里的導入示例,
這里可以把vdma_api.c進行封裝,添加頭文件,把要使用的函數進行聲明。
#ifndef VDMA_API_H_
#define VDMA_API_H_
/* Include File Definitions */
#include "xaxivdma.h"
#include "xparameters.h"
#include "xil_exception.h"
/* General Type Declarations */
typedefenum
{
ONLY_READ=1, //
ONLY_WRITE, //
BOTH //
}vdma_run_mode;
/* Procedure Declarations */
int run_vdma_frame_buffer(XAxiVdma* InstancePtr, int DeviceId, int hsize,
int vsize, int buf_base_addr, int number_frame_count,
int enable_frm_cnt_intr,vdma_run_mode mode);
#endif /* VDMA_API_H_ */
因為系統生成的示例的API是同時開啟了Vdma的讀寫通道,這里正點原子對函數進行了簡單的修改,聲明了一個結構體。這樣可以在進行幀緩存的時候僅使用單獨的讀或者寫通道也可以確保函數的正常運行。對于StartTransfer函數的修改:
對于run_vdma_frame_buffer函數的修改:
之前叫run_triple_frame_buffer
導入display_ctrl_hdmi和dynclk驅動
在迪芝倫的提供的IP中有他們已經封裝好的驅動代碼,這里直接導入我們的SDK工程文件夾即可。
完成驅動導入后,在main.c中鍵入下面代碼:
#include
#include
#include
#include "xil_types.h"
#include "xil_cache.h"
#include "xparameters.h"
#include "xaxivdma.h"
#include "xaxivdma_i.h"
#include "display_ctrl_hdmi/display_ctrl.h"
#include "vdma_api/vdma_api.h"
//宏定義
#define BYTES_PIXEL 3 //像素字節數,RGB888占3個字節
#define DYNCLK_BASEADDR XPAR_AXI_DYNCLK_0_BASEADDR //動態時鐘基地址
#define VDMA_ID XPAR_AXIVDMA_0_DEVICE_ID //VDMA器件ID
#define DISP_VTC_ID XPAR_VTC_0_DEVICE_ID //VTC器件ID
//函數聲明
void colorbar(u8 *frame, u32 width, u32 height, u32 stride);
//全局變量
XAxiVdma vdma;
DisplayCtrl dispCtrl;
VideoMode vd_mode;
//frame buffer的起始地址
unsignedintconst frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x1000000);
int main(void)
{
xil_printf("HDMI Display 1920*1080 \r\n");
//設置video參數,分辨率:1920*1080
vd_mode = VMODE_1920x1080;
//配置VDMA
run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,frame_buffer_addr,0, 0,ONLY_READ);
//初始化Display controller
DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
//設置VideoMode
DisplaySetMode(&dispCtrl, &vd_mode);
DisplayStart(&dispCtrl);
//寫彩條
colorbar((u8*)frame_buffer_addr, vd_mode.width,vd_mode.height, vd_mode.width*BYTES_PIXEL);
return0;
}
//寫彩條函數(彩虹色)
void colorbar(u8 *frame, u32 width, u32 height, u32 stride)
{
u32 color_edge;
u32 x_pos, y_pos;
u32 y_stride = 0;
u8 rgb_r, rgb_b, rgb_g;
color_edge = width * BYTES_PIXEL / 7;
for (y_pos = 0; y_pos < height; y_pos++) {
for (x_pos = 0; x_pos < (width * BYTES_PIXEL); x_pos += BYTES_PIXEL) {
if (x_pos < color_edge) { //紅色
rgb_r = 0xFF;
rgb_g = 0;
rgb_b = 0;
} elseif ((x_pos >= color_edge) && (x_pos < color_edge * 2)) { //橙色
rgb_r = 0xFF;
rgb_g = 0x7F;
rgb_b = 0;
} elseif ((x_pos >= color_edge * 2) && (x_pos < color_edge * 3)) { //黃色
rgb_r = 0xFF;
rgb_g = 0xFF;
rgb_b = 0;
} elseif ((x_pos >= color_edge * 3) && (x_pos < color_edge * 4)) { //綠色
rgb_r = 0;
rgb_g = 0xFF;
rgb_b = 0;
} elseif ((x_pos >= color_edge * 4) && (x_pos < color_edge * 5)) { //青色
rgb_r = 0;
rgb_g = 0xFF;
rgb_b = 0xFF;
} elseif ((x_pos >= color_edge * 5) && (x_pos < color_edge * 6)) { //藍色
rgb_r = 0;
rgb_g = 0;
rgb_b = 0xFF;
} elseif ((x_pos >= color_edge * 6) && (x_pos < color_edge * 7)) { //紫色
rgb_r = 0x8B;
rgb_g = 0;
rgb_b = 0xFF;
}
frame[x_pos + y_stride + 0] = rgb_b;
frame[x_pos + y_stride + 1] = rgb_g;
frame[x_pos + y_stride + 2] = rgb_r;
}
y_stride += stride;
}
Xil_DCacheFlush(); //刷新Cache,數據更新至DDR3中
xil_printf("show color bar\r\n");
}
運行效果
在這里插入圖片描述
Reference
- 正點原子ZYNQ開發視頻