LWIP使用解析_lwip tcp

LWIP使用解析_lwip tcp1:环境STM32F407RT-thread2:结构体使用最上层:structrt_stm32_ethstructrt_stm32_eth{/*inheritfromethernetdevice*/structeth_deviceparent;/*interfaceaddressinfo,hwaddress*/rt_uint8_tdev_addr[MAX_ADDR_LEN];/*ETH_Speed*/

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

1:环境

STM32F407
RT-thread

2:结构体使用

最上层:struct rt_stm32_eth

struct rt_stm32_eth
{ 
   
    /* inherit from ethernet device */
    struct eth_device parent;

    /* interface address info, hw address */
    rt_uint8_t  dev_addr[MAX_ADDR_LEN];
    /* ETH_Speed */
    uint32_t    ETH_Speed;
    /* ETH_Duplex_Mode */
    uint32_t    ETH_Mode;
};

Jetbrains全家桶1年46,售后保障稳定

下一层:struct eth_device parent;

struct eth_device
{ 
   
    /* inherit from rt_device */
    struct rt_device parent;

    /* network interface for lwip */
    struct netif *netif;
    struct rt_semaphore tx_ack;

    rt_uint16_t flags;
    rt_uint8_t  link_changed;
    rt_uint8_t  link_status;

    /* eth device interface */
    struct pbuf* (*eth_rx)(rt_device_t dev);
    rt_err_t (*eth_tx)(rt_device_t dev, struct pbuf* p);
};

一般来说struct rt_device paren是最终被用来向RT-thread进行注册的。
eth_rx 和eth_tx 这两个是比较重要的函数指针,需要再自己的驱动重来实现。

3:DMA描述符

在LWIP内部使用一种结构叫做DMA描述符。
STM32F407以太网模块中的接收/发送FIFO和内存之间的以太网数据包传输是DMA使用DMA描述符完成的。一共两个描述符列表:一个用于接收,一个用于发送,两个列表的基址分别写入ETH_DMARDLAR寄存器和ETH_DMATDLAR寄存器中。
在这里插入图片描述
在 ST 提供的以太网驱
动库 stm32f4x7_eth.c 中使用的是链 接结构,DMA 描述符连接结构的具体描述如图

在这里插入图片描述
1、一个以太网数据包可以跨越一个或多个 DMA 描述符。
2、一个 DMA 描述符只能用于一个以太网数据包
3、DMA 描述符列表中的最后一个描述符指向第一个,形成链式结构。
在 ST 的以太网驱动库 stm32f4x7_eth.h 中有个结构体 ETH_DMADESCTypeDef,这个结构
体定义了 DMA 描述符,ETH_DMADESCTypeDef 结构体代码如下。

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 */
  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 */

} ETH_DMADescTypeDef;

DMA 描述符分为增强描述符和常规描述符,本教程中我们使用的是常规描述符,如果使
用常规描述符的话就只使用 ETH_DMADESCTypeDef 结构体的前四个成员变量

STM32F407 的描述符分为发送描述符和接收描述符,发送描述符和接收描述符
都用 ETH_DMADESCTypeDef 这个结构体来定义,我们这里只以发送描述符(常规 Tx DMA 描
述符)为例讲解一下,接收描述符(常规 Rx DMA 描述符)与其类似。常规 Tx DMA 描述符可以用
加粗样式
可以从图 中看到好像常规 Tx DMA 描述符有 4 个“寄存器”:TDES0、TDES1、
TDES2 和 TDES3,如果是增强描述符的话就会有 8 个“寄存器”。这里一定要注意这 4 个“寄
存器”并不是 STM32F407 真实存在的,你在 STM32F407 的寄存器列表里面是找不到的,这 4
个“寄存器”是由 ETH_DMADESCTypeDef 这个结构体描述的,我将它们称之为“软件寄存器”,
这些“寄存器”也叫做描述符字,描述符字和 ETH_DMADESCTypeDef 这个结构体的关系如表
所示(此处以 Tx DMA 描述符为例)

