发新帖本帖赏金 55.00元(功能说明)我要提问
返回列表
[APM32F4]

增强以太网连接:使用LWIP 2.2实现动态主机配置协议(DHCP)

[复制链接]
2142|2
手机看帖
扫描二维码
随时随地手机跟帖
DKENNY|  楼主 | 2024-2-27 17:45 | 显示全部楼层 |阅读模式
本帖最后由 DKENNY 于 2024-2-27 17:47 编辑

#申请原创# @21小跑堂
  最近学习了LWIP,了解到目前LWIP的版本已经更新到了2.2版本。LWIP 2.2相较于之前的版本,在协议支持、性能、安全性等方面都有了显著的改进,我将在本帖中探讨如何利用LWIP 2.2来实现以太网的DHCP功能,并分享一些我所获得的经验。
1.LWIP简介
  LWIP代表"轻量级IP"(Lightweight IP),是一个嵌入式系统中常用的开源TCP/IP协议栈。它被设计成小巧、高效,适用于资源受限的系统,如嵌入式设备、物联网设备等。LWIP提供了包括IPv4/IPv6协议、TCP、UDP、ICMP等在内的各种网络协议的实现,同时支持各种设备接口和操作系统。通过使用LWIP,开发者可以方便地将网络连接功能集成到他们的嵌入式系统中,而无需从头开始实现网络协议栈。

LWIP1.4.1与LWIP2.2的对比


特性/方面
LWIP1.4.1
LWIP2.2
协议支持
TCP、UDP、IP、ICMP、DHCP、DNS、PPP等
TCP、UDP、IP、ICMP、DHCP、DNS、PPP等,还包括TLS(实验性)
性能
基本性能优化
改进的性能优化
安全性
有限的安全性功能
增强的安全性功能
API变更
稳定的API
一些API变更和新增
Bug修复
针对已知问题的Bug修复
为稳定性进行的Bug修复和补丁
兼容性
与现有LWIP1.x代码兼容
与现有LWIP1.x代码兼容,但可能需要适应性修改
文档
提供了LWIP1.4.1文档
更新的LWIP2.2文档
内存使用
适度的内存使用
优化的内存使用
多线程支持
有限或无多线程支持
改进的多线程支持
TLS支持(安全增强)
不可用
实验性的TLS支持

这里列举了从LWIP1.4.1到目前最新的LWIP2.2版本主要的一些修改点。具体详细的组件支持可去LWIP官网了解。

2.DHCP介绍
  DynamicHost Configuration Protocol(DHCP)是一种网络协议,用于在计算机网络上自动分配IP地址和其他网络配置信息。DHCP的主要目的是简化网络管理,避免手动配置每台计算机的网络参数。
  以下是DHCP的一些关键特性和工作原理:
  1. 自动IP地址分配:DHCP允许网络中的设备自动获取IP地址,而无需管理员手动配置。这对于大型网络特别有用,因为手动分配IP地址可能会变得繁琐和容易出错。
  2. 动态分配:DHCP支持动态分配IP地址,这意味着设备在每次连接到网络时可以获得不同的IP地址。这有助于更有效地利用可用的IP地址池。
  3. 配置其他网络参数:除了IP地址之外,DHCP还可以分配其他网络配置信息,如子网掩码、默认网关、DNS服务器地址等。这些信息是设备与网络通信所需的关键参数。
  4. 租约机制:DHCP通过租约机制来管理分配的IP地址。设备获得一个IP地址并与DHCP服务器建立租约,这个租约在一定时间内有效。设备可以选择在租约到期前续租或请求新的租约。
  5. DHCP服务器:网络中通常有一个或多个DHCP服务器,它们负责分配IP地址和配置信息。当设备连接到网络时,它们发送DHCP请求,DHCP服务器收到请求后分配一个可用的IP地址和相关配置信息。
  6. DHCP客户端:设备上运行的DHCP客户端负责向网络中的DHCP服务器发送请求以获取IP地址和配置信息。DHCP客户端通常在设备启动时触发DHCP过程。
  7. 广播通信:DHCP通信通常使用广播方式进行。DHCP客户端在网络上广播一个请求,DHCP服务器接收到请求后回应,然后客户端使用分配的IP地址和配置信息进行通信。
  总体而言,DHCP简化了网络管理过程,使得设备可以更轻松地连接到网络而无需手动配置网络参数。 DHCP在家庭网络、企业网络和大型互联网服务提供商(ISP)网络中广泛应用。

