返回列表 发新帖我要提问本帖赏金: 100.00元(功能说明)

[APM32F4] ETH应用:基于APM32F407与LAN8720实现LWIPERF网络测试

[复制链接]
 楼主| DKENNY 发表于 2024-12-23 17:36 | 显示全部楼层 |阅读模式
<
本帖最后由 DKENNY 于 2025-1-2 12:11 编辑

#申请原创# #技术资源# @21小跑堂

前言

      在现代网络应用中,性能测试是确保系统高效运行的重要环节。LWIP(轻量级IP)是一款为嵌入式系统设计的TCP/IP协议栈,因其轻量和灵活的特性而广泛应用于各种网络设备。在这个项目中,我们将基于APM32F407微控制器与LAN8720以太网芯片,结合lwiperf工具,进行网络性能测试。这不仅可以帮助开发者评估网络连接的带宽和延迟,还能在实际应用中优化网络性能。通过本项目,我们将深入探讨lwiperf的应用以及iperf2工具的使用,提供一套完整的网络测试解决方案。

一、lwiperf介绍
      lwiperf 是一个轻量级的网络性能测试工具,主要用于在嵌入式系统和物联网设备上测试和评估网络性能。它基于流行的 iperf 工具,并专门为资源有限的环境进行优化。
      主要特性
      l 轻量级:lwiperf 的设计目标是尽可能小巧,适合在内存和处理能力有限的设备上运行。
      l 支持 TCP 和 UDP:lwiperf 支持通过 TCP 和 UDP 协议进行性能测试,用户可以根据实际需求选择适合的协议。
      l 简单易用:lwiperf 提供了简单的命令行接口,用户可以快速上手,进行网络性能测试。
      l 实时结果:测试过程中,lwiperf 可以实时显示传输速率、延迟和丢包率等性能指标,以便用户即时分析网络状况。
      l 兼容性:lwiperf 可以与标准的 iperf 工具进行互操作,允许用户在不同平台之间进行比较和分析。

      使用场景
      l 物联网设备测试:在智能家居、工业自动化等物联网场景中,lwiperf 可以帮助开发者评估设备间的网络性能。
      l 嵌入式系统开发:适用于开发者在嵌入式系统中进行网络性能测试,以优化应用和网络配置。
      l 网络故障排查:在网络故障排查过程中,lwiperf 可以用于快速评估网络的可用性和性能瓶颈。

补充:lwiperf 工作原理图

chart.png

二、iperf2网络测试工具
      iPerf2 是一个专注于测试网络带宽的工具,它是 iPerf 的旧版本,主要提供基本的带宽测量功能。通过在客户端和服务器之间发送测试数据流并测量其性能,用户可以评估网络连接的速度和稳定性。

      iPerf2 的主要功能
      l 测量带宽:iPerf2 可以有效测试网络的带宽,帮助用户了解网络的传输速率,从而评估网络的性能。
      l 测量延迟:除了带宽,iPerf2 还能够测量网络延迟,即数据包从源头到达目的地所需的时间,有助于了解网络的响应速度。
      l 测试网络稳定性:iPerf2 可以用于测试网络的稳定性和可靠性,评估网络连接的质量,包括丢包率等重要指标。
      l 多种测试模式:iPerf2 支持多种测试模式,包括 TCP 和 UDP,用户可以根据自己的需求选择合适的测试协议进行测试。

      为什么选择 iPerf2?
      尽管 iPerf3 已经发布,并提供了更多的新特性和改进,但由于某些测试代码是基于 iPerf2 实现的,因此仍然选择使用 iPerf2 进行网络测试。这使得用户能够在熟悉的环境中,依旧能有效地评估网络性能。

      总结
      iPerf2 是一个简单而有效的工具,能够帮助用户全面评估网络性能,包括带宽、延迟和稳定性等指标。虽然已经有了新版本的 iPerf3,iPerf2 依然在许多场景中发挥着重要作用,特别是在需要使用现有代码的情况下。

      l iperf下载地址:https://iperf.fr/iperf-download.php
      l iperf2说明文档:https://iperf.fr/iperf-doc.php#doc

三、代码实现
      我这里具体实现的测试例程使用的lwip协议栈版本是2.2,并且该协议栈中是有lwiperf测试源码的。
6cc3129d6ed8b2234cb1afd87bc025ee

      官方是原来是只有tcp的测试代码的,这里的udp测试是我后面修改了lwip协议栈后添加进去的。
      协议栈中关于udp的测试实例的实现有点复杂,这里就不过多介绍了,附件后面有源码,大家可下载源码查看具体的实现细节。这里我们主要介绍主函数测试代码编写。