在这里插入图片描述
以 太网描述符结 构体 ETH_DMADESCTypeDef 中 Buffer1Addr 就 是缓冲 区的地址,
Buffer2NextDescAddr 就是下一个描述符的地址

在这里插入图片描述

在 stm32f4x7_eth.c 文件中为了追踪 Rx/Tx DMA 描述符还定义了两个非常重要的全局指针
变量,如下代码。

__IO ETH_DMADESCTypeDef *DMATxDescToSet;
__IO ETH_DMADESCTypeDef *DMARxDescToGet;

这两个指针变量指向 ETH_DMADESCTypeDef 结构体,在使用中他们两个分别指向下一个
要发送或者接受的数据包
在这里插入图片描述

4:函数分析

4.1 初始化函数

static int rt_hw_stm32_eth_init(void)
{ 
   
    rt_err_t state = RT_EOK;

    /* Prepare receive and send buffers */
    Rx_Buff = (rt_uint8_t *)rt_calloc(ETH_RXBUFNB, ETH_MAX_PACKET_SIZE);
    if (Rx_Buff == RT_NULL)
    { 
   
        LOG_E("No memory");
        state = -RT_ENOMEM;
        goto __exit;
    }

    Tx_Buff = (rt_uint8_t *)rt_calloc(ETH_TXBUFNB, ETH_MAX_PACKET_SIZE);
    if (Rx_Buff == RT_NULL)
    { 
   
        LOG_E("No memory");
        state = -RT_ENOMEM;
        goto __exit;
    }

    DMARxDscrTab = (ETH_DMADescTypeDef *)rt_calloc(ETH_RXBUFNB, sizeof(ETH_DMADescTypeDef));
    if (DMARxDscrTab == RT_NULL)
    { 
   
        LOG_E("No memory");
        state = -RT_ENOMEM;
        goto __exit;
    }

    DMATxDscrTab = (ETH_DMADescTypeDef *)rt_calloc(ETH_TXBUFNB, sizeof(ETH_DMADescTypeDef));
    if (DMATxDscrTab == RT_NULL)
    { 
   
        LOG_E("No memory");
        state = -RT_ENOMEM;
        goto __exit;
    }

    stm32_eth_device.ETH_Speed = ETH_SPEED_100M;
    stm32_eth_device.ETH_Mode  = ETH_MODE_FULLDUPLEX;

    /* OUI 00-80-E1 STMICROELECTRONICS. */
    stm32_eth_device.dev_addr[0] = 0x00;
    stm32_eth_device.dev_addr[1] = 0x80;
    stm32_eth_device.dev_addr[2] = 0xE1;
    /* generate MAC addr from 96bit unique ID (only for test). */
    stm32_eth_device.dev_addr[3] = *(rt_uint8_t *)(UID_BASE + 4);
    stm32_eth_device.dev_addr[4] = *(rt_uint8_t *)(UID_BASE + 2);
    stm32_eth_device.dev_addr[5] = *(rt_uint8_t *)(UID_BASE + 0);

    stm32_eth_device.parent.parent.init       = rt_stm32_eth_init;
    stm32_eth_device.parent.parent.open       = rt_stm32_eth_open;
    stm32_eth_device.parent.parent.close      = rt_stm32_eth_close;
    stm32_eth_device.parent.parent.read       = rt_stm32_eth_read;
    stm32_eth_device.parent.parent.write      = rt_stm32_eth_write;
    stm32_eth_device.parent.parent.control    = rt_stm32_eth_control;
    stm32_eth_device.parent.parent.user_data  = RT_NULL;

    stm32_eth_device.parent.eth_rx     = rt_stm32_eth_rx;
    stm32_eth_device.parent.eth_tx     = rt_stm32_eth_tx;

    /* register eth device */
    state = eth_device_init(&(stm32_eth_device.parent), "e0");
    if (RT_EOK == state)
    { 
   
        LOG_D("emac device init success");
    }
    else
    { 
   
        LOG_E("emac device init faild: %d", state);
        state = -RT_ERROR;
        goto __exit;
    }

    /* start phy monitor */
    rt_thread_t tid;
    tid = rt_thread_create("phy",
                           phy_monitor_thread_entry,
                           RT_NULL,
                           1024,
                           RT_THREAD_PRIORITY_MAX - 2,
                           2);
    if (tid != RT_NULL)
    { 
   
        rt_thread_startup(tid);
    }
    else
    { 
   
        state = -RT_ERROR;
    }
__exit:
    if (state != RT_EOK)
    { 
   
        if (Rx_Buff)
        { 
   
            rt_free(Rx_Buff);
        }

        if (Tx_Buff)
        { 
   
            rt_free(Tx_Buff);
        }

        if (DMARxDscrTab)
        { 
   
            rt_free(DMARxDscrTab);
        }

        if (DMATxDscrTab)
        { 
   
            rt_free(DMATxDscrTab);
        }
    }

    return state;
}
INIT_DEVICE_EXPORT(rt_hw_stm32_eth_init);

