ZYNQ|LWIP协议栈学习(1)

本文介绍了如何在XilinxZynq平台上,通过Vitis工具和LWIP库实现以太网通信,创建工程时选择了以太网和串口配置,设置了LWIP参数,并解决了因新版LWIP结构改动引发的编译错误。作者还提供了详细的步骤,如网络调试助手设置和程序代码理解,以实现UDP发送HelloWorld功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、创建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-优快云博客

 有一个需要注意的是,我使用的是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可以在多个接口上运行并进行路由决策。结构体里面通常包括:

  1. 硬件地址:例如MAC地址。
  2. IP地址、子网掩码和网关:这些用于IP层的路由和地址决策。
  3. 状态标志:表示接口的状态,例如是否激活、是否为默认接口等。
  4. 输入和输出函数指针:这些函数用于处理从该接口接收到的数据包或向该接口发送数据包。
  5. 其他驱动特定的数据:例如用于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-优快云博客

这里面的函数在文章中有详细讲解

(6)UDP发送数据函数udp_printf

关于具体每个函数的功能可见文章:学会Zynq(14)UDP发送Hello World_xilinx sdk udp server-优快云博客

(强推这个大佬,让我这个刚上手的小白能找到一点点方向)

总结一下上面函数的大概含义就是:

首先用send_buff 的数组存下我要发送的数据;然后申请 pbuf 资源,把我要发送的数据写进我申请的 pbuf 缓冲区里面,然后调用udp_send函数把 pbuf 中的数据发送出去。最后释放pbuf

中间还有两个概念我不是很理解,比如什么是pcb,是否所有的数据发送都是通过申请 pbuf,然后调用发送函数?

今天是学习LWIP的第一天,明天继续加油!

(所有引用内容都是自学使用,侵删)

<think>嗯,用户问的是如何在Zynq 7020上调试lwIP协议栈中的tcp_write函数。首先,我需要回想一下Zynq 7020的基本情况,它是一款Xilinx的SoC,结合了FPGA和双核Cortex-A9处理器。lwIP作为轻量级的TCP/IP协议栈,常用于嵌入式系统,所以用户可能在FPGA部分实现了一些硬件加速,或者在ARM核上运行lwIP。 用户的问题核心是调试tcp_write函数。首先,我需要确定他们使用的开发环境,比如Vivado和SDK,或者可能转到了Vitis。调试通常涉及设置断点、查看变量、单步执行等。不过lwIP作为协议栈,可能在实时性要求高的场景下运行,调试时可能会影响网络性能,甚至导致超时或连接断开。 接下来,我要考虑如何具体进入tcp_write函数。首先需要确认代码是否已经正确编译并包含调试信息,比如GCC的-g选项。然后,在SDK或Vitis中设置断点,可能还需要检查lwIP的配置,比如是否启用了多线程支持,因为lwIP可能有不同的线程模型,这会影响断点的行为。 另外,用户可能遇到断点无法触发的情况,这可能是因为代码优化导致,需要关闭编译器优化,或者检查代码是否实际执行到了tcp_write。还有,tcp_write可能在中断上下文中执行,这时候调试器可能无法暂停,需要特殊的处理,比如使用日志输出。 还有,内存分配的问题,比如pcb结构体是否正确初始化,发送缓冲区是否足够,这些也会影响tcp_write的执行。可能需要使用静态分析工具或内存调试工具来检查。 总结步骤的话,应该是准备调试环境,设置断点,检查调用栈,处理可能的优化或上下文问题,结合日志和硬件工具。同时,提醒用户注意实时系统调试的挑战,以及多线程环境下的竞态条件等问题。</think>在Zynq 7020平台上调试lwIP协议栈的tcp_write函数,可按照以下步骤实施: --- ### **一、调试环境准备** 1. **硬件连接** - 确保Zynq开发板通过JTAG与主机连接 - 配置正确的串口终端(如Putty)用于观察日志输出 - 连接以太网物理链路并验证PHY芯片初始化正常 2. **软件配置** ```c /* 在lwipopts.h中启用调试选项 */ #define LWIP_DEBUG 1 #define TCP_DEBUG LWIP_DBG_ON #define TCP_OUTPUT_DEBUG LWIP_DBG_ON #define TCP_WND_DEBUG LWIP_DBG_ON ``` - 编译时添加`-g -O0`选项禁用优化 - 确认工程中已包含lwIP源码而非仅库文件 --- ### **二、调试入口设置** 1. **断点设置** - 在SDK/Vitis中定位`tcp_write()`函数(位于`lwip/src/core/tcp_out.c`) - 设置条件断点防止高频触发: ```c if (pcb->state == ESTABLISHED) { /* 在此处设置断点 */ } ``` 2. **触发条件** - 通过测试客户端发送TCP数据包 - 在应用程序中调用: ```c err_t err = tcp_write(pcb, data_ptr, data_len, TCP_WRITE_FLAG_COPY); ``` --- ### **三、关键参数观察** | 观察项 | 说明 | 调试方法 | |-----------------|-----------------------------|--------------------------| | pcb->state | TCP连接状态机 | 内存查看器/watchpoint | | pcb->snd_buf | 发送缓冲区剩余空间 | 表达式监视 | | pcb->unacked | 未确认数据量 | 断点后单步执行 | | err返回值 | 错误类型(ERR_MEM等) | 寄存器查看 | --- ### **四、常见问题排查** 1. **内存分配失败** ```c if (pcb->snd_buf < needed) { LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write: No buffer space\n")); return ERR_MEM; } ``` - 增大`TCP_SND_BUF`配置值 - 检查内存池使用情况`memp_stats_pcb` 2. **报文组装异常** - 使用Wireshark抓包分析TCP报文序列 - 验证`tcp_write`参数: ```c /* 检查flags参数组合合法性 */ LWIP_ASSERT("invalid flags", (flags & TCP_WRITE_FLAG_MASK) == flags); ``` --- ### **五、高级调试技巧** 1. **实时日志追踪** ```c #define TCP_WRITE_DEBUG(msg) \ printf("[%u] tcp_write: %s\n", sys_now(), msg) ``` 2. **性能分析** ```c /* 测量函数执行时间 */ uint32_t start = xthal_get_ccount(); tcp_write(...); uint32_t cycles = xthal_get_ccount() - start; ``` 3. **Xilinx SDK特定功能** - 使用System Performance Analyzer观察中断上下文切换 - 通过AXI Traffic Generator模拟网络负载 --- ### **六、注意事项** 1. 调试TCP协议栈时建议先使用回环测试模式 2. 多线程场景下需关闭任务调度器再设置断点 3. 当使用DMA加速时,需同步检查BD环状态 4. 注意Cache一致性(必要时调用`Xil_DCacheFlush()`) 通过以上方法可系统性地定位tcp_write相关问题,建议结合Vivado Logic Analyzer对MAC层信号进行联合调试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值