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

STM32F401 USB设备描述符发送失败,报EOVERFLOW(-75)错误

STM32F401 CMSIS USB设备描述符二次请求无响应问题排查

问题背景

在STM32F401微控制器上调试CMSIS USB设备固件,已实现读取主机数据包、发送首个设备请求、接收并应用分配地址的功能,但分配地址后主机再次请求设备描述符时,设备无响应,该问题已持续数周。

相关代码片段

数据包接收处理代码

uint32_t data = *fifo;
printf("Rx from FIFO: 0x%08x\n", data);

void rxflvl_handler() {
    uint32_t packet_status = USB_OTG_FS->GRXSTSP;

    uint8_t endpoint_number = (packet_status & USB_OTG_GRXSTSP_EPNUM_Msk) >> USB_OTG_GRXSTSP_EPNUM_Pos;
    uint16_t bcnt = (packet_status & USB_OTG_GRXSTSP_BCNT_Msk) >> USB_OTG_GRXSTSP_BCNT_Pos;
    uint16_t pktsts = (packet_status & USB_OTG_GRXSTSP_PKTSTS_Msk) >> USB_OTG_GRXSTSP_PKTSTS_Pos;

    printf("Received packet of status 0x%02x\n", pktsts);
    switch (pktsts) {
    case 0x06: // SETUP packet (includes data)
        setup_packet_handler(endpoint_number, bcnt);
        break;
    case 0x02: // OUT packet (includes data)
        break;
    case 0x04: // SETUP stage has completed
    case 0x03: // OUT transfer has completed
        // Re-enable the endpoint
        OUT_ENDPOINT(endpoint_number)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK;
        OUT_ENDPOINT(endpoint_number)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA;
        break;
    }
}

主循环监控代码

int main(void)
{
  /* Pins initialization */
  /* USB initialization */
  
  in_packets = (in_endpoint->DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos;
  fifo_space = in_endpoint->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk;
  printf("FIFO free space: %i\n", fifo_space);
  while (1)
  {
    // Checking for USB interrupts
    usb_poll();

    // Notify if packets number changed
    uint32_t npkts = (in_endpoint->DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos;
    if (npkts != in_packets) {
      printf("Packets number update: %i\n", npkts);
      in_packets = npkts;
    }

    // Notify if FIFO space changed
    uint16_t new_space = in_endpoint->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk;
    if (new_space != fifo_space) {
      fifo_space = new_space;
      printf("FIFO free space: %i\n", new_space);
    }
  }
}

设备描述符请求处理代码

void handle_descriptor_request(void) {
  printf("Received descriptor request\n");
  uint32_t* fifo  = (USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE +
          0 * 0x1000);
  USB_OTG_INEndpointTypeDef* in_endpoint = (USB_OTG_INEndpointTypeDef*)(
                  USB_OTG_FS_PERIPH_BASE +
                  USB_OTG_IN_ENDPOINT_BASE +
                  0
                  );

  // flush_txfifo(0);
  // in_endpoint->DIEPTSIZ = 0;
  // in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
  // in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
  
  printf("Manual PKTCNT to 3\n");
  // Set Tx size to 18
  in_endpoint->DIEPTSIZ &= ~USB_OTG_DIEPTSIZ_XFRSIZ_Msk;
  in_endpoint->DIEPTSIZ &= ~USB_OTG_DIEPTSIZ_PKTCNT_Msk;
  in_endpoint->DIEPTSIZ |= 18 << USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
  // Send 3 packets (8 bytes max for each)
  in_endpoint->DIEPTSIZ |= 3 << USB_OTG_DIEPTSIZ_PKTCNT_Pos;
  
  // Enable transmission
  in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
  in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_EPENA;

  // Write a valid device descriptor to the Tx FIFO
  *fifo = 0x02000112;
  *fifo = 0x08000000;

  *fifo = 0x13aa6666;
  *fifo = 0x00000000;
  *fifo = 0x0000012c;

  fifo_space = in_endpoint->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk;
  printf("FIFO free space: %i\n", fifo_space);
}

现象总结

  • 首次设备请求发送成功,FIFO空间从16降至11后恢复为16,待发送包数从3降至2(但Wireshark可见完整18字节描述符,理论应降至0)
  • 接收地址并回复ZLP包后出现异常,后续每次描述符请求会使FIFO空闲空间持续减少,数据包未发出
  • Wireshark显示首次响应成功,第二次响应失败并报EOVERFLOW(-75)错误,主机dmesg偶尔出现-71错误
  • 尝试刷新0号FIFO后,FIFO空闲空间不再低于11,但数据包仍未发出;尝试恢复端点后,第二次请求时设备似乎发送了数据,但FIFO大小仅恢复到15字而非16
  • 无法理解成功传输时包数从3降至2的原因

排查方向建议

1. 端点传输完成中断处理检查

  • 确认IN端点传输完成中断(DIEPINT_XFRC)是否被正确捕获并处理,传输完成后必须清除该中断标志,同时重置DIEPTSIZ的PKTCNT和XFRSIZ字段,否则硬件会认为传输未完成,导致后续请求状态异常。
  • 检查地址分配后,端点0的DIEPCTL_EPADDR字段是否更新为新地址,避免地址不匹配导致主机无法识别设备响应。

2. FIFO数据填充与清空操作修正

  • 端点0最大包大小为8字节,18字节需拆分为3个包(8+8+2),但当前代码写入了5个32位字(20字节),超出需求的2字节会残留于FIFO中,引发后续EOVERFLOW错误。需严格按18字节填充,最后一个32位字只写入低2字节,高6字节保留为0。
  • 每次处理新的描述符请求前,必须先调用flush_txfifo(0)清空Tx FIFO,同时禁用端点(DIEPCTL_EPDIS),修改DIEPTSIZ后再重新启用端点,确保硬件状态完全复位。

3. 传输完成后的状态重置

首次传输后PKTCNT未归零,大概率是中断处理中未正确重置端点状态。需在传输完成中断中执行以下操作:

// 清除传输完成中断标志
in_endpoint->DIEPINT = USB_OTG_DIEPINT_XFRC;
// 重置传输大小和包计数
in_endpoint->DIEPTSIZ = 0;
// 等待端点禁用完成后重新启用
in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
while (in_endpoint->DIEPCTL & USB_OTG_DIEPCTL_EPDIS);
in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_EPENA;

4. ZLP发送后的状态验证

分配地址后发送ZLP时,需确保ZLP传输完成后执行完整的状态重置(清除中断、重置DIEPTSIZ),避免端点处于半完成状态,无法处理后续描述符请求。

5. 硬件寄存器配置核对

  • 检查GCCFG寄存器的PWRDWN位,确保USB外设未被断电。
  • 确认端点0的DIEPCTL_MPSIZ字段设置为8,与主机协商的最大包大小一致,避免包大小不匹配导致传输错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 08:27:37