目录
下位机发送间隔10s
1 测试平台搭建
搭建过程请参考我的另一篇blog的过程:“https://blog.csdn.net/weixin_42066185/article/details/106421611
下位机测试发送的核心代码
具体参考github:https://github.com/Scottars/TCP_UDPtranformonZYNQ
测试结果
测试结果原因
请参考另一篇blog:https://blog.csdn.net/weixin_42066185/article/details/106480029
结论
xemacif_input(echo_netif);
根据上面的这句话,其实际上的作用是将mac层的数据读入到lwip
当中去,根据另外一篇blog 的内容:https://blog.csdn.net/FPGADesigner/article/details/88776615
也对这句话进行了说明了原因。
下位机发送间隔设定1s
下位机代码片段
测试结果
第二次测试的结果,通过计数这个时候共有接收到了多少个数据包
测试结论
我们将延时降低,出现内存错误的地方显然在已经传输额了很多数据之后才出现了,这个时候也验证了上面那一节blog 所提到的原因,此时能够有快频率的将mac中的队列包读入lwip堆栈中。
发送测试设定100us
这样的测试结论,可仍然见我的另一篇blog:
https://blog.csdn.net/weixin_42066185/article/details/106421611
发送测试设定延时10us
上位机测试代码
'''
子系统自身信息:
IP:192.168.127.11
slave:11
port:5001
子系统需要检测的信息
电源电压采样 value1:05 03 07 data crc1 crc2----registerid=07 datatype=float
电源电流采样 value1:05 03 08 data crc1 crc2----registerid=08 datatype=float
'''
import threading
import zmq
import time
import socket
import datetime
import struct
HWM_VAL = 100000*60*31*5
HWM_VAL = 100000
def zmq_recv(context, url):
socket = context.socket(zmq.SUB)
# socket = context.socket(zmq.REP)
socket.connect(url)
socket.setsockopt(zmq.SUBSCRIBE, ''.encode('utf-8')) # 接收所有消息
zhanbao = 0
buzhanbao = 0
start_time = time.clock()
while True:
b = socket.recv();
# socket.send(b'1')
# print(b)
end_time = time.clock()
if len(b) == 1:
# print('总计耗时',end_time-start_time)
break
size = len(b)
# print(size)
# if end_time-start_time > 10:
# pass
# break
if size > 10:
zhanbao = zhanbao + 1
else:
buzhanbao = buzhanbao + 1
print('接收不粘包', buzhanbao)
print('接收粘包', zhanbao)
def set_keepalive_linux(sock, after_idle_sec=1, interval_sec=3, max_fails=5):
"""Set TCP keepalive on an open socket.
It activates after 1 second (after_idle_sec) of idleness,
then sends a keepalive ping once every 3 seconds (interval_sec),
and closes the connection after 5 failed ping (max_fails), or 15 seconds
"""
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, after_idle_sec)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, max_fails)
return sock
def tcp_recv_zmq_send(context, sub_server_addr, syncaddr, down_computer_addr, port):
# socketzmq = context.socket(zmq.PUB)
# socketzmq.bind("tcp://115.156.162.76:6000")
reveiver_url = "ipc://11_Router"
socketzmq = context.socket(zmq.ROUTER)
socketzmq.set_hwm(HWM_VAL)
# socketzmq.connect(reveiver_url)
sendinglist=[]
# client = context.socket(zmq.ROUTER)
# 为了定义一个对象线程
# 创建一个socket:
# tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#创建套接字
# tcp_server_socket.bind((down_computer_addr,port))#绑定本机地址和接收端口
# tcp_server_socket.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,True)
# tcp_server_socket.listen(1)#监听()内为最大监听值
# # tcp_server_socket = set_keepalive_linux(tcp_server_socket)
#
# s,s_addr= tcp_server_socket.accept()#建立连接(accept(无参数)
# s = set_keepalive_linuZZ8888888888888888888888;Bx(s)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接,这个建立的是tcp的链接
# client_socket.setsockopt(socket.IPPROTO_TCP,socket.TCP_NODELAY,True)
IP_Server='192.168.127.202'
Port=5011
client_socket.connect((IP_Server,Port))
# s.connect(('192.168.127.5', 5001))
print('we have connected to the tcp data send server!---port is :', port)
packagenum = 0
zhanbao = 0
buzhanbao = 0
start_time_perf = time.perf_counter()
start_time_process = time.process_time()
count = 0
# 实际上应当启用的市多线程来做这些事情的
# 每一个线程要做的事情就是接收对应的内容
# 我想epics里面做的也是基本想同样的事情 ---最后写一个自动化的脚本多线程
start_flag = True
while True:
# try: #许久没有接收到下位机来的消息,首先会有一个keep alive 的数据包的出现,如果太久没有了就直接关闭当前socket
b = client_socket.recv(10)
print('I',count)
if count==1000000:
break
# size = len(b)
count = count + 1
# timestample = str(datetime.datetime.now()).encode()
# b = b + timestample
#
# sendinglist.append(b)
# print('Sending list size ',len(sendinglist))
print(packagenum)
end_time_perf = time.perf_counter()
end_time_process = time.process_time()
print('Receiving port is: ', port)
print('Package num:', count)
print('receing time cost:', end_time_perf - start_time_perf) #
# print('tcp接收不粘包', buzhanbao)
# print('tcp接收粘包', zhanbao)
# socketzmq.send(b)
socketzmq.close()
s.close()
tcp_server_socket.close()
if __name__ == '__main__':
print('Kaishile ')
context = zmq.Context() # 这个上下文是真的迷,到底什么情况下要用共同的上下文,什么时候用单独的上下文,找时间测试清楚
sub_server_addr = "tcp://115.156.162.123:6000"
syncaddr = "tcp://115.156.162.76:5555"
down_computer_addr = '115.156.163.107'
# down_computer_addr = '127.0.0.1'
down_computer_addr = '192.168.127.11'
down_computer_addr = '192.168.127.100'
down_computer_addr = '127.0.0.1'
# sub_server_addr = "tcp://127.0.0.1:6001"
sub_server_addr = "tcp://192.168.127.100:6002"
# syncaddr = "tcp://127.0.0.1:5555"
syncaddr = "tcp://192.168.127.100:5556"
port = [5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010]
tcp_recv_zmq_send(context,sub_server_addr,syncaddr,down_computer_addr,5011)
# for i in port:
# t2 = threading.Thread(target=tcp_recv_zmq_send,
# args=(context, sub_server_addr, syncaddr, down_computer_addr, port))
# t2.start()
测试结果
测试原因
由于测试中包含大量的xil_print信息,导致实际数据上传的速度并未达到每隔10us上传的速度。
修改去掉printf 后延时10us测试结果
上位机仅仅接收到199个数据包的时候就出现了tcp_write报错,即内存溢出的错误:
第二次测试的结果:
原因剖析
–update- on 2020-06-01
根本原因溯源
根本原因溯源
我们看到我们串口打印的数据的实际上出现在tcp_write 这句话的函数上面。如下:
进一步查看tcpwrite 中报错具体是哪一句话:
进一步查看tcp_write_check 函数的报错
通过上面图片中的代码,我们可以得出的两点原因如下:
原因1:由于发送的数据太多。
原因2:在未发送或者未确认的队列中的pubfs 的总数,超过了所配置的大小;
解决方案
我们看一下上面代码中的 TCP_SND_QUEUELEN的定义:
pcb->snd_queuelen >= TCP_SND_QUEUELEN
查看定义:
我们再进而查看:lwip的配置界面:
通是修改memp_n_pbuf的大小,将从原来默认的16,变成1600,再次进行tcp的数据的测试。
情况并没有出现改观。
上网进行查询了一下如下是几个参考链接:
没有结论:https://bbs.csdn.net/topics/390939619?page=1
这篇转载的的文章提到提高发缓冲区的大小:https://blog.csdn.net/mazihong/article/details/52463895
这篇blog:https://blog.csdn.net/weixin_33888907/article/details/91637939
给出了一个比较明确的答案,也跟我们上面的结论是一样的。
修改 define TCP_SND_QUEUELEN 大小
修改代码位置:
测试结修改前:16 * TCP_SND_BUF)/TCP_MSS
测试修改后:
代码编译报错:
调整如下大小:
测试结果:
修改tcp_send_buf 的大小同样不能解决问题
-------------未完待续---------------------
—update on 2020 - 06 -02
再一次溯源
查询参考资料
https://www.xilinx.com/support/answers/61298.html
这篇blog 中提到了,参数回调的方式。
由于博主之前参考zynq 示例的时候有zynq2018 版本的sdk中,有一个tcp_perf的例程,而其在这个过程中采用的就是函数回调的方式。
例程
/******************************************************************************
*
* Copyright (C) 2018 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/* Connection handle for a TCP Client session */
#include "tcp_perf_client.h"
//pcb = protocol control block
static struct tcp_pcb *c_pcb;
static char send_buf[TCP_SEND_BUFSIZE];
static struct perf_stats client;
void print_app_header()
{
#if LWIP_IPV6==1
xil_printf("TCP client connecting to %s on port %d\r\n",
TCP_SERVER_IPV6_ADDRESS, TCP_CONN_PORT);
xil_printf("On Host: Run $iperf -V -s -i %d -w 2M\r\n",
INTERIM_REPORT_INTERVAL);
#else
xil_printf("TCP client connecting to %s on port %d\r\n",
TCP_SERVER_IP_ADDRESS, TCP_CONN_PORT);
xil_printf("On Host: Run $iperf -s -i %d -w 2M\r\n",
INTERIM_REPORT_INTERVAL);
#endif /* LWIP_IPV6 */
}
static void print_tcp_conn_stats()
{
#if LWIP_IPv6==1
xil_printf("[%3d] local %s port %d connected with ",
client.client_id, inet6_ntoa(c_pcb->local_ip),
c_pcb->local_port);
xil_printf("%s port %d\r\n",inet6_ntoa(c_pcb->remote_ip),
c_pcb->remote_port);
#else
xil_printf("[%3d] local %s port %d connected with ",
client.client_id, inet_ntoa(c_pcb->local_ip),
c_pcb->local_port);
xil_printf("%s port %d\r\n",inet_ntoa(c_pcb->remote_ip),
c_pcb->remote_port);
#endif /* LWIP_IPV6 */
xil_printf("[ ID] Interval\t\tTransfer Bandwidth\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 client session */
static void tcp_conn_report(u64_t diff,
enum report_type report_type)
{
u64_t total_len;
double duration, bandwidth = 0;
char data[16], perf[16], time[64];
if (report_type == INTER_REPORT) {
total_len = client.i_report.total_bytes;
} else {
client.i_report.last_report_time = 0;
total_len = client.total_bytes;
}
/* 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)client.i_report.last_report_time,
(double)(client.i_report.last_report_time + duration));
xil_printf("[%3d] %s %sBytes %sbits/sec\n\r", client.client_id,
time, data, perf);
if (report_type == INTER_REPORT)
client.i_report.last_report_time += duration;
}
/** Close a tcp session */
static void tcp_client_close(struct tcp_pcb *pcb)
{
err_t err;
if (pcb != NULL) {
tcp_sent(pcb, NULL);
tcp_err(pcb, NULL);
err = tcp_close(pcb);
if (err != ERR_OK) {
/* Free memory with abort */
tcp_abort(pcb);
}
}
}
/** Error callback, tcp session aborted */
static void tcp_client_err(void *arg, err_t err)
{
LWIP_UNUSED_ARG(err);
u64_t now = get_time_ms();
u64_t diff_ms = now - client.start_time;
tcp_client_close(c_pcb);
c_pcb = NULL;
tcp_conn_report(diff_ms, TCP_ABORTED_REMOTE);
xil_printf("TCP connection aborted %d\n\r",err);
}
static err_t tcp_send_perf_traffic(void)
{
err_t err;
u8_t apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
if (c_pcb == NULL) {
return ERR_CONN;
}
#ifdef __MICROBLAZE__
/* Zero-copy pbufs is used to get maximum performance for Microblaze.
* For Zynq A9, ZynqMP A53 and R5 zero-copy pbufs does not give
* significant improvement hense not used. */
apiflags = 0;
#endif
while (tcp_sndbuf(c_pcb) > TCP_SEND_BUFSIZE) {
send_buf[0]='i';
send_buf[1]='a';
send_buf[2]='m';
send_buf[3]='a';
send_buf[4]='g';
send_buf[5]='o';
send_buf[6]='o';
send_buf[7]='d';
send_buf[8]='m';
send_buf[9]='a';
send_buf[10]='n';
err = tcp_write(c_pcb, send_buf, TCP_SEND_BUFSIZE, apiflags);
if (err != ERR_OK) {
xil_printf("TCP client: Error on tcp_write: %d\r\n",
err);
return err;
}
err = tcp_output(c_pcb);
if (err != ERR_OK) {
xil_printf("TCP client: Error on tcp_output: %d\r\n",
err);
return err;
}
client.total_bytes += TCP_SEND_BUFSIZE;
client.i_report.total_bytes += TCP_SEND_BUFSIZE;
}
if (client.end_time || client.i_report.report_interval_time) {
u64_t now = get_time_ms();
if (client.i_report.report_interval_time) {
if (client.i_report.start_time) {
u64_t diff_ms = now - client.i_report.start_time;
u64_t rtime_ms = client.i_report.report_interval_time;
if (diff_ms >= rtime_ms) {
tcp_conn_report(diff_ms, INTER_REPORT);
client.i_report.start_time = 0;
client.i_report.total_bytes = 0;
}
} else {
client.i_report.start_time = now;
}
}
if (client.end_time) {
/* this session is time-limited */
u64_t diff_ms = now - client.start_time;
if (diff_ms >= client.end_time) {
/* time specified is over,
* close the connection */
tcp_conn_report(diff_ms, TCP_DONE_CLIENT);
xil_printf("TCP test passed Successfully\n\r");
tcp_client_close(c_pcb);
c_pcb = NULL;
return ERR_OK;
}
}
}
return ERR_OK;
}
static err_t tcp_send_hello_traffic(void)
{
err_t err;
u8_t apiflags = TCP_WRITE_FLAG_COPY | TCP_WRITE_FLAG_MORE;
if (c_pcb == NULL) {
return ERR_CONN;
}
#ifdef __MICROBLAZE__
/* Zero-copy pbufs is used to get maximum performance for Microblaze.
* For Zynq A9, ZynqMP A53 and R5 zero-copy pbufs does not give
* significant improvement hense not used. */
apiflags = 0;
#endif
send_buf[0]='h';
send_buf[1]='e';
send_buf[2]='l';
send_buf[3]='l';
send_buf[4]='o';
send_buf[5]='w';
send_buf[6]='o';
send_buf[7]='r';
send_buf[8]='l';
send_buf[9]='d';
send_buf[10]='!';
err = tcp_write(c_pcb, send_buf, TCP_SEND_BUFSIZE, apiflags);
if (err != ERR_OK) {
xil_printf("TCP client: Error on tcp_write: %d\r\n",
err);
return err;
}
err = tcp_output(c_pcb);
if (err != ERR_OK) {
xil_printf("TCP client: Error on tcp_output: %d\r\n",
err);
return err;
}
client.total_bytes += TCP_SEND_BUFSIZE;
client.i_report.total_bytes += TCP_SEND_BUFSIZE;
xil_printf("we are sending in hello traffic!");
//
// if (client.end_time || client.i_report.report_interval_time) {
// u64_t now = get_time_ms();
// if (client.i_report.report_interval_time) {
// if (client.i_report.start_time) {
// u64_t diff_ms = now - client.i_report.start_time;
// u64_t rtime_ms = client.i_report.report_interval_time;
// if (diff_ms >= rtime_ms) {
// tcp_conn_report(diff_ms, INTER_REPORT);
// client.i_report.start_time = 0;
// client.i_report.total_bytes = 0;
// }
// } else {
// client.i_report.start_time = now;
// }
// }
//
// if (client.end_time) {
// /* this session is time-limited */
// u64_t diff_ms = now - client.start_time;
// if (diff_ms >= client.end_time) {
// /* time specified is over,
// * close the connection */
// tcp_conn_report(diff_ms, TCP_DONE_CLIENT);
// xil_printf("TCP test passed Successfully\n\r");
// tcp_client_close(c_pcb);
// c_pcb = NULL;
// return ERR_OK;
// }
// }
// }
return ERR_OK;
}
/** TCP sent callback, try to send more data */
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
xil_printf("we have sent the package \r\n");
// return tcp_send_perf_traffic();
return ERR_OK;
}
/** TCP connected callback (active connection), send data now */
static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
{
xil_printf("we are 回调!\r\n");
if (err != ERR_OK) {
tcp_client_close(tpcb);
xil_printf("Connection error\n\r");
return err;
}
xil_printf("we have connected to the server!\r\n");
/* store state */
c_pcb = tpcb;
tcp_nagle_disable(c_pcb);
client.start_time = get_time_ms();
client.end_time = TCP_TIME_INTERVAL * 1000; /* ms */
client.client_id++;
client.total_bytes = 0;
/* report interval time in ms */
client.i_report.report_interval_time = INTERIM_REPORT_INTERVAL * 1000;
client.i_report.last_report_time = 0;
client.i_report.start_time = 0;
client.i_report.total_bytes = 0;
print_tcp_conn_stats();
/* set callback values & functions */
tcp_arg(c_pcb, NULL);
tcp_sent(c_pcb, tcp_client_sent);
tcp_err(c_pcb, tcp_client_err);
/* initiate data transfer */
return ERR_OK;
}
void transfer_data(void)
{
if (client.client_id)
//tcp_send_perf_traffic();
tcp_send_hello_traffic();
}
void start_application(void)
{
err_t err;
struct tcp_pcb* pcb;
ip_addr_t remote_addr;
u32_t i;
xil_printf("WE ARE AT start appklication!!!\r\n");
#if LWIP_IPV6==1
remote_addr.type= IPADDR_TYPE_V6;
err = inet6_aton(TCP_SERVER_IPV6_ADDRESS, &remote_addr);
#else
err = inet_aton(TCP_SERVER_IP_ADDRESS, &remote_addr);
#endif /* LWIP_IPV6 */
if (!err) {
xil_printf("Invalid Server IP address: %d\r\n", err);
return;
}
/* Create Client PCB */
pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
if (!pcb) {
xil_printf("Error in PCB creation. out of memory\r\n");
return;
}
err = tcp_connect(pcb, &remote_addr, TCP_CONN_PORT,
tcp_client_connected);
if (err) {
xil_printf("Error on tcp_connect: %d\r\n", err);
tcp_client_close(pcb);
return;
}
client.client_id = 0;
xil_printf("err status , %d\r\n",err);
xil_printf("WE have connected to the server and ready to send\r\n");
/* initialize data buffer being sent with same as used in iperf */
// for (i = 0; i < TCP_SEND_BUFSIZE; i++)
// send_buf[i] = (i % 10) + 'a';//这个就相当于ascill数据码,发出去的时候,再解析。是不是我每次更新这个sendbuf 就会从新更新
// 当我们将TCP_send_buffsize 设定5 就表示每次发送前面三个字节,它是怎么指导我每次发送多少个呢??????哪个机制导致了我发送的呢???
//实际上,我们可以认为这个地方只算是把数据防盗我们的发送缓冲区的部分, 实际上,是由其他的某个地方实现的数据得发送
//send_buf[i] = (i % 10) + '0';
return;
}
上面是zynq开发板作为perf 客户端
设定回调
撰写回调函数
代码迁移
迁移后的echo.c 文件:
/******************************************************************************
*
* Copyright (C) 2009 - 2014 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
#include <stdio.h>
#include <string.h>
#include "sleep.h"
#include "lwip/err.h"
#include "lwip/tcp.h"
#if defined (__arm__) || defined (__aarch64__)
#include "xil_printf.h"
#endif
static struct tcp_pcb *connected_pcb = NULL;
char sendBuffer[10]="HELLOWORLD";
volatile unsigned tcp_client_connected = 0;
err_t transfer_data() {
err_t err;
struct tcp_pcb *tpcb = connected_pcb;
//xil_printf("we ccan in transfer data\r\n", err);
if (!connected_pcb)
return ERR_OK;
err = tcp_write(tpcb, sendBuffer, 10, 3);
if (err != ERR_OK) {
xil_printf("txperf: Error on tcp_write: %d\r\n", err);
connected_pcb = NULL;
return err;
}
err = tcp_output(tpcb);
if (err != ERR_OK) {
xil_printf("txperf: Error on tcp_output: %d\r\n",err);
return err;
}
return ERR_OK;
}
/** Error callback, tcp session aborted */
static void tcp_client_err(void *arg, err_t err)
{
xil_printf("TCP connection aborted %d\n\r",err);
}
void print_app_header()
{
xil_printf("\n\r\n\r-----lwIP TCP echo server ------\n\r");
xil_printf("TCP packets sent to port 6001 will be echoed back\n\r");
}
err_t recv_callback(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err)
{
/* do not read the packet if we are not in ESTABLISHED state */
if (!p) {
tcp_close(tpcb);
tcp_recv(tpcb, NULL);
return ERR_OK;
}
/* indicate that the packet has been received */
tcp_recved(tpcb, p->len);
/* echo back the payload */
/* in this case, we assume that the payload is < TCP_SND_BUF */
if (tcp_sndbuf(tpcb) > p->len) {
err = tcp_write(tpcb, p->payload, p->len, 1);
} else
xil_printf("no space in tcp_sndbuf\n\r");
/* free the received pbuf */
pbuf_free(p);
return ERR_OK;
}
/** TCP sent callback, try to send more data */
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
xil_printf("we have sent the package \r\n");
usleep(100);
return transfer_data();
// return ERR_OK;
}
err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err)
{
static int connection = 1;
/* set the receive callback for this connection */
tcp_recv(newpcb, recv_callback);
/* just use an integer number indicating the connection id as the
callback argument */
tcp_arg(newpcb, (void*)(UINTPTR)connection);
/* increment for subsequent accepted connections */
connection++;
//将新这个接收的这个accept 赋值给全局变量,以便我们进行其他的处理,例如主动发送数据
connected_pcb = newpcb;
//禁用nagle 算法
tcp_nagle_disable(connected_pcb);
// zhiwei
tcp_client_connected=1;
tcp_arg(connected_pcb, NULL);
tcp_sent(connected_pcb, tcp_client_sent);
tcp_err(connected_pcb, tcp_client_err);
transfer_data();
return ERR_OK;
}
int start_application()
{
struct tcp_pcb *pcb;
err_t err;
unsigned port = 5011;
/* create new TCP PCB structure */
pcb = tcp_new();
if (!pcb) {
xil_printf("Error creating PCB. Out of Memory\n\r");
return -1;
}
/* bind to specified @port */
err = tcp_bind(pcb, IP_ADDR_ANY, port);
if (err != ERR_OK) {
xil_printf("Unable to bind to port %d: err = %d\n\r", port, err);
return -2;
}
/* we do not need any arguments to callback functions */
tcp_arg(pcb, NULL);
/* listen for connections */
pcb = tcp_listen(pcb);
if (!pcb) {
xil_printf("Out of memory while tcp_listen\n\r");
return -3;
}
/* specify callback to use for incoming connections */
tcp_accept(pcb, accept_callback);
xil_printf("TCP echo server started @ port %d\n\r", port);
return 0;
}
main.c 文件修改后的内容:
/******************************************************************************
*
* Copyright (C) 2009 - 2014 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
#include <stdio.h>
#include "xparameters.h"
#include "netif/xadapter.h"
#include "platform.h"
#include "platform_config.h"
#if defined (__arm__) || defined(__aarch64__)
#include "xil_printf.h"
#endif
#include "sleep.h"
#include "lwip/tcp.h"
#include "xil_cache.h"
#if LWIP_DHCP==1
#include "lwip/dhcp.h"
#endif
/* defined by each RAW mode application */
void print_app_header();
int start_application();
err_t transfer_data();
void tcp_fasttmr(void);
void tcp_slowtmr(void);
/* missing declaration in lwIP */
void lwip_init();
#if LWIP_DHCP==1
extern volatile int dhcp_timoutcntr;
err_t dhcp_start(struct netif *netif);
#endif
extern volatile int TcpFastTmrFlag;
extern volatile int TcpSlowTmrFlag;
extern volatile unsigned tcp_client_connected;
static struct netif server_netif;
struct netif *echo_netif;
void
print_ip(char *msg, struct ip_addr *ip)
{
print(msg);
xil_printf("%d.%d.%d.%d\n\r", ip4_addr1(ip), ip4_addr2(ip),
ip4_addr3(ip), ip4_addr4(ip));
}
void
print_ip_settings(struct ip_addr *ip, struct ip_addr *mask, struct ip_addr *gw)
{
print_ip("Board IP: ", ip);
print_ip("Netmask : ", mask);
print_ip("Gateway : ", gw);
}
#if defined (__arm__) && !defined (ARMR5)
#if XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 || XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1
int ProgramSi5324(void);
int ProgramSfpPhy(void);
#endif
#endif
#ifdef XPS_BOARD_ZCU102
#ifdef XPAR_XIICPS_0_DEVICE_ID
int IicPhyReset(void);
#endif
#endif
int main()
{
struct ip_addr ipaddr, netmask, gw;
err_t tmperr;
/* the mac address of the board. this should be unique per board */
unsigned char mac_ethernet_address[] =
{ 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
echo_netif = &server_netif;
#if defined (__arm__) && !defined (ARMR5)
#if XPAR_GIGE_PCS_PMA_SGMII_CORE_PRESENT == 1 || XPAR_GIGE_PCS_PMA_1000BASEX_CORE_PRESENT == 1
ProgramSi5324();
ProgramSfpPhy();
#endif
#endif
/* Define this board specific macro in order perform PHY reset on ZCU102 */
#ifdef XPS_BOARD_ZCU102
IicPhyReset();
#endif
//TO enable platform interupt
init_platform();
/* initliaze IP addresses to be used */
IP4_ADDR(&ipaddr, 192, 168, 127, 202);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 127, 254);
print_app_header();
lwip_init();
/* Add network interface to the netif_list, and set it as default */
if (!xemac_add(echo_netif, &ipaddr, &netmask,
&gw, mac_ethernet_address,
PLATFORM_EMAC_BASEADDR)) {
xil_printf("Error adding N/W interface\n\r");
return -1;
}
netif_set_default(echo_netif);
/* now enable interrupts */
platform_enable_interrupts();
/* specify that the network if is up */
netif_set_up(echo_netif);
print_ip_settings(&ipaddr, &netmask, &gw);
/* start the application (web server, rxtest, txtest, etc..) */
start_application();
/* receive and process packets */
while (1) {
if (TcpFastTmrFlag) {
tcp_fasttmr();
TcpFastTmrFlag = 0;
}
if (TcpSlowTmrFlag) {
tcp_slowtmr();
TcpSlowTmrFlag = 0;
}
//xil_printf("we are sending in while loop\r\n");
xemacif_input(echo_netif);
// xemacif_input(echo_netif);
// if (tcp_client_connected) { //连接成功则发送数据
// xil_printf("we are sending in connect\r\n");
// transfer_data();
// }
}
/* never reached */
cleanup_platform();
return 0;
}
上述的代码:
- 加入回调函数的方式
- 在连接成回调函数中调用数据传输函数
- 在数据传输成功的回调函数中调用数据传输函数
- 从而形成循环
结果测试
采用这种类似回调函数的形式,能够正常的工作,但是实际看起zynq的发送的速度很慢。如下是arm平台没有涉恶顶延时的情况得到的结果。