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
相关产品推荐
相关产品推荐

