基于rt-thread+lwip分析数据是怎么从网卡芯片接收数据到pbuf的(lwip源码解析一)

本文详细介绍了在STM32F207+DP83848平台上,基于rt-thread和lwip如何接收网络数据并将其传递给LWIP内核的过程。文章讨论了DMA的初始化、接收和发送缓冲区的设置,以及中断处理中数据如何通过邮箱进入LWIP的pbuf。最后,概述了根据接收到的数据长度动态分配pbuf的步骤。

LWIP是嵌入式设备的网络微协议,基本上实现了标准的TCP/IP的功能,它没有项标准的TCP/IP协议那样有很严格的分层。主要原因是由于嵌入式设别的资源有限,所以避免了每层的COPY动作,在不同层之间是之间共用同一内存操作。那么下面我们来介绍下数据到底是怎么从网卡接收,然后吧数据交给协议处理的:

首先说明下,我用的平台是STM32F207+DP83848平台的,lwip1.4.1

1、首先我们初始化网卡,这里涉及的是硬件上面的配置,在这里就不做详细说明。在配置好了后,我们需要初始化相关参数。由于我们的STM32F207用的是DMAPHY那里获取数据,然后也是通过DMA把发送的数据送到PHY。所以在这里我们首先需要初始化好DMA的数据接收缓存和发送缓存。

1-1.在这里我们需要知道这两个全局变量:

ETH_DMADESCTypeDef  *DMATxDescToSet;//发送缓冲区指针,指向当前发送数据的首地址。

ETH_DMADESCTypeDef  *DMARxDescToGet;//接收缓冲区指针,指向当前接收缓冲区的首地址。

结构体成员变量如下:

typedef struct  {
  __IO uint32_t   Status;                /*!< Status */
  uint32_t   ControlBufferSize;     /*!< Control and Buffer1, Buffer2 lengths */
  uint32_t   Buffer1Addr;           /*!< Buffer1 address pointer */
  uint32_t   Buffer2NextDescAddr;   /*!< Buffer2 or next descriptor address pointer */
/* Enhanced ETHERNET DMA PTP Descriptors */
#ifdef USE_ENHANCED_DMA_DESCRIPTORS//这个红没有打开,所以我们只关心上面几个变量
  uint32_t   ExtendedStatus;        /* Extended status for PTP receive descriptor */
  uint32_t   Reserved1;             /* Reserved */
  uint32_t   TimeStampLow;          /* Time Stamp Low value for transmit and receive */
  uint32_t   TimeStampHigh;         /* Time Stamp High value for transmit and receive */
#endif /* USE_ENHANCED_DMA_DESCRIPTORS */
} ETH_DMADESCTypeDef;

首先来讲解下发送缓冲区:由于rt-thread用到的是一个两维数组做为发送数据的缓冲区,而且是把这个二维数组做成了一个环形的链表,这个就是在初始化需要完成的事情。

下面具体看代码实现:

