• stm32使用LWIP实现DHCP客户端


        LWIP是一款开源的嵌入式网络协议栈,支持的功能很多,而且能在多任务环境下和单任务裸机环境下跑,今天说说他的移植过程,芯片为STM32,网卡为ENC28J60,无操作系统

        首先下载LWIP的源代码,我下载的是1.4.1的源码,下载后解压,文件结构如图

       

    将这四个目录中的文件全部拷贝到工程中,API是一些socket通讯的接口,需要在多任务的环境下实现,core里面存放的内核源码,我们主要使用IPV4,include目录下是需要包含的目录,lwip只要求我们包含include目录,里面的内层目录会自动找到,最后建立的工程目录如下

       

    好了,此时源码已经做好,还有需要做的,在include目录下新建一个文件夹,必须叫arch,里面存放这几个文件,自己新建

    文件的具体内容如下

    cc.h

    /*
     * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
     * All rights reserved. 
     * 
     * Redistribution and use in source and binary forms, with or without modification, 
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice,
     *    this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright notice,
     *    this list of conditions and the following disclaimer in the documentation
     *    and/or other materials provided with the distribution.
     * 3. The name of the author may not be used to endorse or promote products
     *    derived from this software without specific prior written permission. 
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
     * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
     * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
     * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
     * OF SUCH DAMAGE.
     *
     * This file is part of the lwIP TCP/IP stack.
     * 
     * Author: Adam Dunkels <adam@sics.se>
     *
     */
    #ifndef __CC_H__
    #define __CC_H__
    
    #include "cpu.h"
    
    //编译器无关的数据类型定义
    typedef unsigned   char    u8_t;
    typedef signed     char    s8_t;
    typedef unsigned   short   u16_t;
    typedef signed     short   s16_t;
    typedef unsigned   long    u32_t;
    typedef signed     long    s32_t;
    typedef u32_t mem_ptr_t;
    typedef int sys_prot_t;
    
    //lwip调试的时候数据类型定义
    #define U16_F "hu"
    #define S16_F "d"
    #define X16_F "hx"
    #define U32_F "u"
    #define S32_F "d"
    #define X32_F "x"
    #define SZT_F "uz" 
    
    
    //根据不同的编译器的符号定义
    #if defined (__ICCARM__)
    
    #define PACK_STRUCT_BEGIN
    #define PACK_STRUCT_STRUCT 
    #define PACK_STRUCT_END
    #define PACK_STRUCT_FIELD(x) x
    #define PACK_STRUCT_USE_INCLUDES
    
    #elif defined (__CC_ARM)
    
    #define PACK_STRUCT_BEGIN __packed
    #define PACK_STRUCT_STRUCT 
    #define PACK_STRUCT_END
    #define PACK_STRUCT_FIELD(x) x
    
    #elif defined (__GNUC__)
    
    #define PACK_STRUCT_BEGIN
    #define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
    #define PACK_STRUCT_END
    #define PACK_STRUCT_FIELD(x) x
    
    #elif defined (__TASKING__)
    
    #define PACK_STRUCT_BEGIN
    #define PACK_STRUCT_STRUCT
    #define PACK_STRUCT_END
    #define PACK_STRUCT_FIELD(x) x
    
    #endif
    
    #define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0)
    
    #endif /* __CC_H__ */

    cpu.h

    /*
     * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
     * All rights reserved. 
     * 
     * Redistribution and use in source and binary forms, with or without modification, 
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice,
     *    this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright notice,
     *    this list of conditions and the following disclaimer in the documentation
     *    and/or other materials provided with the distribution.
     * 3. The name of the author may not be used to endorse or promote products
     *    derived from this software without specific prior written permission. 
     *
     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
     * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
     * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
     * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
     * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
     * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
     * OF SUCH DAMAGE.
     *
     * This file is part of the lwIP TCP/IP stack.
     * 
     * Author: Adam Dunkels <adam@sics.se>
     *
     */
    #ifndef __CPU_H__
    #define __CPU_H__
    
    //定义cpu的数据模式,大端小端
    #define BYTE_ORDER LITTLE_ENDIAN
    
    #endif /* __CPU_H__ */

    perf.h

    #ifndef __PERF_H__
    #define __PERF_H__
    
    //用于lwip内置的统计功能
    //不使能定义为空就可以了
    #define PERF_START    /* null definition */
    #define PERF_STOP(x)  /* null definition */
    
    #endif /* __PERF_H__ */

    sys_arch.h

    #ifndef __SYS_RTXC_H__
    #define __SYS_RTXC_H__
    
    
    void init_lwip_timer(void);  //初始化LWIP定时器
    
    u8_t timer_expired(u32_t *last_time,u32_t tmr_interval);    //定时器超时判断
    
    #endif /* __SYS_RTXC_H__ */

    sya_arch.c--注意该文件要加入源文件列表中,这是c文件哦

    #include "lwip/debug.h"
    #include "lwip/def.h"
    #include "lwip/sys.h"
    #include "lwip/mem.h"
    #include "timerx.h"
    
    //初始化LWIP定时器
    void init_lwip_timer(void)
    {
         TIM6_Int_Init(1000,719);//100Khz计数频率,计数到100为10ms
    }
    
    //为LWIP提供计时
    extern u32_t lwip_timer;//lwip 计时器,每10ms增加1.
    
    u32_t sys_now(void)
    {
        return lwip_timer;
    }
    
    //定时器超时判断
    //last_time:最近时间
    //tmr_interval:定时器溢出周期
    u8_t timer_expired(u32_t *last_time,u32_t tmr_interval)
    {
        u32_t time;
        time = *last_time;    
        if((lwip_timer-time)>=tmr_interval){
            *last_time = lwip_timer;
            return 1;
        }
        return 0;
    }

    可以看到我们定义了定时器,那么就要修改相关的定时器文件,文件如下

    timerx.c

    #include "timerx.h"
    
    
    u32 lwip_timer=0;//lwip 计时器,每10ms增加1.
    
    //定时器6中断服务程序     
    void TIM6_IRQHandler(void)
    {     
    
        if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
        {
            TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源   
            lwip_timer++;//lwip计时器增加1            
        }     
    
    }
     
    
    
    
    
    //基本定时器6中断初始化
    //这里时钟选择为APB1的2倍,而APB1为36M
    //arr:自动重装值。
    //psc:时钟预分频数
    //这里使用的是定时器3!
    void TIM6_Int_Init(u16 arr,u16 psc)
    {    
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
    
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); //时钟使能
    
        TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值     计数到5000为500ms
        TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
        TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
        TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
     
        TIM_ITConfig( TIM6,TIM_IT_Update|TIM_IT_Trigger,ENABLE);//使能定时器6更新触发中断
     
        TIM_Cmd(TIM6, ENABLE);  //使能TIMx外设
         
          NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;  //TIM3中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  //先占优先级0级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //从优先级3级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
        NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器                                  
    }

    timerx.h

    #ifndef __TIMER_H_
    #define __TIMER_H_
    
    #include "sys.h"
    
    
    
    
    void TIM6_Int_Init(u16 arr,u16 psc);
    
    
    #endif

    好了,这个时候移植就基本完成了,但是编译是过不了的,因为我们缺少一个配置文件.h,这个文件放哪都可以,只要工程包含这个文件的目录,另外,该文件名称不能改动

    lwipopts.h

    #ifndef __LWIPOPTS_H__
    #define __LWIPOPTS_H__
    
    
    
    //回环模式
    //#define LWIP_HAVE_LOOPIF  1
    
    /* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
    #define NO_SYS                          1    //是否使用操作系统
    #define LWIP_NETCONN                    0
    #define LWIP_SOCKET                     0
    
    
    #define LWIP_DHCP                       1     //使能DHCP模块
    #define MEM_ALIGNMENT                   4      //必须4字节对齐 曾出现在memset的时候hardfault
    #define LWIP_DNS                        1
    
    
    
    #define MEM_SIZE                        16000        //使用多大一块内存做lwip内存
    #define TCP_SND_QUEUELEN                40            //tcp发送序列长度
    #define MEMP_NUM_TCP_SEG                TCP_SND_QUEUELEN
    
    #define TCP_MSS                     1460            
    #define TCP_WND                     (4 * TCP_MSS)
    #define TCP_SND_BUF                 (8 * TCP_MSS)
    
    
    
    #define ETHARP_SUPPORT_STATIC_ENTRIES   1
    
    
    
    #endif /* __LWIPOPTS_H__ */

    可以看到,我们没有使用操作系统并且使能了dhcp功能

    这时候就可以编译通过了,但是还有一个必须做的工作,移植网卡的驱动到lwip中,

    也就是netif文件夹中的ethernetif.c文件,该文件修改为如下内容

    //网卡驱动层文件
    
    #if 1 /* don't build, this is only a skeleton, see previous comment */
    
    #include "lwip/def.h"
    #include "lwip/mem.h"
    #include "lwip/pbuf.h"
    #include <lwip/stats.h>
    #include <lwip/snmp.h>
    #include "netif/etharp.h"
    #include "netif/ppp_oe.h"
    #include "enc28j60.h"
    #include "netif/ethernetif.h"
    #include "string.h"
    #include "delay.h"
    
    /* Define those to better describe your network interface. */
    #define IFNAME0 'e'
    #define IFNAME1 'n'
    
    //MAC地址
    const u8 mymac[6]={0x99,0x02,0x35,0x04,0x45,0x61};    //MAC地址
    //定义发送接受缓冲区
    u8 lwip_buf[1500*2];
    
    
    //返回网卡地址
    struct ethernetif 
    {
      struct eth_addr *ethaddr;
      /* Add whatever per-interface state that is needed here. */
    };
    
    
    //网卡的初始化
    static err_t low_level_init(struct netif *netif)
    {
      //mac地址
      netif->hwaddr_len = ETHARP_HWADDR_LEN;
      /* set MAC hardware address */
      netif->hwaddr[0] = mymac[0];
      netif->hwaddr[1] = mymac[1];
      netif->hwaddr[2] = mymac[2];
      netif->hwaddr[3] = mymac[3];
      netif->hwaddr[4] = mymac[4];
      netif->hwaddr[5] = mymac[5];
        
      //最大传输单元
      netif->mtu = MAX_FRAMELEN; 
      if(ENC28J60_Init((u8*)mymac))    //初始化ENC28J60    
      {
        return ERR_IF;            //底层网络接口错误
      }
      //指示灯状态:0x476 is PHLCON LEDA(绿)=links status, LEDB(红)=receive/transmit
      //PHLCON:PHY 模块LED 控制寄存器        
      ENC28J60_PHY_Write(PHLCON,0x0476);      
        
      netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
       
      return ERR_OK;
    }
    
    //从指定网卡输出一部分数据
    static err_t low_level_output(struct netif *netif, struct pbuf *p)
    {
      struct pbuf *q;
      int send_len=0;
      
    #if ETH_PAD_SIZE
      pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
    #endif
    
      for(q = p; q != NULL; q = q->next) {
        /* Send the data from the pbuf to the interface, one pbuf at a
           time. The size of the data in each pbuf is kept in the ->len
           variable. */
        //send data from(q->payload, q->len);
        memcpy((u8_t*)&lwip_buf[send_len], (u8_t*)q->payload, q->len);
        send_len +=q->len;
      }
       // signal that packet should be sent();
      ENC28J60_Packet_Send(send_len,lwip_buf);
    
    
    #if ETH_PAD_SIZE
      pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
    #endif
      
      LINK_STATS_INC(link.xmit);
    
      return ERR_OK;
    }
    
    
    //从指定网卡读取一帧数据回来
    static struct pbuf *low_level_input(struct netif *netif)
    {
    //  struct ethernetif *ethernetif = netif->state;
      struct pbuf *p, *q;
      u16_t len;
      int rev_len=0;
        
      /* Obtain the size of the packet and put it into the "len"
         variable. */
      len = ENC28J60_Packet_Receive(MAX_FRAMELEN,lwip_buf);
    
    #if ETH_PAD_SIZE
      len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
    #endif
    
      /* We allocate a pbuf chain of pbufs from the pool. */
      p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
      
      if (p != NULL) {
    
    #if ETH_PAD_SIZE
        pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
    #endif
    
        /* We iterate over the pbuf chain until we have read the entire
         * packet into the pbuf. */
        for(q = p; q != NULL; q = q->next) {
          /* Read enough bytes to fill this pbuf in the chain. The
           * available data in the pbuf is given by the q->len
           * variable.
           * This does not necessarily have to be a memcpy, you can also preallocate
           * pbufs for a DMA-enabled MAC and after receiving truncate it to the
           * actually received size. In this case, ensure the tot_len member of the
           * pbuf is the sum of the chained pbuf len members.
           */
          //read data into(q->payload, q->len);
            memcpy((u8_t*)q->payload, (u8_t*)&lwip_buf[rev_len],q->len);
            rev_len +=q->len;
            
        }
       // acknowledge that packet has been read();
    
    #if ETH_PAD_SIZE
        pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
    #endif
    
        LINK_STATS_INC(link.recv);
      } else {
        //drop packet();
        LINK_STATS_INC(link.memerr);
        LINK_STATS_INC(link.drop);
      }
    
      return p;  
    }
    
    
    /**
     * This function should be called when a packet is ready to be read
     * from the interface. It uses the function low_level_input() that
     * should handle the actual reception of bytes from the network
     * interface. Then the type of the received packet is determined and
     * the appropriate input function is called.
     *
     * @param netif the lwip network interface structure for this ethernetif
     */
    void ethernetif_input(struct netif *netif)
    {
    //  struct ethernetif *ethernetif;
      struct eth_hdr *ethhdr;
      struct pbuf *p;
    
     // ethernetif = netif->state;
    
      /* move received packet into a new pbuf */
      p = low_level_input(netif);
      /* no packet could be read, silently ignore this */
      if (p == NULL) return;
      /* points to packet payload, which starts with an Ethernet header */
      ethhdr = p->payload;
    
      switch (htons(ethhdr->type)) {
      /* IP or ARP packet? */
      case ETHTYPE_IP:
      case ETHTYPE_ARP:
    #if PPPOE_SUPPORT
      /* PPPoE packet? */
      case ETHTYPE_PPPOEDISC:
      case ETHTYPE_PPPOE:
    #endif /* PPPOE_SUPPORT */
        /* full packet send to tcpip_thread to process */
        if (netif->input(p, netif)!=ERR_OK)
         { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error
    "));
           pbuf_free(p);
           p = NULL;
         }
        break;
    
      default:
        pbuf_free(p);
        p = NULL;
        break;
      }
    }
    
    
    
    /**
     * Should be called at the beginning of the program to set up the
     * network interface. It calls the function low_level_init() to do the
     * actual setup of the hardware.
     *
     * This function should be passed as a parameter to netif_add().
     *
     * @param netif the lwip network interface structure for this ethernetif
     * @return ERR_OK if the loopif is initialized
     *         ERR_MEM if private data couldn't be allocated
     *         any other err_t on error
     */
    err_t ethernetif_init(struct netif *netif)
    {
      struct ethernetif *ethernetif;
    
      LWIP_ASSERT("netif != NULL", (netif != NULL));
        
      ethernetif = mem_malloc(sizeof(struct ethernetif));
      if (ethernetif == NULL) {
        LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory
    "));
        return ERR_MEM;
      }
    
    #if LWIP_NETIF_HOSTNAME
      /* Initialize interface hostname */
      netif->hostname = "lwip";
    #endif /* LWIP_NETIF_HOSTNAME */
    
      /*
       * Initialize the snmp variables and counters inside the struct netif.
       * The last argument should be replaced with your link speed, in units
       * of bits per second.
       */
      NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
    
      netif->state = ethernetif;
      netif->name[0] = IFNAME0;
      netif->name[1] = IFNAME1;
      /* We directly use etharp_output() here to save a function call.
       * You can instead declare your own function an call etharp_output()
       * from it if you have to do some checks before sending (e.g. if link
       * is available...) */
      netif->output = etharp_output;
      netif->linkoutput = low_level_output;
      
      ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
      
      /* initialize the hardware */
      return low_level_init(netif);
    
    }
    
    #endif 

    主要是三个功能,网卡数据输入,网卡数据输入,网卡初始化,而且我们可以看到,lwip的配置是可以支持多个网卡滴

    要是没有网卡驱动的可以看我之前的例程,里面有

    这时候我们算是完整完成了lwip的移植,现在到了使用阶段了

    这是我们的main函数

    int main(void)
    {
        NVIC_Group_Init();//系统默认中断分组
        Debug_Serial_Init(115200);
        Delay_Init();
        Led_Init();
        Key_Exti_Init();
        LCD_Init();
        LCD_Clear(LCD_BLACK);
        
        ipaddr.addr = 0;
        netmask.addr = 0;
        gw.addr = 0;
        init_lwip_timer();  //初始化LWIP定时器
        //初始化LWIP协议栈,执行检查用户所有可配置的值,初始化所有的模块
        lwip_init();
        
        //添加网络接口
        while((netif_add(&enc28j60_netif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input)==NULL))
        {
            LCD_ShowString(0,0,240,320,(u8*)"ENC28J60 Init Failed             ",LCD_BLACK);
            Delay_Ms(200);
            LCD_ShowString(0,0,240,320,(u8*)"                                 ",LCD_BLACK);
            Delay_Ms(200);
        }
        LCD_ShowString(0,0,240,320,(u8*)"ENC28J60 Init OK                     ",LCD_BLACK);
        //注册默认的网络接口
        netif_set_default(&enc28j60_netif);
        //建立网络接口用于处理通信
        netif_set_up(&enc28j60_netif); 
        
        dhcp_start(&enc28j60_netif);             //为网卡创建一个新的DHCP客户端
        
        while(1)
        {
            LWIP_Polling();
            Led_Set(0,0);
            Delay_Ms(100);
            Led_Set(0,1);
            Delay_Ms(500);
        }
    }

    初始化定时器,初始化协议栈,添加网络接口(该函数同时完成网卡的初始化,mac地址在之前的文件中),注册网络接口,建立网络通讯接口,因为是dhcp,所以完全不用管网络的地址是多少,直接给0,后面他会自己获得到的

    接下来启动dhcp服务,因为是裸机程序,没有多任务再跑,所以我们必须轮询系统事件并处理,也就是LWIP_Polling(),该函数如下

    #define CLOCKTICKS_PER_MS 10    //定义时钟节拍
    
    static ip_addr_t ipaddr, netmask, gw;     //定义IP地址
    struct netif enc28j60_netif;              //定义网络接口
    u32_t input_time;
    u32_t last_arp_time;            
    u32_t last_tcp_time;    
    u32_t last_ipreass_time;
    
    u32_t last_dhcp_fine_time;            
    u32_t last_dhcp_coarse_time;  
    u32 dhcp_ip=0;
    
    //LWIP查询
    void LWIP_Polling(void)
    {
        if(timer_expired(&input_time,5)) //接收包,周期处理函数
        {
            ethernetif_input(&enc28j60_netif); 
        }
        if(timer_expired(&last_tcp_time,TCP_TMR_INTERVAL/CLOCKTICKS_PER_MS))//TCP处理定时器处理函数
        {
            tcp_tmr();
        }
        if(timer_expired(&last_arp_time,ARP_TMR_INTERVAL/CLOCKTICKS_PER_MS))//ARP处理定时器
        {
            etharp_tmr();
        }
        if(timer_expired(&last_ipreass_time,IP_TMR_INTERVAL/CLOCKTICKS_PER_MS))//IP重新组装定时器
        { 
            ip_reass_tmr();
        }                                   
        if(timer_expired(&last_dhcp_fine_time,DHCP_FINE_TIMER_MSECS/CLOCKTICKS_PER_MS))//dhcp服务
        {
            dhcp_fine_tmr();
        }
        if(timer_expired(&last_dhcp_coarse_time,DHCP_COARSE_TIMER_MSECS/CLOCKTICKS_PER_MS))//dhcp服务
        {
            dhcp_coarse_tmr();
        }  
    }

    在循环中处理好相应的事件,因为我们没建立tcp链接所以tcp其实可以不要,另外dhcp服务使用的是udp,端口使用的是udp的57,58

    到这里编译连接,系统启动就能查看到路由器自动分配的IP了

    具体工程在下面下载

    http://download.csdn.net/detail/dengrengong/8548851
  • 相关阅读:
    一文摸透从输入URL到页面渲染的过程
    JavaScript实现哈希表
    JavaScript数据结构与算法博客目录
    JavaScript实现图结构
    从宏观到细节为你讲解前端性能优化
    详解HTTP协议
    JavaScript实现排序算法
    Google Stadia免费试用两个月
    安卓手机调成黑白屏幕
    UWP Xbox上隐藏键盘⌨
  • 原文地址:https://www.cnblogs.com/dengxiaojun/p/4379545.html
Copyright © 2020-2023  润新知