野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 7371|回复: 1

M0+MAC+TCP/IP 以太网单芯片方案 W7500EVB用户手册连载(17)--网络篇

[复制链接]
发表于 2018-3-16 17:08:58 | 显示全部楼层 |阅读模式
W7500(W7500P)芯片简介:
W7500 芯片为工业级以太网单芯片解决方案, 集成 ARM Cortex-M0,128KB Flash 及全硬件TCP/IP 核(和W5500、W5100内核一致),特别适用于物联网领域。 使用 W7500EVB, 让您轻松完成原型开发。

全硬件TCP/IP核的最大优点是他在执行联网操作时不需要占用MCU的运行资源,大大增加了MCU的工作效率。

DHCP协议是TCP/IP协议簇中的一种,具体功能为DHCP客户端向DHCP服务器发送自动获取IP请求,DHCP服务器为DNCP客户端自动分配IP地址、网关、DNS服务器地址等信息。本例程就是W7500EVB以DHCP客户端的角色,向DHCP服务器发送自动获取IP信息的请求并最终成功自动获取IP信息。使用前,我们先了解下DHCP协议。


想了解更多关于WIZnet W7500更多信息,或者有技术问题请联系我们:gustin@wisioe.com
深圳炜世科技—WIZnet官方代理商,全程技术支持,价格绝对优势!
电话:0755-86568556
邮箱:support@wisioe.com
回复

使用道具 举报

 楼主| 发表于 2018-3-16 17:10:25 | 显示全部楼层
第十八章 DHCP18.1 DHCP例程概述
       DHCP协议是TCP/IP协议簇中的一种,具体功能为DHCP客户端向DHCP服务器发送自动获取IP请求,DHCP服务器为DNCP客户端自动分配IP地址、网关、DNS服务器地址等信息。
       本例程就是W7500EVB以DHCP客户端的角色,向DHCP服务器发送自动获取IP信息的请求并最终成功自动获取IP信息。使用前,我们先了解下DHCP协议。
18.2 DHCP协议简介
       在常见的小型网络中,网络管理员都是采用手工分配IP地址的方法。但在中、大型网络中,手动分配IP地址的方法就不太实际了。为了避免多台机器分配IP的困扰,引出了一种高效的IP地址分配方法即:DHCP自动分配IP地址。
       在日常生活中,家庭无线路由器都内嵌了DHCP服务器。因此,当PC或智能手机通过路由器联网的时候,无需我们手工输入IP地址、网关、子网掩码和DNS服务器IP地址等网络参数。PC或智能手机都可以通过路由器自动获得这些必要的网络配置信息。这里的PC或智能手机在联网时都会先执行DHCP客户端线程和DHCP服务器通信。
       DHCP是Dynamic Host Configuration Protocol的缩写,它是TCP/IP协议簇中的一种,使用UDP协议进行数据报传递,端口号是67。DHCP协议采用客户端/服务器模型,主机地址的动态分配任务由网络主机驱动。当DHCP服务器接收到来自网络主机申请地址的信息时,才会向网络主机发送相关的地址配置等信息,以实现网络主机地址信息的动态配置。通常被应用在大型的局域网络环境中,主要作用是集中的管理、分配IP地址,使网络环境中的主机动态的获得IP地址、网关地址、DNS服务器地址等信息,并能够提升地址的使用率。
       使用DHCP时必须在网络上有一台DHCP服务器,而其他机器执行DHCP客户端。当DHCP客户端程序发出一个信息,请求一个动态的IP地址时,DHCP服务器会根据目前已经配置的地址,提供一个可供使用的IP地址和子网掩码给客户端。这些被分配的IP地址都是DHCP服务器预先保留的一个由多个地址组成的地址集,并且它们一般是一段连续的地址。
       DHCP使服务器能够动态地为网络中的其他服务器提供IP地址,通过使用DHCP,就可以不给Intranet网中除DHCP、DNS和WINS服务器外的任何服务器设置和维护静态IP地址。使用DHCP可以大大简化配置客户机的TCP/IP的工作,尤其是当某些TCP/IP参数改变时,如网络的大规模重建而引起的IP地址和子网掩码的更改。
       DHCPRelay也叫做DHCP中继代理。DHCP中继代理,就是在DHCP服务器和客户端之间转发DHCP数据包。当DHCP客户端与服务器不在同一个子网上,就必须有DHCP中继代理来转发DHCP请求和应答消息。DHCP中继代理的数据转发,与通常路由转发是不同的,通常的路由转发相对来说是透明传输的,设备一般不会修改IP包内容。而DHCP中继代理接收到DHCP消息后,重新生成一个DHCP消息,然后转发出去。在DHCP客户端看来,DHCP中继代理就像DHCP服务器;在DHCP服务器看来,DHCP中继代理就像DHCP客户端。
