STM32网络丢包问题分析

1. 测试环境说明

硬件平台:NUCLEO-F767ZI 开发板(STM32F7,Cortex-M7,216MHz,2MB Flash,512KB SRAM)
操作系统:FreeRTOS v9.0.0(CMSIS-RTOS v1.02)
TCP/IP协议栈:LwIP v2.0.0

  这里所描述的网络丢包问题的测试程序,是使用 STM32CubeMX 工具(库版本为 STM32Cube_FW_F7_V1.7.0 ),基于 FreeRTOS 和 LwIP 实现的一个以太网 Demo 程序。协议栈已实现 ICMP 包的 echo 功能(即可以通过其他以太网设备 ping 开发板),此外,我们在该程序框架之上添加了 tcp_echoserver 和 udp_echoserver 功能,进行 TCP 和 UDP 网络性能测试,均采用 RAW API 方式实现。
  
  相关的几个测试工具:

  • Ping 测试工具(ATKKPING): http://download.csdn.net/detail/luckydarcy/9914445
  • TCP&UDP测试工具 V1.02: http://download.csdn.net/detail/luckydarcy/9895750
  • TCP/UDP Socket调试工具V2.3: http://download.csdn.net/detail/luckydarcy/9895746

2. 以太网控制器

  要分析网络丢包问题,我们先来看一下 STM32F7 的以太网控制器提供了哪些资源。如下图所示,我们可以把 STM32F7 的以太网控制器划分为三部分,从左到右分别是:DMA 控制器、MAC 控制器、PHY 接口。NUCLEO-F767ZI 开发板上的 PHY 芯片是 8742A,通过 RMII 与 MAC 控制器相连。

STM32网络丢包问题分析_第1张图片

  需要注意的是,这里的 DMA 是以太网外设专用的,它包含两个 FIFO 缓冲区,分别用于以太网数据帧的接收和发送,大小均为 2K 字节。
  以太网数据接收模式设置为中断模式,当产生中断或由发送需求时,对于任务来说,以太网数据的接收和发送是通过操作描述符列表和数据缓冲区来完成的。

3. 数据的接收流程

  下面我们来看一下以太网数据的接收流程:
  中断向量表“__vector_table”中定义了以太网外设中断处理函数 ETH_IRQHandler,对于 Rx 中断来说,实际上调用的是回调函数 HAL_ETH_RxCpltCallback,而该函数只做了一件事,那就是释放信号量:

/**
  * @brief  Ethernet Rx Transfer completed callback
  * @param  heth: ETH handle
  * @retval None
  */
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
  osSemaphoreRelease(s_xSemaphore);
}

  该信号量在 low_level_init 里面被定义为二值信号量,也就是互斥量:

  osSemaphoreDef(SEM);
  s_xSemaphore = osSemaphoreCreate(osSemaphore(SEM) , 1 );

  而等待该信号量的是一个称为“ethernetif_input”的线程,我们可以称它为中断服务程序 ISR,它作为整个 LwIP 协议栈的入口,它在 low_level_init 中被定义为 realtime 优先级别的线程:

osThreadDef(EthIf, ethernetif_input, osPriorityRealtime, 0, INTERFACE_THREAD_STACK_SIZE);

  线程 ethernetif_input 所做的工作也就是等待该信号量,当接收到以太网数据帧,中断处理函数就会释放信号量,从而使得 ethernetif_input 线程进入就绪态,之后便等待操作系统调度,进而进行数据帧解析。

4. 数据的发送流程

  数据的发送流程根据 LwIP 的配置有所区别,但最终都会调用 low_level_output 函数。该函数也是通过操作描述符列表和数据缓冲区来完成发送动作。
  和接收流程不同的是,发送的动作没有使用信号量进行同步,而是根据应用程序的需求进行调用。也就是说,只要应用程序有发送数据的需求都可以直接调用相应的API进行发送。那么,这样就会造成资源冲突,所以在更底层的 HAL_ETH_TransmitFrame 函数中,使用了网卡句柄的锁进行保护:

__HAL_LOCK(heth);
  ......
__HAL_UNLOCK(heth);

  这样确实可以解决资源冲突的问题,但是这个 Demo 程序的问题在于,当前运行的任务无论有没有拿到锁(网卡资源),low_level_output 都返回 OK。这样就会造成丢包现象。

5. 验证测试

  经过上面的分析,我们可以确认,在这个 Demo 程序中,以太网数据的接收和发送机制是存在缺陷的。针对这些缺陷,我们采用 Ping 和 UDP echo 进行测试,并在关键位置添加统计信息。
  一个是 ICMP 的 echo 计数(ping_times),我们在程序认为发送成功时才计数:

/* send an ICMP packet */
ret = ip4_output_if(p, src, LWIP_IP_HDRINCL,ICMP_TTL, 0, IP_PROTO_ICMP, inp);
if (ret != ERR_OK) {
  // ......
  }else{
    ping_times++;
  }

另一个是 UDP 的 echo 计数(udp_times),同样,我们在程序认为发送成功时才计数:

static void udp_echoserver_recv_callback(......)
{
if(ERR_OK == udp_sendto(upcb, p, addr, port))udp_times++;
pbuf_free(p);
}

  另外,我们在 low_level_output 的各个出错部分添加出错统计(output_err),比如:

/* Prepare transmit descriptors to give to DMA */ 
if(HAL_OK != HAL_ETH_TransmitFrame(&heth, framelength)) {
    output_err++;
}

5.1 PC直接连接STM32F7

