ZYNQ实验 基于LWIP的UDP传输实验

实验介绍

  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_ */
  • 3
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伊丽莎白鹅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值