1、lwipopts.h
      开启LWIP_TIMERS。
06ef0447663a0fced8efe594a035b360

2、user_lwiperf.c
      实现sys_now函数。
74a97f0858a9067af167dbade5d9bd1c

3、apm32f4xx_int.c
      编写systick更新,这里设置每1ms更新一次systick(因为原例程中sys的滴答定时器的时基为100us)。
cbd59a6bfe50da021d3adac5e5f0661b

4、main.c
      关键代码介绍:

      l lwiperf_report
  1. static void
  2. lwiperf_report(void *arg, enum lwiperf_report_type report_type,
  3.                const ip_addr_t *local_addr, u16_t local_port, const ip_addr_t *remote_addr, u16_t remote_port,
  4.                u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec)
  5. {
  6.     LWIP_UNUSED_ARG(arg);
  7.     LWIP_UNUSED_ARG(local_addr);
  8.     LWIP_UNUSED_ARG(local_port);

  9.     LWIP_PLATFORM_DIAG(("iperf report: type=%d, remote: %s:%d, total bytes: %" U32_F ", duration in ms: %" U32_F ", kbits/s: %" U32_F "\n",
  10.                         (int)report_type, ipaddr_ntoa(remote_addr), (int)remote_port, bytes_transferred, ms_duration, bandwidth_kbitpsec));
  11. }
     这个函数用于报告iperf测试的结果。它接受多个参数,包含报告类型、本地和远程地址及端口、传输的字节数、持续时间和带宽等信息。函数内部通过 LWIP_PLATFORM_DIAG 输出格式化的报告信息。

     l USER_IRQHandler
  1. volatile char code;
  2. volatile int flag = 0;

  3. void USART_IRQHandler(void)
  4. {
  5.     if (USART_ReadIntFlag(USART1, USART_INT_RXBNE))
  6.     {
  7.         code = USART_RxData(USART1);
  8.         if (code == '1' || code == '2' || code == '3' || code == '4')
  9.         {
  10.             flag = 1;
  11.         }
  12.     }
  13. }
     这是一个中断服务例程,用于处理串口接收数据。当 USART1 接收到数据时,该函数会检查接收中断标志。如果接收到的字符是 '1', '2', '3' 或 '4',则将 flag 设置为 1,以便后续处理函数可以根据接收到的字符进行不同的模式选择。

      l select_mode
  1. static bool select_mode(struct netif *netif, bool *server_mode, bool *tcp, enum lwiperf_client_type *client_type)
  2. {
  3.     if (!netif_is_link_up(netif))
  4.     {
  5.         return false;
  6.     }

  7.     if (flag == 1)
  8.     {
  9.         printf("%c\n", code);

  10.         switch (code)
  11.         {
  12.         case '1':
  13.             *server_mode = true;
  14.             *tcp = true;
  15.             *client_type = LWIPERF_CLIENT;
  16.             break;

  17.         case '2':
  18.             *server_mode = false;
  19.             *tcp = true;
  20.             *client_type = LWIPERF_CLIENT;
  21.             break;

  22.         case '3':
  23.             *server_mode = true;
  24.             *tcp = false;
  25.             *client_type = LWIPERF_CLIENT;
  26.             break;

  27.         case '4':
  28.             *server_mode = false;
  29.             *tcp = false;
  30.             *client_type = LWIPERF_CLIENT;
  31.             break;

  32.         default:
  33.             return false;
  34.         }

  35.         return true;
  36.     }
  37.     return false;
  38. }
     该函数用于选择iperf的运行模式。它首先检查网络接口是否连接。如果 flag 为 1,表示接收到有效的命令字符,则根据接收到的字符设置服务器模式、TCP/UDP模式和客户端类型。该函数通过指针参数返回这些设置。

      l start_iperf
  1. void *start_iperf(void)
  2. {
  3.     bool server = false;
  4.     bool tcp = false;
  5.     enum lwiperf_client_type client_type;
  6.     void *session;
  7.     ip_addr_t remote_addr;

  8.     if (!select_mode(&UserNetif, &server, &tcp, &client_type))
  9.     {
  10.         return NULL;
  11.     }

  12.     if (server)
  13.     {
  14.         if (tcp)
  15.         {
  16.             session = lwiperf_start_tcp_server_default(lwiperf_report, NULL);
  17.         }
  18.         else
  19.         {
  20.             session = lwiperf_start_udp_server(netif_ip_addr4(netif_default), LWIPERF_UDP_PORT_DEFAULT,
  21.                                                lwiperf_report, NULL);
  22.         }
  23.     }
  24.     else
  25.     {
  26.         IP_ADDR4(&remote_addr, COMP_IP_ADDR0, COMP_IP_ADDR1, COMP_IP_ADDR2, COMP_IP_ADDR3);
  27.         if (tcp)
  28.         {
  29.             session = lwiperf_start_tcp_client_default(&remote_addr, lwiperf_report, NULL);
  30.         }
  31.         else
  32.         {
  33.             session = lwiperf_start_udp_client(netif_ip_addr4(netif_default), LWIPERF_UDP_PORT_DEFAULT,
  34.                                                &remote_addr, LWIPERF_UDP_PORT_DEFAULT, client_type,
  35.                                                IPERF_CLIENT_AMOUNT, IPERF_UDP_CLIENT_RATE, 0,
  36.                                                lwiperf_report, NULL);
  37.         }
  38.     }

  39.     return session;
  40. }
     这个函数是启动iperf测试的主函数。它首先调用select_mode来确定运行模式,接着根据选择的模式启动 TCP 或 UDP 服务器或客户端,并传入报告回调函数lwiperf_report。最后返回会话句柄。

      l console_try_receive_byte
  1. uint8_t console_try_receive_byte(void)
  2. {
  3.     uint8_t c = 0;

  4.     c = USART_RxData(USART1);

  5.     return c;
  6. }
     该函数尝试从串口接收一个字节的数据。如果收到数据,则返回该字节。

      l iperf
  1. void iperf(void)
  2. {
  3.     static void *session = NULL;

  4.     if (session == NULL)
  5.     {
  6.         session = start_iperf();
  7.     }

  8.     lwiperf_poll_udp_client();
  9. }
     这是iperf测试的主控制函数。它会检查当前是否有一个iperf会话在运行。如果没有,它调用start_iperf来启动一个新会话;如果已经存在,会话会检查是否接收到空格字符 ' ',如果接收到,则调用lwiperf_abort中止当前会话,并将会话指针设为 NULL。同时,调用lwiperf_poll_udp_client来处理 UDP 客户端的相关操作。
      lwiperf_poll_udp_client
      这个函数在lwiperf.c中实现,用于轮询所有正在运行的 UDP 客户端。它遍历所有连接,如果发现有 UDP 客户端在运行,则调用lwiperf_udp_client_send_more发送更多的数据,以满足指定的带宽要求。

      Main实现的具体代码如下。
  1. /*!
  2. * [url=home.php?mod=space&uid=288409]@file[/url]       main.c
  3. *
  4. * [url=home.php?mod=space&uid=247401]@brief[/url]      ETH Main program body
  5. *
  6. * [url=home.php?mod=space&uid=895143]@version[/url]    V1.0.1
  7. *
  8. * [url=home.php?mod=space&uid=212281]@date[/url]       2023-07-31
  9. *
  10. * @attention
  11. *
  12. *  Copyright (C) 2021-2023 Geehy Semiconductor
  13. *
  14. *  You may not use this file except in compliance with the
  15. *  GEEHY COPYRIGHT NOTICE (GEEHY SOFTWARE PACKAGE LICENSE).
  16. *
  17. *  The program is only for reference, which is distributed in the hope
  18. *  that it will be useful and instructional for customers to develop
  19. *  their software. Unless required by applicable law or agreed to in
  20. *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
  21. *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
  22. *  See the GEEHY SOFTWARE PACKAGE LICENSE for the governing permissions
  23. *  and limitations under the License.
  24. */

  25. #include "user_lwiperf.h"
  26. #include "main.h"
  27. #include "Board.h"
  28. #include "NETConfig.h"
  29. #include "lwip/apps/lwiperf.h"
  30. #include "stdbool.h"
  31. #include "timeouts.h"

  32. /** @addtogroup Examples
  33.   @{
  34.   */

  35. /** @addtogroup ETH_TCP_client
  36.   @{
  37.   */

  38. /** @defgroup ETH_TCP_client_Macros Macros
  39.   @{
  40. */
  41. /** printf function configs to USART1*/
  42. #define DEBUG_USART USART1

  43. /**@} end of group ETH_TCP_client_Macros*/

  44. /** @defgroup ETH_TCP_client_Variables Variables
  45.   @{
  46.   */

  47. /* Ethernet Flags for EthStatus variable */
  48. #define ETH_INIT_FLAG 0x01 /* Ethernet Init Flag */
  49. #define ETH_LINK_FLAG 0x10 /* Ethernet Link Flag */

  50. /* Global pointers on Rx descriptor used to transmit and receive descriptors */
  51. extern ETH_DMADescConfig_T *DMARxDescToGet;
  52. extern ETH_Config_T ETH_InitStructure;

  53. /** current Ethernet LocalTime value */
  54. volatile uint32_t ETHTimer = 0;
  55. uint32_t timingdelay;
  56. /** lwip network interface structure for ethernetif */
  57. struct netif UserNetif;

  58. /** TCP periodic Timer */
  59. uint32_t TCPTimer = 0;
  60. /** ARP periodic Timer */
  61. uint32_t ARPTimer = 0;
  62. /** Link periodic Timer */
  63. uint32_t LinkTimer = 0;
  64. /** MAC address */
  65. uint8_t SetMACaddr[6] = {0, 0, 0, 0, 0, 8};

  66. /**@} end of group ETH_TCP_client_Variables*/

  67. /** @defgroup ETH_TCP_client_Functions Functions
  68.   @{
  69.   */

  70. #ifndef IPERF_CLIENT_AMOUNT
  71. #define IPERF_CLIENT_AMOUNT (-1000) /* 10 seconds */
  72. #endif

  73. #define IPERF_UDP_CLIENT_RATE (100 * 1024 * 1024)

  74. /* Configure SysTick */
  75. void ConfigSysTick(void);
  76. /** User config the different system Clock */
  77. void UserRCMClockConfig(void);
  78. /** Configures COM port */
  79. void APM_BOARD_COMInit(COM_TypeDef COM, USART_Config_T *configStruct);
  80. /** Configures Button GPIO and EINT Line. */
  81. void APM_BOARD_PBInit(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode);
  82. /** Configures LED GPIO. */
  83. void APM_BOARD_LEDInit(Led_TypeDef Led);

  84. void USART_IRQHandler(void);

  85. void LwIP_DHCP_Process_Handle(void);

  86. extern void tcpc_echo_init(ip_addr_t *ipaddr, uint16_t tcpc_port);
  87. extern void tcpc_echo_disable(void);

  88. static void
  89. lwiperf_report(void *arg, enum lwiperf_report_type report_type,
  90.                const ip_addr_t *local_addr, u16_t local_port, const ip_addr_t *remote_addr, u16_t remote_port,
  91.                u32_t bytes_transferred, u32_t ms_duration, u32_t bandwidth_kbitpsec)
  92. {
  93.     LWIP_UNUSED_ARG(arg);
  94.     LWIP_UNUSED_ARG(local_addr);
  95.     LWIP_UNUSED_ARG(local_port);

  96.     LWIP_PLATFORM_DIAG(("iperf report: type=%d, remote: %s:%d, total bytes: %" U32_F ", duration in ms: %" U32_F ", kbits/s: %" U32_F "\n",
  97.                         (int)report_type, ipaddr_ntoa(remote_addr), (int)remote_port, bytes_transferred, ms_duration, bandwidth_kbitpsec));
  98. }

  99. volatile char code;
  100. volatile int flag = 0;

  101. void USART_IRQHandler(void)
  102. {
  103.     if (USART_ReadIntFlag(USART1, USART_INT_RXBNE))
  104.     {
  105.         code = USART_RxData(USART1);
  106.         if (code == '1' || code == '2' || code == '3' || code == '4')
  107.         {
  108.             flag = 1;
  109.         }
  110.     }
  111. }

  112. static bool select_mode(struct netif *netif, bool *server_mode, bool *tcp, enum lwiperf_client_type *client_type)
  113. {
  114.     if (!netif_is_link_up(netif))
  115.     {
  116.         return false;
  117.     }

  118.     if (flag == 1)
  119.     {
  120.         printf("%c\n", code);

  121.         switch (code)
  122.         {
  123.         case '1':
  124.             *server_mode = true;
  125.             *tcp = true;
  126.             *client_type = LWIPERF_CLIENT;
  127.             break;

  128.         case '2':
  129.             *server_mode = false;
  130.             *tcp = true;
  131.             *client_type = LWIPERF_CLIENT;
  132.             break;

  133.         case '3':
  134.             *server_mode = true;
  135.             *tcp = false;
  136.             *client_type = LWIPERF_CLIENT;
  137.             break;

  138.         case '4':
  139.             *server_mode = false;
  140.             *tcp = false;
  141.             *client_type = LWIPERF_CLIENT;
  142.             break;

  143.         default:
  144.             return false;
  145.         }

  146.         return true;
  147.     }
  148.     return false;
  149. }

  150. void *start_iperf(void)
  151. {
  152.     bool server = false;
  153.     bool tcp = false;
  154.     enum lwiperf_client_type client_type;
  155.     void *session;
  156.     ip_addr_t remote_addr;

  157.     if (!select_mode(&UserNetif, &server, &tcp, &client_type))
  158.     {
  159.         return NULL;
  160.     }

  161.     if (server)
  162.     {
  163.         if (tcp)
  164.         {
  165.             session = lwiperf_start_tcp_server_default(lwiperf_report, NULL);
  166.         }
  167.         else
  168.         {
  169.             session = lwiperf_start_udp_server(netif_ip_addr4(netif_default), LWIPERF_UDP_PORT_DEFAULT,
  170.                                                lwiperf_report, NULL);
  171.         }
  172.     }
  173.     else
  174.     {
  175.         IP_ADDR4(&remote_addr, COMP_IP_ADDR0, COMP_IP_ADDR1, COMP_IP_ADDR2, COMP_IP_ADDR3);
  176.         if (tcp)
  177.         {
  178.             session = lwiperf_start_tcp_client_default(&remote_addr, lwiperf_report, NULL);
  179.         }
  180.         else
  181.         {
  182.             session = lwiperf_start_udp_client(netif_ip_addr4(netif_default), LWIPERF_UDP_PORT_DEFAULT,
  183.                                                &remote_addr, LWIPERF_UDP_PORT_DEFAULT, client_type,
  184.                                                IPERF_CLIENT_AMOUNT, IPERF_UDP_CLIENT_RATE, 0,
  185.                                                lwiperf_report, NULL);
  186.         }
  187.     }

  188.     return session;
  189. }

  190. uint8_t console_try_receive_byte(void)
  191. {
  192.     uint8_t c = 0;

  193.     c = USART_RxData(USART1);

  194.     return c;
  195. }

  196. void iperf(void)
  197. {
  198.     static void *session = NULL;

  199.     if (session == NULL)
  200.     {
  201.         session = start_iperf();
  202.     }

  203.     lwiperf_poll_udp_client();
  204. }

  205. /*!
  206. * [url=home.php?mod=space&uid=247401]@brief[/url]       Main program
  207. *
  208. * @param       None
  209. *
  210. * @retval      None
  211. */
  212. int main(void)
  213. {
  214.     char LCDDisplayBuf[100] = {0};

  215.     USART_Config_T usartConfig;

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

  218.     /* Configure SysTick */
  219.     ConfigSysTick();

  220.     /* Configure USART */
  221.     usartConfig.baudRate = 115200;
  222.     usartConfig.wordLength = USART_WORD_LEN_8B;
  223.     usartConfig.stopBits = USART_STOP_BIT_1;
  224.     usartConfig.parity = USART_PARITY_NONE;
  225.     usartConfig.mode = USART_MODE_TX_RX;
  226.     usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;

  227.     APM_BOARD_COMInit(COM1, &usartConfig);

  228.     USART_EnableInterrupt(USART1, USART_INT_RXBNE);
  229.     NVIC_EnableIRQRequest(USART1_IRQn, 1, 1);

  230.     /* Configures LED2 and LED3 */
  231.     APM_BOARD_LEDInit(LED2);
  232.     APM_BOARD_LEDInit(LED3);

  233.     /* KEY init*/
  234.     APM_BOARD_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
  235.     APM_BOARD_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);

  236.     printf("This is a ETH LWIPERF Demo! \r\n");

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

  239.     /* Initilaize the LwIP stack */
  240.     LwIP_Init();

  241.     /* Use Com printf static IP address*/
  242.     sprintf(LCDDisplayBuf, "TINY board Static IP address \r\n");
  243.     printf("%s", LCDDisplayBuf);

  244.     sprintf(LCDDisplayBuf, "IP: %d.%d.%d.%d \r\n",
  245.             IP_ADDR0,
  246.             IP_ADDR1,
  247.             IP_ADDR2,
  248.             IP_ADDR3);
  249.     printf("%s", LCDDisplayBuf);

  250.     sprintf(LCDDisplayBuf, "NETMASK: %d.%d.%d.%d \r\n",
  251.             NETMASK_ADDR0,
  252.             NETMASK_ADDR1,
  253.             NETMASK_ADDR2,
  254.             NETMASK_ADDR3);
  255.     printf("%s", LCDDisplayBuf);

  256.     sprintf(LCDDisplayBuf, "Gateway: %d.%d.%d.%d \r\n",
  257.             GW_ADDR0,
  258.             GW_ADDR1,
  259.             GW_ADDR2,
  260.             GW_ADDR3);
  261.     printf("%s", LCDDisplayBuf);

  262.     printf("\n");
  263.     printf("1: TCP Server Mode\n");
  264.     printf("2: TCP Client Mode\n");
  265.     printf("3: UDP Server Mode\n");
  266.     printf("4: UDP Client Mode\n");
  267.     printf("Please enter one of modes above (e.g. 1 or 2 ...): ");

  268.     while (1)
  269.     {
  270.         /* check if any packet received */
  271.         if (ETH_CheckReceivedFrame())
  272.         {
  273.             /* process received ethernet packet */
  274.             LwIP_Pkt_Handle();
  275.         }
  276.         /* handle periodic timers for LwIP */
  277.         LwIP_Periodic_Handle(ETHTimer);
  278.         iperf();
  279.         sys_check_timeouts();
  280.     }
  281. }

  282. /*!
  283. * [url=home.php?mod=space&uid=247401]@brief[/url]       Configure SysTick
  284. *
  285. * @param       None
  286. *
  287. * @retval      None
  288. */
  289. void ConfigSysTick(void)
  290. {
  291.     if (SysTick_Config(SystemCoreClock / 100))
  292.     {
  293.         /* Capture error */
  294.         while (1)
  295.             ;
  296.     }
  297. }

  298. /**
  299. * [url=home.php?mod=space&uid=247401]@brief[/url]  Inserts a delay time.
  300. * @param  nCount: number of 10ms periods to wait for.
  301. * @retval None
  302. */
  303. void Delay(uint32_t nCount)
  304. {
  305.     /* Capture the current local time */
  306.     timingdelay = ETHTimer + nCount;

  307.     /* wait until the desired delay finish */
  308.     while (timingdelay > ETHTimer)
  309.     {
  310.     }
  311. }

  312. /*!
  313. * [url=home.php?mod=space&uid=247401]@brief[/url]       This function initializes the lwIP stack
  314. *
  315. * @param       None
  316. *
  317. * @retval      None
  318. */
  319. void LwIP_Init(void)
  320. {
  321.     struct ip4_addr ipaddr;
  322.     struct ip4_addr netmask;
  323.     struct ip4_addr gw;

  324.     /* Initializes the dynamic memory heap */
  325.     mem_init();

  326.     /* Initializes the memory pools */
  327.     memp_init();

  328.     IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
  329.     IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
  330.     IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
  331.     /* Config MAC Address */
  332.     ETH_ConfigMACAddress(ETH_MAC_ADDRESS0, SetMACaddr);

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

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

  337.     /* When the netif is fully configured this function must be called */
  338.     netif_set_up(&UserNetif);
  339.     /* Set the link callback function, this function is called on change of link status*/
  340. }

  341. /*!
  342. * @brief       This function received ethernet packet
  343. *
  344. * @param       None
  345. *
  346. * @retval      None
  347. */
  348. void LwIP_Pkt_Handle(void)
  349. {
  350.     /* Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
  351.     ethernetif_input(&UserNetif);
  352. }

  353. /*!
  354. * @brief       This function LwIP periodic tasks
  355. *
  356. * @param       ETHTimer the current Ethernet Timer value
  357. *
  358. * @retval      None
  359. */
  360. void LwIP_Periodic_Handle(__IO uint32_t ETHTimer)
  361. {
  362.     static uint8_t flagToggle = 0;

  363. #if LWIP_TCP
  364.     /* TCP periodic process every 250 ms */
  365.     if (ETHTimer - TCPTimer >= TCP_TMR_INTERVAL)
  366.     {
  367.         TCPTimer = ETHTimer;
  368.         tcp_tmr();
  369.     }
  370. #endif

  371.     /* ARP periodic process every 5s */
  372.     if ((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL)
  373.     {
  374.         ARPTimer = ETHTimer;
  375.         etharp_tmr();
  376.     }

  377.     /* Check link status */
  378.     if ((ETHTimer - LinkTimer) >= 1000)
  379.     {
  380.         if ((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0))
  381.         {
  382.             /* link goes up */
  383.             netif_set_link_up(&UserNetif);
  384.             flagToggle = 1;
  385.         }

  386.         if ((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1))
  387.         {
  388.             EthLinkStatus = 1;
  389.             /* link goes down */
  390.             netif_set_link_down(&UserNetif);
  391.             flagToggle = 0;
  392.         }
  393.     }
  394. }

  395. /*!
  396. * @brief       User config the different system Clock
  397. *
  398. * @param       None
  399. *
  400. * @retval      None
  401. */
  402. void UserRCMClockConfig(void)
  403. {
  404.     RCM_Reset();
  405.     RCM_ConfigHSE(RCM_HSE_OPEN);

  406.     if (RCM_WaitHSEReady() == SUCCESS)
  407.     {
  408.         RCM_ConfigPLL1(RCM_PLLSEL_HSE, 8, 200, RCM_PLL_SYS_DIV_2, 5);
  409.     }
  410.     RCM_EnablePLL1();

  411.     while (RCM_ReadStatusFlag(RCM_FLAG_PLL1RDY) == RESET)
  412.     {
  413.         ;
  414.     }

  415.     RCM_ConfigSYSCLK(RCM_SYSCLK_SEL_PLL);

  416.     while (RCM_ReadSYSCLKSource() != RCM_SYSCLK_SEL_PLL)
  417.     {
  418.         ;
  419.     }
  420. }

  421. /*!
  422. * @brief       Configures COM port.
  423. *
  424. * @param       COM: Specifies the COM port to be configured.
  425. *              This parameter can be one of following parameters:
  426. *              [url=home.php?mod=space&uid=2817080]@ARG[/url] COM1
  427. *              [url=home.php?mod=space&uid=2817080]@ARG[/url] COM2
  428. *
  429. * @retval      None
  430. */
  431. void APM_BOARD_COMInit(COM_TypeDef COM, USART_Config_T *configStruct)
  432. {
  433. #if defined(APM32F407_MINI)
  434.     APM_MINI_COMInit(COM1, configStruct);
  435. #elif defined(APM32F407_ELE_HUETB)
  436.     APM_ELE_HUETB_COMInit(COM1, configStruct);
  437. #elif defined(APM32F407_TINY)
  438.     APM_TINY_COMInit(COM1, configStruct);
  439. #else
  440. #error "Please select first the APM32  board to be used (in board.h)"
  441. #endif
  442. }

  443. /*!
  444. * @brief       Configures Button GPIO and EINT Line.
  445. *
  446. * @param       Button: Specifies the Button to be configured.
  447. *              This parameter can be one of following parameters:
  448. *              [url=home.php?mod=space&uid=2817080]@ARG[/url] BUTTON_KEY1: Key1 Push Button
  449. *              [url=home.php?mod=space&uid=2817080]@ARG[/url] BUTTON_KEY2: Key2 Push Button
  450. * @param       Button_Mode: Specifies Button mode.
  451. *              This parameter can be one of following parameters:
  452. *              [url=home.php?mod=space&uid=2817080]@ARG[/url] BUTTON_MODE_GPIO: Button will be used as simple IO
  453. *              @arg BUTTON_MODE_EINT: Button will be connected to EINT line
  454. *                   with interrupt generation capability
  455. *
  456. * @retval      None
  457. */
  458. void APM_BOARD_PBInit(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode)
  459. {
  460. #if defined(APM32F407_MINI)
  461.     APM_MINI_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
  462.     APM_MINI_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
  463. #elif defined(APM32F407_ELE_HUETB)
  464.     APM_ELE_HUETB_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
  465.     APM_ELE_HUETB_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
  466. #elif defined(APM32F407_TINY)
  467.     APM_TINY_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
  468.     APM_TINY_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
  469. #else
  470. #error "Please select first the APM32  board to be used (in board.h)"
  471. #endif
  472. }

  473. /*!
  474. * @brief       Configures LED GPIO.
  475. *
  476. * @param       Led: Specifies the Led to be configured.
  477. *              This parameter can be one of following parameters:
  478. *              @arg LED1
  479. *              @arg LED2
  480. *              @arg LED3
  481. *
  482. * @retval      None
  483. */
  484. void APM_BOARD_LEDInit(Led_TypeDef Led)
  485. {
  486. #if defined(APM32F407_MINI)
  487.     APM_MINI_LEDInit(Led);
  488.     APM_MINI_LEDInit(Led);
  489. #elif defined(APM32F407_ELE_HUETB)
  490.     APM_ELE_HUETB_LEDInit(Led);
  491.     APM_ELE_HUETB_LEDInit(Led);
  492. #elif defined(APM32F407_TINY)
  493.     APM_TINY_LEDInit(Led);
  494.     APM_TINY_LEDInit(Led);
  495. #else
  496. #error "Please select first the APM32  board to be used (in board.h)"
  497. #endif
  498. }

  499. #if defined(__CC_ARM) || defined(__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

  500. /*!
  501. * @brief       Redirect C Library function printf to serial port.
  502. *              After Redirection, you can use printf function.
  503. *
  504. * @param       ch:  The characters that need to be send.
  505. *
  506. * @param       *f:  pointer to a FILE that can recording all information
  507. *              needed to control a stream
  508. *
  509. * @retval      The characters that need to be send.
  510. *
  511. * @note
  512. */
  513. int fputc(int ch, FILE *f)
  514. {
  515.     /* send a byte of data to the serial port */
  516.     USART_TxData(DEBUG_USART, (uint8_t)ch);

  517.     /* wait for the data to be send */
  518.     while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET)
  519.         ;

  520.     return (ch);
  521. }

  522. #elif defined(__GNUC__)

  523. /*!
  524. * @brief       Redirect C Library function printf to serial port.
  525. *              After Redirection, you can use printf function.
  526. *
  527. * @param       ch:  The characters that need to be send.
  528. *
  529. * @retval      The characters that need to be send.
  530. *
  531. * @note
  532. */
  533. int __io_putchar(int ch)
  534. {
  535.     /* send a byte of data to the serial port */
  536.     USART_TxData(DEBUG_USART, ch);

  537.     /* wait for the data to be send */
  538.     while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET)
  539.         ;

  540.     return ch;
  541. }

  542. /*!
  543. * @brief       Redirect C Library function printf to serial port.
  544. *              After Redirection, you can use printf function.
  545. *
  546. * @param       file:  Meaningless in this function.
  547. *
  548. * @param       *ptr:  Buffer pointer for data to be sent.
  549. *
  550. * @param       len:  Length of data to be sent.
  551. *
  552. * @retval      The characters that need to be send.
  553. *
  554. * @note
  555. */
  556. int _write(int file, char *ptr, int len)
  557. {
  558.     int i;
  559.     for (i = 0; i < len; i++)
  560.     {
  561.         __io_putchar(*ptr++);
  562.     }

  563.     return len;
  564. }

  565. #else
  566. #warning Not supported compiler type
  567. #endif

  568. /**@} end of group ETH_TCP_client_Functions */
  569. /**@} end of group ETH_TCP_client */
  570. /**@} end of group Examples */

      程序实现流程如下。