18.3 DHCP工作流程
       首先,DHCP客户端发送DHCPDISCOVER消息(IP地址租用申请),这个消息通过广播方式发出,所有网络中的DHCP服务器都将接收到这个消息。
       随后,网络中的DHCP服务器会回应一个DHCPOFFER消息(IP地址租用提供),由于这个时候客户端还没有网络地址,所以DHCPOFFER也是通过广播的方式发送出去的。需要注意的是,由于网络中可能存在不止一台的DHCP服务器,所以,如果不考虑网络丢包的话,客户端将接收到不止一条的DHCPOFFER消息。那么客户端会选择它接收到的第一条DHCPOFFER作为获取配置的服务器。
       然后,向该服务器发送DHCPREQUEST消息。虽然这个时候客户端已经明确知道选择的DHCP服务器的地址所在,但仍将采用广播的方式发送DHCPREQUEST消息,这样做不仅可以通知选中的服务器向客户端分配IP地址,同时也可以通知其他没有选中的DHCP服务器不需要再响应它的请求。在DHCPREQUEST消息中将包含客户端申请的IP地址。
       最后,DHCP服务器将回送DHCPACK的响应消息来通知客户端可以使用该IP地址,该确认里面包含了分配的IP地址和该地址的一个稳定期限的租约(默认是8天),并同时更新DHCP数据库。
       当租约过了一半时(即4天),客户端将和设置它的TCP/IP配置的DHCP服务器更新租约。当租约过了85.7%时,如果客户端仍然无法与当初的DHCP服务器联系上,他将与其他DHCP服务器通信,如果网络中再没有任何DHCP服务器在运行时,该客户端停止使用该IP地址,并重新发送一个DHCPDISCOVER消息,再一次重复整个过程。
       DHCP工作时要求客户机和服务器进行交互,由客户端通过广播向服务器发起申请IP地址的请求,然后由服务器分配一个IP地址以及其他的TCP/IP设置信息。DHCPACK整个工作过程如图18.3.1所示,可以分为以下步骤:
