当前位置:   article > 正文

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

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

基于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]}]


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

代码研读

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

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
  • 1
参数列表
位置参数含义
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);
  • 1
  • 2

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

frame_buffer_adder的计算

这个是重点

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

其中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
  • 1
  • 2
  • 3

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

API函数:DisplayInitialize()

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

DisplayInitialize(&dispCtrl, DISP_VTC_ID, DYNCLK_BASEADDR);
  • 1

第二个参数就是当前的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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
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
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

结构句柄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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

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

API函数 DisplayInitialize

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/539336
推荐阅读
相关标签
  

闽ICP备14008679号