3.使用LWIP2.2搭建ETH DHCP例程
  首先,介绍一下该例程主要实现的功能。
  1.可以实时检测以太网是否断开,并将信息打印在串口上。
  2.开发板复位后首先获取DHCP服务器分配的IP地址。若无法查找到DHCP(设置的时间是60s),开发板会使用默认的静态IP地址。
3.1 访问官网,下载LWIP2.2源码
官网:https://savannah.nongnu.org/projects/lwip

image001.png


3.2 解压,复制到工程目录下,并在工程中添加相关文件。
image003.png
我这里是按文件分类添加的,可以自定义添加。

3.3 编写main函数
int main(void)
{
    char LCDDisplayBuf[100] = {0};

    struct ip4_addr DestIPaddr;

    uint8_t flag = 0;

    USART_Config_T usartConfig;

    /* User config the different system Clock */
    UserRCMClockConfig();

    /* Configure SysTick */
    ConfigSysTick();

    /* Configure USART */
    usartConfig.baudRate = 115200;
    usartConfig.wordLength = USART_WORD_LEN_8B;
    usartConfig.stopBits = USART_STOP_BIT_1;
    usartConfig.parity = USART_PARITY_NONE ;
    usartConfig.mode = USART_MODE_TX_RX;
    usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;

    APM_BOARD_COMInit(COM1,&usartConfig);

    /* Configures LED2 and LED3 */
    APM_BOARD_LEDInit(LED2);
    APM_BOARD_LEDInit(LED3);

    /* KEY init*/
    APM_BOARD_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
    APM_BOARD_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);

    printf("This is a ETH TCP Client Demo! \r\n");

    /* Configure ethernet (GPIOs, clocks, MAC, DMA) */
    ConfigEthernet();

    /* Initilaize the LwIP stack */
    LwIP_Init();

#ifndef USE_DHCP
    /* Use Com printf static IP address*/
    sprintf(LCDDisplayBuf,"TINY board Static IP address \r\n");
    printf("%s",LCDDisplayBuf);

    sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d \r\n",
    IP_ADDR0,
    IP_ADDR1,
    IP_ADDR2,
    IP_ADDR3);
    printf("%s",LCDDisplayBuf);

    sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d \r\n",
    NETMASK_ADDR0,
    NETMASK_ADDR1,
    NETMASK_ADDR2,
    NETMASK_ADDR3);
    printf("%s",LCDDisplayBuf);

    sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d \r\n",
    GW_ADDR0,
    GW_ADDR1,
    GW_ADDR2,
    GW_ADDR3);
    printf("%s",LCDDisplayBuf);

    sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d:%d \r\n",
    COMP_IP_ADDR0,
    COMP_IP_ADDR1,
    COMP_IP_ADDR2,
    COMP_IP_ADDR3,
    COMP_PORT);
    printf("%s",LCDDisplayBuf);
#endif

