0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

LWIP协议栈内存管理方案 LWIP网卡设计与实现

CHANBAEK 来源:嵌入式攻城狮 作者: 安迪西 2023-04-19 11:20 次阅读

1.LWIP协议栈内存管理

1.1 内存管理需求

内存管理需求分为两类:

  • 常用内存管理需求:静态分配的变量(RAM),任务堆栈,动态存储器管理malloc/free
  • LWIP内存管理需求:协议栈各层封装的数据

1.2 内存管理方案

LWIP内存管理有两种方案:堆(heap)和池(pool)

堆:堆内存管理机制会根据需要分配的内存大小在空闲的内存块中找到最佳拟合(best fit)的内存区域

图片

LWIP内存堆管理API函数:

//内存堆初始化
void  mem_init(void);
//内存堆分配内存
void *mem_malloc(mem_size_t size);
//内存堆释放内存
void  mem_free(void *mem);

池:池内存管理机制将内存分配成多个大小不一的内存池,每个内存池中又被分为N个相同大小的内存块。 程序可根据需要使用的内存大小直接到不同的内存池中取用即可。 池内存管理机制分配内存更快,效率更高

图片

LWIP内存池管理API函数:

//内存池初始化
void  memp_init(void);
//内存池分配
void *memp_malloc(memp_t type);
//内存池释放
void  memp_free(memp_t type, void *mem);

1.3 网络数据包管理

pbuf就是一个描述协议栈中数据包的数据结构,LWIP 中在 pbuf.c和 pubf.h实现了协议栈数据包管理的所有函数与数据结构

图片

pbuf数据结构

pbuf结构体

//在pbuf.h中定义
struct pbuf {
  /** 指向下一个pbuf结构体,每个pbuf存放的数据有限,若应用有大量的数据的话
  就需要多个pbuf来存放,可以将同一个数据包的pbuf连接在一起构成一个链表  */
  struct pbuf *next;
  /** 指向该pbuf真正的数据存储区的首地址。STM32F4内部网络模块收到数据,并
  将数据提交给LWIP时,就是将数据存储在payload指定的存储区中;同样在发送数
  据的时候将payload指向的存储区数据转给STM32F4的网络模块去发送 */
  void *payload;
  /** 在接收或发送数据的时候数据会存放在pbuf链表中,tot_Len表示当前pbuf和链
  表中后面所有pbuf的数据长度,它们属于一个数据包 */
  u16_t tot_len;
  /** 当前pbuf总数据的长度 */
  u16_t len;
  /** 当前pbuf类型,共有四种:PBUF_RAM/PBUF_ROM/PBUF_REF/PBUF_POOL */
  u8_t  type;
  /** 保留位 */
  u8_t flags;
  /** 该pbuf被引用的次数,当还有其他指针指向这个pbuf的时候ref字段就加一 */
  u16_t ref;
};

pbuf类型:共有四种PBUF_RAM、PBUF_ROM、PBUF_REF、PBUF_POOL

//在pbuf.h中定义
typedef enum {
  /** PBUF_RAM类型的pbuf是通过内存堆分配来的,其payload并未指向数据区的起始
  地址,而是隔了一段区域,在这个区域(offset)里通常存放TCP报文首部、IP首部
  、以太网帧首部等等 */
  PBUF_RAM,
  /** PBUF_ROM的数据指针payload指向外部存储区,外部存储区指不由TCP/IP协议
  栈管理的存储区,它可以是应用程序管理的存储器为用户数据分配的缓存,也可以是
  ROM区域,如静态网页中的字符串常量等 */
  PBUF_ROM,
  /** PBUF_REF和PBUF_ROM的特性非常相似,都可以实现数据的零拷贝 */
  PBUF_REF,
  /** PBUF_POOL类型的pbuf是通过内存池分类来的,pbuf链表的第一个pbuf的payload
  未指向数据区的起始位置,原因通PBUF_RAM一样,用来存放一些首部,pbuf链表后面
  的pbuf结构体中的payload就指向了数据区的起始位置 */
  PBUF_POOL
} pbuf_type;

pbuf层:由于LWIP各层禁止数据拷贝,所以存在不同层次对数据包pbuf的alloc,前面的offest就是为不同层预留的头部字段,下面枚举了4种层次,分配时除了要知道大小、类型还要传入这个层次

