S03_CH09_DMA_4_Video_Switch视频切换系统
S03_CH09_DMA_4_Video_Switch视频切换系统
9.1概述
本例程详细创建过程和本季课程第一课《S03_CH01_AXI_DMA_LOOP 环路测试》非常类似,因此如果读者不清楚如何创建工程,请仔细阅读本季第一课时。本例程的基本原理如下。
PL通过 OV7725 OV7725 实时 采集 1路 64 0×480 0×480 视频,分别将原始彩色、 视频,分别将原始彩色、 R分量、 G分量、 B分量作为常量输出,实现背景为红、绿、蓝 视频共 4路视频通过 4个独立的 AXI DMA IP AXI DMA IPAXI DMA IP AXI DMA IPAXI DMA IPAXI DMA IPAXI DMA IP核传输至 PS 的 DDR 中进行缓存,然后再通过 AXIDMA AXIDMAAXIDMA 将 4路视频同时 路视频同时 从 DDR 读出 ,通过 PL 在 VGA 显示器上 显示器上 切换显示 其中任意 1路。视频切换通过 。视频切换通过 miz702 miz702miz702 底板的 SW2SW2SW2按键 (5个按键的中心位置) 个按键的中心位置) 实现, 每按 下一次 切换一路视频 。
9.2修改OV_Sensor_ML摄像头采集IP
由于MIZ702开发板只有1路摄像头视频输入接口,MIZ701N和MIZ702N只有2路视频输入接口,无法满足演示4路视频输入的接口需求,因此修改OV_Sensor_ML ip 使之输出4路数据通路。修改的代码如下:
表9-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路摄像头了。
9.3搭建硬件系统
9.3.1系统图
完成了IP的修改后,下面就可以搭建硬件系统了,由于VIVADO采用了图形化设计,带来了很大便捷。下面分别把MIZ702的系统构架图贴出来。
由于图片太大,只能看到大概的框架,大家学习的时候可以打开工程放大后去阅读,这里为了分析的时候方便把局部视图放大截图。
9.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。
9.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代码控制。
9.3.4 DMA 和FIFO通路
下图是 DMA和data fifo的链接通路。
上图中data fifo 的M_AXIS将被引出到外部受FPGA代码控制。如下图,FIFO_M_AXIS_0就是连接到axis_data_fifo_0的M_AXIS接口的。双击此接口需要设置时钟,这里的数据速度时钟是25MHZ 。不同的分辨率应当设置对应的分辨率时钟。
9.3.5 vid_out IP的通路
输出部分可以看到vid_out输出的是VGA时序信号,在VGA时序信号上,我们还挂载了一个VGA转HMDI的IP实现了HDMI和VGA同时输出(MIZ701N没有VGA所以无需把VGA信号,引出去)
上图中的vid out IP数据输入通道如图所示
双击这个接口也要设置时钟频率,由于输出像素为640X480因此为25000000HZ
9.3.6 AXI HP通道和DMA中断
由于是四路视频输入,外接了4个DMA模块因此使用了4个HP和8个DMA中断如图
9.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的图像。
9.3.8 时钟管理模块
时钟管理模块前面已经讲过了,640X480的分辨率是设置25MHZ,不在具体累述。
9.3.9VTC 图像时序发生模块
VTC图像时序发生模块的使用只要配置对应的分辨率,这里是设置640X480的分辨率,前面章节已经讲过不再累述。
9.4 FPGA 四路输入以及图像切换源码分析
9.4.1 按钮输入去抖代码
表9-4-1
always@(posedge clk_out1) begin if(!gpio_rtl_tri_o_0) begin button_reg0 <= 1'b0; button_reg1 <= 1'b0; button_reg2 <= 1'b0; button_reg3 <= 1'b0; end else begin button_reg0 <= button; button_reg1 <= button_reg0; button_reg2 <= button_reg1; button_reg3 <= button_reg2; end end assign button_en = button_reg0 & button_reg1 & ~button_reg2 & ~button_reg3; |
好简洁的去抖动代码,信号延迟4个时钟,连续监测到4次,就是认为按下了。关键稳定吗,还真不够稳定,你试试就知道了。有时候按下去会跳过几幅图像。所以按下去的时候要果断点,手指不要抖。哈哈,关键是代码简洁,满足了实验要求。读者如果要做自己的演示系统,还是把去抖动代码写好一些。
9.4.2 DMA 4路视频输入的FPGA代码
表9-4-2-1
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 |
上表可以看到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 行计数器的完成代码如下表。
表9-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,并且会产生一次对应端口的中断信号。
表9-4-2-4
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); |
9.4.3 DMA 输出通道
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 != 11'd639) h_cnt <= h_cnt + 1'b1; else h_cnt <= 11'd0; else h_cnt <= h_cnt; end |
表9-4-3-1
上表是vid out ip 输入数据部分的列计数器,一共有640列。当video_out_tvalid(FIFO输出数据有效信号)和video_out_tready(vid out IP准备好接收数据信号)都为1的时候开始计数。
表9-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 == 11'd639)) if(v_cnt != 11'd479) 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'd639(代表一行数据结束)行计数器v_cnt 加1。
表9-4-3-3
assign video_out_tdata = (channel_switch == 2'b00) ? FIFO_M_AXIS_0_tdata[23:0] : ((channel_switch == 2'b01) ? FIFO_M_AXIS_1_tdata[23:0] : ((channel_switch == 2'b10) ? FIFO_M_AXIS_2_tdata[23:0] : FIFO_M_AXIS_3_tdata[23:0])); assign video_out_tvalid = video_en & ((channel_switch == 2'b00) ? FIFO_M_AXIS_0_tvalid : ((channel_switch == 2'b01) ? FIFO_M_AXIS_1_tvalid : ((channel_switch == 2'b10) ? FIFO_M_AXIS_2_tvalid : FIFO_M_AXIS_3_tvalid))); assign video_out_tuser = video_out_tvalid & video_out_tready & (h_cnt == 11'd0) & (v_cnt == 11'd0); assign video_out_tlast = video_out_tvalid & video_out_tready & (h_cnt == 11'd639); assign FIFO_M_AXIS_0_tready = video_en & video_out_tready; assign FIFO_M_AXIS_1_tready = video_en & video_out_tready; assign FIFO_M_AXIS_2_tready = video_en & video_out_tready; assign FIFO_M_AXIS_3_tready = video_en & video_out_tready; |
上表中,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 输入图像数据的一行结束,每一行结束都要输出 video_out_tlast 为1.
9.5 4路视频切换DMA C处理源码分析
9.5.1 main.c源码
表9-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路)。
9.5.2 dma_intr.h源码
表9-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中断使能函数。
9.5.3 dma_intr.c中断接收源码
表9-5-3 DMA_RxIntrHandler 源码
/*****************************************************************************/ /* * * 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源码
9.5.4 dma_intr.c中断发送源码
表9-5-4-1 DMA_TxIntrHandler 函数源码
/*****************************************************************************/ /* * * 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。
9.6本章小结
本章给出的是一个实用化的DMA应用方案,设计了4路视频通过DMA输入到DDR。在C代码中实现3缓存输出。输出的时候,提供切换FIFO的通道,实现把其中一路输出到显示器。本方案的应用场景可以用于电视广播系统、视频会议等。
S03_CH09_DMA_4_Video_Switch视频切换系统的更多相关文章
- S03_CH10_DMA_4_Video_Stitch视频拼接系统
S03_CH10_DMA_4_Video_Stitch视频拼接系统 10.1概述 注意:本课程和上一课程<S03_CH09_DMA_4_Video_Switch视频切换系统>基本相同,不同 ...
- Qt编写安防视频监控系统7-全屏切换
一.前言 全屏切换这个功能点属于简单的,一般会做到右键菜单中,也提供了快捷键比如alt+enter来触发,恢复全屏则按esc即可,全屏处理基本上都是隐藏通道面板以外的窗体,保持最大化展示,由于采用了模 ...
- Qt编写安防视频监控系统1-通道切换
一.前言 通道切换在视频监控系统中是最基础的必备功能,一般都会提供1通道+4通道+6通道+8通道+9通道+16通道这几个通道切换,可能做得比较好的还会提供24通道+32通道的,这个可能对电脑的配置就有 ...
- 轻松构建基于 Serverless 架构的弹性高可用音视频处理系统
前言 随着计算机技术和 Internet 的日新月异,视频点播技术因其良好的人机交互性和流媒体传输技术倍受教育.娱乐等行业青睐,而在当前, 云计算平台厂商的产品线不断成熟完善, 如果想要搭建视频点播类 ...
- Qt编写安防视频监控系统(界面很漂亮)
一.前言 视频监控系统在整个安防领域,已经做到了烂大街的程序,全国起码几百家公司做过类似的系统,当然这一方面的需求量也是非常旺盛的,各种定制化的需求越来越多,尤其是这几年借着人脸识别的东风,发展更加迅 ...
- Qt编写安防视频监控系统18-云台控制
一.前言 云台控制是视频监控系统中必备的一个功能,对球机进行上下左右的移动,还有焦距的控制,其实核心就是控制XYZ三个坐标轴,为了开发这个模块,特意研究了各种云台控制的方法和开源库比如soap,有些厂 ...
- Qt编写安防视频监控系统13-视频存储
一.前言 一般视频监控行业都会选择把视频存储在本地NVR或者服务器上,而不是存储在客户端电脑,只有当用户经费预算有限的时候,或者用户特殊需求要求存储在本地客户端电脑的时候才会开启存储到本地,正常来说视 ...
- Qt编写安防视频监控系统12-异形布局
一.前言 视频监控系统中,除了常规的1画面.4画面.9画面.16画面以外,还有几个布局比较另类,比如6画面.8画面.13画面,有些通道需要占据不同的行列,4画面.9画面.16画面都是非常对称的布局,行 ...
- Qt编写安防视频监控系统11-动态换肤
一.前言 Qt中的动态换肤技术是非常一流的,直接调用qApp->setStyleSheet(qss);就可以对整个应用程序进行换肤,如果样式表内容不多,或者对应的贴图不对,效率还是蛮好的,不过据 ...
随机推荐
- Tkinter 之RadioButton单选框标签
一.参数说明 语法 作用 Radiobutton(root,text='xxxx') 单选框文本显示内容 Radiobutton(root,variable=color) 单选框索引变量,通过变量的值 ...
- vue从一个组件跳转到另一个组件页面router-link的试用
需求从helloworld.vue页面跳到good.vue页面 1.helloworld.vue页面代码 <template> <div class="hello" ...
- .vue的文件在vscode里面是白色?
https://cnodejs.org/topic/58b8cac17872ea0864fee2ee 要装一个 vetur的插件
- 'internalField' 和'boundaryField'的区别?【翻译】
翻译自:CFD-online 帖子地址:http://www.cfd-online.com/Forums/openfoam/84322-difference-between-internalfield ...
- FLUENT多相流问题后处理中如何显示其中一相的分布【转载】
转载自:http://blog.sina.com.cn/s/blog_6a5314cf0100tnsz.html 多相流问题后处理中很容易显示相界面在某些面上的形态,如图1所示.其实利用ISO-Cli ...
- 【Linux性能调优一】观大局:系统平均负载load average
要测试linux系统性能及调优,首先要从全局检查linux的平均负载 1.什么是平均负载 load average 系统平均负载,平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数, ...
- SpringCloud学习整理
参考文档 [1]: Spring Cloud Ribbon负载均衡
- Eclipse简明使用教程(java集成开发环境)
说明:我使用的是绿色便携版的Eclipse,免安装,解压就能用.链接:https://download.csdn.net/download/loongstyle/10700321 1_Eclipse中 ...
- 20191024-6 Alpha发布用户使用报告
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2019fall/homework/9863 组名:胜利点 组长:贺敬文 组员:彭思雨 杨萍 王志文 位军 ...
- 网络爬虫requests-bs4-re-1
最近了解了爬虫,嗯--------,有时候会搞得有点头晕. 跟着线上老师实现了两个实例.可以用python下载源代码玩玩,爬淘宝的很刺激,虽然违反了ROBOTS协议. GIT地址