//    printf("\n\rKEY1: Connect TCP server \r\n");
//    printf("KEY2: Disconnect TCP server \r\n");

    while(1)
    {

        if ((APM_TINY_PBGetState(BUTTON_KEY1)==0)&&(flag==0))
        {
            APM_TINY_LEDOn(LED2);
            if (EthLinkStatus == 0)
            {
                /* connect to tcp server */
                printf("\n\rConnect TCP server \r\n");
                IP4_ADDR( &DestIPaddr, COMP_IP_ADDR0, COMP_IP_ADDR1, COMP_IP_ADDR2, COMP_IP_ADDR3 );
                tcpc_echo_init(&DestIPaddr,COMP_PORT);
                flag=1;
            }
        }
        if ((APM_TINY_PBGetState(BUTTON_KEY2)==0)&&(flag==1))
        {
            APM_TINY_LEDOff(LED2);
            printf("\n\rDisconnect TCP server \r\n");
            tcpc_echo_disable();
            flag=0;
        }

        /* check if any packet received */
        if (ETH_CheckReceivedFrame())
        {
            /* process received ethernet packet */
            LwIP_Pkt_Handle();
        }
        /* handle periodic timers for LwIP */
        LwIP_Periodic_Handle(ETHTimer);
    }
}
  main函数主要是对一些串口、GPIO的初始化操作。我在main.h中定义了一个USE_DHCP的宏,我们可以打开或注释掉这个宏,选择是否开启DHCP功能。

3.4 LWIP_Init()函数
void LwIP_Init(void)
{
    struct ip4_addr ipaddr;
    struct ip4_addr netmask;
    struct ip4_addr gw;

    /* Initializes the dynamic memory heap */
    mem_init();

    /* Initializes the memory pools */
    memp_init();

#ifdef USE_DHCP
    ipaddr.addr = 0;
    netmask.addr = 0;
    gw.addr = 0;
#else
    IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
    IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
    IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
#endif

    /* Config MAC Address */
    ETH_ConfigMACAddress(ETH_MAC_ADDRESS0, SetMACaddr);

    /* Add a network interface to the list of lwIP netifs */
    netif_add(&UserNetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);

    /* Registers the default network interface */
    netif_set_default(&UserNetif);

  if (ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_BSR) & 1)
    {
        UserNetif.flags |= NETIF_FLAG_LINK_UP;
        
        /* When the netif is fully configured this function must be called */
        netif_set_up(&UserNetif);

#ifdef USE_DHCP
        DHCP_state = DHCP_START;
#endif
    }
    else
    {
        netif_set_down(&UserNetif);
#ifdef USE_DHCP
        DHCP_state = DHCP_LINK_DOWN;
#endif /* USE_DHCP */
        printf("network cable is not connected!\r\n");
    }

    netif_set_link_callback(&UserNetif, ETH_link_callback);
}
  这段代码是用于初始化LwIP网络堆栈的函数 LwIP_Init。让我们逐步分析其功能:
  1. 初始化IP地址、子网掩码和网关地址的结构体变量 ipaddrnetmask gw
  2. 初始化动态内存堆。
  3. 初始化内存池。
  4. 根据是否启用了DHCP,设置IP地址、子网掩码和网关地址。
  5. 配置MAC地址。
  6. 将一个网络接口添加到lwIP网络接口列表中。
  7. 注册默认的网络接口。
  8. 通过读取PHY寄存器的状态来判断网络连接状态:
   - 如果连接状态为链接状态,设置网络接口的标志为链接已建立,将网络接口设置为已启用,并根据是否启用了DHCP,设置DHCP状态为启动。
   - 如果连接状态为断开状态,将网络接口设置为已关闭,并根据是否启用了DHCP,设置DHCP状态为链接断开,并打印消息表示网络电缆未连接。
  9. 设置网络接口的链接状态回调函数为 ETH_link_callback
  综上所述,该函数主要用于初始化LwIP网络堆栈。它包括初始化内存堆和内存池、配置网络接口的IP地址信息、MAC地址、添加网络接口到lwIP列表中、判断网络连接状态并相应地设置网络接口状态和DHCP状态,最后设置网络接口的链接状态回调函数。