1:动态分配了Tx,Rx 的buffer 和 DMA描述符
2:对stm32_eth_device结构体进行赋值, 包括速度,模式,mac地址,操作函数,还有eth_tx,eth_rx发送接收函数
3:注册网络设备,命名e0
4:创建了新的线程phy

其中比较重要的是rt_stm32_eth_init,eth_tx,eth_rx这两个函数必须要进行初始化赋值!

4.2 rt_stm32_eth_init

这个函数要来做一些初始化

/* EMAC initialization function */
static rt_err_t rt_stm32_eth_init(rt_device_t dev)
{ 
   
    __HAL_RCC_ETH_CLK_ENABLE(); 初始化时钟

    phy_reset();  reset PHY 向拉低再拉高

    /* ETHERNET Configuration */
    EthHandle.Instance = ETH;
    EthHandle.Init.MACAddr = (rt_uint8_t *)&stm32_eth_device.dev_addr[0];
    EthHandle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_DISABLE;
    EthHandle.Init.Speed = ETH_SPEED_100M;
    EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
    EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
    EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE;
#ifdef RT_LWIP_USING_HW_CHECKSUM
    EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
#else
    EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_SOFTWARE;
#endif

    HAL_ETH_DeInit(&EthHandle); 清除ETH的配置
    /* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */
    if (HAL_ETH_Init(&EthHandle) != HAL_OK)  配置ETH的一些参数

    { 
   
        LOG_E("eth hardware init failed");
        return -RT_ERROR;
    }
    else
    { 
   
        LOG_D("eth hardware init success");
    }
	下面这两个函数时把DMA Tx Rx的描述符进行初始化串联起来
    /* Initialize Tx Descriptors list: Chain Mode */
    HAL_ETH_DMATxDescListInit(&EthHandle, DMATxDscrTab, Tx_Buff, ETH_TXBUFNB);

    /* Initialize Rx Descriptors list: Chain Mode */
    HAL_ETH_DMARxDescListInit(&EthHandle, DMARxDscrTab, Rx_Buff, ETH_RXBUFNB);

    /* ETH interrupt Init*/ 中断初始化
    HAL_NVIC_SetPriority(ETH_IRQn, 0x07, 0);
    HAL_NVIC_EnableIRQ(ETH_IRQn);

    /* Enable MAC and DMA transmission and reception */使能MAC和DMA
    if (HAL_ETH_Start(&EthHandle) == HAL_OK)
    { 
   
        LOG_D("emac hardware start");
    }
    else
    { 
   
        LOG_E("emac hardware start faild");
        return -RT_ERROR;
    }

    return RT_EOK;
}

4.2.1 HAL_ETH_DMATxDescListInit