//在pbuf.h中定义
typedef enum {
  /** 传输层,预留以太首部+IP首部+TCP首部 */
  PBUF_TRANSPORT,
  /** 网络层,预留以太首部+IP首部 */
  PBUF_IP,
  /** 链路层,预留以太首部 */
  PBUF_LINK,
  /** 原始层,不预留空间 */
  PBUF_RAW_TX,
  /** Use this for input packets in a netif driver when calling netif->input()
   * in the most common case - ethernet-layer netif driver. */
  PBUF_RAW
} pbuf_layer;

pbuf_alloc:内存申请函数

struct pbuf *pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type);
//layer 申请的pbuf所在的层,以此来确定offset值
//length 存放数据的大小
//type pbuf的类型

pbuf_free:数据包释放函数

u8_t pbuf_free(struct pbuf *p);
//p 要释放的pbuf数据包

pbuf_realloc:调整收缩pbuf的大小,在相应pbuf(链表)尾部释放一定的空间,将数据包pbuf中的数据长度减少为某个长度值

void pbuf_realloc(struct pbuf *p, u16_t new_len);
//p 要收缩的pbuf数据包
//new_len 新的长度值

pbuf_header:调整payload指针和长度字段以便为pbuf中的数据预置包头,常用于实现对pbuf预留孔间的操作

u8_t pbuf_header(struct pbuf *p, s16_t header_size_increment);
//p 要操作的pbuf数据包
//header_size_increment 大于0,payload前移,数据传递下层;
//			小于0,表示payload后移,数据传递上层

pbuf_take:用于向pbuf的数据区域拷贝数据

err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
//buf 要填充数据的pbuf
//dataptr 应用程序中的数据缓冲区
//len 数据缓冲区的长度

2.LWIP网卡设计与实现

2.1 LWIP网络接口管理

在LWIP中对于网络接口的描述是通过一个netif结构体完成的,netif结构体在netif.h文件中有定义

netif结构体

//在netif.h中定义
struct netif {
  struct netif *next;  //指向下一个netif结构体
#if LWIP_IPV4
  ip_addr_t ip_addr;	//ip地址
  ip_addr_t netmask;	//子网掩码
  ip_addr_t gw;	//网关地址
#endif /* LWIP_IPV4 */
  netif_input_fn input;	//netif数据包输入接口函数指针
#if LWIP_IPV4
  netif_output_fn output;//netif数据包输出接口函数指针
#endif /* LWIP_IPV4 */
  netif_linkoutput_fn linkoutput;//链路层数据输出接口函数指针
#if LWIP_NETIF_STATUS_CALLBACK
  //当netif状态发生变化时,此接口函数会调用
  netif_status_callback_fn status_callback;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
#if LWIP_NETIF_LINK_CALLBACK
  /* PHY必须和交换机或者路由器或者其他具备网卡的主机相连接,
  我们才可能正常通信比如路由器突然断电,这个函数就会被调用 */
  netif_status_callback_fn link_callback;
#endif /* LWIP_NETIF_LINK_CALLBACK */
#if LWIP_NETIF_REMOVE_CALLBACK
  //netif移除网络驱动接口,这个函数会被调用
  netif_status_callback_fn remove_callback;
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
  void *state;	//主机的状态
#if LWIP_NETIF_HOSTNAME
  const char*  hostname;	//自定义的主机名称
#endif /* LWIP_NETIF_HOSTNAME */
#if LWIP_CHECKSUM_CTRL_PER_NETIF
  u16_t chksum_flags;		
#endif /* LWIP_CHECKSUM_CTRL_PER_NETIF*/
  u16_t mtu;	//数据链路层最大传输大小
  u8_t hwaddr_len;	//mac地址长度
  u8_t hwaddr[NETIF_MAX_HWADDR_LEN];//mac地址
  u8_t flags;	//当前的netif的状态,其实就是下面的netif_flag
  char name[2];	//网卡驱动的名称
  u8_t num;		//网卡驱动的硬件编号
#if LWIP_IPV4 && LWIP_IGMP
  netif_igmp_mac_filter_fn igmp_mac_filter;//组播底层接口
#endif /* LWIP_IPV4 && LWIP_IGMP */
};

netif flag宏定义

/** netif网络接口,可以进行正常使用(即lwIP可以正常使用)了 */
#define NETIF_FLAG_UP           0x01U
/** 广播通讯的标志 */
#define NETIF_FLAG_BROADCAST    0x02U
/** STM32 MAC和PHY可以正常使用 */
#define NETIF_FLAG_LINK_UP      0x04U
/** ARP标志 */
#define NETIF_FLAG_ETHARP       0x08U
/** TCP/IP协议正常通信  */
#define NETIF_FLAG_ETHERNET     0x10U