3.5 ETH_link_callback函数
void ETH_link_callback(struct netif *netif)
{
  __IO uint32_t timeout = 0;
    uint16_t RegValue;
    struct ip4_addr ipaddr;
    struct ip4_addr netmask;
    struct ip4_addr gw;

  if(netif_is_link_up(netif))
  {
    /* Restart the auto-negotiation */
    if(ETH_InitStructure.autoNegotiation == ETH_AUTONEGOTIATION_ENABLE)
    {
      /* Reset Timeout counter */
      timeout = 0;

      /* Enable auto-negotiation */
      ETH_WritePHYRegister(ETH_PHY_ADDRESS, PHY_BCR, PHY_AUTONEGOTIATION);

      /* Wait until the auto-negotiation will be completed */
      do
      {
        timeout++;
      } while (!(ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_BSR) & PHY_AUTONEGO_COMPLETE) && (timeout < (uint32_t)PHY_READ_TIMEOUT));  

      /* Reset Timeout counter */
      timeout = 0;

      /* Read the result of the auto-negotiation */
      RegValue = ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_SR);

      /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */
      if((RegValue & PHY_DUPLEX_STATUS) != (uint16_t)RESET)
      {
        /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */
        ETH_InitStructure.mode = ETH_MODE_FULLDUPLEX;  
      }
      else
      {
        /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */
        ETH_InitStructure.mode = ETH_MODE_HALFDUPLEX;
      }
      /* Configure the MAC with the speed fixed by the auto-negotiation process */
      if(RegValue & PHY_SPEED_STATUS)
      {
        /* Set Ethernet speed to 10M following the auto-negotiation */
        ETH_InitStructure.speed = ETH_SPEED_10M;
      }
      else
      {
        /* Set Ethernet speed to 100M following the auto-negotiation */
        ETH_InitStructure.speed = ETH_SPEED_100M;
      }

      /*------------------------ ETHERNET MACCR Re-Configuration --------------------*/

      /* Set the FES bit according to ETH_Speed value */
      /* Set the DM bit according to ETH_Mode value */
      ETH->CFG_B.SSEL = ETH_InitStructure.speed;
      ETH->CFG_B.DM = ETH_InitStructure.mode;
      Delay(0x00000001);
    }

    /* Restart MAC interface */
    ETH_Start();

#ifdef USE_DHCP
    ipaddr.addr = 0;
    netmask.addr = 0;
    gw.addr = 0;
    DHCP_state = DHCP_START;
#else
    IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);+
    IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
    IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
#endif /* USE_DHCP */

    netif_set_addr(&UserNetif, &ipaddr , &netmask, &gw);
   
    /* When the netif is fully configured this function must be called.*/
    netif_set_up(&UserNetif);   

    /* Display message on the LCD */
    printf("network cable is connected now !\r\n");

    EthLinkStatus = 0;
  }
  else
  {
    ETH_Stop();
#ifdef USE_DHCP
    DHCP_state = DHCP_LINK_DOWN;
    dhcp_stop(netif);
#endif /* USE_DHCP */

    /*  When the netif link is down this function must be called.*/
    netif_set_down(&UserNetif);
      
    printf("network cable is unplugged! \r\n");
  }
}
  这段代码是一个用于处理以太网链接状态变化的回调函数 ETH_link_callback。让我们逐步分析它的功能:
  1. 首先,声明了一些变量,包括超时计数器 timeout、存储从PHY读取的寄存器值的变量 RegValue,以及用于存储IP地址、子网掩码和网关地址的变量。
  2. 如果网络接口的链接状态为链接状态(通过 netif_is_link_up(netif) 判断):
   - 如果启用了以太网自动协商(ETH_InitStructure.autoNegotiation== ETH_AUTONEGOTIATION_ENABLE),则重新启动自动协商过程:
     - 向PHY寄存器写入自动协商命令 PHY_AUTONEGOTIATION
     - 等待自动协商完成,超时时间由 PHY_READ_TIMEOUT 定义。
     - 读取自动协商结果,根据结果配置以太网MAC的速度和双工模式。
     - 根据速度和双工模式重新配置以太网MAC控制器的寄存器。
   - 重新启动MAC接口。
   - 根据是否启用了DHCP,设置IP地址、子网掩码和网关地址,并将网络接口标记为已启用。
   - 打印消息,表示网络电缆已连接。
   - 将 EthLinkStatus 标记为链接状态。
  3. 如果网络接口的链接状态为断开状态,执行以下操作:
   - 停止MAC接口。
   - 如果启用了DHCP,将DHCP状态设置为链接断开,并停止DHCP客户端。
   - 将网络接口标记为已关闭。
   - 打印消息,表示网络电缆已拔出。
  综上所述,该函数主要用于处理以太网链接状态的变化。根据链接状态的变化,重新启动自动协商过程(如果启用了自动协商),配置以太网MAC的参数,并设置IP地址信息。同时,根据链接状态的变化执行相应的动作,例如重新启动或停止MAC接口,并更新相应的状态标志。

