一、创建vivado工程
选择以太网0,选择串口UART1,其他的端口可以根据需要选择
生成output文件,生成xsa文件
二、在vitis中设置
基于生成的xsa文件,创建vitis工程,选择lwip的包
然后配置LWIP相关的参数。可以根据米联客的教程进行修改,也可以选择Socket模式,但是一开始我先还是使用了默认的RAW API,想通过UDP协议练习发送hello world的实验来熟悉LWIP协议栈的工作过程。
创建工程以后复制代码,添加main.c文件,以及中断系统文件和user_udp.c/h文件
代码参考文章学会Zynq(14)UDP发送Hello World_xilinx sdk udp server-CSDN博客
有一个需要注意的是,我使用的是vitis2021.1版本,LWIP的包用的是lwip211,编译的时候出现一个报错
在新版的 lwIP 中,struct ip_addr
被废弃了,而改为使用 ip4_addr
来表示 IPv4 地址。因此需要根据使用的LWIP版本,把结构体的名字修改一下,消除了错误。
三、网络调试助手测试
首先更改电脑的IP地址,win11系统更改IP地址的方式如下:
(1)快捷键win+R,输入control
(2)点击网络和共享中心,更改适配器设置
(3)插上网线以后,会显示以太网,然后右键属性,选择Internet 协议版本 4(TCP/IPV4),更改设置好的IP地址,按照上述程序的话,应该修改为192.168.1.100,子网掩码还是255.255.255.0
然后设置网络调试助手
主要设置左上角的部分,协议类型为UDP,主机地址设置为刚才修改的192.168.1.100
然后下载程序,选择打开,就可以在主界面收到Hello world!的信息!
四、程序代码理解
整体思路:
- 首先设置开发板的MAC地址,设置他的IP地址,子网掩码和网关;
- 初始化中断系统;
- 初始化LWIP库;
- 用 xemac_add 函数,添加网络接口;并且设置为默认接口
- 启动网络;
- 初始化udp
- 创建udp控制块(udp_new)
- 绑定本地端口(udp_bind)
- 设置远程地址(也就是电脑的IP地址)IP4_ADDR(&ipaddr, 192, 168, 1, 100);
- 连接远程地址(udp_connect)这个只有用send函数发送时需要,并且设置一个连接成功标志位,当确认成功了以后再调用发送
- 如果用sendto函数发送,就可以在这里直接申请pbuf资源,无需反复申请和释放
- 将MAC队列中的包传输的LwIP/IP栈中:(一定都要有这一句)xemacif_input(netif);
- 发送数据 udp_printf
- 如果用send函数,就先申请pbuf资源(pbuf_alloc),然后把要发送的数写到这个pbuf里面,调用udp send函数发送。最后释放pbuf(pbuf_free)——缺点是每次发送都要申请和释放一次pbuf
- 如果用sendto函数,就可以直接调用此函数发送
具体函数语句:
(1)LWIP的初始化函数
是封装好的,里面包含了很多协议的初始化函数,可以直接调用
void lwip_init();
(2)设置IP地址
这个函数用来增强代码可视性。习惯上我们用四个字节数据去表示,在这个函数当中就可以根据我们的习惯来编写,配置IP地址和网管等。
IP4_ADDR(ipaddr, a,b,c,d)
(3)添加网络接口的函数
第一个参数是 netif 指针,在lwIP中,netif(网络接口)是一个核心的结构体,它代表了一个网络接口,例如以太网接口、Wi-Fi接口等。netif 结构体用于定义和管理这些接口,使lwIP可以在多个接口上运行并进行路由决策。结构体里面通常包括:
- 硬件地址:例如MAC地址。
- IP地址、子网掩码和网关:这些用于IP层的路由和地址决策。
- 状态标志:表示接口的状态,例如是否激活、是否为默认接口等。
- 输入和输出函数指针:这些函数用于处理从该接口接收到的数据包或向该接口发送数据包。
- 其他驱动特定的数据:例如用于DMA的描述符、缓冲区等。
在写程序的时候需要先定义一个这样的结构体。
存疑一下,这句看起来好奇怪?
然后紧接着的三个参数,看起来分别表示的是IP地址,子网掩码和网关
后面的两个参数是MAC地址还有一个是ETH0外设的一个参数
struct netif *xemac_add (struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask, struct ip_addr *gw, unsigned char *mac_ethernet_address, unsigned mac_baseaddr)
(4)将MAC队列中的包传输的LwIP/IP栈中
该函数只在RAW模式下可用。Xlinx lwIP适配器在中断模式下工作。接收中断处理程序从EMAC/GigE中将包数据移动并存储在队列中。xemacif_input函数从队列中取出这些包,传递给lwIP。因此在RAW mode下,需要使用这个程序。
这个函数在while(1)循环里面使用,该程序会通过回调通知已经接收到的数据。
感觉有点像uart里面的开始接收?
void xemacif_input(struct netif *netif)
(5)UDP连接的初始化函数 user_udp_init
学会Zynq(11)RAW API的TCP和UDP编程_lwip raw 调用 tcp_output-CSDN博客
这里面的函数在文章中有详细讲解
(6)UDP发送数据函数udp_printf
关于具体每个函数的功能可见文章:学会Zynq(14)UDP发送Hello World_xilinx sdk udp server-CSDN博客
(强推这个大佬,让我这个刚上手的小白能找到一点点方向)
总结一下上面函数的大概含义就是:
首先用send_buff 的数组存下我要发送的数据;然后申请 pbuf 资源,把我要发送的数据写进我申请的 pbuf 缓冲区里面,然后调用udp_send函数把 pbuf 中的数据发送出去。最后释放pbuf
中间还有两个概念我不是很理解,比如什么是pcb,是否所有的数据发送都是通过申请 pbuf,然后调用发送函数?
今天是学习LWIP的第一天,明天继续加油!
(所有引用内容都是自学使用,侵删)