S03_CH10_DMA_4_Video_Stitch视频拼接系统

10.1概述

注意:本课程和上一课程《S03_CH09_DMA_4_Video_Switch视频切换系统》基本相同,不同部分用紫色字体或者框图注明。

PL通过 OV7725 OV7725 实时 采集 1路 64 0×480 0×480 视频,分别将原始彩色、 视频,分别将原始彩色、 R分量、 G分量、 B分量作为常量输出,实现背景为红、绿、蓝 视频共4路视频通过4个独立的AXI DMA IP核传输至PS的DDR中进行缓存,然后再通过AXIDMA将4路视频同时从DDR读出,通过PL在VGA显示器上以1080P分辨率同时显示4路原始分辨率拼接而成的视频。

10.2修改OV_Sensor_ML摄像头采集IP

由于MIZ702开发板只有1路摄像头视频输入接口,MIZ701N和MIZ702N只有2路视频输入接口,无法满足演示4路视频输入的接口需求,因此修改OV_Sensor_ML ip 使之输出4路数据通路。修改的代码如下:

表10-2-1:

module OV_Sensor_ML(

input CLK_i,

//---------------------------- CMOS sensor hardware interface --------------------------/

input cmos_vsync_i, //cmos vsync

input cmos_href_i, //cmos hsync refrence

input cmos_pclk_i, //cmos pxiel clock

output cmos_xclk_o, //cmos externl clock

input[7:0] cmos_data_i, //cmos data

output hs_o,//hs signal.

output vs_o,//vs signal.

// output de_o,//data enable.

output [23:0] rgb_o1,//data output,

output [23:0] rgb_o2,//data output,

output [23:0] rgb_o3,//data output,

output [23:0] rgb_o4,//data output,

output vid_clk_ce

);

//----------------------视频输出解码模块---------------------------//

wire  [15:0]rgb_o_r;