087624b45fc615358a9dcb5faa2e7562

       总的来说,这个程序的设计目的是为了在开发板上快速搭建一个以太网通信示例,方便用户进行测试和调试。可以通过串口输入选择不同的操作模式,并在相应模式下进行网络性能测试。

四、测试
      1、测试开发板ip地址是否设置成功,打开cmd,输入以下指令。
Ping 192.168.100.22

94af3d07d7f0b6dca31d9256f1e4965d

      2、TCP Server测试
      l 先通过串口打开开发板的TCP Server:输入字符1;
      l 再打开cmd输入以下指令,启动IPerf TCP Client。
iperf-2.2.1-win64.exe -c 192.168.100.22 -P 1 -i 1 -p 5001 -f k -t 10

f288db2cc40f24ef728c8ac274a550b4

      3、TCP Client测试
      l 先打开cmd,输入以下指令,启动IPerf TCP Server。
iperf-2.2.1-win64.exe -s -c 192.168.100.22 -P 0 -i 1 -p 5001 -f k

    l 再通过串口打开开发板的TCP Client:输入字符2。
88ba37c427ac89e80848e3da2453b631

      这里我在测试的时候,tcp有点不稳定,不过最后的结果是正常的。

      4、UDP Server测试
      l 先通过串口打开开发板的UDP Server:输入字符3;
      l 再打开cmd,输入以下指令,启用IPerf UDP Client。
