ZYNQ学习之旅--PS_AXI_VDMA(利用VDMA实现将PS端的数据显示在PL端的HDMI上)

简介

AXI VDMA(AXI Video Direct Memory Access,以下简称 VDMA),是 Xilinx 提供的软核 IP。其功能
和 AXI DMA(以下简称 DMA)有些类似,都可以为存储器或者 AXI4-Stream 类目标外设之间提供高带宽
直接存储器存取。和 DMA 相比,VDMA 增加了帧缓存的缓冲机制和同步锁相(GenLock)等功能,是为了针对视频图像应用而做的升级版的 DMA。VDMA 集成了视频专用功能,如帧同步和 2D DMA 传输等,非常适合基于 ZYNQ 架构上的图像和视频处理,缩短了开发周期。
DMA 和 VDMA 的对比如表所示。
在这里插入图片描述这里我们引入了帧缓存的缓冲机制和动态同步锁相两个概念,接下来向大家详细介绍这两个概念。

帧缓存

首先我们来看下什么是帧缓存。帧缓存存储器(Frame Buffer),简称帧缓存,也常被称作显存,是为
显示设备(如 HDMI 显示器、RGB LCD 液晶屏等)提供数据缓存的一片存储区域。一般图像输入源和图像显示的传输速率不匹配(如图像输入源传输速度较快或者图像显示端传输速度较快),这个时候需要一片存储区域来缓存输入的数据,以便显示设备读取数据,同时也方便后续对视频数据做图像处理。帧缓存的每一个存储单元对应屏幕上的一个像素,整个帧缓存对应一帧图像。
在实际应用中,一般选择外置的 DDR3 存储器作为帧缓存,而不是选择片内的 BRAM,这是由于 ZYNQ
片内的 BRAM 存储容量非常小。我们知道,ZYNQ7010 的 BRAM 存储容量为 2.1Mbit,ZYNQ7020 的存储容量为 4.9Mbit,假设 RGB LCD 屏的分辨率为 800480,那么存储一帧图像需要的存储容量为(如:RGB888数据格式=24 位)800480*24= 9216000bit≈8.79Mbit,整个 BRAM 的存储容量不足以存储单帧图像,更何况有时需要更多个显存的情况,因此使用外置的 DDR3 存储器作为帧缓存。
对于使用帧缓存存储器来缓存图像数据来说,可以采用单帧缓存或者多帧缓存的方案。单帧缓存是指图
像的输入和图像的显示都是通过读写同一片存储区域来实现的。虽然也可以实现显示设备显示图像的功能,但这会带来一个问题,即读出的单帧图像是输入的两帧图像或者更多帧图像数据叠加在一起的结果,可能会导致显示设备显示的图像出现割裂的现象。单帧缓存方案的读写示意图如下图所示:
在这里插入图片描述
由上图可知,随着输入数据源的不断写入,单帧存储区域会出现单帧图像数据或者两帧图像数据叠加在
一起的情况。当然,如果读出速度比写入速度慢很多的话,也可能出现更多帧图像叠加在一起,从而导致显示设备显示的图像出现割裂的现象。
对于本次 RGB HDMI 屏彩条显示实验来说,由于彩条只需要写入一次,写入完成后,后续只需要进行读
操作,因此本次实验使用单帧缓存的方案即可,那么采用 DMA 或者 VDMA 的方案区别不大。而对于需要频繁写入和读出的应用来说,比如摄像头的图像显示,如果仅仅使用单个帧缓存的话,势必会造成图像叠加的情况,这个时候就要采用 VDMA 的多帧缓存的方案了。本次实验旨在通过 VDMA 的 RGB HDMI 屏显示彩条的实验,让大家熟悉并掌握 VDMA IP 核的使用方法,为后续学习摄像头的图像显示实验打下基础。在 VDMA IP 核中,通过配置 VDMA 的同步锁相模式来配置帧缓存的缓冲进制,接下来我们向大家介绍 VDMA 的同步锁相模式。

同步锁相(Genlock )

在很多的视频应用中,图像输入端和输出端的数据速率不匹配,通常使用帧缓存来避免因速率不匹配而
导致的潜在错误。为了解决单帧缓存区域带来的图像叠加问题,通过分配多个帧缓存区域保存数据,图像输入端在写入其中一个帧缓存时,输出端读取其它的帧缓存。
VDMA 支持四种同步锁相模式,分别是 Genlock Master(同步锁相主模式)、Genlock Slave(同步锁从模式)、Dynamic Genlock Master (动态同步锁相主模式)和 Dynamic Genlock Slave(动态同步锁相从模式)。
VDMA 有一个写通道(S2MM)和一个读通道(MM2S),用户通过写通道将输入端数据写入帧缓存,
通过读通道将从帧缓存中读出数据。VDMA 的每一个通道都可以选择以上四种模式中的一种,接下来我们分别向大家介绍这四种同步模式。

Genlock Master

当写通道(S2MM)或者读通道(MM2S)配置为 Genlock Master 时,该通道不会跳过或者重复任一帧
缓存区域,按照帧缓存顺序读出数据。配置为 Genlock Slave 的通道应当紧跟 Genlock Master 通道变化,但有一定的延迟,延迟的大小在寄存器(*frmdly_stride[28:24])中配置。

