ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32H573 USB不工作排查:TrustZone与时钟树是关键

STM32H573 USB不工作排查:TrustZone与时钟树是关键 前阵子折腾一块 STM32H573VITxQ 的板子USB 插上电脑以后设备管理器纹丝不动连个未知设备都不给。按 F4 时代的老经验查供电、查上拉、查描述符折腾了大半天最后发现根因居然在 H5 系列新增的安全机制上。这篇文章想把 H573VITxQ 这类带 TrustZone 的中高端 MCU 上 USB 不工作的排查路径完整梳理一遍从硬件供电、时钟树到安全属性、中间件配置把每个环节的坑都摊开讲。如果你也在调 H5 系列 USB或者正准备用这颗芯片做 CDC 虚拟串口、HID、MSC 之类的设备这篇文章应该能帮你省下一整天的排查时间。1. 现象分层先确定 USB 死在哪个环节1.1 三种典型表现对应三种完全不同的排查方向USB 连上电脑后不工作其实有好几种完全不同的死法每一种对应的排查范围差别很大。我把最常见的现象分成三类如果你正在调板子先对号入座现象你的直接感受根因大概率在哪完全无反应设备管理器没有任何新设备连声音都没有硬件供电、D 上拉、VBUS 检测、时钟使能未知设备弹出无法识别的 USB 设备设备管理器出现 Unknown Device枚举协议流程描述符回复异常EP0 问题反复断开重连设备能识别但过一会就掉或者数据传输时死掉FIFO 配置、时钟精度、电气完整性、驱动问题这个分类不是随便分的它对应的底层机制完全不同。第一类完全无反应本质是主机根本没有检测到有设备插入。USB 设备检测靠的是 D / D- 线上的上拉全速设备会在 D 线上通过 1.5kΩ 电阻上拉到 3.3V主机看到 D 电平拉高才知道有设备接入了。如果你的固件压根没有启用这个上拉或者 USB 外设的供电根本没通主机就完全感知不到你。第二类未知设备说明主机已经检测到了 D 上拉开始走枚举流程了但设备在响应主机请求时出了问题。设备无法正确回复设备描述符或者回复的数据格式不对主机只能给你一个 Unknown Device 的结果。第三类反复断开通常是高速信号质量、FIFO 瓶颈或者时钟漂移。枚举能过说明基本链路是通的问题出在量或者时序上。1.2 为什么 H573 的排查路径不能照搬 F1/F4很多老手调 USB 时习惯打开 F4 的例程直接抄但 STM32H573VITxQ 这颗芯片和 F1/F4 有一个本质区别它用的是 Cortex-M33 内核且整个系统引入了 TrustZone 安全架构。M33 内核不是简单地在 M4 基础上加了点指令而是多了一整个安全状态机。芯片上电后外设到底属于安全世界还是非安全世界决定了你的普通应用代码能不能访问它的寄存器。很多 USB 不工作的问题根源不在 USB 协议本身而是你的代码压根没碰到 USB 外设——访问被安全机制挡掉了。另外H573 的 USB 控制器是带高速能力的 OTG_HS 控制器它既可以通过内部全速 PHY 跑 Full Speed也可以外接 ULPI PHY 跑 High Speed。CubeMX 里配置错一个选项USB 就完全不工作而且不会报任何错误。这些 F4 上没有的新坑后面我会逐一展开。2. H573 特有的第一道坎TrustZone 外设安全归属2.1 你以为写寄存器了其实总线层就给你拦了这块可能出乎很多人意料。H573 出厂或者经过某些烧录流程之后option bytes 里的 TZEN 位可能是置位的。TZEN1 意味着 TrustZone 被启用芯片复位后大部分外设默认归属为安全Secure资源。这时候如果你跑的是一个普通的非安全Non-secure应用——不用任何安全启动、不用 TrustZone 的基础裸机程序——你去访问 USB 外设的寄存器总线层的安全过滤器会直接拦截寄存器写进去的数据等于石沉大海。外设时钟开了GPIO 配了但 USB 核心压根没被真正初始化。我实际遇到的情况是前一任调试者给板子烧过带安全启动的固件TZEN 被置1了我后来直接烧普通固件进去USB 完全没反应。查了供电、查了引脚、查了时钟最后读 option bytes 才发现是 TZEN 的问题。2.2 怎么判断 TZEN 是不是罪魁祸首判断方法很简单用 STM32CubeProgrammer 连上 ST-LINK连接到目标芯片后切到Option Bytes页面找到TZEN位看看当前状态。如果 TZEN1而你根本不需要 TrustZone 安全特性那就把它关掉然后做一次全片擦除再重新烧录你的普通固件。具体操作步骤我给你列一下打开 STM32CubeProgrammer通过 ST-LINK 连接目标板点击Option Bytes标签找到 TZEN 字段将 TZEN 清0点击Apply执行Full chip erase全片擦除这一步会清掉 flash 里之前所有内容重新烧录你的应用固件跑起来再看 USB。提示调整 option bytes 前一定先备份原有固件和配置尤其是从别人手里转过来的板子。有些板子 RDP读保护级别设成了 1 甚至 2改 TZEN 前需要先把 RDP 降级过程会触发全片擦除。RDP2 级别的芯片基本没法通过标准工具降级这种情况比较麻烦最好先确认板子的来源状态。2.3 如果你确实要用 TrustZoneUSB 外设的安全属性要单独配有些项目需要用 TrustZone 做安全隔离不愿意关掉。那你就得通过 GTZCGlobal TrustZone Controller把 USB 外设的安全属性配置成非安全Non-secure否则非安全端的应用代码永远访问不了 USB。这个配置涉及几个层面的设置不只是外设本身GTZC1 的 SECCFGR 寄存器把 USB_OTG_HS 对应的位清零表示该外设属于非安全世界中断控制器USB 中断也要配置到非安全中断组否则 CPU 在非安全状态收不到中断Flash/SRAM 的属性USB DMA 要访问的缓冲区内存也需要允许非安全访问。在 CubeMX 里如果开了 TrustZone 相关选项会生成对应的安全配置代码你需要找到GTZC_Config相关的初始化函数确认 USB 外设被加到了非安全列表里。这块就不是简单关掉 TZEN 那么省事了建议非必要不碰。3. 48MHz 时钟树USB 没有它什么都白搭3.1 H573 的 USB 时钟不是自动就有的STM32 上 USB 全速设备需要精确的 48MHz 时钟这是 USB 协议规定的。但 H573 在时钟树上和 F4 不太一样它没有独立的 HSI48 RC 振荡器可用。F4 上你可以直接选 HSI48 当 USB 时钟H5 上则必须通过 PLL 锁相环倍频出来。也就是说USB 的 48MHz 必须来自 PLL1、PLL2 或者 PLL3 的某个输出分频。CubeMX 的时钟配置页里你会看到一个USB Clock Mux或者类似的选项里面默认可能会选某个 PLL 的输出。很多新手在这个环节翻车是因为看了别人工程直接照抄但板载晶振频率不同PLL 分频系数完全不匹配导致 48MHz 压根没有生成。最直接的检查方式是打开 CubeMX 的Clock Configuration页面找到 USB 外设那一行看它的时钟频率是不是 48MHz。如果是红色或者显示 0说明 PLL 配置有问题。3.2 一个标准的 HSE 25MHz 晶振配置案例绝大多数开发板会用 25MHz 外部晶振HSE。下面这套 PLL1 参数是我在 H573 上常用的能同时跑出 240MHz 的系统主频和 48MHz 的 USB 时钟时钟源HSE 25MHzPLL1 M 5得到 5MHz 的 PLL 参考时钟PLL1 N 96VCO 输出 480MHzPLL1 P 2系统时钟 240MHzPLL1 Q 10USB 时钟 480 / 10 48MHz在 CubeMX 的 Clock Configuration 页面里把 PLL1 的源选成 HSEM 填 5N 填 96在 USB 时钟源下拉框里选 PLL1_Q页面会实时显示 USB 那一路变成了 48MHz而且字体是蓝色说明时钟树满足要求。3.3 没有 HSE 的板子HSI 方案能做但精度有风险如果你的板子省掉了外部晶振只能靠内部 HSI16 做 PLL 源数学上也能凑出 48MHzHSI1616MHz→ M2 → 8MHz → N48 → 384MHz → Q8 → 48MHz。但这里有个隐患USB 全速设备对时钟精度要求是 48MHz ±0.25%而内部 RC 振荡器的出厂精度一般在 ±1% 量级温度和电压变了还会漂。内部 RC 做串口、I2C 这类低速外设没问题但做 USB 枚举可能时好时坏有时能识别有时完全没反应。所以我的建议很直接如果你准备做 USB 设备板子上最好放一颗外部晶振或者至少确认 HSI16 在目标工作温度范围内满足精度。省一颗晶振的钱换来的可能是无休止的 USB 掉线排查不值。4. 硬件与引脚VDDUSB、PA11/PA12、VBUS Sense 三件套4.1 VDDUSB 没接通USB 收发器直接罢工这是最容易被忽略的硬件问题尤其是从 F103 转过来的人。STM32F1 的 USB 收发器供电和 MCU 主电源共用但 H573 的 USB 收发器有一个独立供电引脚 VDDUSB。VDDUSB 需要接 3.0~3.6V 的外部电源并且要加 100nF 去耦电容。如果这个引脚悬空或者供电电压不对USB 物理层的模拟电路就不会工作。表现就是固件看起来一切正常D 上拉也开启了但示波器量 D 电平就是拉不起来或者只有很微弱的电压。我之前见过一块板子为了做低功耗设计把 VDDUSB 单独接到一个 load switch 控制结果 load switch 默认关断USB 插上电脑完全没反应。所以调试 USB 之前先拿万用表量一下 VDDUSB 引脚对地电压是不是 3.3V。4.2 CubeMX 里配成了 High Speed但板上没有外部 PHYH573 的 USB 控制器名称是USB_OTG_HS和很多 F4 上的 USB_OTG_FS 不一样。这个控制器本身有高速能力但要在高速模式下工作芯片上必须外接一颗 ULPI PHY 芯片比如常见的 USB3300、USB3320通过 ULPI 并行接口连接。问题来了如果你在 CubeMX 里把 USB_OTG_HS 的 Mode 选成了High Speed而你的板子上根本没有外部 ULPI PHYUSB 当然不会工作。HAL 库会去试图初始化 ULPI 接口的 IO 和时序但你的硬件上压根没这个东西。H573VITxQ 的 LQFP100 封装上如果你用内部全速 PHY就应该用PA11USB_DM和 PA12USB_DP这两个引脚直连 USB 座子。CubeMX 配置里USB_OTG_HS 的 Speed 选项要选Full SpeedInternal Phy而不是 High Speed。这个坑隐蔽就隐蔽在很多教程和例程基于 F429 这类有独立 OTG_FS 的芯片F429 上选 OTG_FS 直接就是全速。H573 没有独立的 OTG_FS只有一个 OTG_HS你必须主动告诉它我要在全速模式下用内部 PHY否则它默认按高速外接 PHY 去初始化。4.3 VBUS sensing 开着但 PA9 没接 VBUS 信号这是另一个高频翻车点尤其在自供电Self-powered设备上。STM32 的 OTG 控制器在设备模式下可以配置 VBUS 检测功能通过专门的 VBUS 引脚一般是 PA9检测 USB 总线上是否有 5V 电源以此判断是否连接了主机。如果启用了 VBUS sensingHAL_PCD_Start() 之后控制器会等待 VBUS 有效才把 D 上拉。很多自供电设备的电路设计USB 座子只接了 D、D-、GNDVBUS 引脚根本没连到 MCU。这时候如果你忘了关 VBUS sensingUSB 控制器永远等不到主机接入这个事件后续所有枚举流程都不会启动。表现就是插上电脑设备管理器完全没反应D 也量不到上拉电压。解决办法有两种硬件上把 USB 座子的 VBUS 通过电阻分压后接到 PA9让 MCU 能检测到 5V软件上如果你的设计不需要 VBUS 检测就把 CubeMX 里 USB_DEVICE 中间件配置中的 VBUS sensing 选项禁用掉。生成代码后对应的 HAL 配置里hpcd.Init.VbusSensingEnable会是 0。从简化设计和降低故障率的角度自供电设备我一般直接关掉 VBUS sensing让 USB 设备不管插没插主机都认为已连接等着主机来枚举。4.4 别忘了 D / D- 的物理连接细节这块是排查完全无反应时顺手要查的。PA11 是 DMPA12 是 DP这两个脚如果接反了主机永远无法识别。有些 PCB 布局人员会把 DM/DP 网络标号搞混我在实际项目里真遇到过。另外FS 模式下有些设计会在 D / D- 上各串一个 22Ω 的电阻用来抑制振铃和过冲一般放在 MCU 引脚和 USB 座子之间。走线尽量等长、并行走、避免过孔这个在低速全速设备上要求不算苛刻但别太离谱就行。5. 中间件与 HAL 配置USB 不工作的高频故障点5.1 中间件选错了裸机 USB_Device 还是 USBX如果你在 CubeMX 里启用了 USB 外设还会面临中间件的选择。H573 可以搭配 ST 官方的 USB Device Library裸机也可以搭配 Azure RTOS ThreadX 生态里的 USBX。如果你没有用 RTOS就用裸机的USB_Device中间件。如果你用了 ThreadX 并且想用 USBX 的协议栈就选 USBX。两者代码结构和初始化方式完全不同选错的话项目根本编译不过或者即使编译过了初始化流程也很拧巴。大多数项目用裸机 USB_Device 中间件就够了CDC 虚拟串口、HID、MSC 这些类都是现成的。对于 H573 这种主频 240MHz、Flash 2MB 的芯片跑 USB 设备库的负载非常小不值得为 USB 单独引入 ThreadX 生态。5.2 端点与 FIFO 分配的坑H573 的 OTG_HS 控制器内部有一个共享的 FIFO RAM设备模式下要手动把这块 RAM 划分给接收 FIFO 和各个端点的发送 FIFO。CubeMX 生成的代码里一般会有一段类似下面的逻辑分配 FIFOHAL_PCDEx_SetRxFiFo(hpcd, 0x80); HAL_PCDEx_SetTxFiFo(hpcd, 0, 0x40); HAL_PCDEx_SetTxFiFo(hpcd, 1, 0x40);如果你用的是 CubeMX 默认生成的配置一般不会出问题。但有些同学自己改过描述符或者端点FIFO 分配没跟着改就可能出现枚举能过但一收发数据就死机或者枚举到一半 USB 控制器报错。举个实际例子CDC 虚拟串口通常会用到 3 个端点——EP0控制传输64 字节、一个批量 OUT 端点、一个批量 IN 端点。如果你把 EP0 的 TX FIFO 配得太小而主机发来的控制请求数据超过 FIFO 容量USB 控制器会忙于处理溢出导致 GET_DESCRIPTOR 回复超时主机直接判定为 Unknown Device。遇到这类问题最简单的做法是先把 FIFO 分配恢复成 CubeMX 默认值验证 USB 是否恢复正常再去折腾优化。5.3 中断路径IRQHandler 到底跑没跑USB 是中断驱动很强的外设枚举过程的每一个状态变化都是靠中断通知 CPU 的。如果中断处理路径断了USB 必然不工作。H573 的 USB 全局中断入口是USB_OTG_HS_IRQHandler它内部应该调用HAL_PCD_IRQHandler(hpcd)。在stm32h5xx_it.c里应该能看到这个函数。如果你项目里这部分代码缺失或者没启动 NVIC 中断USB 外设没有任何办法与 CPU 交互枚举自然无从谈起。排查技巧在HAL_PCD_IRQHandler入口打一个断点插上 USB看断点有没有触发。如果一次都没触发说明中断链路有问题——可能是 NVIC 没使能、中断优先级配置问题、或者 TrustZone 把中断划到了安全组但你运行在非安全状态。如果断点频繁触发说明 USB 控制器在正常工作问题可能在协议层。5.4 一个典型的 CDC 虚拟串口配置示例如果你正打算用 H573 做一个 USB 转串口设备下面是 CubeMX 里最基础的配置路径Connectivity → USB_OTG_HS勾选 Device_OnlySpeed 选 Full SpeedInternal PhyMiddleware and Software Packs → USB_DEVICEClass for FS IP 选 Communication Device ClassVirtual Port COMClock Configuration确认 USB 那一路显示 48MHz生成代码后main.c里调用MX_USB_DEVICE_Init()。这样生成出来的固件插上电脑后 Windows 应该识别为一个 COM 口。但注意如果你直接用了 CubeMX 生成的原生 CDC 代码它默认是回显模式——你从电脑发什么它回什么。实际项目里通常要改CDC_Receive_FS()回调函数把收到的数据转发到 UART 或者自己的业务逻辑。CDC 代码有个经典大坑CDC_Receive_FS()里处理完数据后必须重新调用一次CDC_Receive_FS()来接收下一包数据。很多新手忘了这个结果设备只能收一次数据之后就死了然后误以为是 USB 的问题其实只是接收缓冲区没有重新武装。6. 实测链路从 D 上拉量到枚举失败6.1 示波器量 D 是最快的心搏检查软件层面查了一圈都没问题的时候就该拿出示波器了。USB 排查的第一件事就是把探头点在 PA12D上然后插 USB。如果是全速设备空闲状态下 D 应该被拉到 3.0~3.3V 左右的高电平。量到这个高电平说明两件事一是 USB 设备硬件基本是活的二是设备的上拉已经启用主机应该能检测到设备接入。如果 D 一直是 0V那就是设备端压根没有主动报告主机自己存在。如果 D 有高电平但设备管理器依然没有动静把 D- 也挂上探头观察插入瞬间总线上的波形。主机检测到设备后会先拉低 D 大概 10ms 做一次复位后面开始发 SETUP 包。逻辑分析仪如果有 USB 解码功能可以直接抓 D/D- 两个通道的波形把枚举报文解析出来看。6.2 枚举流程里最容易出错的几个环节USB 枚举流程说白了就是主机和设备之间的几次对话。完整流程包括主机复位总线 → 设备默认地址 0 → 主机发 GET_DESCRIPTOR设备描述符→ 设备回复 18 字节设备描述符 → 主机分配地址 → 再次 GET_DESCRIPTOR 读取完整配置 → 设置配置 → 设备进入配置状态。在这个流程里最容易出问题的是设备描述符回复。只要设备描述符回复的数据格式不对、字节数不对、或者回复超时主机就直接放弃表现为 Unknown Device。设备描述符里有一个特别关键的字段是bMaxPacketSize0它表示端点 0 的最大包长。FS 模式下一般填 64。如果这个值和代码里实际配置的端点 0 FIFO 大小不一致主机和设备对包长的理解不一样枚举也会失败。6.3 抓包工具怎么选UsbTreeView、Bus Hound 和 WiresharkWindows 上排查 USB 枚举问题我常用的工具是这几个UsbTreeViewUSB Device Tree Viewer能直观看到主机识别到的 USB 设备树包括设备状态、描述符内容、当前速度特别适合判断设备是否进入了配置状态Bus Hound可以抓取总线层面的传输包能看到主机发出来的 SETUP 包和设备回的数据或者 STALL 握手定位协议层错误非常精准Wireshark在 Linux 下配合 usbmon 内核模块可以抓 USB 包Windows 下配合 USBPcap 也能用界面熟悉就是配置稍微麻烦一点。我的排错习惯是先用 UsbTreeView 看设备有没有出现在总线上、状态是什么。如果设备树里压根没有回到硬件和 D 上拉如果有设备但状态异常再用 Bus Hound 抓包看枚举过程停在哪一步。6.4 HAL 错误码到底告诉你什么H5 的 USB HAL 库在发生异常时会把错误类型记到hpcd.ErrorCode字段里比如HAL_PCD_ERROR_UNDEFINED_EP、HAL_PCD_ERROR_NAK、HAL_PCD_ERROR_CRC。但这些错误码很多时候不是根因而是结果——你看到的是 USB 传输层报错但真正的原因可能是时钟不稳、供电劣化、或者 FIFO 不够。我建议在HAL_PCD_IRQHandler后面加一个错误打印或者断点
返回列表