ZYNQ学习之路(一):LWIP数据传输与数据处理(一)(PS处理)

  • Post author:
  • Post category:其他


LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。LwIP实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用,它只需十几KB的RAM和40K左右的ROM就可以运行,这使LwIP协议栈适合在低端的

嵌入式系统

中使用。

关于以太网的相关信息这里不做详细介绍,我只介绍如何通过ZYNQ实现数据的环路测试。实验目的是通过在SDK修改代码,实现将数据+1后返回的操作。

具体步骤:

一、VIVADO部分配置:创建处理器并设置


我的这里串口选的UART1的48和49。自己还需要配置一下DDR。


把PL部分的时钟和系统复位关闭,把GP0接口也关闭,到这里算是配置完成,其他默认即可。然后点击自动连接。可以验证下正确性,接下来生成output  products和HDL wrapper。最终结果如下:

由于没有生成bit文件,所以接下来就在File->Export->Export Hardware时不需要导出bit文件。然后launch SDK。

二、SDK部分操作:创建UDP server工程并修改文件

file->new->application project

打开udp_perf_derver.c,然后将文件修改如下:

/*
 * Copyright (C) 2017 - 2018 Xilinx, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 */

/** Connection handle for a UDP Server session */

#include "udp_perf_server.h"

extern struct netif server_netif;
static struct udp_pcb *pcb;
static struct perf_stats server;
/* Report interval in ms */
#define REPORT_INTERVAL_TIME (INTERIM_REPORT_INTERVAL * 1000)

void print_app_header(void)
{
	xil_printf("UDP server listening on port %d\r\n",
			UDP_CONN_PORT);
	xil_printf("On Host: Run $iperf -c %s -i %d -t 300 -u -b <bandwidth>\r\n",
			inet_ntoa(server_netif.ip_addr),
			INTERIM_REPORT_INTERVAL);

}

static void print_udp_conn_stats(void)
{
	xil_printf("[%3d] local %s port %d connected with ",
			server.client_id, inet_ntoa(server_netif.ip_addr),
			UDP_CONN_PORT);
	xil_printf("%s port %d\r\n", inet_ntoa(pcb->remote_ip),
			pcb->remote_port);
	xil_printf("[ ID] Interval\t     Transfer     Bandwidth\t");
	xil_printf("    Lost/Total Datagrams\n\r");
}

static void stats_buffer(char* outString,
		double data, enum measure_t type)
{
	int conv = KCONV_UNIT;
	const char *format;
	double unit = 1024.0;

	if (type == SPEED)
		unit = 1000.0;

	while (data >= unit && conv <= KCONV_GIGA) {
		data /= unit;
		conv++;
	}

	/* Fit data in 4 places */
	if (data < 9.995) { /* 9.995 rounded to 10.0 */
		format = "%4.2f %c"; /* #.## */
	} else if (data < 99.95) { /* 99.95 rounded to 100 */
		format = "%4.1f %c"; /* ##.# */
	} else {
		format = "%4.0f %c"; /* #### */
	}
	sprintf(outString, format, data, kLabel[conv]);
}


/** The report function of a TCP server session */
static void udp_conn_report(u64_t diff,
		enum report_type report_type)
{
	u64_t total_len, cnt_datagrams, cnt_dropped_datagrams, total_packets;
	u32_t cnt_out_of_order_datagrams;
	double duration, bandwidth = 0;
	char data[16], perf[16], time[64], drop[64];

	if (report_type == INTER_REPORT) {
		total_len = server.i_report.total_bytes;
		cnt_datagrams = server.i_report.cnt_datagrams;
		cnt_dropped_datagrams = server.i_report.cnt_dropped_datagrams;
	} else {
		server.i_report.last_report_time = 0;
		total_len = server.total_bytes;
		cnt_datagrams = server.cnt_datagrams;
		cnt_dropped_datagrams = server.cnt_dropped_datagrams;
		cnt_out_of_order_datagrams = server.cnt_out_of_order_datagrams;
	}

	total_packets = cnt_datagrams + cnt_dropped_datagrams;
	/* Converting duration from milliseconds to secs,
	 * and bandwidth to bits/sec .
	 */
	duration = diff / 1000.0; /* secs */
	if (duration)
		bandwidth = (total_len / duration) * 8.0;

	stats_buffer(data, total_len, BYTES);
	stats_buffer(perf, bandwidth, SPEED);
	/* On 32-bit platforms, xil_printf is not able to print
	 * u64_t values, so converting these values in strings and
	 * displaying results
	 */
	sprintf(time, "%4.1f-%4.1f sec",
			(double)server.i_report.last_report_time,
			(double)(server.i_report.last_report_time + duration));
	sprintf(drop, "%4llu/%5llu (%.2g%%)", cnt_dropped_datagrams,
			total_packets,
			(100.0 * cnt_dropped_datagrams)/total_packets);
	xil_printf("[%3d] %s  %sBytes  %sbits/sec  %s\n\r", server.client_id,
			time, data, perf, drop);

	if (report_type == INTER_REPORT) {
		server.i_report.last_report_time += duration;
	} else if ((report_type != INTER_REPORT) && cnt_out_of_order_datagrams) {
		xil_printf("[%3d] %s  %u datagrams received out-of-order\n\r",
				server.client_id, time,
				cnt_out_of_order_datagrams);
	}
}