Genlock Slave

当写通道(S2MM)或者读通道(MM2S)配置为 Genlock Slave 时,该通道会通过跳过或者重复一些帧缓存区域的方式,尝试与 Genlock Master 通道同步。

Dynamic Genlock Master

当写通道(S2MM)或者读通道(MM2S)配置为 Dynamic Genlock Master 时,该通道会跳过 Dynamic
Genlock Slave 通道正在操作的帧缓存,通过跳过或者重复一些帧缓存区域的方式来完成。
这里以分配三个帧缓存为例。当配置为 Dynamic Genlock Master 的通道访问帧缓存时,没有检测到 Slave通道访问的帧缓存,那么它会循环访问帧缓存 0 1 2 0 1 2;而如果检测到 Slave 访问的帧缓存区域,它们它会跳过该区域并开始访问下一帧缓存。因此,如果 Slave 通道长时间访问帧缓存 1,则 Master 会循环访问帧缓存 1 和 3。

Dynamic Genlock Slave

当写通道(S2MM)或者读通道(MM2S)配置为 Dynamic Genlock Slave 时,该通道会操作 Dynamic
Genlock Master 通道上一周期操作的帧。

VDMA 概述

VDMA 用于将 AXI Stream 格式的数据流转换为 Memory Map 格式或将 Memory Map 格式的数据转换
为 AXI Stream 数据流,也就是说 VDMA 内核旨在提供从 AXI4 域到 AXI4-Stream 域的视频读/写传输功能,反之亦然,从而实现系统内存(主要指 DDR3)和基于 AXI4-Stream 的目标视频 IP 之间的高速数据移动。VDMA 的框图如下图所示:
在这里插入图片描述
AXI4-Lite 可以对寄存器进行编程(配置),从而实现软件动态配置 VDMA 的功能。通过 AXI4-Lite 接
口对寄存器进行编程后,控制/状态逻辑块会为 DataMover 生成适当的命令,以在 AXI4 主接口上启动写入和读取命令。可配置的异步 line buffer 用于在将像素数据写入 AXI4-Memory Map 接口或 AXI4-Stream 接口之前临时保存像素数据。
VDMA 数据接口可以分为读、写两个通道,且写入和读取独立运行。用户可以通过写通道将 AXI-Stream
类型的数据流写入系统存储器(主要指 DDR3)。在读通道中,VDMA 使用 AXI4 主接口从系统存储器读取数据并在 AXI4-Stream 主接口上输出。可以看到,VDMA 本质上是一个数据搬运的 IP,可以看作是为视频图像处理做特殊优化的带有帧缓冲功能的高性能 DMA,为数据进出系统存储器提供了一种便捷的方案。
VDMA 内核不仅具有帧缓冲功能,而且集成了视频专用功能,如 Gen-Lock 和帧同步,用于完全同步的
帧 DMA 操作和 2D DMA 传输。除了同步之外,还可以使用帧存储编号和 scatter gather 或寄存器直接模式操作,以便中央处理器控制。在本设计中,不使用 VDMA scatter gather 功能,因为可以使用 VDMA 的更简单的寄存器直接模式充分实现系统,从而避免实现 scatter gather 功能带来的面积成本。只有在系统需要对VDMA 进行相对复杂的软件控制时,才应启用 scatter gather。

BD设计

在这里插入图片描述

软件设计

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xil_types.h"
#include "xil_cache.h"
#include "xparameters.h"
#include "xgpio.h"
#include "xaxivdma.h"
#include "xaxivdma_i.h"
#include "display_ctrl/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
#define AXI_GPIO_0_ID      XPAR_AXI_GPIO_0_DEVICE_ID  //PL端  AXI GPIO 0(lcd_id)器件ID
#define AXI_GPIO_0_CHANEL  1                          //PL按键使用AXI GPIO(lcd_id)通道1

//函数声明
void colorbar(u8 *frame, u32 width, u32 height, u32 stride);

//全局变量
XAxiVdma     vdma;
DisplayCtrl  dispCtrl;
XGpio        axi_gpio_inst;   //PL端 AXI GPIO 驱动实例
VideoMode    vd_mode;
//frame buffer的起始地址
unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x1000000);
unsigned int lcd_id=0;        //LCD ID

int main(void)
{
	vd_mode = VMODE_1280x1024;
	//配置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,vd_mode);
    //设置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 = 0x0F;
				rgb_g = 0;
				rgb_b = 0;
			} else if ((x_pos >= color_edge) && (x_pos < color_edge * 2)) {     //橙色
				rgb_r = 0x1F;
				rgb_g = 0x7F;
				rgb_b = 0;
			} else if ((x_pos >= color_edge * 2) && (x_pos < color_edge * 3)) { //黄色
				rgb_r = 0xFF;
				rgb_g = 0x0F;
				rgb_b = 0;
			} else if ((x_pos >= color_edge * 3) && (x_pos < color_edge * 4)) { //绿色
				rgb_r = 0;
				rgb_g = 0xF0;
				rgb_b = 0;
			} else if ((x_pos >= color_edge * 4) && (x_pos < color_edge * 5)) { //青色
				rgb_r = 0;
				rgb_g = 0x0F;
				rgb_b = 0x0F;
			} 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");
}
  • 7
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值