HAL_StatusTypeDef HAL_ETH_DMATxDescListInit(ETH_HandleTypeDef *heth, ETH_DMADescTypeDef *DMATxDescTab, uint8_t *TxBuff, uint32_t TxBuffCount)
{ 
   
  uint32_t i = 0U;
  ETH_DMADescTypeDef *dmatxdesc;
  
  /* Process Locked */
  __HAL_LOCK(heth);
  
  /* Set the ETH peripheral state to BUSY */
  heth->State = HAL_ETH_STATE_BUSY; 设置状态等于busy
  
  /* Set the DMATxDescToSet pointer with the first one of the DMATxDescTab list */
  heth->TxDesc = DMATxDescTab;获取第一个描述符
  
  /* Fill each DMATxDesc descriptor with the right values */   
  for(i=0U; 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 */给描述符的buffer赋值地址
    dmatxdesc->Buffer1Addr = (uint32_t)(&TxBuff[i*ETH_TX_BUF_SIZE]);
    
    if ((heth->Init).ChecksumMode == ETH_CHECKSUM_BY_HARDWARE)
    { 
   
      /* Set the DMA Tx descriptors checksum insertion */
      dmatxdesc->Status |= ETH_DMATXDESC_CHECKSUMTCPUDPICMPFULL;
    }
    
    /* Initialize the next descriptor with the Next Descriptor Polling Enable */
    if(i < (TxBuffCount-1U))如果小于最大值,描述符的next就指向下一个,否则就指向第一个描述符
    { 
   
      /* Set next descriptor address register with next descriptor base address */
      dmatxdesc->Buffer2NextDescAddr = (uint32_t)(DMATxDescTab+i+1U);
    }
    else
    { 
   
      /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ 
      dmatxdesc->Buffer2NextDescAddr = (uint32_t) DMATxDescTab;  
    }
  }
  
  /* Set Transmit Descriptor List Address Register */ 把描述符的地址赋值给DMA的寄存器
  (heth->Instance)->DMATDLAR = (uint32_t) DMATxDescTab;
  
  /* Set ETH HAL State to Ready */设置状态等于ready
  heth->State= HAL_ETH_STATE_READY;
  
  /* Process Unlocked */
  __HAL_UNLOCK(heth);
  
  /* Return function status */
  return HAL_OK;
}

4.3 eth_tx

/* ethernet device interface */
/* transmit data*/
rt_err_t rt_stm32_eth_tx(rt_device_t dev, struct pbuf *p)
{ 
   
    rt_err_t ret = RT_ERROR;
    HAL_StatusTypeDef state;
    struct pbuf *q;
    uint8_t *buffer = (uint8_t *)(EthHandle.TxDesc->Buffer1Addr);
    __IO ETH_DMADescTypeDef *DmaTxDesc;
    uint32_t framelength = 0;
    uint32_t bufferoffset = 0;
    uint32_t byteslefttocopy = 0;
    uint32_t payloadoffset = 0;

    DmaTxDesc = EthHandle.TxDesc;
    bufferoffset = 0;

    /* copy frame from pbufs to driver buffers */
    for (q = p; q != NULL; q = q->next) 遍历pbuf来获取其中的数据
    { 
   
        /* Is this buffer available? If not, goto error */
        if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
        { 
   
            LOG_D("buffer not valid");
            ret = ERR_USE;
            goto error;
        }

        /* Get bytes in current lwIP buffer */
        byteslefttocopy = q->len;
        payloadoffset = 0;

        /* Check if the length of data to copy is bigger than Tx buffer size*/
        while ((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE)当要copy的数据和剩余数据之和大于DMA描述符buffer中最大值的时候
        { 
   
            /* Copy data to Tx buffer*/
            memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset));//向DMA buffer的复制数据

            /* Point to next descriptor */
            DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);//让描述符指向下一个描述符

            /* Check if the buffer is available */
            if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)//判断一下描述符的状态是否空闲
            { 
   
                LOG_E("dma tx desc buffer is not valid");
                ret = ERR_USE;
                goto error;
            }

            buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);//buffer又指向了DMA描述符的buffer地址

            byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);计算剩余要复制的数据
            payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);计算已经发送的数据
            framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);计算已经发送的数据
            bufferoffset = 0;
        }

        /* Copy the remaining bytes */剩余的数据不大于DMA描述符的buffer
        memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), byteslefttocopy);剩余数据复制到buffer中
        bufferoffset = bufferoffset + byteslefttocopy;每个pbuf分次传输后,当最后剩余的小大未将一个描述符的大小未被填满时,可以利用这个值来定位剩余空间的起始位置。
        便于下一个pbuf的数据,从这个位置继续填充
        framelength = framelength + byteslefttocopy;帧长度总量
    }