assign rgb_o1 = {rgb_o_r[4:0],3'd0 ,rgb_o_r[10:5],2'd0,rgb_o_r[15:11],3'd0};

assign rgb_o2 = {5'b11111    ,3'd0 ,rgb_o_r[10:5],2'd0,rgb_o_r[15:11],3'd0};

assign rgb_o3 = {rgb_o_r[4:0],3'd0 ,rgb_o_r[10:5],2'd0,5'b11111      ,3'd0};

assign rgb_o4 = {rgb_o_r[4:0],3'd0 ,6'b111111    ,2'd0,rgb_o_r[15:11],3'd0};

reg [7:0]cmos_data_r;

reg cmos_href_r;

reg cmos_vsync_r;

always@(posedge cmos_pclk_i)

begin

cmos_data_r <= cmos_data_i;

cmos_href_r <= cmos_href_i;

cmos_vsync_r<= cmos_vsync_i;

end

//assign rgb_o = 24'b11111111_00000000_11111111;

cmos_decode cmos_decode_u0(

//system signal.

.cmos_clk_i(CLK_i),//cmos senseor clock.

.rst_n_i(RESETn_i2c),//system reset.active low.

//cmos sensor hardware interface.

.cmos_pclk_i(cmos_pclk_i),//(cmos_pclk),//input pixel clock.

.cmos_href_i(cmos_href_r),//(cmos_href),//input pixel hs signal.

.cmos_vsync_i(cmos_vsync_r),//(cmos_vsync),//input pixel vs signal.

.cmos_data_i(cmos_data_r),//(cmos_data),//data.

.cmos_xclk_o(cmos_xclk_o),//(cmos_xclk),//output clock to cmos sensor.

//user interface.

.hs_o(hs_o),//hs signal.

.vs_o(vs_o),//vs signal.

// .de_o(de_o),//data enable.

.rgb565_o(rgb_o_r),//data output

.vid_clk_ce(vid_clk_ce)

);

count_reset_v1#(

.num(20'hffff0)

)(

.clk_i(CLK_i),

.rst_o(RESETn_i2c)

);

修改后代码为的为OV_Sensor_ML.v上表中红色字体部分,可以看出,代码之作了简单修改,增加了3路数据输出,为了让数据颜色有对比,第一路保持原始图像数据颜色,剩余三路增加了颜色背景。这样在做切换测试的时候就可以看到不同的通路变化了。

当然对于MIZ701N和MIZ702N可以使用2个OV_Sensor_ML接2个摄像头,每个模块出来2个数据通道就可以了。如果要接4个摄像头,可以购买我们的外扩摄像头模块再扩展2路摄像头就可以实现4路摄像头了。

10.3搭建硬件系统

10.3.1系统图

完成了IP的修改后,下面就可以搭建硬件系统了,由于VIVADO采用了图形化设计,带来了很大便捷。下面分别把MIZ702的系统构架图贴出来。

由于图片太大,只能看到大概的框架,大家学习的时候可以打开工程放大后去阅读,这里为了分析的时候方便把局部视图放大截图。

10.3.2 OV_Sensor_ML IP接线图

下图中主要是前面我们自定义OV_Sensor_ML采集IP修改后的图形界面。可以看到多出了rgb_o1、rgb_o2、rgb_o3、rgb_o4接口这样我们就虚拟了4路摄像头数据输入接口啦。OV_Sensor_ML前天的信号还是不变。下图中,还有2个信号分别是FCLK_CLK0和clk_out1他们分别是VID_IN IP和VID_OUT IP相关的时钟,把它们引出去到顶层模块中,后面需要使用到。

当然对于MIZ701N和MIZ702N可接2路摄像头的,那么这个模块只要接出2个通道就可以了,并且使用2个这样的模块。如下图是MIZ702N和MIZ701N连接2路摄像头。这里是链接了rbg_o1和rgb_o2。


10.3.3 vid_in IP的接线图

下图大家可以放大后观看,vid_in IP的输入接口是连接到摄像头采样输出IP的。vid_in IP的输出接口是和和DMA链接了。DMA输入相关的信号被引出到外部,用来添加FPGA代码实现写DMA时序。还有一个vid_io_reset信号,是用来控制所有vid_in 和vid_out IP的同步,也是连接到外部,用FPGA代码控制。


10.3.4 DMA 和FIFO通路

下图是 DMA和data fifo的链接通路。

上图中data fifo 的M_AXIS将被引出到外部受FPGA代码控制。如下图,FIFO_M_AXIS_0就是连接到axis_data_fifo_0的M_AXIS接口的。双击此接口需要设置时钟,这里的数据速度时钟是150MHZ(MIZ701N是标准的148.5MHZ) 。不同的分辨率应当设置对应的分辨率时钟。


10.3.5 vid_out IP的通路

输出部分可以看到vid_out输出的是VGA时序信号,在VGA时序信号上,我们还挂载了一个VGA转HMDI的IP实现了HDMI和VGA同时输出(MIZ701N没有VGA所以无需把VGA信号,引出去)

上图中的vid out IP数据输入通道如图所示

双击这个接口也要设置时钟频率,由于输出像素为1920X108060HZ因此为15000000HZ(MIZ701N是标准的148500000)


10.3.6 AXI HP通道和DMA中断

由于是四路视频输入,外接了4个DMA模块因此使用了4个HP和8个DMA中断如图


10.3.7 DMA IP的设置

下图中,同时勾选读通道和写通道,另外设置,Wideh of buffer length register 为23bit 这个含义是2的23次方8,388,607bytes 8M大小。一副1080P的图像大小为 1920X1080//1024/1024*4=7.9M因此一次DMA就可以传输一副1080P的图像。


10.3.7 时钟管理模块

时钟管理模块前面已经讲过了,1920X1080的分辨率是设置150MHZ(MIZ701N是148.5MHZ),不在具体累述。

10.3.8 VTC 图像时序发生模块

VTC图像时序发生模块的使用只要配置对应的分辨率,这里是设置640X480的分辨率,前面章节已经讲过不再累述。

10.4 FPGA 四路输入以及图像拼接源码分析

10.4.1 图像常量参数

表10-4-1

localparam     MONITOR_HEIGHT = 11'd1080;//设置输出行分辨率

localparam     MONITOR_WIDTH  = 11'd1920;//设置输出列分辨率

localparam     VIDEO_HEIGHT = 11'd480;//设置输入视频行分辨率

localparam     VIDEO_WIDTH  = 11'd640;//设置输入视频列分辨率

localparam     GAP_HEIGHT = 11'd100;//设置四副图形中间的空白尺寸高度

localparam     GAP_WIDTH  = 11'd100;//设置四副图形中间的空白尺寸宽度

localparam     BLACK_HEIGHT = (MONITOR_HEIGHT - 2 * VIDEO_HEIGHT - GAP_HEIGHT) >> 1;//上下边框高度 BLACK_HEIGHT = 10

localparam     BLACK_WIDTH  = (MONITOR_WIDTH - 2 * VIDEO_WIDTH - GAP_WIDTH) >> 1;//左右边框宽度 BLACK_WIDTH =270

localparam     CH01_V_START = BLACK_HEIGHT;//第一路图像垂直开始 CH01_V_START = 10

localparam     CH01_V_END   = BLACK_HEIGHT + VIDEO_HEIGHT;//第一路图像垂直结束 CH01_V_END=490

localparam     CH23_V_START = BLACK_HEIGHT + VIDEO_HEIGHT + GAP_HEIGHT;//第二路图像垂直开始 CH23_V_START=590

localparam     CH23_V_END   = BLACK_HEIGHT + VIDEO_HEIGHT + GAP_HEIGHT + VIDEO_HEIGHT;//第二路图像垂直结束 CH23_V_END= 1070

localparam     CH02_H_START = BLACK_WIDTH;//第一路图像水平开始 CH02_H_START=270

localparam     CH02_H_END   = BLACK_WIDTH + VIDEO_WIDTH;//第一路图像水平结束CH02_H_END=910

localparam     CH13_H_START = BLACK_WIDTH + VIDEO_WIDTH + GAP_WIDTH;//第二路图像水平开始1010

localparam     CH13_H_END   = BLACK_WIDTH + VIDEO_WIDTH + GAP_WIDTH + VIDEO_WIDTH;第二路图像水平结束//1550

以上代码是对图像的输出分辨率,被拼接图像的分辨率、空白、边框、背景进行设置。

10.4.2 DMA 4路视频输入的FPGA代码

always@(posedge FCLK_CLK0)

begin

if(!gpio_rtl_tri_o_0)

v_cnt_0 <= 11'd0;

else

if(m_axis_video_0_tvalid & s_axis_dma_0_tready & m_axis_video_0_tlast)

if(v_cnt_0 != 11'd479)

v_cnt_0 <= v_cnt_0 + 1'b1;

else

v_cnt_0 <= 11'd0;

else

v_cnt_0 <= v_cnt_0;

end

表10-4-2-1

上表可以看到gpio_rtl_tri_o_0就是可编程的复位信号,可以用C代码控制同步时序。上表的代码实现的是视频通路0的vs 行计数器。可以看出来计数器在m_axis_video_0_tvalid (vid in输出数据有效)、 s_axis_dma_0_tready(DMA通道准备好) 、m_axis_video_0_tlast (vid_in 行结束信号)都有效的时候累加1。这里的分辨率是640X480因此累计一共480行数据。由于使用了4个输入输入通道,因此vs 行计数器的完成代码如下表。

表10-4-2-2

always@(posedge FCLK_CLK0)

begin

if(!gpio_rtl_tri_o_0)

v_cnt_0 <= 11'd0;

else

if(m_axis_video_0_tvalid & s_axis_dma_0_tready & m_axis_video_0_tlast)

if(v_cnt_0 != 11'd479)

v_cnt_0 <= v_cnt_0 + 1'b1;

else

v_cnt_0 <= 11'd0;

else

v_cnt_0 <= v_cnt_0;

end

always@(posedge FCLK_CLK0)

begin

if(!gpio_rtl_tri_o_0)

v_cnt_1 <= 11'd0;

else

if(m_axis_video_1_tvalid & s_axis_dma_1_tready & m_axis_video_1_tlast)

if(v_cnt_1 != 11'd479)

v_cnt_1 <= v_cnt_1 + 1'b1;

else

v_cnt_1 <= 11'd0;

else

v_cnt_1 <= v_cnt_1;

end

always@(posedge FCLK_CLK0)

begin

if(!gpio_rtl_tri_o_0)

v_cnt_2 <= 11'd0;

else

if(m_axis_video_2_tvalid & s_axis_dma_2_tready & m_axis_video_2_tlast)

if(v_cnt_2 != 11'd479)

v_cnt_2 <= v_cnt_2 + 1'b1;

else

v_cnt_2 <= 11'd0;

else

v_cnt_2 <= v_cnt_2;

end

always@(posedge FCLK_CLK0)

begin

if(!gpio_rtl_tri_o_0)

v_cnt_3 <= 11'd0;

else

if(m_axis_video_3_tvalid & s_axis_dma_3_tready & m_axis_video_3_tlast)

if(v_cnt_3 != 11'd479)

v_cnt_3 <= v_cnt_3 + 1'b1;

else

v_cnt_3 <= 11'd0;

else

v_cnt_3 <= v_cnt_3;

end

下表是s_axis_dma_0_tlast、s_axis_dma_1_tlast、s_axis_dma_2_tlast、s_axis_dma_3_tlast代表每个通道一副图像传输完成后的last 信号。这个信号为高电平1个周期,提交一次DMA数据到DDR,并且会产生一次对应端口的中断信号。

表10-4-2-3

assign s_axis_dma_0_tlast = m_axis_video_0_tvalid & s_axis_dma_0_tready & m_axis_video_0_tlast &(v_cnt_0 == 11'd479);

assign s_axis_dma_1_tlast = m_axis_video_1_tvalid & s_axis_dma_1_tready & m_axis_video_1_tlast &(v_cnt_1 == 11'd479);

assign s_axis_dma_2_tlast = m_axis_video_2_tvalid & s_axis_dma_2_tready & m_axis_video_2_tlast &(v_cnt_2 == 11'd479);

assign s_axis_dma_3_tlast = m_axis_video_3_tvalid & s_axis_dma_3_tready & m_axis_video_3_tlast &(v_cnt_3 == 11'd479);

10.4.3 DMA 输出通道

表10-4-3-1

always@(posedge clk_out1)

begin

if(!gpio_rtl_tri_o_0)

h_cnt <= 11'd0;

else

if(video_out_tvalid & video_out_tready)        

if(h_cnt != (MONITOR_WIDTH - 1'b1))

h_cnt <= h_cnt + 1'b1;

else

h_cnt <= 11'd0;

else

h_cnt <= h_cnt;

end

上表是vid out ip 输入数据部分的列计数器,一共有1920列。当video_out_tvalid(FIFO输出数据有效信号)和video_out_tready(vid out IP准备好接收数据信号)都为1的时候开始计数。

表10-4-3-2

always@(posedge clk_out1)

begin

if(!gpio_rtl_tri_o_0)

v_cnt <= 11'd0;

else

if(video_out_tvalid & video_out_tready & (h_cnt == (MONITOR_WIDTH - 1'b1)))

if(v_cnt != (MONITOR_HEIGHT - 1'b1))

v_cnt <= v_cnt + 1'b1;

else

v_cnt <= 11'd0;

else

v_cnt <= v_cnt;

end

上表是vid out IP 输入数据的行计数器,当video_out_tvalid (FIFO数据输出有效) video_out_tready (vid out 准备好接收数据信号)和h_cnt == 11'd1919 共计1920点(代表一行数据结束)行计数器v_cnt 加1。

always@(posedge clk_out1)

begin

if(!gpio_rtl_tri_o_0)

channel_switch <= 3'd4;

else    

if((v_cnt >= CH01_V_START) && (v_cnt < CH01_V_END))

if((h_cnt >= CH02_H_START) && (h_cnt < CH02_H_END))

channel_switch <= 3'd0;

else if((h_cnt >= CH13_H_START) && (h_cnt < CH13_H_END))

channel_switch <= 3'd1;

else

channel_switch <= 3'd4;

else if((v_cnt >= CH23_V_START) && (v_cnt < CH23_V_END))

if((h_cnt >= CH02_H_START) && (h_cnt < CH02_H_END))

channel_switch <= 3'd2;

else if((h_cnt >= CH13_H_START) && (h_cnt < CH13_H_END))

channel_switch <= 3'd3;

else

channel_switch <= 3'd4;

else

channel_switch <= 3'd4;                        

end

表10-4-3-3

上表代码实现了视频在显示器上的拼接输出,有点类似前面的四路切换方案,区别是这次是把所有视频全部输出到1080P的显示器上了。

表10-4-3

assign video_out_tdata =   (channel_switch == 3'd0) ? FIFO_M_AXIS_0_tdata[23:0] :

((channel_switch == 3'd1) ? FIFO_M_AXIS_1_tdata[23:0] :

((channel_switch == 3'd2) ? FIFO_M_AXIS_2_tdata[23:0] :

((channel_switch == 3'd3) ? FIFO_M_AXIS_3_tdata[23:0] : 24'd0)));

assign video_out_tvalid =  (channel_switch == 3'd0) ? FIFO_M_AXIS_0_tvalid :

((channel_switch == 3'd1) ? FIFO_M_AXIS_1_tvalid :

((channel_switch == 3'd2) ? FIFO_M_AXIS_2_tvalid :

((channel_switch == 3'd3) ? FIFO_M_AXIS_3_tvalid : 1'b1)));

assign video_out_tuser = video_out_tvalid & video_out_tready & (h_cnt == 11'd0) & (v_cnt == 11'd0);    

assign video_out_tlast = (h_cnt == (MONITOR_WIDTH - 1'b1)) ? 1'b1 : 1'b0;

assign FIFO_M_AXIS_0_tready = (channel_switch == 3'd0) ? video_out_tready : 1'b0;

assign FIFO_M_AXIS_1_tready = (channel_switch == 3'd1) ? video_out_tready : 1'b0;

assign FIFO_M_AXIS_2_tready = (channel_switch == 3'd2) ? video_out_tready : 1'b0;

assign FIFO_M_AXIS_3_tready = (channel_switch == 3'd3) ? video_out_tready : 1'b0;

上表中,video_out_tvalid 是代表了FIFO输出的有效数据的信号,通过channel_switch 切换到当前选定的FIFO valid 信号上。

上表中,video_out_tdata 是代表了FIFO输出的数据通道,通过channel_switch 切换到当前选定的FIFO数据通道。

上表中,video_out_tuser  是代表了vid out 一帧图像开始信号。每行从0开始第一个数据。当video_out_tvalid(FIFO 输出数据有效)、 video_out_tready(vid out 可以接收数据信号)、h_cnt==11’d0(第一行第一个数据)、v_cnt ==11’d0(一帧图像的第0行)都满足条件video_out_tuser输出1,告知vid_out IP 一帧图像开始。

上表中,video_out_tlast 代表了vid out 输入图像数据的最后一行最后一个数据,这里是1920X1080的图像,因此到1919,每一行最后一个数据都要输出 video_out_tlast 为1.

10.5 4路视频切换DMA C处理源码分析

10.5.4.1 main.c源码

表10-5-1 main.c

/*

*

* www.osrc.cn

* www.milinker.com

* copyright by nan jin mi lian dian zi www.osrc.cn`

* axi dma test

*

*/

#include "dma_intr.h"

#include "sys_intr.h"

#include "xgpio.h"

volatile int TxDone0;

volatile int TxDone1;

volatile int TxDone2;

volatile int TxDone3;

volatile int RxDone0;

volatile int RxDone1;

volatile int RxDone2;

volatile int RxDone3;

volatile u8 tx0_buffer_index;

volatile u8 rx0_buffer_index;

volatile u8 tx1_buffer_index;

volatile u8 rx1_buffer_index;

volatile u8 tx2_buffer_index;

volatile u8 rx2_buffer_index;

volatile u8 tx3_buffer_index;

volatile u8 rx3_buffer_index;

volatile int Error;

u32 *BufferPtr0[3];

u32 *BufferPtr1[3];

u32 *BufferPtr2[3];

u32 *BufferPtr3[3];

XAxiDma AxiDma0;

XAxiDma AxiDma1;

XAxiDma AxiDma2;

XAxiDma AxiDma3;

/************************** Variable Definitions *****************************/

static XScuGic Intc; //GIC

static XGpio Gpio;

#define AXI_GPIO_DEV_ID         XPAR_AXI_GPIO_0_DEVICE_ID

int init_intr_sys(void)

{

// initial DMA interrupt handle

DMA_Intr_Init(&AxiDma0,XPAR_AXIDMA_0_DEVICE_ID);

DMA_Intr_Init(&AxiDma1,XPAR_AXIDMA_1_DEVICE_ID);

DMA_Intr_Init(&AxiDma2,XPAR_AXIDMA_2_DEVICE_ID);

DMA_Intr_Init(&AxiDma3,XPAR_AXIDMA_3_DEVICE_ID);

Init_Intr_System(&Intc); // initial DMA interrupt system

Setup_Intr_Exception(&Intc);

DMA_Setup_Intr_System(&Intc,&AxiDma0,TX0_INTR_ID,RX0_INTR_ID);//setup dma interrpt system

DMA_Setup_Intr_System(&Intc,&AxiDma1,TX1_INTR_ID,RX1_INTR_ID);//setup dma interrpt system

DMA_Setup_Intr_System(&Intc,&AxiDma2,TX2_INTR_ID,RX2_INTR_ID);//setup dma interrpt system

DMA_Setup_Intr_System(&Intc,&AxiDma3,TX3_INTR_ID,RX3_INTR_ID);//setup dma interrpt system

DMA_Intr_Enable(&Intc,&AxiDma0);

DMA_Intr_Enable(&Intc,&AxiDma1);

DMA_Intr_Enable(&Intc,&AxiDma2);

DMA_Intr_Enable(&Intc,&AxiDma3);

}

int main(void)

{

int Status;

XGpio_Initialize(&Gpio, AXI_GPIO_DEV_ID);

XGpio_SetDataDirection(&Gpio, 1, 0);

BufferPtr0[0] = (u32 *)CH0_BUFFER0_BASE;

BufferPtr0[1] = (u32 *)CH0_BUFFER1_BASE;

BufferPtr0[2] = (u32 *)CH0_BUFFER2_BASE;

BufferPtr1[0] = (u32 *)CH1_BUFFER0_BASE;

BufferPtr1[1] = (u32 *)CH1_BUFFER1_BASE;

BufferPtr1[2] = (u32 *)CH1_BUFFER2_BASE;

BufferPtr2[0] = (u32 *)CH2_BUFFER0_BASE;

BufferPtr2[1] = (u32 *)CH2_BUFFER1_BASE;

BufferPtr2[2] = (u32 *)CH2_BUFFER2_BASE;

BufferPtr3[0] = (u32 *)CH3_BUFFER0_BASE;

BufferPtr3[1] = (u32 *)CH3_BUFFER1_BASE;

BufferPtr3[2] = (u32 *)CH3_BUFFER2_BASE;

/* Initialize flags before start transfer test  */

TxDone0 = 0;

TxDone1 = 0;

TxDone2 = 0;

TxDone3 = 0;

RxDone0 = 0;

TxDone1 = 0;

TxDone2 = 0;

TxDone3 = 0;

tx0_buffer_index = 0;

rx0_buffer_index = 0;

tx1_buffer_index = 0;

rx1_buffer_index = 0;

tx2_buffer_index = 0;

rx2_buffer_index = 0;

tx3_buffer_index = 0;

rx3_buffer_index = 0;

Error = 0;

init_intr_sys();

Miz702_EMIO_init();

ov7725_init_rgb();

XGpio_DiscreteWrite(&Gpio, 1, 1);

Status = XAxiDma_SimpleTransfer(&AxiDma0, (u32)BufferPtr0[tx0_buffer_index],

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma0 failed! %d\r\n", Status);

return XST_FAILURE;

}

Status = XAxiDma_SimpleTransfer(&AxiDma1, (u32)BufferPtr1[tx1_buffer_index],

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma1 failed! %d\r\n", Status);

return XST_FAILURE;

}

Status = XAxiDma_SimpleTransfer(&AxiDma2, (u32)BufferPtr2[tx2_buffer_index],

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma2 failed! %d\r\n", Status);

return XST_FAILURE;

}

Status = XAxiDma_SimpleTransfer(&AxiDma3, (u32)BufferPtr3[tx3_buffer_index],

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma3 failed! %d\r\n", Status);

return XST_FAILURE;

}

Status = XAxiDma_SimpleTransfer(&AxiDma0, (u32)BufferPtr0[rx0_buffer_index],

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

xil_printf("rx axi dma0 failed! %d\r\n", Status);

return XST_FAILURE;

}

Status = XAxiDma_SimpleTransfer(&AxiDma1, (u32)BufferPtr1[rx1_buffer_index],

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

xil_printf("rx axi dma1 failed! %d\r\n", Status);

return XST_FAILURE;

}

Status = XAxiDma_SimpleTransfer(&AxiDma2, (u32)BufferPtr2[rx2_buffer_index],

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma2 failed! %d\r\n", Status);

return XST_FAILURE;

}

Status = XAxiDma_SimpleTransfer(&AxiDma3, (u32)BufferPtr3[rx3_buffer_index],

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma3 failed! %d\r\n", Status);

return XST_FAILURE;

}

while (1) ;

return XST_SUCCESS;

}

上表中的代码我们很熟悉了,这里是注册了4个DMA通道,8个中断(接收和发送4路)。

10.5.4.2 dma_intr.h源码

表10-5-2 dma_intr.h

/*

*

* www.osrc.cn

* www.milinker.com

* copyright by nan jin mi lian dian zi www.osrc.cn

*/

#ifndef DMA_INTR_H

#define DMA_INTR_H

#include "xaxidma.h"

#include "xparameters.h"

#include "xil_exception.h"

#include "xdebug.h"

#include "xscugic.h"

/************************** Constant Definitions *****************************/

/*

* Device hardware build related constants.

*/

#define RX_INTR_ID XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR

#define TX_INTR_ID XPAR_FABRIC_AXI_DMA_0_MM2S_INTROUT_INTR

#define IMAGE_WIDTH     640

#define IMAGE_HEIGHT 480

#define BYTES_PER_PIXEL 4

#define MAX_BUFFER_NUM 8

#define MEM_BASE_ADDR 0x10000000

#define DMA0_DEV_ID XPAR_AXIDMA_0_DEVICE_ID

#define DMA1_DEV_ID XPAR_AXIDMA_1_DEVICE_ID

#define DMA2_DEV_ID XPAR_AXIDMA_2_DEVICE_ID

#define DMA3_DEV_ID XPAR_AXIDMA_3_DEVICE_ID

#define RX0_INTR_ID XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR

#define TX0_INTR_ID XPAR_FABRIC_AXI_DMA_0_MM2S_INTROUT_INTR

#define RX1_INTR_ID XPAR_FABRIC_AXI_DMA_1_S2MM_INTROUT_INTR

#define TX1_INTR_ID XPAR_FABRIC_AXI_DMA_1_MM2S_INTROUT_INTR

#define RX2_INTR_ID XPAR_FABRIC_AXI_DMA_2_S2MM_INTROUT_INTR

#define TX2_INTR_ID XPAR_FABRIC_AXI_DMA_2_MM2S_INTROUT_INTR

#define RX3_INTR_ID XPAR_FABRIC_AXI_DMA_3_S2MM_INTROUT_INTR

#define TX3_INTR_ID XPAR_FABRIC_AXI_DMA_3_MM2S_INTROUT_INTR

#define CH0_BUFFER0_BASE (MEM_BASE_ADDR)

#define CH0_BUFFER1_BASE (CH0_BUFFER0_BASE +     IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH0_BUFFER2_BASE (CH0_BUFFER0_BASE + 2 * IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH1_BUFFER0_BASE (CH0_BUFFER0_BASE + MAX_BUFFER_NUM * IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH1_BUFFER1_BASE (CH1_BUFFER0_BASE +     IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH1_BUFFER2_BASE (CH1_BUFFER0_BASE + 2 * IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH2_BUFFER0_BASE (CH1_BUFFER0_BASE + MAX_BUFFER_NUM * IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH2_BUFFER1_BASE (CH2_BUFFER0_BASE +     IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH2_BUFFER2_BASE (CH2_BUFFER0_BASE + 2 * IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH3_BUFFER0_BASE (CH2_BUFFER0_BASE + MAX_BUFFER_NUM * IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH3_BUFFER1_BASE (CH3_BUFFER0_BASE +     IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

#define CH3_BUFFER2_BASE (CH3_BUFFER0_BASE + 2 * IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

/* Timeout loop counter for reset

*/

#define RESET_TIMEOUT_COUNTER 10000

/* test start value

*/

#define TEST_START_VALUE 0xC

/*

* Buffer and Buffer Descriptor related constant definition

*/

#define MAX_PKT_LEN (IMAGE_WIDTH * IMAGE_HEIGHT * BYTES_PER_PIXEL)

/*

* transfer times

*/

#define NUMBER_OF_TRANSFERS 100000

extern volatile int TxDone0;

extern volatile int TxDone1;

extern  volatile int TxDone2;

extern  volatile int TxDone3;

extern  volatile int RxDone0;

extern  volatile int RxDone1;

extern  volatile int RxDone2;

extern  volatile int RxDone3;

extern  volatile u8 tx0_buffer_index;

extern  volatile u8 rx0_buffer_index;

extern  volatile u8 tx1_buffer_index;

extern  volatile u8 rx1_buffer_index;

extern  volatile u8 tx2_buffer_index;

extern  volatile u8 rx2_buffer_index;

extern  volatile u8 tx3_buffer_index;

extern  volatile u8 rx3_buffer_index;

extern  volatile int Error;

extern  u32 *BufferPtr0[3];

extern  u32 *BufferPtr1[3];

extern  u32 *BufferPtr2[3];

extern  u32 *BufferPtr3[3];

extern  XAxiDma AxiDma0;

extern  XAxiDma AxiDma1;

extern  XAxiDma AxiDma2;

extern  XAxiDma AxiDma3;

int  DMA_CheckData(int Length, u8 StartValue);

int  DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);

int  DMA_Intr_Enable(XScuGic * IntcInstancePtr,XAxiDma *DMAPtr);

int  DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId);

#endif

上表中主要定义DMA用到的变量,每个DMA通道的地址分配,DMA通道对象的定义,以及DMA中断函数、DMA中断使能函数。

10.5.4.3 dma_intr.c中断接收源码

表10-5-4-3

/*****************************************************************************/

/*

*

* This is the DMA RX interrupt handler function

*

* It gets the interrupt status from the hardware, acknowledges it, and if any

* error happens, it resets the hardware. Otherwise, if a completion interrupt

* is present, then it sets the RxDone flag.

*

* @param Callback is a pointer to RX channel of the DMA engine.

*

* @return None.

*

* @note None.

*

******************************************************************************/

static void DMA_RxIntrHandler(void *Callback)

{

u32 IrqStatus;

int Status;

XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

/* Read pending interrupts */

IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);

/* Acknowledge pending interrupts */

XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);

/*

* If no interrupt is asserted, we do not do anything

*/

if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {

xil_printf("no interrupt! \r\n");

return;

}

/*

* If error interrupt is asserted, raise error flag, reset the

* hardware to recover from the error, and return with no further

* processing.

*/

if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {

// Error = 1;

xil_printf("rx error! \r\n");

/* Reset could fail and hang

* NEED a way to handle this or do not call it??

*/

// XAxiDma_Reset(AxiDmaInst);

// TimeOut = RESET_TIMEOUT_COUNTER;

// while (TimeOut) {

// if(XAxiDma_ResetIsDone(AxiDmaInst)) {

// break;

// }

// TimeOut -= 1;

// }

return;

}

/*

* If completion interrupt is asserted, then set RxDone flag

*/

if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

if(AxiDmaInst == &AxiDma0)

{

RxDone0++;

if(rx0_buffer_index == 2)

rx0_buffer_index = 0;

else

rx0_buffer_index++;

Status = XAxiDma_SimpleTransfer(&AxiDma0, (u32)BufferPtr0[rx0_buffer_index],

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

xil_printf("rx axi dma0 failed! 0 %d\r\n", Status);

return;

}

}

else if(AxiDmaInst == &AxiDma1)

{

RxDone1++;

if(rx1_buffer_index == 2)

rx1_buffer_index = 0;

else

rx1_buffer_index++;

Status = XAxiDma_SimpleTransfer(&AxiDma1, (u32)BufferPtr1[rx1_buffer_index],

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

xil_printf("rx axi dma1 failed! 0 %d\r\n", Status);

return;

}

}

else if(AxiDmaInst == &AxiDma2)

{

RxDone2++;

if(rx2_buffer_index == 2)

rx2_buffer_index = 0;

else

rx2_buffer_index++;

Status = XAxiDma_SimpleTransfer(&AxiDma2, (u32)BufferPtr2[rx2_buffer_index],

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

xil_printf("rx axi dma2 failed! 0 %d\r\n", Status);

return;

}

}

else if(AxiDmaInst == &AxiDma3)

{

RxDone3++;

if(rx3_buffer_index == 2)

rx3_buffer_index = 0;

else

rx3_buffer_index++;

Status = XAxiDma_SimpleTransfer(&AxiDma3, (u32)BufferPtr3[rx3_buffer_index],

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

xil_printf("rx axi dma3 failed! 0 %d\r\n", Status);

return;

}

}

else

xil_printf("error!\r\n");

}

}

上表中和单独DMA视频的却别就是通过AxiDmaInst 判断当前DMA输入的通路,来确定当前输入当道下一次接收的数据需要保存到的BUFFER地址。

表9-5-4-3 dma_intr.c源码

10.5.4.4 dma_intr.c中断发送源码

表10-5-4-4

/*****************************************************************************/

/*

*

* This is the DMA TX Interrupt handler function.

*

* It gets the interrupt status from the hardware, acknowledges it, and if any

* error happens, it resets the hardware. Otherwise, if a completion interrupt

* is present, then sets the TxDone.flag

*

* @param Callback is a pointer to TX channel of the DMA engine.

*

* @return None.

*

* @note None.

*

******************************************************************************/

static void DMA_TxIntrHandler(void *Callback)

{

u32 IrqStatus;

int Status;

XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

/* Read pending interrupts */

IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);

/* Acknowledge pending interrupts */

XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);

/*

* If no interrupt is asserted, we do not do anything

*/

if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {

xil_printf("no interrupt! \r\n");

return;

}

/*

* If error interrupt is asserted, raise error flag, reset the

* hardware to recover from the error, and return with no further

* processing.

*/

if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {

//Error = 1;

xil_printf("tx error! \r\n");

// /*

//  * Reset should never fail for transmit channel

//  */

// XAxiDma_Reset(AxiDmaInst);

//

// TimeOut = RESET_TIMEOUT_COUNTER;

//

// while (TimeOut) {

// if (XAxiDma_ResetIsDone(AxiDmaInst)) {

// break;

// }

//

// TimeOut -= 1;

// }

return;

}

/*

* If Completion interrupt is asserted, then set the TxDone flag

*/

if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

if(AxiDmaInst == &AxiDma0)

{

TxDone0++;

if(rx0_buffer_index == 0)

tx0_buffer_index = 2;

else

tx0_buffer_index = rx0_buffer_index - 1;

Status = XAxiDma_SimpleTransfer(&AxiDma0, (u32)BufferPtr0[tx0_buffer_index],

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma0 failed! 0 %d\r\n", Status);

return;

}

}

else if(AxiDmaInst == &AxiDma1)

{

TxDone1++;

if(rx1_buffer_index == 0)

tx1_buffer_index = 2;

else

tx1_buffer_index = rx1_buffer_index - 1;

Status = XAxiDma_SimpleTransfer(&AxiDma1, (u32)BufferPtr1[tx1_buffer_index],

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma1 failed! 0 %d\r\n", Status);

return;

}

}

else if(AxiDmaInst == &AxiDma2)

{

TxDone2++;

if(rx2_buffer_index == 0)

tx2_buffer_index = 2;

else

tx2_buffer_index = rx2_buffer_index - 1;

Status = XAxiDma_SimpleTransfer(&AxiDma2, (u32)BufferPtr2[tx2_buffer_index],

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma2 failed! 0 %d\r\n", Status);

return;

}

}

else if(AxiDmaInst == &AxiDma3)

{

TxDone3++;

if(rx3_buffer_index == 0)

tx3_buffer_index = 2;

else

tx3_buffer_index = rx3_buffer_index - 1;

Status = XAxiDma_SimpleTransfer(&AxiDma3, (u32)BufferPtr3[tx3_buffer_index],

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

xil_printf("tx axi dma3 failed! 0 %d\r\n", Status);

return;

}

}

else

xil_printf("error!\r\n");

}

}

上表的发送中断函数,和接收中断函数处理机制一致,也是通过AxiDmaInst 判断当前DMA的通道,并且为当前DMA通道发送数据,指定对应的 BUFFER。

10.6测试结果

S03_CH10_DMA_4_Video_Stitch视频拼接系统的更多相关文章

  1. 开源倾情奉献:基于.NET打造IP智能网络视频监控系统(一)开放源代码

    本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载. 开源倾情奉献系列链接 开源倾情奉献:基于.NET打造IP智能网络视频监控系统(一)开放源代码 开源倾 ...

  2. 基于SURF特征的图像与视频拼接技术的研究和实现(一)

    基于SURF特征的图像与视频拼接技术的研究和实现(一)      一直有计划研究实时图像拼接,但是直到最近拜读西电2013年张亚娟的<基于SURF特征的图像与视频拼接技术的研究和实现>,条 ...

  3. 基于.NET打造IP智能网络视频监控系统

    开源倾情奉献:基于.NET打造IP智能网络视频监控系统(一)开放源代码   开源倾情奉献系列链接 开源倾情奉献:基于.NET打造IP智能网络视频监控系统(一)开放源代码 开源倾情奉献:基于.NET打造 ...

  4. VSAM:视频监控系统 A System for Video Surveillance and Monitoring

    VSAM(VideoSurveillance and Monitoring)视频监控系统 Robotics Institute CMU 1:引言 2:试验床介绍 3:基本的视频分析算法:运动目标检测, ...

  5. 2个YUV视频拼接技术

    http://blog.csdn.net/huahuahailang/article/details/9040847 2个YUV视频拼接技术 http://zhongcong386.blog.163. ...

  6. [视频]mac系统下虚拟机parallels安装ubuntu 14.04视频教程

    此文是http://www.mr-wu.cn/install-ubuntu-14-04-on-parallels-for-mac/这篇博文的补充,为整个ubuntu 14.04安装过程的视频录像. m ...

  7. EasySwoole+ElasticSearch打造 高性能 小视频服务系统

    EasySwoole+ElasticSearch打造高性能小视频服务 第1章 课程概述 第2章 EasySwoole框架快速上手 第3章 性能测试 第4章 玩转高性能消息队列服务 第5章 小视频服务平 ...

  8. 轻松构建基于 Serverless 架构的弹性高可用音视频处理系统

    前言 随着计算机技术和 Internet 的日新月异,视频点播技术因其良好的人机交互性和流媒体传输技术倍受教育.娱乐等行业青睐,而在当前, 云计算平台厂商的产品线不断成熟完善, 如果想要搭建视频点播类 ...

  9. Qt编写安防视频监控系统(界面很漂亮)

    一.前言 视频监控系统在整个安防领域,已经做到了烂大街的程序,全国起码几百家公司做过类似的系统,当然这一方面的需求量也是非常旺盛的,各种定制化的需求越来越多,尤其是这几年借着人脸识别的东风,发展更加迅 ...

随机推荐

  1. 小程序 image跟view标签上下会有间隙

    图片文字等inline元素默许是跟父级元素的baseline对齐,而baseline又和父级底边有必定间距 我是使用: 加上这个消除了间隙,如果没有解决,你可以分别用 vertical-align:t ...

  2. 2018-2019-2 20165234 《网络对抗技术》 Exp8 网络欺诈防范 Web基础

    Exp8 网络欺诈防范 Web基础 一. 实践内容 1. Web前端HTML 能正常安装.启停Apache.理解HTML,理解表单,理解GET与POST方法,编写一个含有表单的HTML. 2. Web ...

  3. Java--常用API介绍

    Scanner类--键盘输入,室友起来三个步骤: 第一,导包:import java.util.Scanner 第二,创建:Scanner sc = new Scanner(System.in) 第三 ...

  4. django + vue3 解决跨越问题

    django跨域 解决: https://yq.aliyun.com/articles/517215 vue3 跨越(此处没必要,django处理即可): https://blog.csdn.net/ ...

  5. oracle利用触发器实现将ddl操作存入数据表中

    先创建DDL数据库事件操作表: create table ddl_event( sys_time date primary key, event_name ), ), obj_type ), obj_ ...

  6. Mac地址转换成long长整型 2

    数据之间的转换可以使用   System.Convert Mac地址转换成long长整型 /// <summary> /// 解析长整形的数据使其转换为macID /// </sum ...

  7. 使用ConstraintLayout(约束布局)构建响应式UI

    使用ConstraintLayout(约束布局)构建响应式UI 转 https://www.300168.com/yidong/show-2740.html     核心提示:ConstraintLa ...

  8. Oracle 表的行数、表占用空间大小,列的非空行数、列占用空间大小 查询

    --表名,表占用空间大小(MB),行数select table_name, round(num_rows * avg_row_len /1024/1024, 8) as total_len, num_ ...

  9. AtomicInteger的CAS算法浅析

    之前浅析过自旋锁(自旋锁浅析),我们知道它的实现原理就是CAS算法.CAS(Compare and Swap)即比较并交换,作为著名的无锁算法,它也是乐观锁的实现方式之一.JDK并发包里也有许多代码中 ...

  10. 重新认识Java 8的HashMap

    [转自]美团技术博客 HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型.随着JDK(Java Developmet Kit)版本的更新,JDK1.8对HashMap底层的实 ...