测试一、UDP echo测试(间隔100ms,长度1000字节)

  PC 机计数显示成功发送 18005 个数据包,成功接收 17999 个数据包。也即丢失 6 个,丢包率为 0.0333%。

STM32网络丢包问题分析_第2张图片

  STM32F7 打印数据显示,程序“认为”成功发送 18005 个 UDP 数据包,但实际上有 6 个没有发送成功。与 PC 端统计数据相符。
(串口打印数据只需关注最后三个数字,分别表示 ping_times、udp_times、output_err)

STM32网络丢包问题分析_第3张图片

测试二、UDP echo测试(间隔10ms,长度1000字节)

  PC 机计数显示成功发送 59255 个数据包,成功接收 59233 个数据包。也即丢失 22 个,丢包率为 0.0371%。

STM32网络丢包问题分析_第4张图片

  STM32F7 打印数据显示,程序“认为”成功发送 59255 个 UDP 数据包,但实际上有 22 个没有发送成功。与 PC 端统计数据相符。

STM32网络丢包问题分析_第5张图片

测试三、UDP echo测试(间隔1ms,长度1000字节)

  PC 机计数显示成功发送 1920341 个数据包,成功接收 1919724 个数据包。也即丢失 617 个,丢包率为 0.0321%。

STM32网络丢包问题分析_第6张图片

  STM32F7 打印数据显示,程序“认为”成功发送 1920341 个 UDP 数据包,但实际上有 617 个没有发送成功。与 PC 端统计数据相符。

STM32网络丢包问题分析_第7张图片

测试四、Ping测试(以最快速度Ping,长度32字节)

  PC 机计数显示成功发送 15409 个 ICMP 包,超时 8 个,丢包率为 0.05%。

STM32网络丢包问题分析_第8张图片

  STM32F7 打印数据显示,程序“认为”成功发送 15409 个 ICMP 包,但实际上有 1 个没有发送成功。与 PC 端显示丢失 8 个不相符。

STM32网络丢包问题分析_第9张图片

测试五、Ping测试(以最快速度Ping,长度1472字节)

  PC 机计数显示成功发送 15402 个 ICMP 包,超时 30 个,丢包率为 0.19%。

STM32网络丢包问题分析_第10张图片

  STM32F7 打印数据显示,程序“认为”成功发送 15402 个 ICMP 包,但实际上有 2 个没有发送成功。与 PC 端显示丢失 30 个不相符。

STM32网络丢包问题分析_第11张图片

5.2 PC通过交换机连接STM32F7(交换机连接外网)

测试一、UDP echo测试(间隔10ms,长度1000字节)

  PC 机计数显示成功发送 61240 个数据包,成功接收 61209 个数据包。也即丢失 31 个,丢包率为 0.0506%。

STM32网络丢包问题分析_第12张图片

  STM32F7 打印数据显示,程序“认为”成功发送 61240 个 UDP 数据包,但实际上有 12 个没有发送成功。与 PC 端显示丢失 31 个不相符。

STM32网络丢包问题分析_第13张图片

测试二、Ping测试(以最快速度Ping,长度1472字节)

  PC 机计数显示成功发送 21827 个 ICMP 包,超时 161 个,丢包率为 0.74%。

STM32网络丢包问题分析_第14张图片

  STM32F7 打印数据显示,程序“认为”成功发送 21827 个 ICMP 包,但实际上有 3 个没有发送成功。与 PC 端显示丢失 161 个不相符。

STM32网络丢包问题分析_第15张图片

6. 总结

  根据以上测试,我们可以判断,不管是 UDP 测试还是 ICMP 测试,STM32F7 接收到的数据包都与 PC 端统计的成功发送数量完全相同。也就是说,对于 STM32F7 来说并没有丢包。但对于 PC 来说,确实是丢包了。情况如下:
  (1)在 UDP 的 echo 测试中,我们发现在与 PC 直连的环境下,STM32F7 的发送失败统计数据与 PC 端统计的丢包数据完全吻合。而在交换机环境下,STM32F7 的发送失败统计数据却小于 PC 端的丢包数据。
  (2)在 Ping 测试中,不管直连还是存在交换机,STM32F7 接收到的包与 PC 端发送的一致,但 STM32F7 的发送失败统计数据却总小于 PC 端的丢包数据。但 Ping 存在超时时间等条件限制,建议以 UDP 测试数据为准。

  经分析认为,STM32F7 出现网络丢包的主要原因不在于 LwIP 协议栈、网卡驱动以及中断响应,而是该 Demo 程序所实现的以太网数据收发机制存在缺陷所致。即:
  (1)对于接收部分,采用二值信号量来同步以太网外设中断和中断服务程序 ISR 是个不错的主意,在上述测试中也确实没出现接收端丢包的情况。但是如果网络数据过于频繁,系统任务繁重,使得中断服务程序还没执行完就出现了下一个中断,也可能会导致丢包。
  (2)对于发送部分,采用网卡句柄的锁来保护共享资源的做法有待商榷,Demo 中的这种机制会直接导致资源冲突的时候丢包。

  此外,根据测试情况,Demo 程序中应该还存在其他导致数据包发送时丢失的路径。所以,建议相关开发工程师不要过分依赖 Demo 程序,如果确定使用 STM32 + FreeRTOS + LwIP 的设计方案,应该明确项目需求,在硬件资源有限的条件下,结合 FreeRTOS 和 LwIP 协议栈的特性设计出更合理的程序。

你可能感兴趣的:(STM32&uC/OSII,单片机,STM32开发)