赞
踩
开发环境:Vivado2020.2;硬件设备:ZYNQ7010
参考正点原子的教程《领航者ZYNQ之嵌入式Vitis开发指南v1_2》中的实验《 基于 OV5640 的 PS 以太网视频传输实验》配置,配置以太网、串口、中断等。
参考博客:
【JokerのZYNQ7020】AXI_DMA_PL_PS
ZYNQ通过AXI DMA实现PL发送连续大量数据到PS DDR
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2024/02/27 16:35:14 // Design Name: // Module Name: data_gen // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // module data_gen #( parameter TRANS_NUM = 32'd2047 //1514*1024 ) ( input clk , input i_rstn , input trans_start, input M_AXIS_tready , output [31 : 0] M_AXIS_tdata , output M_AXIS_tvalid , output M_AXIS_tlast , output [3 : 0] M_AXIS_tkeep ); reg [31 : 0] r_M_AXIS_tdata ; reg r_M_AXIS_tvalid ; reg r_M_AXIS_tlast ; reg [3 : 0] r_M_AXIS_tkeep ; reg [1 : 0] r_current_state ; reg [1 : 0] r_next_state; reg trans_start_0, trans_start_1; wire pos_trans_start; assign pos_trans_start = trans_start_0 & (~trans_start_1); always @(posedge clk) begin if(!i_rstn) begin trans_start_0 <= 1'd0; trans_start_1 <= 1'd0; end else begin trans_start_0 <= trans_start; trans_start_1 <= trans_start_0; end end localparam IDLE = 2'd0; localparam TRAN = 2'd1; localparam LAST = 2'd2; always @(posedge clk ) begin if(!i_rstn) r_current_state <= IDLE; else r_current_state <= r_next_state; end always @(*) begin case(r_current_state) IDLE : r_next_state = (pos_trans_start && M_AXIS_tready) ? TRAN : IDLE; TRAN : r_next_state = (r_M_AXIS_tdata == TRANS_NUM) ? LAST : TRAN; LAST : r_next_state = M_AXIS_tready ? IDLE : LAST; default : r_next_state = IDLE; endcase end always @(posedge clk ) begin case(r_current_state) IDLE : begin r_M_AXIS_tdata <= 32'd0; r_M_AXIS_tvalid <= 1'd0; r_M_AXIS_tlast <= 1'd0; r_M_AXIS_tkeep <= 4'b1111; end TRAN : begin r_M_AXIS_tvalid <= 1'd1; if(M_AXIS_tready)begin r_M_AXIS_tdata <= r_M_AXIS_tdata + 32'd1; if(r_M_AXIS_tdata == TRANS_NUM) r_M_AXIS_tlast <= 1'd1; else r_M_AXIS_tlast <= 1'd0; end else r_M_AXIS_tdata <= r_M_AXIS_tdata; end LAST : begin if(!M_AXIS_tready)begin r_M_AXIS_tvalid <= 1'd1; r_M_AXIS_tlast <= 1'd1; r_M_AXIS_tdata <= r_M_AXIS_tdata; end else begin r_M_AXIS_tvalid <= 1'd0; r_M_AXIS_tlast <= 1'd0; r_M_AXIS_tdata <= 32'd0; end end default : begin r_M_AXIS_tdata <= 32'd0; r_M_AXIS_tvalid <= 1'd0; r_M_AXIS_tlast <= 1'd0; r_M_AXIS_tkeep <= 4'b1111; end endcase end assign M_AXIS_tdata = r_M_AXIS_tdata ; assign M_AXIS_tvalid = r_M_AXIS_tvalid ; assign M_AXIS_tlast = r_M_AXIS_tlast ; assign M_AXIS_tkeep = r_M_AXIS_tkeep ; endmodule
模块写好后直接添加进block design即可。
虽然引出了读数据计数引脚,但其实并未用到,可以关掉。
按上述配置完之后自动布线,然后valite design、generate output products、create HDL wrapper,一切无误之后综合布线,生成bit流
参考:正点原子的教程《领航者ZYNQ之嵌入式Vitis开发指南v1_2》中的实验《 基于 OV5640 的 PS 以太网视频传输实验》软件设计部分,下面贴出修改的部分
//****************************************Copyright (c)***********************************// //原子哥在线教学平台:www.yuanzige.com //技术支持:www.openedv.com //淘宝店铺:http://openedv.taobao.com //关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。 //版权所有,盗版必究。 //Copyright(C) 正点原子 2020-2030 //All rights reserved //---------------------------------------------------------------------------------------- // File name: main.c // Last modified Date: 2023/08/05 15:59:46 // Last Version: V1.0 // Descriptions: PS端网口传输OV5640摄像头视频在上位机显示 //---------------------------------------------------------------------------------------- // Created by: 正点原子 // Created date: 2023/08/02 15:59:52 // Version: V1.0 // Descriptions: The original version //server_netif //---------------------------------------------------------------------------------------- //****************************************************************************************// /***************************** Include Files *********************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "xil_types.h" #include "xparameters.h" // #include "emio_sccb_cfg/emio_sccb_cfg.h" #include "axi_gpio_cfg/axi_gpio_cfg.h" #include "axi_dma/axi_dma.h" // #include "ov5640/ov5640_init.h" #include "sys_intr/sys_intr.h" #include "udp_perf_server.h" #include "netif/xadapter.h" #include "platform.h" #include "platform_config.h" #include "xil_printf.h" #include "lwip/tcp.h" #include "sleep.h" #include "lwip/priv/tcp_priv.h" #include "lwip/init.h" #include "lwip/inet.h" #include "xil_cache.h" extern volatile int TcpFastTmrFlag; extern volatile int TcpSlowTmrFlag; #define DEFAULT_IP_ADDRESS "192.168.1.10" #define DEFAULT_IP_MASK "255.255.255.0" #define DEFAULT_GW_ADDRESS "192.168.1.1" void start_application(void); void print_app_header(void); int lwip_udp_init(); #define MAX_PKT_LEN 2048 //发送包长度 //函数声明 struct netif *netif; extern volatile int rx_done; extern u8 *rx_buffer_ptr; u32 fifo_count = 0; u8 dma_start_flag = 0; //0:启动DMA ,1:关闭DMA struct netif server_netif; static XScuGic Intc; //GIC int main(void) { // u32 status; u16 cmos_h_pixel; //ov5640 DVP 输出水平像素点数 u16 cmos_v_pixel; //ov5640 DVP 输出垂直像素点数 u16 total_h_pixel; //ov5640 水平总像素大小 u16 total_v_pixel; //ov5640 垂直总像素大小 cmos_h_pixel = 640; cmos_v_pixel = 480; total_h_pixel = 2844; total_v_pixel = 1968; // // emio_init(); //初始化EMIO // // status = ov5640_init( cmos_h_pixel, //初始化ov5640 // cmos_v_pixel, // total_h_pixel, // total_v_pixel); // // if(status == 0) // xil_printf("OV5640 detected successful!\r\n"); // else // xil_printf("OV5640 detected failed!\r\n"); axi_gpio_init(); // 初始AXI-GPIO接口 axi_dma_cfg(); // 配置AXI DMA Init_Intr_System(&Intc); // 初始DMA中断系统 Setup_Intr_Exception(&Intc); // 启用来自硬件的中断 dma_setup_intr_system(&Intc); // 建立DMA中断系统 lwip_udp_init(); // UDP通信配置 //接收和处理数据包 while (1) { xemacif_input(netif); // fifo_count = get_fifo_count(); //PS端读取FIFO中的读数据计数 //FIFO中的读数据计数个数达到发送包长度后,开始启动DMA从FIFO中读取1024个数据存储进DDR中 if((dma_start_flag == 0)){ axi_gpio_out1(); axi_dma_start(MAX_PKT_LEN); axi_gpio_out0(); dma_start_flag = 1; } //DMA搬运1024个数据完成后,网口就可以从DDR中取数据进行发送了 if(rx_done){ udp_tx_data(rx_buffer_ptr,8192); rx_done = 0; dma_start_flag = 0; } } return 0; } static void print_ip(char *msg, ip_addr_t *ip) { print(msg); xil_printf("%d.%d.%d.%d\r\n", ip4_addr1(ip), ip4_addr2(ip), ip4_addr3(ip), ip4_addr4(ip)); } static void print_ip_settings(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw) { print_ip("Board IP: ", ip); print_ip("Netmask : ", mask); print_ip("Gateway : ", gw); } //设置静态IP地址 static void assign_default_ip(ip_addr_t *ip, ip_addr_t *mask, ip_addr_t *gw) { int err; xil_printf("Configuring default IP %s \r\n", DEFAULT_IP_ADDRESS); err = inet_aton(DEFAULT_IP_ADDRESS, ip); if (!err) xil_printf("Invalid default IP address: %d\r\n", err); err = inet_aton(DEFAULT_IP_MASK, mask); if (!err) xil_printf("Invalid default IP MASK: %d\r\n", err); err = inet_aton(DEFAULT_GW_ADDRESS, gw); if (!err) xil_printf("Invalid default gateway address: %d\r\n", err); } int lwip_udp_init() { /*设置领航者开发板的MAC地址 */ unsigned char mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 }; netif = &server_netif; xil_printf("\r\n\r\n"); xil_printf("-----lwIP RAW Mode UDP Server Application-----\r\n"); /* 初始化lwIP*/ lwip_init(); /* 将网络接口添加到netif_list,并将其设置为默认网络接口 (用于输出未找到特定路由的所有数据包) */ if (!xemac_add(netif, NULL, NULL, NULL, mac_ethernet_address, PLATFORM_EMAC_BASEADDR)) { xil_printf("Error adding N/W interface\r\n"); return -1; } netif_set_default(netif); /* 指定网络是否已启动*/ netif_set_up(netif); //设置静态IP地址 assign_default_ip(&(netif->ip_addr), &(netif->netmask), &(netif->gw)); //打印IP设置 print_ip_settings(&(netif->ip_addr), &(netif->netmask), &(netif->gw)); xil_printf("\r\n"); /* 打印应用程序标题 */ print_app_header(); /* 启动应用程序*/ start_application(); xil_printf("\r\n"); return 0; }
//****************************************Copyright (c)***********************************// //原子哥在线教学平台:www.yuanzige.com //技术支持:www.openedv.com //淘宝店铺:http://openedv.taobao.com //关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。 //版权所有,盗版必究。 //Copyright(C) 正点原子 2018-2028 //All rights reserved //---------------------------------------------------------------------------------------- // File name: axi_gpio_cfg.c // Last modified Date: 2020/04/15 10:59:46 // Last Version: V1.0 // Descriptions: AXI GPIO配置 //---------------------------------------------------------------------------------------- // Created by: 正点原子 // Created date: 2023/06/30 15:59:52 // Version: V1.0 // Descriptions: The original version // //---------------------------------------------------------------------------------------- //****************************************************************************************// #include"axi_gpio_cfg.h" #define AXI_GPIO_0_ID XPAR_AXI_GPIO_0_DEVICE_ID //AXI GPIO 0器件 ID #define AXI_GPIO_0_CHANEL1 1 XGpio axi_gpio_inst0; //AXI GPIO 0 驱动实例 //AXI GPIO初始化 void axi_gpio_init(void) { //AXI GPIO 0驱动 XGpio_Initialize(&axi_gpio_inst0, AXI_GPIO_0_ID); //配置AXI GPIO 0 通道1为输入 XGpio_SetDataDirection(&axi_gpio_inst0, AXI_GPIO_0_CHANEL1,0); } //通过AXI GPIO获取FIFO数据个数 //u32 get_fifo_count(void) //{ // u32 fifo_count = 0; // fifo_count = XGpio_DiscreteRead(&axi_gpio_inst0, AXI_GPIO_0_CHANEL1); // return fifo_count; //} void axi_gpio_out1(void) { XGpio_DiscreteWrite(&axi_gpio_inst0, 1, 0x01); } void axi_gpio_out0(void) { XGpio_DiscreteWrite(&axi_gpio_inst0, 1, 0x00); }
//****************************************Copyright (c)***********************************// //原子哥在线教学平台:www.yuanzige.com //技术支持:www.openedv.com //淘宝店铺:http://openedv.taobao.com //关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。 //版权所有,盗版必究。 //Copyright(C) 正点原子 2018-2028 //All rights reserved //---------------------------------------------------------------------------------------- // File name: emio_sccb_cfg.h // Last modified Date: 2019/06/30 15:59:46 // Last Version: V1.0 // Descriptions: SCCB驱动 //---------------------------------------------------------------------------------------- // Created by: 正点原子 // Created date: 2019/06/30 15:59:52 // Version: V1.0 // Descriptions: The original version // //---------------------------------------------------------------------------------------- //****************************************************************************************// #include "xil_types.h" #include "xgpio.h" #ifndef AXI_GPIO_CFG_ #define AXI_GPIO_CFG_ void axi_gpio_init(void); void axi_gpio_out0(void); void axi_gpio_out1(void); //u32 get_fifo_count(void); #endif /* sccb_EMIO_CFG_ */
//****************************************Copyright (c)***********************************// //原子哥在线教学平台:www.yuanzige.com //技术支持:www.openedv.com //淘宝店铺:http://openedv.taobao.com //关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。 //版权所有,盗版必究。 //Copyright(C) 正点原子 2018-2028 //All rights reserved //---------------------------------------------------------------------------------------- // File name: axi_dma.c // Last modified Date: 2023/08/9 10:59:46 // Last Version: V1.0 // Descriptions: Axi dma驱动程序在中断模式下接收数据包 //---------------------------------------------------------------------------------------- // Created by: 正点原子 // Created date: 2019/06/30 15:59:52 // Version: V1.0 // Descriptions: The original version // //---------------------------------------------------------------------------------------- //****************************************************************************************// /***************************** Include Files *********************************/ #include "axi_dma.h" /************************** Constant Definitions *****************************/ #define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID #define RX_INTR_ID XPAR_FABRIC_AXIDMA_0_VEC_ID #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID #define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR //0x00100000 #define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x1000000) //0x01100000 #define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) //0x01400000 #define RESET_TIMEOUT_COUNTER 10000 //复位时间 /************************** Variable Definitions *****************************/ static XAxiDma axidma; //XAxiDma实例 volatile int rx_done=0; //接收完成标志 volatile int error; //传输出错标志 u32 *rx_buffer_ptr; /************************** Function Definitions *****************************/ int axi_dma_cfg(void) { int status; XAxiDma_Config *config; rx_buffer_ptr = (u32 *) RX_BUFFER_BASE; xil_printf("\r\n--- Entering axi_dma_cfg --- \r\n"); config = XAxiDma_LookupConfig(DMA_DEV_ID); if (!config) { xil_printf("No config found for %d\r\n", DMA_DEV_ID); return XST_FAILURE; } //初始化DMA引擎 status = XAxiDma_CfgInitialize(&axidma, config); if (status != XST_SUCCESS) { xil_printf("Initialization failed %d\r\n", status); return XST_FAILURE; } if (XAxiDma_HasSg(&axidma)) { xil_printf("Device configured as SG mode \r\n"); return XST_FAILURE; } xil_printf("AXI DMA CFG Success\r\n"); return XST_SUCCESS; } //启用AXI DMA int axi_dma_start(u32 pkt_len) { int status; u32 a; //初始化标志信号 error = 0; a = (u32)(pkt_len*sizeof(u32)); xil_printf("%d",a); status = XAxiDma_SimpleTransfer(&axidma, (u32) rx_buffer_ptr, (u32)(pkt_len*sizeof(u32)), XAXIDMA_DEVICE_TO_DMA); if (status != XST_SUCCESS) { xil_printf("AXI DMA Start FAILURE\r\n"); return XST_FAILURE; } Xil_DCacheFlushRange((UINTPTR) rx_buffer_ptr, pkt_len); //刷新Data Cache return XST_SUCCESS; } //DMA RX中断处理函数 void rx_intr_handler(void *callback) { u32 irq_status; int timeout; XAxiDma *axidma_inst = (XAxiDma *) callback; irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA); XAxiDma_IntrAckIrq(axidma_inst, irq_status, XAXIDMA_DEVICE_TO_DMA); //Rx出错 if ((irq_status & XAXIDMA_IRQ_ERROR_MASK)) { error = 1; xil_printf("XAxiDma error"); XAxiDma_Reset(axidma_inst); timeout = RESET_TIMEOUT_COUNTER; while (timeout) { if (XAxiDma_ResetIsDone(axidma_inst)) break; timeout -= 1; } return; } //Rx完成 if ((irq_status & XAXIDMA_IRQ_IOC_MASK)) rx_done = 1; irq_status = XAxiDma_IntrGetIrq(axidma_inst, XAXIDMA_DEVICE_TO_DMA); } //建立DMA中断系统 // @param int_ins_ptr是指向XScuGic实例的指针 // @param AxiDmaPtr是指向DMA引擎实例的指针 // @param rx_intr_id是RX通道中断ID // @return:成功返回XST_SUCCESS,否则返回XST_FAILURE int dma_setup_intr_system(XScuGic * int_ins_ptr) { int status; //设置优先级和触发类型 XScuGic_SetPriorityTriggerType(int_ins_ptr, RX_INTR_ID, 0xA0, 0x3); //为中断设置中断处理函数 status = XScuGic_Connect(int_ins_ptr, RX_INTR_ID, (Xil_InterruptHandler) rx_intr_handler, &axidma); if (status != XST_SUCCESS) { return status; } XScuGic_Enable(int_ins_ptr, RX_INTR_ID); //使能DMA中断 XAxiDma_IntrEnable(&axidma,XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA); return XST_SUCCESS; }
#include <stdio.h> #include <string.h> #include "lwip/err.h" #include "lwip/udp.h" #include "xil_printf.h" #include "lwip/inet.h" static struct udp_pcb *pcb; #define SER_PORT 8000 //打印应用程序 void print_app_header() { xil_printf("\r\n-----Network port UDP transmission camera video display on upper computer ------\r\n"); } //UDP发送功能函数 void udp_tx_data(u8 *buffer_ptr,unsigned int len){ static struct pbuf *ptr; ptr = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_POOL); /* 申请内存 */ if (ptr) { pbuf_take(ptr, buffer_ptr,len); /* 将buffer_ptr中的数据打包进pbuf结构中 */ udp_send(pcb, ptr); /* udp发送数据 */ pbuf_free(ptr); /* 释放内存 */ } } void start_application() { err_t err; /* 设置客户端的IP地址 */ ip_addr_t DestIPaddr; IP4_ADDR( &DestIPaddr,192,168,1,102); //创建新的UDP PCB pcb = udp_new(); if (!pcb) { xil_printf("Error creating PCB. Out of Memory\r\n"); return; } //绑定端口 err = udp_bind(pcb, IP_ADDR_ANY, SER_PORT); if (err != ERR_OK) { xil_printf("Unable to bind to port %d; err %d\r\n", SER_PORT, err); udp_remove(pcb); return; } /* 设置客户端的端口 */ udp_connect(pcb, &DestIPaddr, 8000); xil_printf("UDP server started @ port %d\n\r", SER_PORT); }
下篇文章主要说下本次项目在调试的过程中遇见的问题
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。