3.6 LwIP_Periodic_Handle
void LwIP_Periodic_Handle(__IO uint32_t ETHTimer)
{
    static uint8_t flagToggle = 0;

#if LWIP_TCP
    /* TCP periodic process every 250 ms */
    if (ETHTimer - TCPTimer >= TCP_TMR_INTERVAL)
    {
        TCPTimer =  ETHTimer;
        tcp_tmr();
    }
#endif

    /* ARP periodic process every 5s */
    if ((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL)
    {
        ARPTimer =  ETHTimer;
        etharp_tmr();
    }

    /* Check link status */
    if ((ETHTimer - LinkTimer) >= 1000)
    {
        if ((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0))
        {
            /* link goes up */
            netif_set_link_up(&UserNetif);
            flagToggle = 1;
        }

        if ((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1))
        {
            EthLinkStatus = 1;
            /* link goes down */
            netif_set_link_down(&UserNetif);
            flagToggle = 0;
        }
    }
#ifdef USE_DHCP
  /* Fine DHCP periodic process every 500ms */
  if (ETHTimer - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS)
  {
    DHCPfineTimer =  ETHTimer;
    dhcp_fine_tmr();
    if ((DHCP_state != DHCP_ADDRESS_ASSIGNED) &&
        (DHCP_state != DHCP_TIMEOUT) &&
          (DHCP_state != DHCP_LINK_DOWN))
    {
      /* toggle LED1 to indicate DHCP on-going process */
      APM_TINY_LEDOn(LED2);

      /* process DHCP state machine */
      LwIP_DHCP_Process_Handle();
    }
  }

  /* DHCP Coarse periodic process every 60s */
  if (ETHTimer - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS)
  {
    DHCPcoarseTimer =  ETHTimer;
    dhcp_coarse_tmr();
  }
#endif
}
  以上函数是一个用于处理LwIP(Lightweight IP)网络堆栈的周期性任务的函数。让我们逐段分析它:
  1. staticuint8_t flagToggle = 0;:定义了一个静态变量 flagToggle,用于跟踪网络连接状态的变化。
  2. if(ETHTimer - TCPTimer >= TCP_TMR_INTERVAL):检查TCP定时器是否超过了TCP_TMR_INTERVAL(TCP定时器间隔),如果超过,则调用 tcp_tmr() 函数进行TCP定时器处理。
  3. if((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL):检查ARP定时器是否超过了ARP_TMR_INTERVAL(ARP定时器间隔),如果超过,则调用 etharp_tmr() 函数进行ARP定时器处理。
  5. if((ETHTimer - LinkTimer) >= 1000):检查链接状态是否需要更新,如果超过了一定时间(这里设置为1000ms),则进行链接状态检查。
  6. if((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0)):检查网络链接状态是否正常,并且之前的状态是断开的,如果是,则设置网络接口为链接状态。
  7. if((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1)):检查网络链接状态是否异常,并且之前的状态是连接的,如果是,则设置网络接口为断开状态。
  9. if(ETHTimer - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS):检查是否需要进行DHCP的Fine定时处理,如果超过了DHCP_FINE_TIMER_MSECS(Fine定时器间隔),则调用 dhcp_fine_tmr() 函数。
  10. if(ETHTimer - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS):检查是否需要进行DHCP的Coarse定时处理,如果超过了DHCP_COARSE_TIMER_MSECS(Coarse定时器间隔),则调用 dhcp_coarse_tmr() 函数。
  在整个函数中,主要处理了TCP、ARP、网络链接状态和DHCP的周期性任务。这些任务包括定时器的处理以及相应的动作,例如发送ARP请求、更新网络链接状态和处理DHCP状态机等。

3.7 LwIP_DHCP_Process_Handle
void LwIP_DHCP_Process_Handle(void)
{
  struct ip4_addr ipaddr;
  struct ip4_addr netmask;
  struct ip4_addr gw;
  uint8_t iptab[4] = {0};
  char LCDDisplayBuf[100] = {0};

  switch (DHCP_state)
  {
      case DHCP_START:
      {
          DHCP_state = DHCP_WAIT_ADDRESS;
          dhcp_start(&UserNetif);
          /* IP address should be set to 0
             every time we want to assign a new DHCP address */
          IPaddress = 0;
          printf("     Looking for DHCP server, please wait.....  \r\n");
      }
      break;
        
      case DHCP_WAIT_ADDRESS:
        {
          /* Read the new IP address */
          struct dhcp *dhcp = netif_dhcp_data(&UserNetif);

          if (dhcp->offered_ip_addr.addr != 0 && dhcp->offered_sn_mask.addr != 0 && dhcp->offered_gw_addr.addr != 0)
          {
              DHCP_state = DHCP_ADDRESS_ASSIGNED;

              printf("IP address assigned by a DHCP server! \r\n");

              IPaddress = dhcp->offered_ip_addr.addr;
              iptab[0] = (uint8_t)(IPaddress >> 24);
              iptab[1] = (uint8_t)(IPaddress >> 16);
              iptab[2] = (uint8_t)(IPaddress >> 8);
              iptab[3] = (uint8_t)(IPaddress);
              IP4_ADDR(&ipaddr, iptab[3] ,iptab[2] , iptab[1] , iptab[0] );
              sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d \r\n",
                iptab[3],
                iptab[2],
                iptab[1],
                iptab[0]);
                printf("%s",LCDDisplayBuf);

              IPaddress = dhcp->offered_sn_mask.addr;
              iptab[0] = (uint8_t)(IPaddress >> 24);
              iptab[1] = (uint8_t)(IPaddress >> 16);
              iptab[2] = (uint8_t)(IPaddress >> 8);
              iptab[3] = (uint8_t)(IPaddress);
              IP4_ADDR(&netmask, iptab[3] ,iptab[2] , iptab[1] , iptab[0] );
              sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d \r\n",
                iptab[3],
                iptab[2],
                iptab[1],
                iptab[0]);
                printf("%s",LCDDisplayBuf);
                                  IPaddress = dhcp->offered_gw_addr.addr;
              iptab[0] = (uint8_t)(IPaddress >> 24);
              iptab[1] = (uint8_t)(IPaddress >> 16);
              iptab[2] = (uint8_t)(IPaddress >> 8);
              iptab[3] = (uint8_t)(IPaddress);
              IP4_ADDR(&gw, iptab[3] ,iptab[2] , iptab[1] , iptab[0]);
              sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d \r\n",
                iptab[3],
                iptab[2],
                iptab[1],
                iptab[0]);
                printf("%s",LCDDisplayBuf);

              IPaddress = dhcp->server_ip_addr.addr;
              iptab[0] = (uint8_t)(IPaddress >> 24);
              iptab[1] = (uint8_t)(IPaddress >> 16);
              iptab[2] = (uint8_t)(IPaddress >> 8);
              iptab[3] = (uint8_t)(IPaddress);
              sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d \r\n",
                iptab[3],
                iptab[2],
                iptab[1],
                iptab[0]);
                printf("%s",LCDDisplayBuf);

//              IP4_ADDR( &s_ipaddr, iptab[3] ,iptab[2] , iptab[1] , iptab[0] );
//              udp_connect();

              /* Stop DHCP */
              dhcp_stop(&UserNetif);
              
//              UserNetif.ip_addr.addr = ipaddr.addr;
//              UserNetif.netmask.addr = netmask.addr;
//              UserNetif.gw.addr = gw.addr;
                netif_set_addr(&UserNetif, &ipaddr , &netmask, &gw);

              APM_TINY_LEDOn(LED2);
          }
          else
          {
            /* DHCP timeout */
            if (dhcp->tries > 4)
            {
                DHCP_state = DHCP_TIMEOUT;

                /* Stop DHCP */
                dhcp_stop(&UserNetif);

                /* Static address used */
                /* Use Com printf static IP address*/
                printf("DHCP timeout!\r\n");
      
                /* Static address used */
                IP4_ADDR(&ipaddr, IP_ADDR0 ,IP_ADDR1 , IP_ADDR2 , IP_ADDR3 );
                IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
                IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
                netif_set_addr(&UserNetif, &ipaddr , &netmask, &gw);
                sprintf(LCDDisplayBuf,"TINY board Static IP address \r\n");
                printf("%s",LCDDisplayBuf);
                sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d \r\n",
                IP_ADDR0,
                IP_ADDR1,
                IP_ADDR2,
                IP_ADDR3);
                printf("%s",LCDDisplayBuf);
                sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d \r\n",
                NETMASK_ADDR0,
                NETMASK_ADDR1,
                NETMASK_ADDR2,
                NETMASK_ADDR3);
                printf("%s",LCDDisplayBuf);
                sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d \r\n",
                GW_ADDR0,
                GW_ADDR1,
                GW_ADDR2,
                GW_ADDR3);
                printf("%s",LCDDisplayBuf);
                sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d:%d \r\n",
                COMP_IP_ADDR0,
                COMP_IP_ADDR1,
                COMP_IP_ADDR2,
                COMP_IP_ADDR3,
                COMP_PORT);
                printf("%s",LCDDisplayBuf);
                APM_TINY_LEDOn(LED2);
            }
          }
        }
        break;
        default: break;
  }
}
  这段代码是用于处理LwIP网络堆栈中DHCP过程的函数 LwIP_DHCP_Process_Handle。它通过状态机来处理DHCP过程的不同阶段。让我们逐步分析其功能:
  1. 在 DHCP_START 状态下,将状态切换到 DHCP_WAIT_ADDRESS,并启动DHCP客户端。
  2. 在 DHCP_WAIT_ADDRESS 状态下,如果DHCP客户端获得了IP地址、子网掩码和网关地址,将状态切换到 DHCP_ADDRESS_ASSIGNED,打印获得的IP地址、子网掩码和网关地址,并停止DHCP客户端。
  3. 如果DHCP超时(尝试次数超过4次),将状态切换到 DHCP_TIMEOUT,停止DHCP客户端,并使用静态IP地址(如果配置了静态IP地址)。
  综上所述,该函数主要用于处理LwIP网络堆栈中DHCP过程的不同阶段。根据DHCP状态的不同,执行不同的操作,例如启动DHCP客户端、处理获取到的IP地址信息、处理超时情况等。

4. 实验现象


1.串口打印相关信息
image013.png

2.正常获取IP后,cmd执行ping命令,正常通信
image014.png



附件是该例程MDK源码,有需要的可自行下载,欢迎各位讨论交流。

ETH_TCP_DHCP_client.zip (9.59 MB)

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 55.00 元 2024-02-29
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2024-2-29 17:25 回复TA
非常详细的使用LWIP 2.2实现动态主机配置协议(DHCP)测试实验,针对调用的函数进行详细的注解,方便移植用户更好地理解。格式清晰,实验结果正常。 
lulugl| | 2024-3-15 07:03 | 显示全部楼层
大佬的文章太优秀了,感谢分享呀,我下载下来好好学习!

使用特权

评论回复
发新帖 本帖赏金 55.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

30

帖子

4

粉丝