2.2 netif典型API函数

netif的API函数是供应用层调用的函数

netif_add:添加网卡驱动到lwip

struct netif *netif_add(struct netif *netif,
			const ip4_addr_t *ipaddr, 
			const ip4_addr_t *netmask, 
			const ip4_addr_t *gw,
			void *state, 
			netif_init_fn init, 
			netif_input_fn input);

netif_set_default:把网卡恢复出厂设置,目前lwip有一套默认参数

void netif_set_default(struct netif *netif);

netif_set_up&netif_set_down:设置我们网卡工作状态,是上线还是离线

void netif_set_up(struct netif *netif);
void netif_set_down(struct netif *netif);

callback:需要自己实现link_callback函数

#if LWIP_NETIF_LINK_CALLBACK
void netif_set_link_callback(struct netif *netif, 
		netif_status_callback_fn link_callback);
#endif /* LWIP_NETIF_LINK_CALLBACK */

2.3 netif底层接口函数

netif底层接口即与硬件打交道的函数接口,主要包括以下函数

ethernetif_init:初始化网卡驱动(会调用底层驱动)

err_t ethernetif_init(struct netif *netif){
#if LWIP_IPV4
#if LWIP_ARP || LWIP_ETHERNET
//arp相关的函数接口赋值
#if LWIP_ARP
  netif->output = etharp_output;
#else
  netif->output = low_level_output_arp_off;
#endif /* LWIP_ARP */
#endif /* LWIP_ARP || LWIP_ETHERNET */
#endif /* LWIP_IPV4 */
  //链路层数据输出函数接口赋值
  netif->linkoutput = low_level_output;
  /* 底层接口初始化 */
  low_level_init(netif);
  return ERR_OK;
}

ethernetif_input:网卡数据输入(会调用底层接口)

void ethernetif_input(void const * argument){
  struct pbuf *p;
  struct netif *netif = (struct netif *) argument;
  for( ;; )
  {
    if (osSemaphoreWait(s_xSemaphore, TIME_WAITING_FOR_INPUT) == osOK)
    {
      do
      {   
        p = low_level_input( netif );
        if   (p != NULL)
        {
          if (netif->input( p, netif) != ERR_OK )
          {
            pbuf_free(p);
          }
        }
      } while(p!=NULL);
    }
  }
}

low_level_init:网卡底层驱动,主要针对硬件(STM32网卡初始化会在此调用)

