ZYNQ-使用HDMI显示器进行VDMA彩条显示测试

简介: ZYNQ-使用HDMI显示器进行VDMA彩条显示测试

学习内容


本文使用带有HDMI接口的显示器,构建图像视频显示的测试工程,利用VDMA进行传输图像视频数据,进行彩条显示的测试。

开发环境


vivado 18.3&SDK,PYNQ-Z2开发板。

准备工作


所用到的IP:VDMA、video out IP、Video Timing Controller、动态时钟配置 IP和DVI IP。

详细介绍参考前文:

  1. ZYNQ-AXI_VDMA IP简介
  2. ZYNQ-Video out IP和Video Timing Controller IP简介

系统框图


通过控制器(MCU)把彩条数据写入DDR,缓存到VDMA。然后通过AXI-stream to video out IP和VTC IP将视频数据转换为普通的视频接口的时序信号,然后通过GP0控制视频时序输出,,由显示器显示输出彩条图片。

image.png

硬件平台搭建


新建工程,创建 block design。

配置ZYNQ7


添加ZYNQ7 IP,对zynq进行初始化配置,

image.png

勾选M_AXI_GP0和S_AXI_HP0,

image.png

并添加时钟和复位信号。

image.png

完成配置后点击OK,配置后入下图:

image.png

配置VDMA IP


并添加VDMA ip核,如下图:

image.png

双击vdma ip核打开,进行配置ip核的相关信息。因为这里仅仅做显示测试,所以不需要进行帧缓存和调用写通道的资源,对于传输数据的位宽等信息根据自己将要显示图像大家进行合理配置。

image.png

配置Video out IP


添加 AXI4-Stream to Video Out IP,双击ip核打开,进行配置ip核的相关信息。按着图示配置即可,

image.png

配置VTC(Video Timing Controller ) IP


添加 Video Timing Controller IP,勾选AXI-Lite ,取消勾选监视器。

image.png

配置根据使用的显示器进行显示分辨率的设计,这里我就配置默认生成是1080p的

image.png

添加动态时钟配置 IP和DVI IP


迪芝伦公司提供的IP,IP 链接,用于动态生成视频时序信号的时钟。

连接数据通路


完成视频时序信号和视频信号的连接,

image.png

完成连接FCLK_CLK1信号连接,

image.png

完成动态时钟输出的视频时钟的连接,

image.png

完成连接后点击自动连接,勾选全部即可。

完成连接最终系统设计


完成连接最终系统设计如下:

image.png

然后我们进行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这里的导入示例,

image.png

这里可以把vdma_api.c进行封装,添加头文件,把要使用的函数进行声明。

image.png

#ifndef VDMA_API_H_
#define VDMA_API_H_
/*        Include File Definitions            */
#include "xaxivdma.h"
#include "xparameters.h"
#include "xil_exception.h"
/*          General Type Declarations         */
typedef enum
{
  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的读写通道,这里正点原子对函数进行了简单的修改,声明了一个结构体。这样可以在进行帧缓存的时候仅使用单独的读或者写通道也可以确保函数的正常运行。

在vdma_api.c,的函数Function Prototypes部分,也要修改添加参数。

image.png

对于StartTransfer函数的修改:

image.png

对于run_vdma_frame_buffer函数的修改:

image.png

导入display_ctrl_hdmi和dynclk驱动


image.png

在迪芝伦的提供的IP中有他们已经封装好的驱动代码,这里直接导入我们的SDK工程文件夹即可。

image.png

完成驱动导入后,在main.c中键入下面代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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的起始地址
unsigned int const 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);
    return 0;
}
//写彩条函数(彩虹色)
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;
      } else if ((x_pos >= color_edge) && (x_pos < color_edge * 2)) {     //橙色
        rgb_r = 0xFF;
        rgb_g = 0x7F;
        rgb_b = 0;
      } else if ((x_pos >= color_edge * 2) && (x_pos < color_edge * 3)) { //黄色
        rgb_r = 0xFF;
        rgb_g = 0xFF;
        rgb_b = 0;
      } else if ((x_pos >= color_edge * 3) && (x_pos < color_edge * 4)) { //绿色
        rgb_r = 0;
        rgb_g = 0xFF;
        rgb_b = 0;
      } else if ((x_pos >= color_edge * 4) && (x_pos < color_edge * 5)) { //青色
        rgb_r = 0;
        rgb_g = 0xFF;
        rgb_b = 0xFF;
      } else if ((x_pos >= color_edge * 5) && (x_pos < color_edge * 6)) { //蓝色
        rgb_r = 0;
        rgb_g = 0;
        rgb_b = 0xFF;
      } else if ((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");
}

运行效果


image.png

Reference


  1. 正点原子ZYNQ开发视频
目录
相关文章
|
缓存 开发工具 内存技术
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试(二)
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试
442 0
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试(二)
|
内存技术
zynq操作系统:DDR带宽测试
个别时候,嵌入式设备的DDR除了常规的遍历读写和压力测试外,会有提供读写带宽的需求,下面介绍一种没有精确要求的测法,参考自http://github.com/raas/mbw
387 0
|
监控 开发工具 Perl
ZYNQ-使用AXI DMA IP进行环路测试
ZYNQ-使用AXI DMA IP进行环路测试
438 0
ZYNQ-使用AXI DMA IP进行环路测试
|
监控 开发工具 内存技术
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试(一)
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试
731 0
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试(一)
|
21天前
|
测试技术 C语言
网站压力测试工具Siege图文详解
网站压力测试工具Siege图文详解
27 0
|
2月前
|
JavaScript jenkins 测试技术
这10款性能测试工具,收藏起来,测试人的工具箱!
这10款性能测试工具,收藏起来,测试人的工具箱!
|
2月前
|
测试技术
现代软件测试中的自动化工具与挑战
传统软件测试面临着越来越复杂的系统架构和不断增长的测试需求,自动化测试工具应运而生。本文将探讨现代软件测试中自动化工具的应用和挑战,深入分析其优势与局限性,为软件测试领域的发展提供思路和启示。
|
2月前
|
测试技术 持续交付
现代软件测试中的自动化工具应用与挑战
随着信息技术的快速发展,软件行业对于质量和效率的要求日益提高,自动化测试工具在软件开发过程中扮演着至关重要的角色。本文将探讨现代软件测试中自动化工具的应用现状以及所面临的挑战,旨在帮助开发人员更好地理解并充分利用这一技术手段。
|
3天前
|
机器学习/深度学习 数据采集 人工智能
【专栏】利用AI辅助工具提高软件测试效率与准确性
【4月更文挑战第27天】本文探讨了AI在软件测试中的应用,如自动执行测试用例、识别缺陷和优化测试设计。AI辅助工具利用机器学习、自然语言处理和图像识别提高效率,但面临数据质量、模型解释性、维护更新及安全性挑战。未来,AI将更注重用户体验,提升透明度,并在保护隐私的同时,通过联邦学习等技术共享知识。AI在软件测试领域的前景广阔,但需解决现有挑战。
|
2月前
|
jenkins 测试技术 持续交付
现代软件测试中的自动化工具与挑战
随着软件开发领域的不断发展,自动化测试工具在测试过程中扮演着越来越重要的角色。本文将探讨现代软件测试中自动化工具的应用及面临的挑战,旨在帮助开发人员和测试人员更好地理解和应对自动化测试中的问题。

热门文章

最新文章