You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过STM32 USB栈正确检测CDC设备发送管道空闲状态

解决STM32 USB CDC多数据包发送及吞吐量问题

问题根源

连续调用CDC_Transmit_FS()时第二个数据包发送失败,以及while(CDC_Transmit_FS()==USBD_BUSY);死循环的核心原因:

  • CDC_Transmit_FS()内部的TxState标记仅在USB发送完成中断触发后才会重置为0;
  • 若未开启USB中断、未正确处理发送完成回调,TxState会一直保持非0状态,导致后续调用返回USBD_BUSY,且无法自动恢复。

正确的发送状态检测与实现方式

方式1:利用发送完成回调(推荐,高吞吐量)

这是STM32 USB栈设计的标准异步发送流程,无需轮询,能最大化传输效率:

  1. 确保启用发送完成回调
    在CubeMX配置USB CDC时,勾选Transmit Complete中断;或在代码中确保USBD_CDC_SetTxCallback已正确绑定到自定义回调函数。

  2. 重写发送完成回调函数
    在usbd_cdc_if.c中修改CDC_TransmitCplt_FS,添加发送完成的标志位或直接发起下一次发送:

    // 定义全局或静态标志位,标记发送就绪状态
    static uint8_t tx_ready = 1;
    
    void CDC_TransmitCplt_FS(uint8_t *Buf, uint32_t *Len)
    {
      uint8_t result = USBD_OK;
      UNUSED(Buf);
      UNUSED(Len);
    
      // 标记当前发送已完成,允许下一次发送
      tx_ready = 1;
    
      // 若需要连续发送,可在此处直接调用下一次CDC_Transmit_FS
      // CDC_Transmit_FS(next_buf, next_len);
    
      result = USBD_CDC_TransmitPacket(&hUsbDeviceFS);
      if(result != USBD_OK)
      {
        Error_Handler();
      }
    }
    
  3. 发送逻辑示例

    // 等待发送就绪后再发起新的发送
    while(!tx_ready);
    tx_ready = 0;
    CDC_Transmit_FS(tx_buf, tx_len);
    

方式2:轮询状态(仅适用于低吞吐量场景)

若必须使用轮询,需确保USB中断正常开启,否则TxState永远无法更新:

  1. 确认USB中断已启用
    检查代码中是否存在如下中断开启语句(通常由CubeMX自动生成):

    HAL_NVIC_EnableIRQ(USBD_IRQn);
    HAL_NVIC_SetPriority(USBD_IRQn, 0, 0);
    
  2. 正确的轮询逻辑
    不要直接轮询CDC_Transmit_FS()的返回值,而是直接检查hcdc.TxState(需确保hcdc可访问):

    // 等待发送状态就绪
    while(hcdc.TxState != 0);
    // 发起新的发送
    CDC_Transmit_FS(tx_buf, tx_len);
    

关键注意事项

  • 禁止使用HAL_Delay()来等待发送完成:这种方式完全依赖延迟时间,不可靠且会大幅降低传输吞吐量;
  • 高吞吐量场景下必须使用回调异步触发发送,避免轮询占用CPU资源;
  • 若使用CubeMX生成代码,需确保USB CDC的中断配置完整,未被误关闭。

内容的提问来源于stack exchange,提问作者Christopher Theriault

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 17:55:15