RT-Thread论坛
直播中

潘飘稚

8年用户 965经验值
私信 关注

使用USB传输数据一段时间后能正常运行但是不会再上发数据,为什么?怎么解决?

尝试过两种程序都会出现这种问题:使用USB传输数据一段时间后能正常运行但是不会再上发数据,都是基于正点原子水星开发板,主控为STM32H743iit6进行测试
使用的是PB14/PB15的HS,上发速率是10ms上发331个字节,同时1s上位机下发一个需要回复的包,10字节以内比较短


1、正点原子的USB虚拟串口
调试发现是USBD_CDC_TransmitPacket函数中,hcdc->TxState一直为1导致无法调用到USBD_LL_Transmit以进行发送,而是返回USBD_BUSY,然后调试确定是……\HALLIB\STM32H7xx_HAL_Driver\Src\stm32h7xx_hal_pcd.c中431行的if(__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_IEPINT))一直进不去,进而hcdc->TxState一直为1,无法置为0再次进行发送


通过强制复位USB可以短暂解决问题,但是过了一段时间(大概30~60分钟)后就有两种情况出现,一种是能正常运行程序但是调用USB数据发送函数不会发送数据,另一种是虚拟串口一直闪烁,一会连接一会断开。


2、CherryUSB
基于CherryUSB例程中的stm32h743vbt6,添加了DMA,然后可以正常的发送接收数据,但是传输一段时间后(大概3分钟)卡死,找到后发现是usbd_core.c中USB_LOG_ERR("ep:%02x clear halt\r\n", ep);这一句,无法正常printf导致的,但是注释本句后出现上述问题,能正常运行但无法上发数据,测试时发现是可以接收数据但是调用usbd_ep_start_write无法上传

回帖(1)

一曲作罢

2025-6-17 18:08:03

根据你描述的现象(USB传输一段时间后无法发送,hcdc->TxState卡在1导致USBD_BUSY),以及定位到的HAL库代码位置,这很可能是USB传输完成中断未被正确处理导致的状态机阻塞问题。以下是原因分析和解决方案:




根本原因分析




  1. 传输完成中断丢失/未处理



    • STM32 USB外设通过中断通知主机传输完成(例如HAL_PCD_IRQHandler中的__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))。

    • 当传输完成但中断未被正确处理(如中断被屏蔽、处理延迟、标志未清除)时,TxState无法复位为0,后续发送请求会返回USDD_BUSY




  2. HAL库内部状态机卡死



    • USBD_CDC_TransmitPacket()依赖底层USBD_LL_Transmit()将数据推送到USB端点。

    • USBD_LL_Transmit()内部通过HAL_PCD_EP_Transmit()实现,后者需等待hpcd->IN_ep[ep_addr].xfer_count == 0(即上一次传输完成)。

    • 若中断未正确触发,状态机无法推进,TxState永久为1。




  3. HAL_PCD_HandleRxQ_ISR问题



    • 你提到的stm32h7xx_hal_pcd.c第431行是中断服务函数中的状态处理逻辑(如HAL_PCD_HandleRxQ_ISR())。

    • 此处中断标志USB_OTG_GINTSTS_RXFLVL若未清除,会导致中断重复触发或后续中断丢失。






解决方案步骤


1. 确保USB中断优先权和响应



  • 提升USB中断优先级:避免被其他中断阻塞。
     HAL_NVIC_SetPriority(OTG_HS_IRQn, 5, 0); // 提高优先级(如5)
    HAL_NVIC_EnableIRQ(OTG_HS_IRQn);

  • 检查中断处理延迟:确保ISR中没有耗时操作(如打印日志),改用标志位+后台处理。


2. 修复中断标志清除逻辑



  • HAL_PCD_IRQHandler()中,确保USB_OTG_GINTSTS_RXFLVL正确清除:
     // 在stm32h7xx_hal_pcd.c中找到HAL_PCD_IRQHandler()
    if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL)) {
         // 处理接收FIFO
         HAL_PCD_HandleRxQ_ISR(hpcd);
         // 强制清除标志位(关键!)
         __HAL_PCD_CLEAR_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL);
    }


3. 检查DMA配置(如果使用)



  • STM32H7的USB HS支持DMA。若启用DMA:

    • 确认描述符链无断裂(HAL_PCD_Start()正确初始化DMA)。

    • 检查缓存一致性:使用SCB_CleanDCache_by_Addr()清理发送缓存区地址。
      SCB_CleanDCache_by_Addr((uint32_t*)tx_buffer, sizeof(tx_buffer));



4. 端点状态机超时处理



  • USBD_CDC_TransmitPacket()中添加超时机制:
     uint32_t timeout = 500; // 超时时间(根据实际情况调整)
    while (hcdc->TxState == 1) {
         if (--timeout == 0) {
             hcdc->TxState = 0; // 强制复位状态
             break;
         }
         HAL_Delay(1);
    }
    // 正常调用USBD_LL_Transmit()


    警告:此方法仅作应急,需配合底层修复。




5. 更新HAL库及CubeMX配置



  • STM32H7的USB HAL库早期版本存在已知问题:

    1. 升级到最新HAL库(如STM32H7 HAL v1.11.0+)。

    2. 使用CubeMX重新生成USB初始化代码:

      • 检查PHY时钟配置(需48MHz)。

      • 确认端点FIFO大小对齐(331字节建议512字节缓冲)。




6. 总线挂起/恢复处理



  • 若USB总线进入挂起状态(Suspend),需在恢复时重置端点:
     void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) {
         // 复位所有IN端点状态
         for (uint8_t ep = 0; ep < hpcd->Init.dev_endpoints; ep++) {
             HAL_PCD_EP_Close(hpcd, ep | 0x80);
             HAL_PCD_EP_Open(hpcd, ep | 0x80, max_packet_size, 0);
         }
    }




验证建议



  1. 逻辑分析仪抓包

    使用USB协议分析仪(如Ellisys、Saleae)确认中断触发后主机是否返回ACK。

  2. 简化测试代码

    注释接收逻辑,仅测试单向发送,判断是否接收中断干扰。

  3. 监控SOF包

    若USB总线无Start-Of-Frame包,说明主机已挂起设备。




终极备用方案


若以上无效,尝试绕过HAL库,直接操作寄存器复位端点状态:


// 强制复位发送端点(例如EP1_IN)
USB_OTG_HS->DIEPEMPMSK &= ~(1 << 1);     // 禁用端点中断
USB_OTG_HS_INEP(1)->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS; // 禁用端点
USB_OTG_HS_INEP(1)->DIEPCTL |= USB_OTG_DIEPCTL_EPENA; // 重新使能端点
hcdc->TxState = 0; // 手动清状态


注意:直接操作寄存器需谨慎,建议参考ST参考手册(RM0433)。



通过以上步骤,应能解决因中断丢失导致的发送阻塞问题。聚焦中断标志清除状态机超时处理两大关键点,90%的类似问题可被修复。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分