基于EBAZ4205矿板的图像处理:03摄像头采集HDMI输出视频图像

本文介绍了如何使用EBAZ4205矿板配合OV5640摄像头进行图像处理,通过Vivado进行代码配置,重点关注了时钟设置、VDMA框架缓冲和DisplayInitializeAPI的使用,以实现1280x720分辨率的HDMI输出。
摘要由CSDN通过智能技术生成

基于EBAZ4205矿板的图像处理:03摄像头采集HDMI输出视频图像

先看效果

请添加图片描述
请添加图片描述

项目简介

  1. 我是使用的EBAZ4205矿板,超级大电工的转接板和我自己买的一块没有xclk的ov5640完成的该项目,没有设备需自备。
  2. 我就是跑通了正点原子的开源代码(下文会提供路径和链接),我感觉直接把正点原子的代码下载下来,然后改一下引脚就可以。但是我建议照着他们的代码和文档,自己搭一遍(我也是这么做的),因为一是我的vivado版本是2021.2,和他的vivado版本不同,二也是为了学习嘛。
  3. 本项目,设置OV5640输出分辨率为1280*720 ,PCLK = 72Mhz

项目内容

我的代码和正点原子的只有三处不同

  1. 许多IP版本不一致,所以建议自己搭一遍,然后照着正点原子的设置去改。
  2. video timing controller因IP版本不一致而带来的引脚不一致:我的vivado的vtc是6.2版本的,它多了一个sof_state,但经过我的实际测试,它空着不接即可。不会影响现有功能。
  3. 引脚定位不一致:下文我会给出我的xdc代码

代码

这里只提供xdc的代码,其他代码请直接看正点原子的开源代码

开源代码链接及其路径:领航者v2/2_Embedded_System/ZYNQ_SDK_7010/26_ov5640_hdmi

XDC

#----------------------摄像头接口的时钟---------------------------
#72M
create_clock -period 13.888 -name cam_pclk_0 [get_ports cam_pclk_0]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets cam_pclk_0_IBUF_BUFG]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets cam_pclk_0_IBUF]