void ETH_DMATxDescChainInit(ETH_DMADESCTypeDef *DMATxDescTab, uint8_t* TxBuff, uint32_t TxBuffCount)
{
  uint32_t i = 0;
  ETH_DMADESCTypeDef *DMATxDesc;
  /* Set the DMATxDescToSet pointer with the first one of the DMATxDescTab list */
  DMATxDescToSet = DMATxDescTab;//首先是把数组DMATxDescTab的首地址传入
  /* Fill each DMATxDesc descriptor with the right values */   
  for(i=0; i < TxBuffCount; i++)//这个for循环就是把数据缓冲区形成一个单项链表
  {
    /* Get the pointer on the ith member of the Tx Desc list */
    DMATxDesc = DMATxDescTab + i;
    /* Set Second Address Chained bit */
    DMATxDesc->Status = ETH_DMATxDesc_TCH;  
       
    /* Set Buffer1 address pointer */
    DMATxDesc->Buffer1Addr = (uint32_t)(&TxBuff[i*ETH_TX_BUF_SIZE]);
    
    /* Initialize the next descriptor with the Next Descriptor Polling Enable */
    if(i < (TxBuffCount-1))//这里是连接TxBuffCount-1个接收缓冲区,形成的只是一个单项链表
    {
      /* Set next descriptor address register with next descriptor base address */
      DMATxDesc->Buffer2NextDescAddr = (uint32_t)(DMATxDesc
<think>嗯,用户想知道如何在基于lwIP的TCP/IP协议栈中实现以太网数据流的收发控制。首先,我需要回忆lwIP的设计特点。根据引用[2],lwIP为了节省资源,各层之间共享数据,避免拷贝,这可能影响数据流控制的方式。接着,用户提到的收发控制可能涉及到数据包的缓冲、流量控制机制以及驱动层的实现。 我记得数据包处理在lwIP中使用pbuf结构,引用[1]提到避免复制数据包,所以应该直接操作pbuf的payload。这可能和发送接收队列的管理有关。然后,驱动层的结构也很重要,引用[3]说明了rt-thread下的网络设备结构,可能涉及到中断处理和DMA传输,这些都会影响数据流的控制。 用户可能想知道具体的方法,比如如何配置发送和接收缓冲区,如何实现流量控制,以及如何处理拥塞。需要分步骤解释,比如初始化配置、中断处理、零拷贝实现、流量控制算法等。此外,优化策略如调整窗口大小、使用ACK策略也很重要。可能还需要提到应用层接口,比如socket API或者raw API的选择。 需要确保回答覆盖了数据包处理机制、驱动层实现、流量控制的具体方法,以及优化技巧。同时,要结合引用中的信息,比如避免数据拷贝和协议栈结构的特点。可能还需要检查是否有相关的配置选项或API函数需要提及,比如tcp_sndbuf、tcp_recv等。最后,生成的相关问题要围绕实现细节、优化、调试和扩展应用展开,帮助用户深入理解。</think>在基于lwIP的TCP/IP协议栈中实现以太网数据流的收发控制,需要结合协议栈特性、驱动层实现和流量管理机制。以下是分步骤实现方法: ### 数据包处理机制 1. **零拷贝设计** 通过直接操作$pbuf$结构体指针实现,将网卡DMA缓冲区映射到$pbuf->payload$字段,避免数据复制[^1]。例如接收时: ```c p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); etharp_input(p, netif); // 直接将DMA缓冲区地址传入 ``` 2. **发送队列控制** 使用$sys_mbox$消息队列管理发送任务,通过$tcpip_send_msg()$函数实现线程安全的数据投递: ```c #define SEND_QUEUE_SIZE 32 static sys_mbox_t send_mbox; sys_mbox_new(&send_mbox, SEND_QUEUE_SIZE); ``` ### 二、驱动层实现 3. **中断与轮询混合模式** 在rt-thread驱动框架下(如引用[3]),配置中断阈值: ```c #define RX_THRESHOLD 64 // 中断触发阈值 #define POLLING_INTERVAL 10ms // 轮询间隔 ``` 4. **DMA环形缓冲区管理** 创建双缓冲结构减少锁冲突: ```c struct eth_dma_buf { volatile uint32_t head; volatile uint32_t tail; uint8_t buffer[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; }; ``` ### 三、流量控制方法 5. **滑动窗口动态调整** 根据$tcp_snd_buf()$返回值动态调整发送窗口: ```c u16_t snd_wnd = tcp_snd_buf(tpcb); snd_wnd = LWIP_MIN(snd_wnd, tcp_snd_queuelen(tpcb)*TCP_MSS); ``` 6. **RED随机早期检测** 在网卡驱动层实现队列管理: ```c #define QAVG_WEIGHT 0.125 // 指数加权因子 qavg = (1 - QAVG_WEIGHT) * qavg + QAVG_WEIGHT * current_qlen; if(qavg > max_th) drop_packet(); ``` ### 四、关键配置参数 | 参数 | 推荐值 | 说明 | |--------------------|-------------|----------------------| | TCP_WND | 4*TCP_MSS | 发送窗口大小 | | MEM_SIZE | 16KB | 内存池大小 | | PBUF_POOL_SIZE | 32 | pbuf缓冲池数量 | | ETH_RXBUFNB | 8 | DMA接收描述符数量 | ### 五、调试与优化 - 使用$stats$结构体监控流量: ```c printf("TCP sent:%d recv:%d retrans:%d\n", lwip_stats.tcp.xmit, lwip_stats.tcp.recv, lwip_stats.tcp.retrans); ``` - 通过$netif_set_link_callback()$注册链路状态回调函数,实现动态速率调整
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值