图18.3.1 DHCP工作过程
(1)IP地址租用申请(DHCPDISCOVER):DHCP客户机通过UDP68端口发送DHCPDISCOVER广播信息来查找DHCP服务器。网络上每一台安装了TCP/IP协议的主机都会接收到这种广播信息,但只有DHCP服务器才会做出响应。DHCP客户机发送的DHCPDISCOVER数据包的源地址是0.0.0.0,目标地址是255.255.255.255。
(2)IP地址租用提供(DHCPOFFER):当网络中的DHCP服务器接收到DHCPDISCOVER广播时,将确定是否可以用自己的数据库来为该请求提供服务。如果可以为该请求提供服务,DHCP服务器就从尚未出租的IP地址范围中选择最前面的空置IP,连同其他TCP/IP设定,通过UDP67端口以单播DHCPOFFER的形式为客户端提供信息。可能有多台DHCP服务器收到DHCPDISCOVER广播,并且向DHCP客户端响应DHCPOFFER。客户接收到的DHCPOFFER数据包中包含客户的MAC地址,后面跟着服务器能提供的IP地址、子网掩码、租约期限以及DHCP服务器的IP地址。
(3)IP地址租用选择(DHCPREQUEST):DHCP客户端通常是接收第一个收到的DHCPOFFER所提供的信息,并且会向网络发送一个DHCPREQUEST广播风暴,告诉所有DHCP服务器它将接收哪一台服务器提供的IP地址。
(4)IP地址租用确认(DHCPACK):当DHCP服务器收到DHCPREQUEST信息之后,便向DHCP客户端发送一个单播的DHCPACK信息,以确认IP租约的正式生效。然后DHCP客户端便将其TCP/IP协议与网卡绑定。
18.4 DHCP例程解析
       在本演示程序中,W7500EVB充当DHCP客户端的角色,DHCP服务器则是我们连接的路由器。现在绝大多数路由器都能够实现DHCP服务器的功能。W7500EVB发送广播封包并接收路由器的IP地址分配,完成了完整的DHCP工作过程。
       主程序初始化单片机和W7500EVB,在主循环里面则是主要运行do_dhcp()函数。main.c程序中调用了dhcp.c的DHCP执行函数。这些函数完成DHCP协议的组包及对接收到的数据包的解析。在初始化DHCP客户端的函数中,因为DHCP服务器端监听了UDP的67端口,DHCP客户端需要打开本地68端口同服务器通信。因为IP地址等网络参数由DHCP服务器分配,在进入主循环之前,初始化过程不执行set_W7500_ip()函数。除此之外,初始化函数与Network install例程基本一样,在此就不多讲解。
       主要分析DHCP执行函数do_dhcp():
  1. 1.      void do_dhcp(void)
  2. 2.      {
  3. 3.        uint8_t tmp[8];
  4. 4.        uint32_t my_dhcp_retry = 0;
  5. 5.        switch(DHCP_run())          //判断DHCP运行状态
  6. 6.        {
  7. 7.        case DHCP_IP_ASSIGN:           //首次分配IP地址状态
  8. 8.        case DHCP_IP_CHANGED:              //从DHCP服务器获取新的IP地址状态
  9. 9.            toggle = 1;
  10. 10.         if(toggle)
  11. 11.         {
  12. 12.           toggle = 0;
  13. 13.           close(SOCK_TCPS);
  14. 14.         }
  15. 15.     break;
  16. 16.     case DHCP_IP_LEASED:           //成功租赁到IP地址状态
  17. 17.         if(toggle)
  18. 18.         {
  19. 19.           set_w7500_ip();            //设置IP地址
  20. 20.           toggle = 0;
  21. 21.         }
  22. 22.     break;
  23. 23.     case DHCP_FAILED:         //DHCP 获取失败
  24. 24.         my_dhcp_retry++;
  25. 25.         if(my_dhcp_retry > MY_MAX_DHCP_RETRY)
  26. 26.         {
  27. 27.           #if DEBUG_MODE != DEBUG_NO
  28. 28.           printf(">> DHCP %d Failed\r\n",my_dhcp_retry);
  29. 29.           #endif
  30. 30.           my_dhcp_retry = 0;
  31. 31.          DHCP_stop();                  // if restart, recall DHCP_init()
  32. 32.        }
  33. 33.    break;
  34. 34.    default:
  35. 35.    break;
  36. 36.    }
  37. 37.  }