/** 硬件初始化,其实就STM32 ETH外设初始化 */
static void low_level_init(struct netif *netif){ 
  uint32_t regvalue = 0;
  HAL_StatusTypeDef hal_eth_init_status;
  /* Init ETH */
  uint8_t MACAddr[6] ;
  heth.Instance = ETH;
  heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
  heth.Init.PhyAddress = DP83848_PHY_ADDRESS;
  MACAddr[0] = 0x00;
  MACAddr[1] = 0x80;
  MACAddr[2] = 0xE1;
  MACAddr[3] = 0x00;
  MACAddr[4] = 0x00;
  MACAddr[5] = 0x00;
  heth.Init.MACAddr = &MACAddr[0];
  heth.Init.RxMode = ETH_RXINTERRUPT_MODE;
  heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
  heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;

  hal_eth_init_status = HAL_ETH_Init(&heth);
  if (hal_eth_init_status == HAL_OK)
  {
    /* 当初始化成功后,会置位flag,同时在tcp/ip初始化完毕后,会
       进行判断,此标志位决定网卡驱动是否	可以正常使用 */  
    netif->flags |= NETIF_FLAG_LINK_UP;
  }
  /* Initialize Tx Descriptors list: Chain Mode */
  HAL_ETH_DMATxDescListInit(&heth, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);     
  /* Initialize Rx Descriptors list: Chain Mode  */
  HAL_ETH_DMARxDescListInit(&heth, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); 
#if LWIP_ARP || LWIP_ETHERNET 
  /* MAC地址初始化 */
  netif->hwaddr_len = ETH_HWADDR_LEN;
  netif->hwaddr[0] =  heth.Init.MACAddr[0];
  netif->hwaddr[1] =  heth.Init.MACAddr[1];
  netif->hwaddr[2] =  heth.Init.MACAddr[2];
  netif->hwaddr[3] =  heth.Init.MACAddr[3];
  netif->hwaddr[4] =  heth.Init.MACAddr[4];
  netif->hwaddr[5] =  heth.Init.MACAddr[5]; 
  /* maximum transfer unit */
  netif->mtu = 1500; 
  /* Accept broadcast address and ARP traffic */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  #if LWIP_ARP
    netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
  #else 
    netif->flags |= NETIF_FLAG_BROADCAST;
  #endif /* LWIP_ARP */ 
  /* 二值信号量,用于信息同步,当网卡接口到数据后,会释放二值信号量让其他任务进行解析 */
  osSemaphoreDef(SEM);
  s_xSemaphore = osSemaphoreCreate(osSemaphore(SEM), 1);
  /* 创建网卡数据接收解析任务-ethernetif_input */
  osThreadDef(EthIf, ethernetif_input, osPriorityRealtime, 0, INTERFACE_THREAD_STACK_SIZE);
  osThreadCreate (osThread(EthIf), netif);
  /* 使能 网卡 发送和接口 */
  HAL_ETH_Start(&heth); 
  /* 上面的都是针对STM32 ETH外设进行初始化
  	但是实际网络交互是用过PHY,下面就是初始化PHY */
  /* Read Register Configuration */
  HAL_ETH_ReadPHYRegister(&heth, PHY_MICR, ®value); 
  regvalue |= (PHY_MICR_INT_EN | PHY_MICR_INT_OE);
  /* Enable Interrupts */
  HAL_ETH_WritePHYRegister(&heth, PHY_MICR, regvalue );
  /* Read Register Configuration */
  HAL_ETH_ReadPHYRegister(&heth, PHY_MISR, ®value);
  regvalue |= PHY_MISR_LINK_INT_EN;    
  /* Enable Interrupt on change of link status */
  HAL_ETH_WritePHYRegister(&heth, PHY_MISR, regvalue);
#endif /* LWIP_ARP || LWIP_ETHERNET */
}

low_level_output:底层网卡的数据输出,实际的数据输出,是通过pbuf进行封装管理的

static err_t low_level_output(struct netif *netif, struct pbuf *p){
  err_t errval;
  struct pbuf *q;
  uint8_t *buffer = (uint8_t *)(heth.TxDesc->Buffer1Addr);
  __IO ETH_DMADescTypeDef *DmaTxDesc;
  uint32_t framelength = 0;
  uint32_t bufferoffset = 0;
  uint32_t byteslefttocopy = 0;
  uint32_t payloadoffset = 0;
  DmaTxDesc = heth.TxDesc;
  bufferoffset = 0;

  /* copy frame from pbufs to driver buffers */
  for(q = p; q != NULL; q = q->next)
  {
    /* Is this buffer available? If not, goto error */
    if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
    {
      errval = 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 data to Tx buffer*/
      memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) );      
      /* Point to next descriptor */
      DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
      /* Check if the buffer is available */
      if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
      {
        errval = ERR_USE;
        goto error;
      }
      buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
      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 */
    memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy );
    bufferoffset = bufferoffset + byteslefttocopy;
    framelength = framelength + byteslefttocopy;
  }  
  /* 把pbuf里面的数据,发送到ETH外设里面 */ 
  HAL_ETH_TransmitFrame(&heth, framelength);  
  errval = ERR_OK;  
