根据你描述的现象(USB传输一段时间后无法发送,hcdc->TxState卡在1导致USBD_BUSY),以及定位到的HAL库代码位置,这很可能是USB传输完成中断未被正确处理导致的状态机阻塞问题。以下是原因分析和解决方案:
传输完成中断丢失/未处理:
HAL_PCD_IRQHandler中的__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))。TxState无法复位为0,后续发送请求会返回USDD_BUSY。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。HAL_PCD_HandleRxQ_ISR问题:
stm32h7xx_hal_pcd.c第431行是中断服务函数中的状态处理逻辑(如HAL_PCD_HandleRxQ_ISR())。USB_OTG_GINTSTS_RXFLVL若未清除,会导致中断重复触发或后续中断丢失。 HAL_NVIC_SetPriority(OTG_HS_IRQn, 5, 0); // 提高优先级(如5)
HAL_NVIC_EnableIRQ(OTG_HS_IRQn);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);
}HAL_PCD_Start()正确初始化DMA)。SCB_CleanDCache_by_Addr()清理发送缓存区地址。SCB_CleanDCache_by_Addr((uint32_t*)tx_buffer, sizeof(tx_buffer));USBD_CDC_TransmitPacket()中添加超时机制: uint32_t timeout = 500; // 超时时间(根据实际情况调整)
while (hcdc->TxState == 1) {
if (--timeout == 0) {
hcdc->TxState = 0; // 强制复位状态
break;
}
HAL_Delay(1);
}
// 正常调用USBD_LL_Transmit()
警告:此方法仅作应急,需配合底层修复。
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);
}
}若以上无效,尝试绕过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%的类似问题可被修复。
举报
更多回帖