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开发视频
目录
相关文章
|
3天前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
46 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
缓存 开发工具 内存技术
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试(二)
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试
945 0
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试(二)
|
监控 开发工具 Perl
ZYNQ-使用AXI DMA IP进行环路测试
ZYNQ-使用AXI DMA IP进行环路测试
693 0
ZYNQ-使用AXI DMA IP进行环路测试
|
监控 开发工具 内存技术
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试(一)
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试
1120 0
ZYNQ-使用自定义AXI总线IP核进行DDR读写测试(一)
|
内存技术
zynq操作系统:DDR带宽测试
个别时候,嵌入式设备的DDR除了常规的遍历读写和压力测试外,会有提供读写带宽的需求,下面介绍一种没有精确要求的测法,参考自http://github.com/raas/mbw
589 0
|
Linux 测试技术 芯片
|
存储 编解码 测试技术
4K显示器测试
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/catoop/article/details/79600737 1、打开的画图工具...
3040 0
|
1月前
|
数据可视化 前端开发 测试技术
接口测试新选择:Postman替代方案全解析
在软件开发中,接口测试工具至关重要。Postman长期占据主导地位,但随着国产工具的崛起,越来越多开发者转向更适合中国市场的替代方案——Apifox。它不仅支持中英文切换、完全免费不限人数,还具备强大的可视化操作、自动生成文档和API调试功能,极大简化了开发流程。
|
5天前
|
JSON 前端开发 测试技术
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
43 10
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡

热门文章

最新文章