[STM32F4] 关于STM32F407ZET6单片机使用高速USB实现虚拟串口时主机不识别的问题记录

[复制链接]
115|1
晓伍 发表于 2025-10-12 08:11 | 显示全部楼层 |阅读模式
芯片型号:

单片机:STM32F407ZET6

外部PHY芯片 USB3300

工具:STM32CubeMX 6.9.2

MDK5

USB种类:

高速USB(HS),单片机作为设备,PC机(WIN 11  64位)作为主机。

因项目需要,要使用高速USB来实现数据的快速传输,因此在单片机开发板上进行高速USB虚拟串口功能的验证。

以前有全速USB虚拟串口的使用经验,在外接了PHY芯片,第一次使用高速USB。

接线图:

2863868e8ab35d6b71.png

4794668e8ab31ab8b1.png

然后使用CUBMX进行配置,选择New Project创建新工程

7295868e8ab2c30772.png

选择芯片:

5455168e8ab28ebfbc.png

设置DeBug模式 我用的是STLink,因此选择JTAG(4Pins)

9437168e8ab23781d5.png

4709168e8ab1ef16a5.png

3285868e8ab1915221.png

接下来配置时钟

3361668e8ab1398ca9.png

8827168e8ab0fab8e0.png

时钟配置的时候,PLL Q这一项是无法设置的,但是USB时钟应该设置为48MHZ,既然无法配置,只能创建工程后在代码里面进行修改。

7001668e8ab0c4b90d.png

生成代码,用MDK打开,然后全编译。

7124468e8ab072f79e.png

按道理来讲,现在虽然没有通过USB虚拟串口首发数据,但是主机端竟然不识别

9289568e8aafc35672.png 1915468e8ab01c6b0b.png

经过多方排查,发现在工程中的stm32f4xx_hal_pcd.c文件中的

void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)

这个函数里面,有这样一个判断:

    /* Handle Suspend Interrupt */
    if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_USBSUSP))
    {
      if ((USBx_DEVICE->DSTS & USB_OTG_DSTS_SUSPSTS) == USB_OTG_DSTS_SUSPSTS)
      {
#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)
        hpcd->SuspendCallback(hpcd);
#else
        HAL_PCD_SuspendCallback(hpcd);
#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
      }
      __HAL_PCD_CLEAR_FLAG(hpcd, USB_OTG_GINTSTS_USBSUSP);
    }



这里这个"Handle Suspend Interrupt"这个注释的意思是挂起中断。

这段代码是 STM32 HAL 库中处理 USB 设备模式下挂起中断(Suspend Interrupt) 的核心逻辑,主要功能是响应 USB 主机发出的挂起信号并执行相应的低功耗处理。

当 USB 主机需要让设备进入低功耗状态时,会发送挂起信号,此时 USB 控制器会触发USB_OTG_GINTSTS_USBSUSP中断标志。这段代码的作用是:

1、检测该中断标志是否置位(即是否收到挂起信号);

2、确认设备确实处于挂起状态后,调用挂起回调函数执行低功耗操作;

3、清除中断标志,避免重复响应。

分析一下这段代码:

if (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_USBSUSP))

通过 HAL 库宏__HAL_PCD_GET_FLAG检查全局中断状态寄存器(GINTSTS)中的USB_OTG_GINTSTS_USBSUSP标志位,判断是否发生了挂起中断。USB_OTG_GINTSTS_USBSUSP是 USB 控制器的全局中断标志之一,当主机发送挂起信号时,该位会被硬件置 1。

if ((USBx_DEVICE->DSTS & USB_OTG_DSTS_SUSPSTS) == USB_OTG_DSTS_SUSPSTS)

进一步通过设备状态寄存器(DSTS)的SUSPSTS位,确认设备当前是否真的处于挂起状态。

USBx_DEVICE->DSTS:设备状态寄存器,记录 USB 设备的当前状态;

USB_OTG_DSTS_SUSPSTS:该位为 1 时,表示设备已进入挂起状态(硬件自动置位);

只有当该条件成立时,才会执行后续的挂起回调函数。

#if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)

  hpcd->SuspendCallback(hpcd);

#else

  HAL_PCD_SuspendCallback(hpcd);

#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */

根据是否启用 “回调函数注册” 功能,调用对应的挂起处理函数。

若USE_HAL_PCD_REGISTER_CALLBACKS宏定义为 1(CubeMX 中可配置),则调用用户注册的SuspendCallback(灵活自定义处理逻辑);

否则调用默认的HAL_PCD_SuspendCallback(HAL 库默认实现,通常会执行关闭时钟、进入低功耗模式等操作)。

__HAL_PCD_CLEAR_FLAG(hpcd, USB_OTG_GINTSTS_USBSUSP);

功能:通过 HAL 库宏清除USB_OTG_GINTSTS_USBSUSP中断标志,避免中断服务程序(ISR)重复响应同一中断。

注意:中断标志必须在处理完成后清除,否则会导致 CPU 持续进入中断处理流程。

问题:

在 STM32F407 等部分芯片的高速 USB 模式下,这段代码的条件判断可能存在逻辑矛盾:

主机在枚举过程中可能会发送 “短暂挂起信号”(用于测试设备响应),随后立即发送唤醒信号,此时SUSPSTS位会快速从 1 变为 0;

若代码在SUSPSTS=1时就调用挂起回调,可能导致设备在枚举未完成时提前进入低功耗状态,进而引发主机识别失败。

修改判断条件:

修改前:

if ((USBx_DEVICE->DSTS & USB_OTG_DSTS_SUSPSTS) == USB_OTG_DSTS_SUSPSTS)

修改后:

if ((USBx_DEVICE->DSTS & USB_OTG_DSTS_SUSPSTS) != USB_OTG_DSTS_SUSPSTS)

即唤醒后再处理,避免了枚举阶段的误触发。

修改后,重新编译,运行:

3362668e8aaed695da.png

虚拟串口被识别为STMicroelectonics Virtual COM Port(COM19).

思考:

CUBMX生成工程是快,但就是容易出各种BUG,目前我已经遇到过:使用CUBMX生成通过SDIO接口读取EMMC初始化函数导致死机、且在调用初始化函数是先初始化SDMMC,后初始化DMA,而对DMA的配置却在初始化SDMMC里面进行,导致时钟未开启初始化无效,进而导致DMA传输中断不被触发点BUG。

总是,使用CUBMX这样的工具,还是需要探究底层的原理,不然面对生成的代码,没有排查思路会很吃力。如果不是项目进度紧张,我宁愿自己建立工程,而不是用CUBMX ,希望有一天能够彻底摆脱cubmx。
————————————————
版权声明:本文为CSDN博主「用歌声补够」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dgw2016/article/details/151756986

jf101 发表于 2025-10-12 22:09 | 显示全部楼层
高速USB实例很不错的案例
您需要登录后才可以回帖 登录 | 注册

本版积分规则

108

主题

4389

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部