iperf-2.2.1-win64.exe -c 192.168.100.22 -u -P 1 -i 1 -p 5001 -f k -b 100.0M -t 10 -T 1

9e94853a1cfd28bf542f989144661d5a

      5、UDP client测试
      l 先打开cmd,输入以下指令,打开IPerf UDP Server。
iperf-2.2.1-win64.exe -s -c 192.168.100.22 -u -P 0 -i 1 -p 5001 -f k

    l 再通过串口打开开发板的UDP Client:输入字符4。
c6fc2eba5f97f5c6b418482cf500980c

五、总结
      通过本项目的实现,我们成功地利用APM32F407微控制器和LAN8720以太网芯片搭建了一个基于LWIP的网络测试平台。通过lwiperf和iperf2工具,我们能够高效地进行网络性能测试,获取带宽、延迟等关键指标。这些数据为后续的系统优化和网络调试提供了重要参考。

附件
      1、ETH_LWIPERF例程: ETH_LWIPERF.zip (2.93 MB, 下载次数: 13)
      2、IPERF测试工具(2.0版本): iperf-2.2.1-win64.zip (575.2 KB, 下载次数: 10)


  


打赏榜单

21小跑堂 打赏了 100.00 元 2025-01-16
理由:恭喜通过原创审核!期待您更多的原创作品~~

评论

基于APM32F407和LAN8720的LWIPERF协议栈测试案例,通过对LWIPERF协议的解读,辅以流程图和源码分析,高效学习LWIPERF协议栈  发表于 2025-1-16 17:22
补充 LWIP 工作原理图  发表于 2025-1-2 12:12
风之呢喃 发表于 2025-1-9 17:59 | 显示全部楼层
流程很清晰,很容易理解
您需要登录后才可以回帖 登录 | 注册

本版积分规则

59

主题

104

帖子

16

粉丝
快速回复 在线客服 返回列表 返回顶部