#ifdef ETH_TX_DUMP
    dump_hex(buffer, p->tot_len);
#endif

    /* Prepare transmit descriptors to give to DMA */
    /* TODO Optimize data send speed*/
    LOG_D("transmit frame lenth :%d", framelength);

    /* wait for unlocked */
    while (EthHandle.Lock == HAL_LOCKED);

    state = HAL_ETH_TransmitFrame(&EthHandle, framelength);调用这个函数进行描述符的发送
    if (state != HAL_OK)
    { 
   
        LOG_E("eth transmit frame faild: %d", state);
    }

    ret = ERR_OK;

error:

    /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
    if ((EthHandle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
    { 
   
        /* Clear TUS ETHERNET DMA flag */
        EthHandle.Instance->DMASR = ETH_DMASR_TUS;

        /* Resume DMA transmission*/
        EthHandle.Instance->DMATPDR = 0;
    }

    return ret;
}

这个函数主要的功能就是填充DMA描述符里面的buffer。
这里用到了一个结构体pbuf,payload指向具体的数据,数据存放到描述符中buffer。

4.3.1 pbuf

/** Main packet buffer struct */
struct pbuf { 
   
  /** next pbuf in singly linked pbuf chain */
  struct pbuf *next;

  /** pointer to the actual data in the buffer */
  void *payload;

  /** * total length of this buffer and all next buffers in chain * belonging to the same packet. * * For non-queue packet chains this is the invariant: * p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
  u16_t tot_len;

  /** length of this buffer */
  u16_t len;

  /** pbuf_type as u8_t instead of enum to save space */
  u8_t /*pbuf_type*/ type;

  /** misc flags */
  u8_t flags;

  /** * the reference count always equals the number of pointers * that refer to this pbuf. This can be pointers from an application, * the stack itself, or pbuf->next pointers from a chain. */
  u16_t ref;
};

调用HAL库函数的HAL_ETH_TransmitFrame进行真正的传输。

HAL_StatusTypeDef HAL_ETH_TransmitFrame(ETH_HandleTypeDef *heth, uint32_t FrameLength)
{ 
   
  uint32_t bufcount = 0U, size = 0U, i = 0U;
  
  /* Process Locked */
  __HAL_LOCK(heth);
  
  /* Set the ETH peripheral state to BUSY */
  heth->State = HAL_ETH_STATE_BUSY;
  
  if (FrameLength == 0U) 
  { 
   
    /* Set ETH HAL state to READY */
    heth->State = HAL_ETH_STATE_READY;
    
    /* Process Unlocked */
    __HAL_UNLOCK(heth);
    
    return  HAL_ERROR;                                    
  }  
  
  /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */
  if(((heth->TxDesc)->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
  { 
     
    /* OWN bit set */
    heth->State = HAL_ETH_STATE_BUSY_TX;
    
    /* Process Unlocked */
    __HAL_UNLOCK(heth);
    
    return HAL_ERROR;
  }
  
  /* Get the number of needed Tx buffers for the current frame */
  if (FrameLength > ETH_TX_BUF_SIZE)
  { 
   
    bufcount = FrameLength/ETH_TX_BUF_SIZE;
    if (FrameLength % ETH_TX_BUF_SIZE) 
    { 
   
      bufcount++;
    }
  }
  else 
  { 
     
    bufcount = 1U;
  }
  if (bufcount == 1U)
  { 
   
    /* Set LAST and FIRST segment */
    heth->TxDesc->Status |=ETH_DMATXDESC_FS|ETH_DMATXDESC_LS;
    /* Set frame size */
    heth->TxDesc->ControlBufferSize = (FrameLength & ETH_DMATXDESC_TBS1);
    /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
    heth->TxDesc->Status |= ETH_DMATXDESC_OWN;
    /* Point to next descriptor */
    heth->TxDesc= (ETH_DMADescTypeDef *)(heth->TxDesc->Buffer2NextDescAddr);
  }
  else
  { 
   
    for (i=0U; i< bufcount; i++)
    { 
   
      /* Clear FIRST and LAST segment bits */
      heth->TxDesc->Status &= ~(ETH_DMATXDESC_FS | ETH_DMATXDESC_LS);
      
      if (i == 0U) 
      { 
   
        /* Setting the first segment bit */
        heth->TxDesc->Status |= ETH_DMATXDESC_FS;  
      }
      
      /* Program size */
      heth->TxDesc->ControlBufferSize = (ETH_TX_BUF_SIZE & ETH_DMATXDESC_TBS1);
      
      if (i == (bufcount-1U))
      { 
   
        /* Setting the last segment bit */
        heth->TxDesc->Status |= ETH_DMATXDESC_LS;
        size = FrameLength - (bufcount-1U)*ETH_TX_BUF_SIZE;
        heth->TxDesc->ControlBufferSize = (size & ETH_DMATXDESC_TBS1);
      }
      
      /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */
      heth->TxDesc->Status |= ETH_DMATXDESC_OWN;
      /* point to next descriptor */
      heth->TxDesc = (ETH_DMADescTypeDef *)(heth->TxDesc->Buffer2NextDescAddr);
    }
  }
  
  /* When Tx Buffer unavailable flag is set: clear it and resume transmission */
  if (((heth->Instance)->DMASR & ETH_DMASR_TBUS) != (uint32_t)RESET)
  { 
   
    /* Clear TBUS ETHERNET DMA flag */
    (heth->Instance)->DMASR = ETH_DMASR_TBUS;
    /* Resume DMA transmission*/
    (heth->Instance)->DMATPDR = 0U;
  }
  
  /* Set ETH HAL State to Ready */
  heth->State = HAL_ETH_STATE_READY;
  
  /* Process Unlocked */
  __HAL_UNLOCK(heth);
  
  /* Return function status */
  return HAL_OK;
}

4.4 etx_rx

这个函数会先通过HAL_ETH_GetReceivedFrame_IT来获取DMA的描述符

/* receive data*/
struct pbuf *rt_stm32_eth_rx(rt_device_t dev)
{ 
   

    struct pbuf *p = NULL;
    struct pbuf *q = NULL;
    HAL_StatusTypeDef state;
    uint16_t len = 0;
    uint8_t *buffer;
    __IO ETH_DMADescTypeDef *dmarxdesc;
    uint32_t bufferoffset = 0;
    uint32_t payloadoffset = 0;
    uint32_t byteslefttocopy = 0;
    uint32_t i = 0;

    /* Get received frame */
    state = HAL_ETH_GetReceivedFrame_IT(&EthHandle);获取描述符
    if (state != HAL_OK)
    { 
   
        LOG_D("receive frame faild");
        return NULL;
    }

    /* Obtain the size of the packet and put it into the "len" variable. */
    len = EthHandle.RxFrameInfos.length;得到数据长度
    buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer;得到buffer首地址

    LOG_D("receive frame len : %d", len);

    if (len > 0)
    { 
   
        /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
        p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);给pbuf的指针分配空间
    }

#ifdef ETH_RX_DUMP
    dump_hex(buffer, p->tot_len);
#endif

    if (p != NULL)
    { 
   
        dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc;
        bufferoffset = 0;
        for (q = p; q != NULL; q = q->next)
        { 
   
            byteslefttocopy = q->len;
            payloadoffset = 0;

            /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
            while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE)数据长度大于结束buffer的最大值
            { 
   
                /* Copy data to pbuf */把数据从描述符的buffer重复制到pbuf中去
                memcpy((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));

                /* Point to next descriptor */描述符指向下一个
                dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
                buffer = (uint8_t *)(dmarxdesc->Buffer1Addr);

                byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
                payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
                bufferoffset = 0;
            }
            /* Copy remaining data in pbuf */复制剩下的数据
            memcpy((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), byteslefttocopy);
            bufferoffset = bufferoffset + byteslefttocopy;
        }
    }

    /* Release descriptors to DMA */
    /* Point to first descriptor */
    dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc;
    /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
    for (i = 0; i < EthHandle.RxFrameInfos.SegCount; i++)
    { 
   
        dmarxdesc->Status |= ETH_DMARXDESC_OWN;
        dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
    }

    /* Clear Segment_Count */
    EthHandle.RxFrameInfos.SegCount = 0;

    /* When Rx Buffer unavailable flag is set: clear it and resume reception */
    if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET)
    { 
   
        /* Clear RBUS ETHERNET DMA flag */
        EthHandle.Instance->DMASR = ETH_DMASR_RBUS;
        /* Resume DMA reception */
        EthHandle.Instance->DMARPDR = 0;
    }

    return p;把数据pbuf的指针返回
}