static void reset_stats(void)
{
	server.client_id++;
	/* Save start time */
	server.start_time = get_time_ms();
	server.end_time = 0; /* ms */
	server.total_bytes = 0;
	server.cnt_datagrams = 0;
	server.cnt_dropped_datagrams = 0;
	server.cnt_out_of_order_datagrams = 0;
	server.expected_datagram_id = 0;

	/* Initialize Interim report paramters */
	server.i_report.start_time = 0;
	server.i_report.total_bytes = 0;
	server.i_report.cnt_datagrams = 0;
	server.i_report.cnt_dropped_datagrams = 0;
	server.i_report.last_report_time = 0;
}
//主要修改代码部分:新定义了一个数组用来存放接受的数据
#define MAX_FLASH_LEN 16*1024*1024
unsigned long total_bytes = 0;
unsigned char  rxbuffer[MAX_FLASH_LEN];
/** Receive data on a udp session */
static void udp_recv_perf_traffic(void *arg, struct udp_pcb *tpcb,
		struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
	static u8_t first = 1;
	u32_t drop_datagrams = 0;
	s32_t recv_id;
	int i = 0;
//下面为核心修改处
	while (p->tot_len != p->len) {
			            memcpy(&rxbuffer[total_bytes], p->payload, p->len);
			            total_bytes += p->len;
			            p = p->next;

			        }
	memcpy(&rxbuffer[total_bytes], p->payload, p->len);
	total_bytes = p->len;
	for(int i=0;i<total_bytes;i++)
	{
		rxbuffer[i]++;
		p->payload = &rxbuffer[i];
		p->tot_len = 1;
	    udp_sendto(tpcb,p,addr,port);
	}//修改结束


	/* first, check if the datagram is received in order */
#ifdef __MICROBLAZE__
	/* For Microblaze, word access are at 32 bit boundaries.
	 * To read complete 4 byte of UDP ID from data payload,
	 * we should read upper 2 bytes from current word boundary
	 * of payload and lower 2 bytes from next word boundary of
	 * payload.
	 */
	s16_t *payload;
	payload = (s16_t *) (p->payload);
	recv_id = (ntohs(payload[0]) << 16) | ntohs(payload[1]);
#else
	recv_id = ntohl(*((int *)(p->payload)));
#endif
	if (first && (recv_id == 0)) {
		/* First packet should always start with recv id 0.
		 * However, If Iperf client is running with parallel
		 * thread, then this condition will also avoid
		 * multiple print of connection header
		 */
		pcb->remote_ip = *addr;
		pcb->remote_port = port;
		reset_stats();
		/* Print connection statistics */
		print_udp_conn_stats();
		first = 0;
	} else if (first) {
		/* Avoid rest of the packets if client
		 * connection is already terminated.
		 */
		return;
	}

	if (recv_id < 0) {
		u64_t now = get_time_ms();
		u64_t diff_ms = now - server.start_time;
		/* Send Ack */
		udp_sendto(tpcb, p, addr, port);
		udp_conn_report(diff_ms, UDP_DONE_SERVER);
		xil_printf("UDP test passed Successfully\n\r");
		first = 1;
		pbuf_free(p);
		return;
	}

	/* Update dropped datagrams statistics */
	if (server.expected_datagram_id != recv_id) {
		if (server.expected_datagram_id < recv_id) {
			drop_datagrams =
				recv_id - server.expected_datagram_id;
			server.cnt_dropped_datagrams += drop_datagrams;
			server.expected_datagram_id = recv_id + 1;
		} else if (server.expected_datagram_id > recv_id) {
			server.cnt_out_of_order_datagrams++;
		}
	} else {
		server.expected_datagram_id++;
	}

	server.cnt_datagrams++;

	/* Record total bytes for final report */
	server.total_bytes += p->tot_len;

	if (REPORT_INTERVAL_TIME) {
		u64_t now = get_time_ms();

		server.i_report.cnt_datagrams++;
		server.i_report.cnt_dropped_datagrams += drop_datagrams;

		/* Record total bytes for interim report */
		server.i_report.total_bytes += p->tot_len;
		if (server.i_report.start_time) {
			u64_t diff_ms = now - server.i_report.start_time;

			if (diff_ms >= REPORT_INTERVAL_TIME) {
				udp_conn_report(diff_ms, INTER_REPORT);
				/* Reset Interim report counters */
				server.i_report.start_time = 0;
				server.i_report.total_bytes = 0;
				server.i_report.cnt_datagrams = 0;
				server.i_report.cnt_dropped_datagrams = 0;
			}
		} else {
			/* Save start time for interim report */
			server.i_report.start_time = now;
		}
	}

	pbuf_free(p);
	return;
}

void start_application(void)
{
	err_t err;

	/* Create Server PCB */
	pcb = udp_new();
	if (!pcb) {
		xil_printf("UDP server: Error creating PCB. Out of Memory\r\n");
		return;
	}

	err = udp_bind(pcb, IP_ADDR_ANY, UDP_CONN_PORT);
	if (err != ERR_OK) {
		xil_printf("UDP server: Unable to bind to port");
		xil_printf(" %d: err = %d\r\n", UDP_CONN_PORT, err);
		udp_remove(pcb);
		return;
	}

	/* specify callback to use for incoming connections */
	udp_recv(pcb, udp_recv_perf_traffic, NULL);

	return;
}

这部分代码实现的功能是将接受到的数据拷贝到另一块内存中,然后对数据进行加一操作,再通过建立工程后自动定义的UDP回传函数将加一后的数据传回去。

三:测试

首先需要设置网口,在控制面板里设置如下:

在以太网的属性中将IPV4设置如下:保证电脑IP与ZYNQ网口IP在一个网段。

然后开始测试:连接好串口后,SDK会打印如下信息,代表网口连接成功


然后再网口助手中设置一下端口信息:

发送0 1 2 3 4后,返回数据如下:可以看到预设的功能以实现。



版权声明:本文为qushuo123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。