quartz/content/Obsidian/踩过的坑/以太网BUG解决过程记录.md
2024-10-30 17:46:45 +08:00

6.0 KiB
Raw Blame History

date created date updated tags share link
2024-08-15 17:30 2024-10-30 17:46
BUG
true false

过程

MCP使用以太网连接平台长时间挂机后会提示HAL_ETH_GetDMAError错误。 使用wireshark抓包可以发现以太网有像桩端发送数据但是没有收到应答。 image.png ping网关可以看到网关有接收到桩端发送的ARP请求并且有应答但是桩没有接收到报文。

ST论坛看到一样的问题解决办法是在HAL_ETH_ErrorCallback中断回调中去清除寄存器。

void HAL_ETH_ErrorCallback(ETH_HandleTypeDef *heth)
{
  if((HAL_ETH_GetDMAError(heth) & ETH_DMACSR_RBU) == ETH_DMACSR_RBU)
  {
	printf( "ETH DMA Rx Error\n" );
	osSemaphoreRelease(RxPktSemaphore);
	// Clear RBUS ETHERNET DMA flag
	heth->Instance->DMACSR = ETH_DMACSR_RBU;
	// Resume DMA reception
	heth->Instance->DMACRDTPR = 0;
  }
  if((HAL_ETH_GetDMAError(heth) & ETH_DMACSR_TBU) == ETH_DMACSR_TBU)
  {
	printf( "ETH DMA Tx Error\n" );
	osSemaphoreRelease(TxPktSemaphore);
	//Clear TBU flag to resume processing
	heth->Instance->DMACSR = ETH_DMACSR_TBU;
	//Instruct the DMA to poll the transmit descriptor list
	heth->Instance->DMACTDTPR = 0;
  }
}

以及在上层的LWIP中修改tcp接收窗口这个没有用查看LWIP的值一直都是他修复后的数值。

尝试了上面的方法后发现没有什么作用。选择先规避这个问题在上面的ERROR中断回调中增加以太网的重新初始化。

  if((HAL_ETH_GetDMAError(heth) & ETH_DMACSR_TBU) == ETH_DMACSR_TBU)
  {
	printf( "ETH DMA Tx Error\n" );
	osSemaphoreRelease(TxPktSemaphore);
	//Clear TBU flag to resume processing
	heth->Instance->DMACSR = ETH_DMACSR_TBU;
	//Instruct the DMA to poll the transmit descriptor list
	heth->Instance->DMACTDTPR = 0;
	HAL_ETH_DeInit(&EthHandle);
    HAL_ETH_Init(&EthHandle);
  }

测试了下虽然会导致websocket连接断掉但是可以接受。但是测试使用以太网升级固件的时候因为数据量大产生错包的概率增加重新初始化后又进入了RX ERROR中导致无限重新初始化。

然后再ST的github上搜索ETH_DMACSR_RBU找到了一个已解决的方案就是把上面的清除的动作放到rx的while线程中执行RX ERROR 中断中不执行任何操作。

void HAL_ETH_ErrorCallback(ETH_HandleTypeDef *heth)
{
  if((HAL_ETH_GetDMAError(heth) & ETH_DMACSR_RBU) == ETH_DMACSR_RBU)
  {
	osSemaphoreRelease(RxPktSemaphore);
  }
  if((HAL_ETH_GetDMAError(heth) & ETH_DMACSR_TBU) == ETH_DMACSR_TBU)
  {
	osSemaphoreRelease(TxPktSemaphore);
  }
}

static void ethernet_watchdog(void) {
    if ((ETH->DMACSR & ETH_DMACSR_RBU) == ETH_DMACSR_RBU ) 
    {
        // Clear RBUS ETHERNET DMA flag
        ETH->DMACSR = ETH_DMACSR_RBU;  
        // Resume DMA reception
        ETH->DMACRDTPR = 0;
    }
    if ((ETH->DMACSR & ETH_DMACSR_TBU) == ETH_DMACSR_TBU ) 
    {
        // Clear RBUS ETHERNET DMA flag
        ETH->DMACSR = ETH_DMACSR_TBU;  
        // Resume DMA reception
        ETH->DMACTDTPR = 0;
    }
}

void ethernetif_input( void * pvParameters )
{
  struct pbuf *p;
 
  for( ;; )
  {
    . . .
   ethernet_watchdog();
  }
} 

此时测试上面的使用以太网升级固件的问题发现可以升级成功。但是挂测了一段时间后发现又回到了原点进入RX ERROR后以太网又无法使用了。

再ethernet_watchdog函数中添加打印发现以太网的状态分为3个阶段第一个阶段是正常的使用第二个阶段是ETH_DMACSR_RBU被置位然后又被DMA看门狗清除不断的重复这个过程但是此时的以太网是可以使用的。第三个阶段是进入了RX ERROR的回调中以太网彻底无法使用。

在ST的官方论坛中类似的现象上面没有确切的解决办法call fae问原厂拿解决方法。

把stm32h7xx_hal_eth.c文件中的一段代码 tailidx = (descidx + 1U) % ETH_RX_DESC_CNT; 改成: tailidx = (ETH_RX_DESC_CNT + descidx - 1U) % ETH_RX_DESC_CNT; 目的是将tail pointer指向前一个描述符而不是后一个描述符。 说这样有助于更好地提高效率和阻止丢包。

在H5中找到类似的函数将描述符从后一个改为前一个后发现这个改动会产生死机 016403ea5a7a4c6747c3bbc95deef7c.png

因为上面的思路是提高以太网的效率之前都是想解决RX ERROR产生的DMA Buffer被占用完的问题于是打开DCACHE想提高以太网的效率看能否产生一样的效果。

打开DCACHE后挂测一天发现虽然还是会产生ETH_DMACSR_RBU的情况但是可以自恢复且多数情况下不会与平台断联。

但是并没有完全解决这个问题ST论坛的出了关于h5解决上面的那个问题的方法了和fae给出的方法是一样的。。这说明我的hal库和官方现有的不一致更新hal库挂测2天发现问题解决了。

总结

  • 尽量不要使用STM32CubeMX生成的代码直接去github上找新的代码。在fae给我他的解决方案的时候我是有怀疑过hal库不是最新的还重新用STM32CubeMX生成一遍后和原有的代码比较。没想到的是他的源就不是最新只能怀疑是h5和h7的写法不同。不然这个问题应该很早就能解决。STM32CubeMX只适合拿来入门真正要拿来工程使用的还是得自己搭建。
  • 对以太网底层的原理理解得不深刻不能只依赖hal库的修复太过被动了。后续有时间得过下hal库的以太网代码增加理解。