error:  
  /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
  if ((heth.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
  {
    /* Clear TUS ETHERNET DMA flag */
    heth.Instance->DMASR = ETH_DMASR_TUS;
    /* Resume DMA transmission*/
    heth.Instance->DMATPDR = 0;
  }
  return errval;
}

low_level_input:底层网卡的数据接口,当接收到网卡数据后,会通过此函数,封装为pbuf提供上层使用

static struct pbuf * low_level_input(struct netif *netif){
  struct pbuf *p = NULL;
  struct pbuf *q = NULL;
  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;  
  /* 通过HAL库,获取网卡帧数据 */
  if (HAL_ETH_GetReceivedFrame_IT(&heth) != HAL_OK)
    return NULL;  
  /* 获取网卡数据超度,及内存地址 */
  len = heth.RxFrameInfos.length;
  buffer = (uint8_t *)heth.RxFrameInfos.buffer;
  //网卡中数据有效
  if (len > 0){
    /* 网卡数据不能大于1500,属于原始层接口  */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
  }
  //如果pbuf创建成功,则从ETH中拷贝数据到pbuf里,最终把pbuf返回给上层应用
  if (p != NULL){
    dmarxdesc = heth.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 ){
        /* Copy data to 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 = heth.RxFrameInfos.FSRxDesc;
    /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
    for (i=0; i< heth.RxFrameInfos.SegCount; i++){  
      dmarxdesc->Status |= ETH_DMARXDESC_OWN;
      dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
    }   
    /* Clear Segment_Count */
    heth.RxFrameInfos.SegCount =0;    
    /* When Rx Buffer unavailable flag is set: clear it and resume reception */
    if ((heth.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET)  {
      /* Clear RBUS ETHERNET DMA flag */
      heth.Instance->DMASR = ETH_DMASR_RBUS;
      /* Resume DMA reception */
      heth.Instance->DMARPDR = 0;
    }
  return p;
}

tcpip_init:工作在操作系统下的初始化

/**
 * @工作在操作系统下的初始化:
 * - 初始化所有的子功能模块
 * - 启动tcp/ip任务(tcp/ip网络协议栈的实现是一个任务里面执行的)
 * @param 用于用户初始化的函数指针,在lwip初始化完成,tcp/ip任务开始执行就是进行调用
 * @param 用户初始化相关参数传入
 */
void tcpip_init(tcpip_init_done_fn initfunc, void *arg){
  //lwip的初始化---初始化lwip所有功能模块
  lwip_init();
  //用户初始化函数指针赋值,参数赋值
  tcpip_init_done = initfunc;
  tcpip_init_done_arg = arg;
  //消息邮箱(freeRTOS是通过消息队列实现),任务与任务间消息通信,网卡收到数据,网络分层解析
  if (sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
    LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
  }
#if LWIP_TCPIP_CORE_LOCKING
  //创建互斥锁(互斥信号量),保护共享资源的
  if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
    LWIP_ASSERT("failed to create lock_tcpip_core", 0);
  }
#endif /* LWIP_TCPIP_CORE_LOCKING */
  //这是标准的cmis接口,其实内部调用的freeRTOS的创建任务接口
  sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
}

lwip_init:工作在裸机模式下的初始化

/**
 * @工作在裸机模式下的初始化
 * Use this in NO_SYS mode. Use tcpip_init() otherwise.
 */
void lwip_init(void){
  /* Modules initialization */
  //状态初始化
  stats_init();
#if !NO_SYS
  //与操作系统相关的初始化
  sys_init();
#endif /* !NO_SYS */
  //内存堆 内存池 pbuf netif初始化
  mem_init();
  memp_init();
  pbuf_init();
  netif_init();
#if LWIP_IPV4
  //ip层初始化
  ip_init();
#if LWIP_ARP
  //arp+以太网相关的初始化
  etharp_init();
#endif /* LWIP_ARP */
#endif /* LWIP_IPV4 */
#if LWIP_RAW
  //原生接口初始化
  raw_init();
#endif /* LWIP_RAW */
#if LWIP_UDP
  udp_init();
#endif /* LWIP_UDP */
#if LWIP_TCP
  tcp_init();
#endif /* LWIP_TCP */
#if LWIP_IGMP
  igmp_init();
#endif /* LWIP_IGMP */
#if LWIP_DNS
  dns_init();
#endif /* LWIP_DNS */
#if PPP_SUPPORT
  ppp_init();
#endif 
#if LWIP_TIMERS
  //lwip内部有很多超时机制,是通过timeouts实现的(一个软件定时器
  sys_timeouts_init();
#endif /* LWIP_TIMERS */
}

MX_LWIP_Init:HAL库实现的lwip初始化函数

void MX_LWIP_Init(void){
  /* IP 地址初始化 */
  IP_ADDRESS[0] = 192;
  IP_ADDRESS[1] = 168;
  IP_ADDRESS[2] = 1;
  IP_ADDRESS[3] = 10;
  NETMASK_ADDRESS[0] = 255;
  NETMASK_ADDRESS[1] = 255;
  NETMASK_ADDRESS[2] = 255;
  NETMASK_ADDRESS[3] = 0;
  GATEWAY_ADDRESS[0] = 192;
  GATEWAY_ADDRESS[1] = 168;
  GATEWAY_ADDRESS[2] = 1;
  GATEWAY_ADDRESS[3] = 1;  
  /* 初始化lwip协议栈 */
  tcpip_init( NULL, NULL );
  /* 数组格式的IP地址转换为lwip格式的地址 */
  IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
  IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
  IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
  /* 装载网卡驱动,并初始化网卡 */
  netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
  /* gnetif注册为默认网卡驱动 */
  netif_set_default(&gnetif);
  // 判断phy和mac层是否正常工作
  if (netif_is_link_up(&gnetif)){
    /* netif 网卡驱动可以正常使用,上线 */
    netif_set_up(&gnetif);
  }
  else{
    /* netif 网卡驱动下线 */
    netif_set_down(&gnetif);
  }
}
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储器
    +关注

    关注

    38

    文章

    7150

    浏览量

    161998
  • 网卡
    +关注

    关注

    2

    文章

    285

    浏览量

    27088
  • 内存管理
    +关注

    关注

    0

    文章

    163

    浏览量

    14056
  • LwIP
    +关注

    关注

    1

    文章

    82

    浏览量

    26620
  • 协议栈
    +关注

    关注

    2

    文章

    129

    浏览量

    33459
收藏 人收藏

    评论

    相关推荐

    #硬声创作季 #LWIPLWIP-08 LWIP内存管理-4

    LwIPLwIP协议
    水管工
    发布于 :2022年11月11日 14:40:57

    #硬声创作季 #LWIPLWIP-08 LWIP内存管理-5

    LwIPLwIP协议
    水管工
    发布于 :2022年11月11日 14:41:19

    LwIP协议的设计与实现_中文译稿

    LwIP协议的设计与实现_中文译稿
    发表于 08-20 08:18

    LwIP协议源码详解

    LwIP协议源码详解
    发表于 08-20 23:17

    LwIP 内存配置

    还是opt.h进行修改,都必须保证是在已经对你所改动的内容足够的了解的情况下进行,所做的改动是正确的,否则有可能导致协议 不能正常工作,或者效率低下。LwIP内存
    发表于 08-23 16:10

    LwIP协议的设计与实现资料分享!

    LwIP协议的设计与实现_中文译稿LwIP协议
    发表于 07-31 23:47

    求指点使用Lwip协议做双网卡通信

    论坛里有没有大虾,试过使用Lwip协议做双网卡通信成功的,或者有比较清晰清晰思路的,急求指点啊。
    发表于 03-27 04:35

    网络协议-lwip2.1.2移植

    本篇目标:上一篇freertos的优化(stm32f4_os_app)中,RTOS的移植已经告一段落了,接下来就是移植重要的网络协议-lwip2.1.2!硬件配置:STM32_ETH以太网
    发表于 08-16 08:08

    lwIP协议是什么?

    lwIP协议是什么?
    发表于 12-20 06:17

    Lwip协议栈的设计方案

    LWIP是TCP/IP协议栈的一种实现LWIP的主要目的是减少存储器利用量和代码尺寸,使LWIP适合应用于小的、资源有限的处理器如嵌入式系
    发表于 09-16 15:18 33次下载
    <b class='flag-5'>Lwip</b><b class='flag-5'>协议</b>栈的设计<b class='flag-5'>方案</b>

    lwip协议中文版

    LWIP是TCP/IP协议栈的一种实现LWIP的主要目的是减少存储器利用量和代码尺寸,使LWIP适合应用于小的、资源有限的处理器如嵌入式系
    发表于 02-03 16:47 0次下载
    <b class='flag-5'>lwip</b><b class='flag-5'>协议</b>中文版

    LwIP协议详解

    LwIP协议详解,LwIP是Light Weight (轻型)IP协议,有无操作系统的支持都可以运行。LwIP
    发表于 11-09 18:25 48次下载

    TCPIP协议栈的实现lwip

    TCPIP协议栈的实现lwip方便初学者刚开始接触lwip,有个大概的了解与认识。
    发表于 03-14 15:40 13次下载

    LWIP内存管理知识汇总

    LWIP内存管理LWIP内存管理使用了2种方式:内存
    的头像 发表于 03-06 10:01 6545次阅读
    <b class='flag-5'>LWIP</b><b class='flag-5'>内存</b><b class='flag-5'>管理</b>知识汇总

    Xilinx LwIP 例程解析:网卡驱动(接收篇)

    对于 LwIP 协议栈的移植来说,用户的主要工作是为其提供网卡驱动函数。LwIP 可以运行在多种不同的硬件平台上,配合不同型号的网络 Phy ...
    发表于 02-07 10:09 9次下载
    Xilinx <b class='flag-5'>LwIP</b> 例程解析:<b class='flag-5'>网卡</b>驱动(接收篇)