复制代码
       第5行获取DHCP的状态,其实就是通过DHCP_run函数取得W7500EVB动态获取IP过程中返回的状态值。第5行到第36行其实就是一个状态机模式,DHCP的状态分为:DHCP_IP_ASSIGN首次分配IP地址状态、DHCP_IP_CHANGED从DHCP服务器获取新的IP地址状态、DHCP_IP_LEASED成功租赁到IP地址状态、DHCP_FAILED DHCP 获取失败转态,共4个状态。通过switch语句完成对所有状态的处理。第19行将获取的IP地址通过set_w7500_ip()函数写入W7500EVB。
       DHCP_run();函数实现了W7500EVB作为DHCP客户端的详细工作过程,由于程序篇幅比较大,此处仅摘录讲解(部分程序删除,请对照源程序)。
  1. 1.      uint8_t DHCP_run(void)
  2. 2.      {
  3. 3.        uint8_t  type;
  4. 4.        uint8_t  ret;
  5. 5.        uint8_t  i;
  6. 6.        if(dhcp_state == STATE_DHCP_STOP) return DHCP_STOPPED;
  7. 7.        if(getSn_SR(DHCP_SOCKET) != SOCK_UDP)         //开启UDP模式
  8. 8.          socket(DHCP_SOCKET, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00);
  9. 9.        ret = DHCP_RUNNING;
  10. 10.     type = parseDHCPMSG();
  11. 11.     
  12. 12.     switch ( dhcp_state )
  13. 13.     {
  14. 14.       case STATE_DHCP_INIT :                              //DHCP初始化状态
  15. 15.         for (i = 0; i < 4; i++) DHCP_allocated_ip[i] = 0;
  16. 16.         send_DHCP_DISCOVER();                       //发送DISCOVER包
  17. 17.         dhcp_state = STATE_DHCP_DISCOVER;
  18. 18.         break;
  19. 19.       case STATE_DHCP_DISCOVER :                //DISCOVER状态
  20. 20.         if (type == DHCP_OFFER)
  21. 21.         {
  22. 22.           #ifdef _DHCP_DEBUG_
  23. 23.           printf("> Receive DHCP_OFFER\r\n");
  24. 24.           #endif
  25. 25.           for (i = 0; i < 4; i++)
  26. 26.           {
  27. 27.               DHCP_allocated_ip[i] = pDHCPMSG->yiaddr[i];
  28. 28.           }
  29. 29.           send_DHCP_REQUEST();
  30. 30.           dhcp_state = STATE_DHCP_REQUEST;
  31. 31.         } else ret = check_DHCP_timeout();
  32. 32.           break;
  33. 33.        
  34. 34.      case STATE_DHCP_REQUEST :                   //REQUET请求状态
  35. 35.        if (type == DHCP_ACK)                          //DHCP请求响应成功
  36. 36.        {
  37. 37.          #ifdef _DHCP_DEBUG_
  38. 38.          printf("> Receive DHCP_ACK\r\n");
  39. 39.          #endif
  40. 40.          printf("> Receive !!!\r\n");
  41. 41.         
  42. 42.          if (check_DHCP_leasedIP())                //IP地址租赁成功
  43. 43.          {
  44. 44.            // Network info assignment from DHCP
  45. 45.            dhcp_ip_assign();                      //从DHCP服务器分配IP地址
  46. 46.            reset_DHCP_timeout();                            //复位超时时间
  47. 47.            dhcp_state = STATE_DHCP_LEASED;
  48. 48.          }
  49. 49.          else
  50. 50.          {
  51. 51.            // IP address conflict occurred
  52. 52.            reset_DHCP_timeout();                            //复位超时时间
  53. 53.            dhcp_ip_conflict();                            //判断iP地址是否冲突
  54. 54.              dhcp_state = STATE_DHCP_INIT;
  55. 55.          }
  56. 56.        }
  57. 57.        else if (type == DHCP_NAK)                  //DHCP请求响应不成功
  58. 58.        {
  59. 59.          #ifdef _DHCP_DEBUG_
  60. 60.          printf("> Receive DHCP_NACK\r\n");
  61. 61.          #endif
  62. 62.          reset_DHCP_timeout();                        //复位超时时间
  63. 63.          dhcp_state = STATE_DHCP_DISCOVER;
  64. 64.        }
  65. 65.        else ret = check_DHCP_timeout();                  //判断请求是否超时
  66. 66.      break;
  67. 67.      case STATE_DHCP_LEASED :                     //IP地址租赁状态
  68. 68.        ret = DHCP_IP_LEASED;
  69. 69.        if ((dhcp_lease_time != INFINITE_LEASETIME) && ((dhcp_lease_time/2) < dhcp_tick_1s))
  70. 70.        {                 
  71. 71.          #ifdef _DHCP_DEBUG_
  72. 72.          printf("> Maintains the IP address \r\n");
  73. 73.          #endif
  74. 74.          type = 0;
  75. 75.          for (i = 0; i < 4; i++)
  76. 76.          {
  77. 77.            OLD_allocated_ip[i] = DHCP_allocated_ip[i];   //保存得到的IP地址
  78. 78.          }
  79. 79.          DHCP_XID++;
  80. 80.          send_DHCP_REQUEST();                             //发送请求
  81. 81.          reset_DHCP_timeout();
  82. 82.          dhcp_state = STATE_DHCP_REREQUEST;
  83. 83.        }
  84. 84.      break;
  85. 85.      case STATE_DHCP_REREQUEST : //重新发送请求,判断IP地址是否成功分配 87.   ret = DHCP_IP_LEASED;
  86. 88.        if (type == DHCP_ACK)                   //重传请求应答成功
  87. 89.        {
  88. 90.          dhcp_retry_count = 0;
  89. 91.          if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] ||
  90. 92.              OLD_allocated_ip[1] != DHCP_allocated_ip[1] ||
  91. 93.              OLD_allocated_ip[2] != DHCP_allocated_ip[2] ||
  92. 94.              OLD_allocated_ip[3] != DHCP_allocated_ip[3])
  93. 95.          {
  94. 96.            ret = DHCP_IP_CHANGED;
  95. 97.            dhcp_ip_update();
  96. 98.            #ifdef _DHCP_DEBUG_
  97. 99.            printf(">IP changed.\r\n");
  98. 100.           #endif
  99. 101.         }
  100. 102.         #ifdef _DHCP_DEBUG_
  101. 103.         else printf(">IP is continued.\r\n");
  102. 104.         #endif                                      
  103. 105.         reset_DHCP_timeout();
  104. 106.         dhcp_state = STATE_DHCP_LEASED;
  105. 107.       }
  106. 108.       else if (type == DHCP_NAK)          //重传请求应答不成功
  107. 109.       {
  108. 110.         #ifdef _DHCP_DEBUG_
  109. 111.         printf("> Receive DHCP_NACK, Failed to maintain ip\r\n");
  110. 112.         #endif
  111. 113.         reset_DHCP_timeout();                //复位超时时间
  112. 114.         dhcp_state = STATE_DHCP_DISCOVER;
  113. 115.       } else ret = check_DHCP_timeout();
  114. 116.       break;
  115. 117.     default :
  116. 118.       break;
  117. 119.   }
  118. 120.   return ret;
  119. 121. }
