基于LWIP的UDP传输实验在ZYNQ平台上的实现

实验介绍

  LWIP是嵌入式设备中较为常用的TCP/IP协议栈,本文将使用UDP协议传输较大的txt文件并写入PS端的DDR中,实验对文件传输的速率和准确率要求不高因此调用简单的UDP协议即可。
实验难点:

  • LWIP的pbuf的理解。
  • 对UDP接收回调函数的使用。
  • 实验基本设置

    如果不知道如何创建和使用SDK可以参考该文章:如何创建PS-PL工程

    1.Vivado中搭建硬件环境,启用UART和以太网外设,UART也是需要的将打印一些提示信息。
    这个要根据自己使用的板子选择

    2.SDK设置
    工程中的需要使用支持lwip的BSP包。这个BSP包可以是重新生成也可以添加lwip到已有的BSP包中。

    如果需要配置lwip可以在以下界面中设置,这里只说一下api_modle,lwip支持3种api接口(接口区别在本文参考资料中有写),这里只能选择前两种API接口。

  • RAW/Callback API
  • Socket API
  • NETCONN API
  • SDK代码

    工程配置好后需要添加4个代码文件才能使用udp

  • lwIP需要启动中断系统,需要sys_intr.c与sys_intr.h
  • UDP使用则需要添加一些初始化和回调函数包含在udp.process.c与udp.process.h
    这4个文件我附在了最后的完整代码中
  • 主要代码介绍

    main.c

  • 设置开发板MAC地址
  • 开启中断系统
  • 设置本地IP地址
  • 初始化lwIP
  • 添加网络接口 设置默认网络接口
  • 启动网络
  • 初始化UDP连接
  • 进行数据传输
  • #include "sys_intr.h"
    #include "udp_process.h"
    #include "xil_printf.h"
    
    /* 基本参数设置 */
    #define MAX_LEN    2500
    u8 UDPsentBuffer[MAX_LEN]={0};
    u8 UDPrecvBuffer[MAX_LEN]={0};
    //udp传输来的指令存放数组
    u8 PC2PScmd[MAX_LEN];
    u8 PS2PCcmd[MAX_LEN];
    //收到的数据长度
    u16 revnum=0;
    
    extern unsigned udp_connected_flag;
    static  XScuGic Intc;   //GIC 中断管理
    
    int main(void)
    {
    /* UDP init*/
    struct netif *netif, server_netif;
    ip_addr_t ipaddr, netmask, gw;
    
    /*  开发板MAC地址  */
    unsigned char mac_ethernet_address [] ={0x00, 0x0a, 0x35, 0x00, 0x01, 0x02};
    /*  开启中断系统  */
    Init_Intr_System(&IntcInstPtr);
    Setup_Intr_Exception(&IntcInstPtr);
    /*  添加本机IP地址,远端设备IP在udp.process.c中设置  */
    netif = &server_netif;
    IP4_ADDR(&ipaddr,  192, 168,   1, 30);
    IP4_ADDR(&netmask, 255, 255, 255, 0);
    IP4_ADDR(&gw,      192, 168,   1, 1);
    
    lwip_init();   //初始化lwIP库
    
    /* 添加网络接口并将其设置为默认接口 */
    if (!xemac_add(netif, &ipaddr, &netmask, &gw, mac_ethernet_address, XPAR_XEMACPS_0_BASEADDR)) {
    		xil_printf("Error adding N/W interface\r\n");
    		return -1;
    }
    netif_set_default(netif);
    netif_set_up(netif);        //启动网络
    user_udp_init();            //初始化UDP
    
    while (1)
    {
    	/*  将MAC队列中的包传输的LwIP/IP栈中   */
    	xemacif_input(netif);
    	revnum=udp_ReadData(UDPrecvBuffer);
    	/*  接收到的数据PC2PScmd即存在其中   */
    	memcpy(PC2PScmd,UDPrecvBuffer,revnum);
    	/*  将值通过udp发送回去   */
    	if(revnum>0)
    	{
    		memcpy(UDPsentBuffer,PC2PScmd,revnum);
    		udp_SentData(revnum,UDPsentBuffer);
    	}
    }
    return 0;
    }
    

    udp_recv_callback函数

    该函数是重要的接收回调函数

    void udp_recv_callback(void *arg,struct udp_pcb *tpcb,struct pbuf *p, ip_addr_t *addr, u16_t port)
    {
    	struct pbuf *p_r;
    
        xil_printf("Received from %d.%d.%d.%d port %d\r\n", (addr->addr) & 0xFF,
        			(addr->addr>>8) & 0xFF, (addr->addr>>16) & 0xFF, (addr->addr>>24) & 0xFF, port);
    
        /* Tell the client that we have accepted it */
        //memcpy(RxBuffer,(unsigned char*)p->payload,p->len);
        //xil_printf("Received:%d,len=%d",RxBuffer,p->len);
        if(p != NULL)
    	 {
    	 		//默认内存块为530字节左右,接收数据超过后会被存到多个内存块中这里将读取所有的内存块读出完整的数据
    			for(p_r = p; p_r != NULL; p_r = p_r->next) 
    			{
    				 memcpy(RxBuffer,(unsigned char*)p_r->payload,p_r->len);
    				 RxCount += p_r->len;
    			}
    	  }
        /* Free the p buffer */
        pbuf_free(p);
    }
    

    实验效果

    设置网络调试助手发送hello world,ZYNQ返回hello world。
    主机地址端口和远端地址端口要与代码写的一致,PC主机的地址在网络中心中可以设置

    参考资料

    学会Zynq(14)UDP发送Hello World
    STM32 LWIP UDP3000字节接收
    STM32以太网通信-LWIP简介

    完整代码

    sys_intr.c

    #include "sys_intr.h"
    void Setup_Intr_Exception(XScuGic * IntcInstancePtr)
    {
    	/* Enable interrupts from the hardware */
    	Xil_ExceptionInit();
    	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
    			(Xil_ExceptionHandler)XScuGic_InterruptHandler,
    			(void *)IntcInstancePtr);
    
    	Xil_ExceptionEnable();
    }
    
    int Init_Intr_System(XScuGic * IntcInstancePtr)
    {
    	int Status;
    
    	XScuGic_Config *IntcConfig;
    	/*
    	 * Initialize the interrupt controller driver so that it is ready to
    	 * use.
    	 */
    	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    	if (NULL == IntcConfig) {
    		return XST_FAILURE;
    	}
    
    	Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
    					IntcConfig->CpuBaseAddress);
    	if (Status != XST_SUCCESS) {
    		return XST_FAILURE;
    	}
    	return XST_SUCCESS;
    }
    

    sys_intr.h

    #ifndef SYS_INTR_H_
    #define SYS_INTR_H_
    
    #include "xparameters.h"
    #include "xil_exception.h"
    #include "xdebug.h"
    #include "xscugic.h"
    
    #define INTC_DEVICE_ID          XPAR_SCUGIC_SINGLE_DEVICE_ID
    int Init_Intr_System(XScuGic * IntcInstancePtr);
    void setup_Intr_Exception(XScuGic * IntcInstancePtr);
    #endif
    

    udp.process.c

    #include "udp_process.h"
    //---------------------------------------------------------
    //                    变量定义
    //---------------------------------------------------------
    struct udp_pcb *connected_pcb = NULL;
    static struct pbuf *pbuf_to_be_sent = NULL;
    
    static unsigned local_port = 9000;      //本地端口
    static unsigned remote_port = 9000;  //远程端口
    volatile unsigned udp_connected_flag = 0;  //连接标志
    
    #define MAX_LEN    2500
    //缓冲数组
    uint8_t TxBuffer[MAX_LEN] = {0};
    uint8_t RxBuffer[MAX_LEN] = {0};
    uint16_t RxCount = 0;
    
    void udp_recv_callback(void *arg,struct udp_pcb *tpcb,struct pbuf *p, ip_addr_t *addr, u16_t port)
    {
    	struct pbuf *p_r;
    
        xil_printf("Received from %d.%d.%d.%d port %d\r\n", (addr->addr) & 0xFF,
        			(addr->addr>>8) & 0xFF, (addr->addr>>16) & 0xFF, (addr->addr>>24) & 0xFF, port);
    
        /* Tell the client that we have accepted it */
        //memcpy(RxBuffer,(unsigned char*)p->payload,p->len);
        //xil_printf("Received:%d,len=%d",RxBuffer,p->len);
        if(p != NULL)
    	 {
    				for(p_r = p; p_r != NULL; p_r = p_r->next)
    			{
    				 memcpy(RxBuffer,(unsigned char*)p_r->payload,p_r->len);
    				 RxCount += p_r->len;
    			}
    	  }
        /* Free the p buffer */
        pbuf_free(p);
    }
    //---------------------------------------------------------
    //                  UDP连接初始化函数
    //---------------------------------------------------------
    int user_udp_init(void)
    {
    	struct udp_pcb *pcb;
    	ip_addr_t ipaddr;
    	err_t err;
    	udp_connected_flag = 0;
    
    	/*  创建UDP控制块   */
    	pcb = udp_new();
    	if (!pcb) {
    		xil_printf("Error Creating PCB.\r\n");
    		return -1;
    	}
    	/*  绑定本地端口   */
    	err = udp_bind(pcb, IP_ADDR_ANY, local_port);
    	if (err != ERR_OK) {
    		xil_printf("Unable to bind to port %d\r\n", local_port);
    		return -2;
    	}
    	/*  连接远程地址   */
    	IP4_ADDR(&ipaddr, 192, 168, 1, 28);
    	err = udp_connect(pcb, &ipaddr, remote_port);
    	if (err != ERR_OK) {
    		xil_printf("Unable to connect remote port.\r\n");
    		return -3;
    	}
    	else {
    		xil_printf("Connected Success.\r\n");
    		connected_pcb = pcb;
    		udp_connected_flag = 1;
    	}
    
    	udp_recv(pcb, udp_recv_callback, NULL);  //设置接收回调函数
    
    	return 0;
    }
    //---------------------------------------------------------
    //                   UDP接收数据函数
    //---------------------------------------------------------
    uint16_t udp_ReadData(uint8_t *buff)
    {
    	uint16_t len = RxCount;
    	if(len > 0)
    		memcpy(buff,RxBuffer,len);
    	memset(RxBuffer,0,len); //memset过大的重置内存块会对UDP会有异常
    	RxCount = 0;
    	return len;
    }
    //---------------------------------------------------------
    //                   UDP发送数据函数
    //---------------------------------------------------------
    void udp_SentData(uint32_t num, u8 * send_buff)
    {
    	err_t err;
    	struct udp_pcb *tpcb = connected_pcb;
    		if (!tpcb) {
    			xil_printf("error connect.\r\n");
    		}
    		/*  申请pbuf资源  */
    		pbuf_to_be_sent = pbuf_alloc(PBUF_TRANSPORT, num, PBUF_POOL);
    		memset(pbuf_to_be_sent->payload, 0, num);
    		memcpy(pbuf_to_be_sent->payload, (u8 *)send_buff, num);
    		/*  发送字符串  */
    		err = udp_send(tpcb, pbuf_to_be_sent);
    		if (err != ERR_OK) {
    			xil_printf("Error on udp send : %d\r\n", err);
    			pbuf_free(pbuf_to_be_sent);
    			return;
    		}
    		pbuf_free(pbuf_to_be_sent);  //释放pbuf
    }
    

    udp.process.h

    #ifndef SRC_USER_UDP_H_
    #define SRC_USER_UDP_H_
    
    #include "lwip/err.h"
    #include "lwip/udp.h"
    #include "lwip/init.h"
    #include "lwipopts.h"
    #include "lwip/err.h"
    #include "lwipopts.h"
    #include "netif/xadapter.h"
    #include "xil_printf.h"
    
    int user_udp_init(void);
    void udp_SentData(uint32_t num, u8 * send_buff);
    uint16_t udp_ReadData(uint8_t *buff);
    
    #endif /* SRC_USER_UDP_H_ */
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » 基于LWIP的UDP传输实验在ZYNQ平台上的实现

    发表评论