https://blog.csdn.net/weixin_42066185/article/details/106421611
对于udp的建立,在2018版本的vivado 上的,有相关对应的例程,然而,我是用的版本的是2017,当前并无相关udp的通信例程,因此,我主要参考的都是如下的参考的链接:
https://blog.csdn.net/FPGADesigner/article/details/88746532
https://blog.csdn.net/FPGADesigner/article/details/88748846(最主要参考)
https://blog.csdn.net/FPGADesigner/article/details/88751502
(1) 整体建立过程
/*
* 设置开发板MAC地址
* 开启中断系统
* 设置本地IP地址
* 初始化lwIP
* 添加网络接口
* 设置默认网络接口
* 启动网络
* 初始化TCP或UDP连接(自定义函数)
* */
(2)设置开发板的mac地址
struct netif *netif;
struct ip_addr ipaddr, netmask, gw;
/* the mac address of the board. this should be unique per board */ //设定开发板的MAC地址
unsigned char mac_ethernet_address[] = {
0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
netif = &server_netif;
热555555555555555555555(3) 初始化平台,开启中断
init_platform(); //开启中断系统,这个里面设定的时定时中断
(4)初始化网卡的ip
/* 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();
(5) 初始化lwip, 添加网卡的netlif , 并且将这个网卡设置成默认网卡
/* 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);
(6)初始化udp
/* start the application (web server, rxtest, txtest, etc..) */
// start_application();
xil_printf("初始化udp网络/r/n");
user_udp_init(); //初始化UDP
(7) 循环初始化
while (1) {
xil_printf("we are sending \r\n");
/* 将MAC队列中的包传输的LwIP/IP栈中 */
xemacif_input(netif);
// if (udp_connected_flag) { //发送
sleep(1);
// xil_printf("we are in if nei\r\n");
udp_printf();
// }
}
/* never reached */
cleanup_platform();
整个主函数文件内容:
/******************************************************************************
*
* Copyright (C) 2017 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
#include "xparameters.h"
#include "netif/xadapter.h"
#include "platform.h"
#include "platform_config.h"
#include "lwipopts.h"
#include "xil_printf.h"
#include "sleep.h"
#include "sleep.h"
#include "sys_intr.h"
#include "lwip/init.h"
#include "lwip/inet.h"
#include "xil_cache.h"
//#include "ip_addr.h"
#include "user_udp.h"
#if LWIP_DHCP==1
#include "lwip/dhcp.h"
extern volatile int dhcp_timoutcntr;
#endif
static XScuGic Intc; //GIC
extern unsigned udp_connected_flag;
extern volatile int TcpFastTmrFlag;
extern volatile int TcpSlowTmrFlag;
//
#define DEFAULT_IP_ADDRESS "192.168.127.202"
#define DEFAULT_IP_MASK "255.255.255.0"
#define DEFAULT_GW_ADDRESS "192.168.127.254"
//#define DEFAULT_IP_ADDRESS "115.156.163.175"
//#define DEFAULT_IP_MASK "255.255.254.0"
//#define DEFAULT_GW_ADDRESS "115.156.163.254"
void platform_enable_interrupts(void);
void start_application(void);
void print_app_header(void);
#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
struct netif server_netif;
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);
}
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 main(void)
{
/*
* 设置开发板MAC地址
* 开启中断系统
* 设置本地IP地址
* 初始化lwIP
* 添加网络接口
* 设置默认网络接口
* 启动网络
* 初始化TCP或UDP连接(自定义函数)
* */
struct netif *netif;
struct ip_addr ipaddr, netmask, gw;
/* the mac address of the board. this should be unique per board */ //设定开发板的MAC地址
unsigned char mac_ethernet_address[] = {
0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
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
xil_printf("we are in here\r\n");
ProgramSi5324();
ProgramSfpPhy();
#endif
#endif
// Xil_ICacheEnable();
// Xil_DCacheEnable();
init_platform(); //开启中断系统,这个里面设定的时定时中断
// Init_Intr_System(&Intc);
// Setup_Intr_Exception(&Intc);
//定义一个网卡
netif = &server_netif;
/*两种方案实现对网卡ip、子网掩码、以及网关*/
//方案1
// IP_ADDR(&ipaddr, 192, 168, 127, 202);
// IP_ADDR(&netmask, 255, 255, 255, 0);
// IP_ADDR(&gw, 192, 168, 127, 254);
assign_default_ip(&(netif->ip_addr), &(netif->netmask), &(netif->gw));
xil_printf("\r\n\r\n");
xil_printf("-----lwIP RAW Mode UDP Server Application-----\r\n");
/* initialize lwIP */
lwip_init();
xil_printf("we have finnished lwip init");
/* Add network interface to the netif_list, and set it as default */
if (!xemac_add(netif, NULL, NULL, NULL, mac_ethernet_address,
PLATFORM_EMAC_BASEADDR)) {
xil_printf("Error adding N/W interface\r\n");
return -1;
}
xil_printf("before the the default set");
netif_set_default(netif); //将当前的网卡设定成默认的网卡
xil_printf("after the default set");
/* now enable interrupts */
platform_enable_interrupts(); //开启之前设定的中断
/* specify that the network if is up */
netif_set_up(netif); //启动之前所设定的网络
print_ip_settings(&(netif->ip_addr), &(netif->netmask), &(netif->gw));
xil_printf("\r\n");
/* print app header */
//print_app_header();
/* start the application*/
//start_application(); //初始化udp,启动服务器
user_udp_init();
xil_printf("\r\n");
while (1) {
xil_printf("we are sending \r\n");
/* 将MAC队列中的包传输的LwIP/IP栈中 */
xemacif_input(netif);
// if (udp_connected_flag) { //发送
sleep(1);
// xil_printf("we are in if nei\r\n");
udp_printf();
// }
}
/* never reached */
cleanup_platform();
return 0;
}
#include "user_udp.h"
//---------------------------------------------------------
// 变量定义
//---------------------------------------------------------
struct udp_pcb *connected_pcb = NULL;
static struct pbuf *pbuf_to_be_sent = NULL;
char *send_buff = "udploWorld"; //待发送字符
int *send_buffint = 100;
struct ip_addr ipaddr;
static unsigned local_port = 5002; //本地端口
static unsigned remote_port = 8080; //远程端口
//---------------------------------------------------------
// UDP连接初始化函数
//---------------------------------------------------------
int user_udp_init(void)
{
struct udp_pcb *pcb;
err_t err;
/* 创建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, 127, 200);
// IP4_ADDR(&ipaddr, 192, 168, 1, 101);
// IP4_ADDR(&ipaddr, 115, 156, 162, 123);
// IP4_ADDR(&ipaddr, 115, 156, 162, 76);
connected_pcb = pcb;
/* 申请pbuf资源 */
pbuf_to_be_sent = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_ROM);
memset(pbuf_to_be_sent->payload, 0, 10);
// memcpy(pbuf_to_be_sent->payload, (u8 *)send_buff, 4);
return 0;
}
//---------------------------------------------------------
// UDP发送数据函数
//---------------------------------------------------------
void udp_printf(void)
{
err_t err;
struct udp_pcb *tpcb = connected_pcb;
if (!tpcb) {
xil_printf("error connect.\r\n");
}
//
// send_buff[0] ='0';
// send_buff[1] = '1';
// send_buff[2] = '2';
// send_buff[3] = '3';
// send_buff[4] = '4';
// send_buff[5] = '5';
// send_buff[6] = '6';
// send_buff[7] = '7';
// send_buff[8] = '8';
// send_buff[9] = '9';
*send_buff = "0123456789";
// memset(pbuf_to_be_sent->payload, 0, 10);
memcpy(pbuf_to_be_sent->payload, (u8 *)send_buff, 10);
/* 发送字符串 */
err = udp_sendto(tpcb, pbuf_to_be_sent, &ipaddr, remote_port);
if (err != ERR_OK) {
// xil_printf("Error on udp send : %d\r\n", err);
return;
}
}
void udp_senddata(int data)
{
err_t err;
struct udp_pcb *tpcb = connected_pcb;
if (!tpcb) {
xil_printf("error connect.\r\n");
}
// data =100;
*send_buffint = data;
memcpy(pbuf_to_be_sent->payload, send_buffint, 2);
xil_printf("we are sending here:%d\r\n",*send_buffint);
/* 发送字符串 */
err = udp_sendto(tpcb, pbuf_to_be_sent, &ipaddr, remote_port);
if (err != ERR_OK) {
// xil_printf("Error on udp send : %d\r\n", err);
return;
}
}
/*
* usr_udp.h
*
* Created on: 2020年1月16日
* Author: Scottar
*/
#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_printf(void);
void udp_senddata(int data);
#endif /* SRC_USER_UDP_H_ */
reveiver_url = "ipc://11_Router"
socketzmq = context.socket(zmq.ROUTER)
socketzmq.set_hwm(HWM_VAL)
sendinglist=[]
client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client_socket.bind(("192.168.127.200", 8080))
print('we have connected to the tcp data send server!---port is :', port)
packagenum = 0
start_time_perf = time.perf_counter()
start_time_process = time.process_time()
count = 0
# 实际上应当启用的市多线程来做这些事情的
# 每一个线程要做的事情就是接收对应的内容
# 我想epics里面做的也是基本想同样的事情 ---最后写一个自动化的脚本多线程
start_flag = True
while True:
b, addr = client_socket.recvfrom(10)
print('I',count)
if count==1000000:
break
count = count + 1
# timestample = str(datetime.datetime.now()).encode()
# b = b + timestample
#
# sendinglist.append(b)
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) #
socketzmq.close()
(1)下位机的测试程序
(2)测试结果如下:
(1)下位机的测试的程序
(2) 测试结果
上位机的测试结果:
(3) 上位机去掉print 打印进行结果测试
(1)上位机数据接收程序以及下位机发程序
(2)测试结果:
测试结论:
对于udp来说,以10us的速度进行上传,上位机的接收速度没有办法完全达到10us 的接受速度, 这样的原因,可能是由与下位机在发送的时候设定的程序的延时就是10us,但是,由于其调用udp的发送程序,其也会出现相应的时间的消耗,所以整体出现了接收1M个数据包会出现较多的耗时,经过不断的测试,可以发现将下位机的发送程序设定的延时在7us的情况下,上位机接收到1M个数据包,所消耗的时间十分接近10s,也就是100k/s的上传的速度。
(1)上位机的测试代码
(2)以下位机以100us的速度 ,通过udp进行上传。上位机接收到数据后,打上时间标签。
(3) 测试结果如下:
可见,对于这样的速度的udp来说,也基本不会出现丢包的问题。
(1) 下位机数据上传代码:
下面的主要代码是提前设定好了,数据包发送的速度是10us,然后每个数字每10个一轮回
(2)上位机测试代码:
(3) 测试结果
通过上面的测试结果,我们可以得出,数据包的传输会出现相对较多的丢包情况。
测试打时间标签的耗时影响。
(1)下位机测试程序代码:
(2)上位机测试程序
(3) 测试结果
(4) 判断丢包情况
通过上面的测试结果,可以看到会丢包情况。
(1) 测试接收代码:
(2) 测试接收耗时:
(1)上位机测试代码
(2) 测试结果:
(1) 上位机接收代码:
(2)中间测试结果
(3) 测试结论
中间累积10个数据包再进行发送,可以大量减少中间数据中转的情况,但是可以看到实际的数据丢包的情况,其接近1%的丢包率。这个丢包率应该是建立在pub与sub之间的数据包丢包,应当,出现这样的原因显然是由于pub-sub的定义的客户端和服务器不合适,我们通过现在将pub定义成客户端,连接好sub服务器之后,再进行数据的发送。
(1)第一次测试结果:
(2)第二次测试结果
(3) 第三次测试结果:
(4) 第四次测试结果:
(5) 第五次测试结果:
ps:以上所有的测试结果中,第三层次的测试耗时,都与第二层次的耗时基本一样,如下:
(6) 第六次测试结果
通过上面的测试过程与结果,我们可以得到zmq 的发送函数zmq.send 函数频繁的调用,会出现比较消耗系统的资源,占用一定的时间。因此,当我们累积10个之后,再进行数据的发送,在这样的情况下,理论上,可以将由于数据发送的耗时降低10倍
应当首先默认下位机的上传的速度是能够大概在每隔10us 上传一次数据,我们需要验证上位机的打时标的前后两个数据包的时间,保证其前后两个数据包的时间个间隔在可接受的范围内,通过上述的测试的结果,我们可以计算每个数据包之间平均间隔。
对于10.68s,对应1M个数据包,即每个数据包的时间为如下:10.68us,即约10us。
10.68/1000000
1.068e-05
但是实际上,两个数据包之间的数据的间隔是总是变化的,因此,我们将这样的数据包通过单上时间标签,然后通过通过对包的数据格式的解析之后,将其存储到数据库当中,然后对之后存储进去的数据进行实际的验证。
---------------updata----on 2020-0601-------------
(1)测试结果
中间数据监测层耗时:
第三层的数据解析与存储层次的耗时:
(2) 第二次的测试结果
中间监测层的耗时:
第三层的数据的解析与存储层次的耗时:
(3) 第三次测试结果
中间数据接收和监测层的耗时:
第三层数据解析与存储层的耗时:
通过上面存入到数据库当中,的时间标签间隔,可以发现很多数据之间的监测大概都在13us左右,由于我们接收的数据的时间为13s,因此,每个数据包的时间的间隔约为13s/1M=13us。
根据上一小节展示的结果,我们可以看到结果是存在一定成都上的数据包的丢失,但是丢失很少。这也是udp通讯协议不可避免的地方。
(1) 在数据传输过程中的系统监测图:
(2)监测结论
>>> 100000*(64)*8/1024/1024
48.828125
当下位机设备通过100k/s 的速度进行上传数据包的时候,我们可以通过将udp的头部,ip头部,数据长度加在一起,可以大致计算出实际上传的速度的大小。
对于tcp,udp ip mac 的帧格式,请参考:https://www.cnblogs.com/OctoptusLian/p/8580052.html
udp+ip的头部整体的字节数为:8个字节(udp头部) + 20字节(ip)+ mac(14) =42字节,我们再额外加上10个字节的数据,我们可以得到52个字节。由于mac会帮助我们进行数据的填充,所以实际还是应当按照64个字节进行计算,结果如上所示,并且也可验证出我们的结果的正确性。
占用了更多的系统的资源,此时对于udp的数据的接收来说,我们可以通过降低udp数据的发送延时,来提高其发送的速度,从而使得实际的这边的数据的接收的时间的间隔更加短一些。
中间的某些时刻有可能会出现,上位机接收不到的情况,很有可能是由于phy芯片自动协商的问题,这个时候,由于笔记本的电脑采用一般都是千兆的网卡, 但是有时候由于介质的原因可能协商到百兆,就会出现意向不到问题,总之,要确定自己的网卡协调后的网速应当是1Gb,如下图