复制代码
       DHCP协议客户端也是一个状态机的实现,它的工作过程是这样的:W7500作为客户端初始化时,我们设定DHCP的状态为STATE_DHCP_READY,首先如果现在DHCP socket处于打开状态,我们解析获得的数据包类型,并存入变量Type中;如果现在DHCP socket处于关闭状态,我们初始化DHCP socket并开启一个端口,端口号为了方便辨识,我们命名为DHCP_CLIENT_PORT,即68,协议类型为UDP协议。之后就开始了我们的状态机模式。
       7~8行判断SOCKET如果关闭,初始化DHCP客户端,打开端口。第10行是解析DHCP服务器的应答信息。第16行就是发送DISCOVER 封包,然后客户端就跳转到STATE_DHCP_DISCOVER状态。第19行,当处于DISCOVER状态时,如果接收到Offer封包,客户端就会发送REQUEST封包,然后进入STATE_DHCP_REQUEST状态,如果没有接收到,就检查时间是否超时,跳出循环。第34行,当处于REQUEST状态时,如果接收到ACK封包,确认是不是自己的IP地址,如果确认是IP租约,就跳转到IP租约(租约时间由路由器自动分配,或设置)绑定状态,返回DHCP的IP更新的状态值,并把获得的IP地址等信息写入单片机程序的变量中,并给W7500配置。然后打印串口信息出来,我们就能看到我们所获得的信息;如果和先前的IP租约不一致或者接收到NAK封包,返回到READY状态,并返回IP地址冲突的值。这就是DHCP中一个典型的获取IP的流程。
       此外,DHCP还有对网络参数的分配的管理。也就是状态机中的“续租”处理部分。第67行,当处于绑定IP地址的状态时,如果租约时间超过一半,第77行会把现在客户端的IP地址copy到原来的IP地址处,并再次发送REQUEST封包进入STATE_DHCP_REREQUEST状态,跳出循环,相当于发出一个“续租”的请求。第86行,当处于REREQUEST状态时,我们分以下情况:当接收到ACK封包的情况下,如果IP地址与原来的不同,则IP地址更新,返回IP更新的状态值;如果相同则完成IP续租的过程,仍然使用原来的IP地址。
       至此,DHCP例程代码解析就结束了。将DHCP例程编译烧录后打印串口信息结果如图18.4.1。
2.JPG
图18.4.1 DHCP例程打印结果
    打印串口信息显示已获取IP地址,我们尝试PC机能否Ping通W7500EVB。结果如图18.4.2,成功Ping通W7500EVB,至此证明W7500EVB自动获取IP地址配置成功,已与PC机接入同一网络中。
3.JPG
图18.4.2 Ping结果




回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-5-1 12:13 , Processed in 0.032022 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表