set_property PACKAGE_PIN M17 [get_ports cam_vsync_0]
set_property IOSTANDARD LVCMOS33 [get_ports cam_vsync_0]
set_property PACKAGE_PIN J20 [get_ports cam_pclk_0]
set_property IOSTANDARD LVCMOS33 [get_ports cam_pclk_0]
set_property PACKAGE_PIN G19 [get_ports cam_pwdn_0]
set_property IOSTANDARD LVCMOS33 [get_ports cam_pwdn_0]
set_property PACKAGE_PIN N20 [get_ports cam_href_0]
set_property IOSTANDARD LVCMOS33 [get_ports cam_href_0]
set_property PACKAGE_PIN M18 [get_ports cam_rst_n_0]
set_property IOSTANDARD LVCMOS33 [get_ports cam_rst_n_0]
set_property PACKAGE_PIN H20 [get_ports {cam_data_0[7]}]
set_property PACKAGE_PIN K19 [get_ports {cam_data_0[6]}]
set_property PACKAGE_PIN J19 [get_ports {cam_data_0[5]}]
set_property PACKAGE_PIN L20 [get_ports {cam_data_0[4]}]
set_property PACKAGE_PIN L19 [get_ports {cam_data_0[3]}]
set_property PACKAGE_PIN L17 [get_ports {cam_data_0[2]}]
set_property PACKAGE_PIN L16 [get_ports {cam_data_0[1]}]
set_property PACKAGE_PIN M20 [get_ports {cam_data_0[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data_0[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data_0[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data_0[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data_0[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data_0[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data_0[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data_0[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {cam_data_0[0]}]
set_property PACKAGE_PIN H16 [get_ports UART_0_0_rxd]
set_property PACKAGE_PIN H17 [get_ports UART_0_0_txd]
set_property IOSTANDARD LVCMOS33 [get_ports UART_0_0_rxd]
set_property IOSTANDARD LVCMOS33 [get_ports UART_0_0_txd]
set_property PACKAGE_PIN B19 [get_ports {TMDS_0_tmds_data_p[2]}]
set_property PACKAGE_PIN C20 [get_ports {TMDS_0_tmds_data_p[1]}]
set_property PACKAGE_PIN D19 [get_ports {TMDS_0_tmds_data_p[0]}]
set_property PACKAGE_PIN F19 [get_ports TMDS_0_tmds_clk_p]

#cam_scl:
set_property -dict {PACKAGE_PIN P18 IOSTANDARD LVCMOS33} [get_ports {emio_sccb_tri_io[0]}]
#cam_sda:
set_property -dict {PACKAGE_PIN M19 IOSTANDARD LVCMOS33} [get_ports {emio_sccb_tri_io[1]}]
set_property PULLUP true [get_ports {emio_sccb_tri_io[1]}]


代码研读

既然是学习,就不能浅尝辄止,下面是一些我学习时的笔记。

API函数:run_vdma_frame_buffer()

这个函数将根据ID配置VDMA的读写路径,ID就是直接上xparameters.h找到你在vivado中配置的VDMA ID即可。

#define XPAR_AXIVDMA_0_DEVICE_ID XPAR_AXI_VDMA_0_DEVICE_ID
参数列表
位置参数含义
1InstancePtr是XAxiVdma数据结构的句柄
2DeviceId当前VDMA的设备ID
3hize帧的水平大小,以像素为单位
4vsize帧的垂直大小。
5buf_base_addrVDMA写入和读取帧的缓冲区地址
6number_frame_count指定中断应该在多少帧之后到来
7enable_frm_cnt_intr置1时启用帧计数中断。
8select可设置读取通道、写入通道或读取和写入通道。
返回值

XST_SUCCESS 和 -XST_FAILURE

本项目设置
run_vdma_frame_buffer(&vdma, VDMA_ID, vd_mode.width, vd_mode.height,
						frame_buffer_addr,0,0,BOTH);

其中,选择关闭帧计数中断,和中断计数,并将通道选择为both,即为读写通道

frame_buffer_adder的计算

这个是重点

unsigned int const frame_buffer_addr = (XPAR_PS7_DDR_0_S_AXI_BASEADDR
										+ 0x1000000);

其中XPAR_PS7_DDR_0_S_AXI_BASEADDR比较好理解,它就是我们在建立BD文件时,为这个VDMA分配的帧缓存空间的起始地址

/* Definitions for peripheral PS7_DDR_0 */
#define XPAR_PS7_DDR_0_S_AXI_BASEADDR 0x00100000
#define XPAR_PS7_DDR_0_S_AXI_HIGHADDR 0x0FFFFFFF

而加上0x1000000是为了给ps端运行留一些内存,让他使用的。

API函数:DisplayInitialize()

这个API函数能够用来配置我们放置在BD中的动态时钟的,并且它能够通过当前的video timing contraller 的ID获知,当前使用的是什么格式的视频流,进而查表后输出相应的时钟频率。
最终要的是,它能够返回一个dispCtrl结构句柄,我们可以使用这个句柄完成各种操作。

DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);

第二个参数就是当前的video timing contraller 的ID,然后他会把得到的视频格式信息写入dispCtrl结构句柄中,给后续流程使用。

typedef struct {
		u32 	 dynClkAddr;/*Physical Base address of the dynclk core*/
		XVtc vtc;		 	/*VTC driver struct*/
		VideoMode vMode; 	/*Current Video mode*/
		double pxlFreq;		/* Frequency of clock currently being generated */
		DisplayState state; /* Indicates if the Display is currently running */
} DisplayCtrl;
typedef struct {
	char label[64]; /* Label describing the resolution */
	u32 width; /*Width of the active video frame*/
	u32 height; /*Height of the active video frame*/
	u32 hps; /*Start time of Horizontal sync pulse, in pixel clocks (active width + H. front porch)*/
	u32 hpe; /*End time of Horizontal sync pulse, in pixel clocks (active width + H. front porch + H. sync width)*/
	u32 hmax; /*Total number of pixel clocks per line (active width + H. front porch + H. sync width + H. back porch) */
	u32 hpol; /*hsync pulse polarity*/
	u32 vps; /*Start time of Vertical sync pulse, in lines (active height + V. front porch)*/
	u32 vpe; /*End time of Vertical sync pulse, in lines (active height + V. front porch + V. sync width)*/
	u32 vmax; /*Total number of lines per frame (active height + V. front porch + V. sync width + V. back porch) */
	u32 vpol; /*vsync pulse polarity*/
	double freq; /*Pixel Clock frequency*/
} VideoMode;

static const VideoMode VMODE_480x272 = {
	.label = "480x272@60Hz",
	.width = 480,
	.height = 272,
	.hps = 482,
	.hpe = 523,
	.hmax = 525,
	.hpol = 0,
	.vps = 274,
	.vpe = 284,
	.vmax = 286,
	.vpol = 0,
	.freq = 9
};

static const VideoMode VMODE_640x480 = {
	.label = "640x480@60Hz",
	.width = 640,
	.height = 480,
	.hps = 656,
	.hpe = 752,
	.hmax = 799,
	.hpol = 0,
	.vps = 490,
	.vpe = 492,
	.vmax = 524,
	.vpol = 0,
	.freq = 25.12
};

结构句柄dispCtrl

这个句柄相当重要,它的成员变量有:动态CLK的地址dynClkAddr,当前视频流vtc结构体vtc,视频格式vMode,当前视频流的pclk,和用于控制这个结构句柄指向的视频流的工作状态state

typedef struct {
		u32 	 dynClkAddr;/*Physical Base address of the dynclk core*/
		XVtc vtc;		 	/*VTC driver struct*/
		VideoMode vMode; 	/*Current Video mode*/
		double pxlFreq;		/* Frequency of clock currently being generated */
		DisplayState state; /* Indicates if the Display is currently running */
} DisplayCtrl;

通过调整state就能控制视频流是否工作。

typedef enum {
	DISPLAY_STOPPED = 0,
	DISPLAY_RUNNING = 1
} DisplayState;
dispPtr->state = DISPLAY_RUNNING

API函数 DisplayInitialize

用于初始化dispCtrl结构句柄,注意它会缺省地将视频输出模式设置为800X480,所以需要用DisplaySetMode对其进行配置

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值