这个函数只要的功能就是,从DMA的描述符中获取数据并存放到pbuf中去。最后把pbuf的指针返回。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/227254.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • docker无法访问宿主机_docker访问宿主机端口

    docker无法访问宿主机_docker访问宿主机端口背景已通过docker启动mongodb,监听端口为27017.直接启动应用(不通过docker)可以正常访问到mongodb,但是通过docker访问却不行,访问的url为:mongodb://127.0.0.1:27017或mongodb://localhost:270172019-04-1806:05:52.694[cluster-ClusterId{value=’5cb813…

  • Python之字典添加元素

    Python之字典添加元素手动推荐知识点字典创建->创建字典(7种方式)删除元素->字典删除元素(6种方式)修改元素->字典修改元素(4种方式)遍历元素->字典遍历元素(4种方式)查找元素->字典查找元素(3种方式)本文使用代码book_dict={“price”:500,”bookName”:”Python设计”,”weight”:”250g”}第一种方式:使用[]book_dict[“owner”]=”tyson”说明:中.

  • VBA数组的排序_vba函数返回值 数组

    VBA数组的排序_vba函数返回值 数组我们平时用的表格排序,只相对来说是在在表格中的升序降序。今天就好奇如果系统中实现排序他是怎么实现的呢。经过一番折腾查找,真是一看吓一跳,真是感觉蚂蚁看大象,发现排序分为:今天仅整理了最简单的两种排序。。。先来看下定义和实现的方法吧。选择排序(Selectionsort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据

  • linux命令行修改用户名_linux 更改用户密码

    linux命令行修改用户名_linux 更改用户密码一、《Linux的chmod命令》。在shell中,可以使用chown命令来改变文件所有者及用户组,chgrp命令来改变文件所在用户组。在Linux的C程序中,可以使用chown函数来改变文件所有者,及所在用户组。另外,在shell中,要修改文件当前的用户必须具有管理员root的权限。可以通过su命令切换到root用户,也可以通过sudo获得root的权限。二、使用chown命令更改文件拥有…

  • 分别以递归调用和迭代的方法求数列_迭代算法和递归算法

    分别以递归调用和迭代的方法求数列_迭代算法和递归算法对于数列,递归和迭代的联系非常紧密。a0,a1,a2,…,an−1,ana_0,a_1,a_2,…,a_{n-1},a_na0​,a1​,a2​,…,an−1​,an​数列就是一串数字,数列来源于生活,有用的数列中蕴含着规则。要完整描述一个数列,方法有二:通项公式an=f(n)a_n=f(n)an​=f(n)递推公式其中通项公式是最一般的情况。由通项公式可以求得任意一…

  • 字符串常量池有什么用_字符串常量池在堆中还是方法区

    字符串常量池有什么用_字符串常量池在堆中还是方法区看网上的介绍,对于字符串常量池中到底保存的是字符串对象,还是字符串对象的引用,众说纷纭…看jdk1.8对intern()的说明.Whentheinternmethodisinvoked,ifthepoolalreadycontainsastringequaltothisStringobjectasdeterminedbytheequals(…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号