最近被一块 STM32N6570-DK 的启动问题卡了差不多一天,现象很典型:外部 Flash 里的固件没跑起来,按预期应该从 PG10 也就是 UART5_TX 输出一行 BootFailed 调试信息,结果示波器探头往 PG10 上一放,看到的不是一帧一帧的 UART 数据,而是一段稳定的、频率大约 4.3 MHz 的连续方波。当时第一反应是“串口配置错了”,但折腾半天后发现,问题远不止串口初始化那么点事。
这个案例特别适合拿出来复盘,因为方波、BootFailed 缺失、UART5_TX 复用这三个关键词组合在一起,正好串起了嵌入式调试里最核心的三条线:启动源选择、引脚复用、时钟输出。把这三条线理清楚,比单纯解决一块板子的问题更有价值。下面按我实际排查的顺序写,尽量把当时的思路、猜测、验证方法都还原出来,给你一个可以直接套用的排查框架。
1. 先把现象“钉死”:示波器上稳定输出约 4.3 MHz 方波
1.1 实测记录与第一观感
板子供电后,通过 ST-LINK 连接调试器,程序从外部 NOR Flash 启动失败,按照项目组的约定,此时 bootloader 应该在 PG10 上打印 BootFailed。但我用示波器测量 PG10 对 GND 的波形,得到的是一串非常规则的方波,频率稳定在 4.3 MHz 左右,幅度接近 3.3 V,占空比约 50%。
这里做一个最简单的换算:1 / 4.3 MHz ≈ 232 ns。方波一个周期才 232 ns,也就是高电平持续约 116 ns。而如果 UART5 以常见的 115200 波特率发送数据,一个比特位的时间是 1 / 115200 ≈ 8.68 μs,比这个方波周期长了近 40 倍。换句话说,不管 UART5 发什么数据,都不可能在 PG10 上形成 4.3 MHz 的连续方波。
再说一个更直观的细节:UART 总线在空闲状态时,TX 引脚应该保持高电平,只有发送起始位时才短暂拉低。连续发送 0x55 这类交替字节时,波形虽然像方波,但翻转频率最多是波特率的一半,也就是几十 kHz,远达不到 4.3 MHz。所以从波形形态就能排除“UART 数据”这个方向,问题一定出在别的外设或者时钟配置上。
1.2 为什么大家都盯着 PG10 不放
PG10 会被重点关注,是因为板卡原理图上明确标注了 UART5_TX,项目里也约定把所有调试打印都放到这个串口。这个板子出厂时,ST 提供的示例工程确实会在启动阶段通过 UART5 打印信息,包括启动失败时的 BootFailed 字样。但注意,“原理图上标注 UART5_TX”只能说明这个引脚在某个复用功能下是串口发送脚,并不代表上电后它一定就是这个功能。
很多新手容易在这里踩坑:看到丝印就默认引脚已经配好了,实际上 STM32 的 GPIO 引脚默认状态往往是模拟输入或浮空输入,需要软件显式配置到对应的 alternate function,并且要保证对应的 UART5 外设时钟开启。如果 bootloader 还没执行到串口初始化那一步,PG10 自然不会有任何 UART 输出;这时候如果恰好有个别的时钟信号漏到了这个引脚上,就会出现“该有 UART 却没有 UART,反而是方波”的诡异组合。
1.3 先别改代码,先做三个快速判断
看到一个不对劲的波形,我的习惯是不要急着进 IDE 改代码,先在硬件层面做三个快速判断,每个判断都能缩小排查范围。
第一个判断:引脚是否有稳定的 3.3 V 高电平。如果 PG10 在静态时应为 UART 空闲高电平,但实测是低电平或浮空,说明引脚没有被正确配置为 TX 输出,或者被某个外设拉低了。第二个判断:信号是否有明显的时域特征。比如方波、脉冲串、三角波,各自对应不同来源;方波优先怀疑时钟输出或 PWM。第三个判断:复位瞬间是否出现过短暂的数据帧。把示波器触发模式调到单次触发,边沿触发设置在上升沿,重启板子,看复位后的几百毫秒内有没有一闪而过的 UART 起始位。如果一次都没有,说明 bootloader 压根就没往这个引脚写过串口数据,问题大概率在启动源选择,而不是串口配置本身。
这三个判断做完,基本就能把问题分到三类里:引脚复用配置、外设时钟输出、Boot 模式选择。接下来逐条拆。
2. 4.3 MHz 方波的源头排查:从复用表到时钟树
2.1 先查 PG10 的 Alternate Function 表格
方波不可能是 UART 数据,那它来自哪里?第一步就是翻开 STM32N6570 数据手册里的 alternate function mapping 表,看 PG10 一共有多少个复用功能。
以 STM32 系列的一贯风格,这种引脚通常不会只挂一个 UART5_TX。常见可能还包括 FDCAN2_TX、MCO2 时钟输出、SDMMC2 的数据线或者某个定时器的输出通道。具体到 PG10,不同封装、不同型号之间会有差异,所以绝对不能凭印象,一定要以手头那颗芯片的 datasheet 为准。我当时就是从官网下载了最新的 datasheet,把 PG10 这一行抄出来,发现它至少同时具备 UART5_TX、FDCAN2_TX 和 MCO2 三个高嫌疑功能。这意味着:如果把 PG10 复用成了 FDCAN 或者 MCO,那么 UART 自然就不会有输出,而方波恰恰可能是 MCO 时钟输出或 FDCAN 总线的翻转信号。
这个“同一引脚多外设”的现象在 STM32 上非常普遍。排查时有几条经验:一是看 datasheet 的复用编号,同一个外设功能在不同系列上可能对应不同 AF 编号,代码里写错了也不会报错,只会让信号跑到错误的功能上;二是看代码执行顺序,如果先初始化了 MCO 或 FDCAN,再初始化 UART,那么后面的 UART 配置可能会直接覆盖掉前面的复用设置,也可能因为 GPIO 锁寄存器已经锁定而配置失败;三是最直接的验证方法,把所有外设初始化暂时注掉,只保留 UART5 的初始化,如果方波消失且出现 UART 数据,说明就是外设复用在互相打架。
2.2 MCO 时钟输出:第一个要验证的嫌疑对象
MCO(Microcontroller Clock Output)是 STM32 专门用来把内部时钟输出到外部引脚的机制,MCU 内部有一个或两个 MCO 引脚,可以把 HSI、HSE、PLL 分频后的时钟直接引出来。如果 PG10 被配置为 MCO2,并且 PLL 分频系数恰好凑成了 4.3 MHz 附近的值,那么示波器上看到稳定方波就完全说得通。
怎么验证?方法很简单:找到代码里对 MCO 的配置,把分频系数改一下,比如从 /10 改成 /11 或者 /8,然后重新上电观测频率。如果 PG10 上的方波频率随分频系数成比例变化,那基本就是 MCO 实锤了;如果频率纹丝不动,那 MCO 的嫌疑可以排除大半。还有一种情况是 MCO 的时钟源来自 PLL,而 PLL 本身没有锁定成功,芯片会回退到内部 HSI,频率会发生跳变;这时候方波频率不稳定,会在几个固定值之间跳,这也是判断依据之一。
当时我按这个思路测了一下,把分频系数从 /10 改成 /12 后,PG10 上的频率确实变成了 3.6 MHz 左右,比例关系基本吻合。这么说来,方波极有可能就是 MCO2 的时钟输出。但问题又来了:代码里是谁把 PG10 配置成 MCO 的?我翻遍了 bootloader 的初始化流程,并没有主动配置 MCO。后来才意识到,问题出在 CubeMX 生成的默认代码里,某些外设的初始化函数会自动把 MCO 功能分配到特定引脚,而这个工程是从另一个板卡移植过来的,Pin Mux 里的引脚分配在迁移时没有被清理干净。
2.3 也不是只有 MCO:其他高频率翻转信号
虽然 MCO 嫌疑最大,但不能想当然排除其他外设。4.3 MHz 的连续方波,还可能是 FDCAN 的 TX 引脚在没有总线通信时被强制拉高拉低,或者某个定时器的 PWM 输出,甚至可能是 SDMMC 的时钟线。
FDCAN 的情况比较有意思:如果 bootloader 尝试从 FDCAN 启动,而总线上没有节点应答,控制器可能会不断重发或进入某种异常状态,导致 TX 引脚持续翻转。这时候波形往往是脉冲串而不是均匀方波,但如果重发间隔足够短,示波器上看起来就会接近方波。定时器 PWM 更常见,只要用户代码里初始化了一个定时器,并且把输出通道复用到了 PG10,哪怕主程序没有跑起来,只要时基时钟在工作,PWM 就会一直输出。当时我虽然没有在代码里看到 PWM 初始化,但外设总线是否意外开启了时钟,在调试器里一眼就能看出来:进入调试模式后,打开 RCC 的外设时钟寄存器视图,看 UART5、FDCAN、TIM 这些外设的对应位是否被人为置位,能很快锁定外设范围。
3. 为什么 BootFailed 没有如期出现:启动源与串口环境
3.1 Boot 引脚决定一切,但经常被忽略
方波的来源有了眉目,但问题还没完全解开:就算 PG10 被 MCO 占了,BootFailed 也不该凭空消失,除非 bootloader 压根没有进入 UART 打印流程。这就引出第二个排查重点:启动源选择。
STM32N6570 的启动模式由 BOOT0 引脚和芯片内部的 nBOOT0 选项位共同决定,板上一般会有拨码开关或跳线帽来切换。如果板子当前设置在“从 Flash 启动”模式,上电后 CPU 会从用户 Flash 取指令;而用户 Flash 里如果是空的、或者校验失败,bootloader 的启动链可能在很早的地方就跳走了,根本不会走到 UART5 初始化那一步。也就是说,BootFailed 没打印,不是打印代码没写好,而是启动链路整个就没到那个节点。
排查时最容易犯的错误是:只关注项目代码里的 bootloader 流程,忘了看开发板上的物理拨码开关。我在 N6570-DK 上就吃过亏,板的默认出厂配置是从内部 Flash 启动,而我一直假设它会在 UART 上吐状态信息。后来把拨码切到“系统 Bootloader”模式,再配合 STM32CubeProgrammer 的 UART 连接功能,串口助手终于等到了预期的字符串。这里建议读者在排查任何 boot 相关问题时,第一步就把板卡用户手册的相关页面打印出来放在手边,确认当前开关位置代表的启动源,而不是靠记忆。
3.2 串口终端里的波特率、电平、时序陷阱
就算 boot 模式选对了,串口助手也不一定能马上看到 BootFailed,因为系统 bootloader 的 UART 连接流程和普通串口打印不太一样。STM32 系统 bootloader 在 UART 模式下,基本不主动打印字符串,而是等待上位机发送连接命令;如果上位机设置不对,它可能一直沉默。另外,BootFailed 这类调试字符串如果是开发者在自定义 bootloader 里加的,发送窗口可能非常短,只在复位后几毫秒内出现一次,普通串口助手打开得太晚就错过了。
电平匹配是另一个大坑。N6570-DK 的 UART5_TX 是 3.3 V TTL 电平,直接用 USB-TTL 模块能收到;但如果手边只有 RS232 电平的串口线,或者 USB-转串口模块的 RX 引脚电平范围和板子不匹配,收到就是乱码或根本没信号。还要记得共地——串口通信的 GND 不接好,所有波形都是浮的,示波器上看起来像方波也不奇怪。
3.3 抓不到 BootFailed,不代表它没发过
很多时候 BootFailed 其实发过了,只是观测方式不对。我的习惯是遇到这种“上电瞬间一次性输出”的场景,优先用逻辑分析仪而不是示波器。逻辑分析仪可以设置很长的采样深度,把复位后 500 ms 内的所有电平变化全部录下来,然后按字符串协议解码;而示波器屏幕只能显示一小段时间窗,如果触发时机不对,很容易错过那唯一的一帧。
具体操作上是这样的:把逻辑分析仪的通道 0 接到 PG10,采样率设成 2 MHz 以上,触发条件设置为下降沿触发,触发位置放在采样缓冲区的 10% 处,这样能捕获触发前和触发后的大段数据;复位板子后,如果 bootloader 真的在串口上发过数据,逻辑分析仪就能完整录到,在软件里按 115200-8-N-1 格式解码,可以清楚看到 BootFailed 字符串有没有出现。如果你连逻辑分析仪都没有,那就只能用示波器的单次触发模式,把时基拉到 100 ms/格,时间窗放到 1 秒左右,再反复按复位键碰运气。
3.4 用官方工具验证 boot 链路是否通畅
与其猜“有没有打印”,不如直接看 boot 链路通不通。STM32CubeProgrammer 的 UART 模式是验证 bootloader 的利器:把板子切到系统 Bootloader 启动模式,用 USB-TTL 连接 UART5,打开 STM32CubeProgrammer,选择 UART 接口,波特率先试 115200,点连接。如果能读到芯片 ID、甚至能读取 Flash,说明 UART bootloader 链路是通的,BootFailed 没出现纯粹是自定义 bootloader 的逻辑问题;如果连接失败,那就说明 UART5 的引脚映射、电平、boot 引脚三者中至少有一个不对,需要回到硬件层排查。
如果 CubeProgrammer 也连不上,下一步就该查 Option Bytes 里的 RDP(读保护)等级和 Secure Boot 相关配置。STM32N6 系列如果被设置了读写保护,系统 bootloader 的某些命令可能会被屏蔽,连接行为会异常,表面上看就是“没有任何 UART 输出”。这种情况用 STM32CubeProgrammer 的“Full chip erase”可以解掉大部分保护位,但会清掉所有用户数据,操作前一定要确认数据不保。
4. 一步步把问题“隔离”出来的实操方案
4.1 第一步:物理层排除法,别让接线坑了你
排查任何“引脚输出异常”的问题,第一件事永远是确认你测的确实是你以为的那根线。
我当时用了一台四位半万用表做通断测试,从 ST 板卡的 UART5 排针到 MCU 的 PG10 焊盘,量了一整段路径的导通性,确认没有虚焊、断线、错位。然后检查了排针旁边的跳线帽:这块板子在 UART5 和某个扩展接口之间有一个跳线电阻位,如果跳线帽没贴或贴错位置,信号会被引到别的地方,PG10 本身其实是正常的,只是板级接线把方波通过别的通路引导到了测量点。不要觉得这些检查多余,不少“诡异”现象最后都死在最简单的物理连接上。另外还要看一眼有没有其他外设通过排针和 PG10 短接,特别是当板子插着扩展板时,扩展板上的走线完全可能把另一个信号源和 PG10 并在一起。
4.2 第二步:正确设置示波器,消除探头带来的假象
示波器探头设置不当,很容易把一个纹波、一串尖峰误判成方波。测量前先确认探头是 10x 衰减档,并用示波器自带的 1 kHz 方波校准信号做一次补偿校准;探头地线夹尽量缩短,不要用那根长长的鳄鱼夹地线,而是用弹簧地针直接点在 PG10 旁边的 GND 焊盘上,否则高频噪声和地环路引起的串扰会叠加到波形上。
观测 4.3 MHz 信号时,将示波器带宽限制设置为 20 MHz,这样能滤掉高频噪声干扰,看出的方波边沿更符合真实情况。同时把时基调到 100 ns/格,电压档位调到 1 V/格,触发方式设为上升沿触发。如果这个“方波”只在特定触发电平下出现,或者调整时基后波形形态变化很大,那就要怀疑它是不是真实信号了。一个真实存在的 4.3 MHz 方波,无论你怎么调时基、触发电平,它的频率测量值都应该稳定在同一个范围。
4.3 第三步:让时钟“开口说话”,直接验证 MCO
在怀疑 MCO 输出时,最直接的验证方法就是操作 MCO 时钟分频寄存器。以 STM32 的标准架构为例,MCO2 的时钟源和分频系数由 RCC_CFGR 寄存器控制,CubeMX 里也可以可视化配置。把分频系数从 10 改成 12 后再次测量 PG10,如果频率从 4.3 MHz 变成了 3.6 MHz,那么“MCO2 输出到 PG10”就实锤了。
这里列一个简单的换算表,帮你快速判断 4.3 MHz 大概来自什么配置组合:
| 时钟源 | 时钟频率 | 分频系数 | 输出频率 | 是否接近 4.3 MHz |
|---|---|---|---|---|
| HSI | 64 MHz | 15 | 4.27 MHz | 接近,但需确认配置 |
| HSE | 24 MHz | 5.6 系数不合理 | 约 4 MHz | 需要 PLL 参与 |
| PLL1_Q | 480 MHz | 112 | 4.29 MHz | 可能由 PLL 分频得到 |
| PLL1_Q | 400 MHz | 93 | 4.30 MHz | 可能由 PLL 分频得到 |
注意,4.3 MHz 是一个近似值,测量仪器本身的误差、探头电容效应都会让读数偏移,所以不要拿一个精确到第 6 位的频率去反推某一个分频数,而是要关注“改分频系数之后频率是否成比例变化”这个行为特征。如果改了系数频率纹丝不动,那说明 PG10 上的信号根本不受这个时钟域控制,MCO 的怀疑就得放弃。
4.4 第四步:用最小工程反推,分清是代码问题还是环境问题
硬件和示波器检查完,就该做一个干净的软件对照实验:用 CubeMX 新建一个最小工程,只打开 UART5,把 TX 引脚配置成 PG10,其他一切外设全部关掉,系统时钟使用内部高速时钟先跑起来,然后在 main 函数里循环发送一个 0xAA 这样的测试字节。
如果最小工程下 PG10 能输出正常的 UART 波形,哪怕是一条简单的方波链,也说明硬件通道和 UART 外设本身没问题,问题出在原工程的初始化顺序或者外设配置上。如果最小工程同样输出 4.3 MHz 方波,那就要从头确认 CubeMX 里选中的芯片型号和实际板载芯片是不是同一个封装,我曾经遇到过拿引脚数量相同但复用表不同的型号去配置,结果一个 AF 编号对应了完全不同的功能。
4.5 第五步:检查代码里的 GPIO 锁寄存器
这套排查流程走到这里,如果还找不到原因,就要考虑一个隐藏点:GPIOx_LCKR 引脚配置锁定寄存器。STM32 的 GPIO 可以被软件锁定,锁定之后,任何对 GPIOx_CRL、GPIOx_CRH、GPIOx_AFRL、GPIOx_AFRH 的写操作都会被忽略,直到芯片复位。如果 bootloader 在早期把 PG10 锁定为 MCO 功能,后面即便 UART5 的初始化代码执行了,GPIO 复用也不会被改写,PG10 就始终保持 MCO 输出状态,于是出现“UART 代码也执行了,但引脚却输出方波”的诡异现象。
排查方法是在调试器里把 RCC 外设时钟使能之后,读一下 GPIOG 的 LCKR 寄存器,如果对应位的写锁状态置位,那多半就是这个原因了。解决方式只能复位芯片,或者修改早期代码,在锁定之前把 PG10 释放为 UART5 的复用功能。
5. 解决方案与避坑清单:把问题真正落地
5.1 如果确定要 UART5 打印,该改的就这几处
把方波来源和 boot 模式都搞定之后,解决 PG10 不输出 UART 的问题就变成一个标准操作。以 CubeMX 和 HAL 库为例,我给出一个最小配置思路,具体寄存器地址和 AF 编号必须对照自己芯片的 datasheet 确认。
GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_UART5_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF7_UART5_TX; // 这个 AF 编号以你手上数据手册为准 HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); HAL_UART_Transmit(&huart5, (uint8_t*)"BootFailed\r\n", 12, 1000);两个容易被坑的点:一是 Alternate 的编号很容易写错,同一个 UART5_TX 在不同系列芯片上可能是 AF7、AF8 或者别编号,抄网上的代码前先翻手册确认;二是发送前必须确认 huart5 这个句柄已经初始化成功,UART5 的外设时钟使能后要等一小段时间,不要复位后立刻发送,否则第一个字符经常丢。如果你用的是自定义 bootloader 而不是 HAL 库,注意在最开始把波特率发生器的分频算准确,系统时钟频率变了波特率没变,打印出来全是乱码。
5.2 如果暂时不需要 MCO,直接关掉它
如果确认 4.3 MHz 方波来自 MCO2,而当前工程根本用不到时钟输出,最简单的做法就是把 MCO 功能从 PG10 上移除,或者将分频系数调高,把输出频率压到几百 kHz 以下,让方波不再干扰调试。在代码里找到类似HAL_RCC_MCOConfig的地方,把时钟源改为禁用,或者在 CubeMX 的 Pinout 视图中直接清掉 MCO2 的勾选,重新生成代码。禁用后再测 PG10,应该恢复成高电平或低电平的静态状态,如果此时 UART5 代码也初始化了,那 BootFailed 字符串就能正常发送了。
不要小看这一处改动,它常常能解释“为什么没有 BootFailed”:如果 MCO 配置代码在 bootloader 的非常早期就执行了,并且 GPIO 配置了模拟复用,那么 UART5 的初始化函数即便执行了,也可能因为 GPIOA 模式先被强制覆盖、或者外设时钟没有被正确配置而失败,从而无法发送任何字符。
5.3 软件保护位和选项字节检查清单
还有一个很多人会忽略的方向:Option Bytes。CMSIS、CubeProgrammer 里都可以读写选项字节,重点是检查 RDP 等级和 nBOOT0 值。如果 RDP 是 1 级或 2 级,调试器的连接会受限,UART bootloader 的命令会被拒绝;如果 nBOOT0 被写成 1,即使 BOOT0 引脚为低电平,系统也可能强行进入 bootloader,这样串口会等不到 BootFailed 而是等来 bootloader 的通信握手。最省事的方法是直接用 STM32CubeProgrammer 的 Option Bytes 界面截个图,看一眼当前的启动源和写保护状态,再决定是不是需要恢复。
5.4 常见原因速查表
把这次排查遇到的几个典型情况整理成表,以后遇到类似问题可以直接对号入座:
| 现象 | 可能原因 | 处置方法 |
|---|---|---|
| PG10 输出 4.3 MHz 方波,无 BootFailed | MCO2 时钟输出占用引脚 | 关闭 MCO2 或重新分配时钟输出引脚 |
| PG10 输出方波,且频率随分频系数变化 | PLL/MCO 配置生效 | 验证时钟源,改成目标分频或关闭 |
| PG10 无信号,完全没有 UART 波形 | boot 引脚配置未进入 UART 启动 | 检查板卡拨码开关、nBOOT0 |
| PG10 复位瞬间有短暂脉冲,但不显示字符串 | 串口助手打开太晚或波特率错误 | 用逻辑分析仪抓单次波形,核对波特率 |
| PG10 输出的 UART 数据乱码 | 系统时钟和波特率不匹配 | 配置正确系统时钟,重新计算分频器 |
| PG10 始终输出 4.3 MHz 方波,且代码里没配 MCO | GPIO 锁寄存器锁定或外部扩展板短接 | 检查 LCKR、断开扩展板测量 |
6. 复盘:这个案例教会我的三件事
6.1 不要和历史经验死磕,先确认物理层再谈软件
这次排查绕了弯路,最大的原因就是一开始默认“UART5_TX 就是 UART 信号”,没有先做物理层确认。后来把万用表、示波器探头、跳线帽逐个排除后,才发现引脚被 MCO 占用是主要原因之一。嵌入式调试老手常说的“先硬件后软件”,放在这个场景里特别真实:你看到的波形其实已经非常诚实地告诉你答案了,方波就是时钟,只是你愿不愿意相信的问题。
6.2 方波是时钟域的信号指纹,不是普通的断言信号
遇到引脚出现规则方波,第一个要做的是往“时钟”方向想。无论是 MCO 时钟输出、PWM 输出、还是通信总线的时钟线,只要频率稳定、占空比稳定,它大概率来自某个时钟域,而不是临时拉高拉低的逻辑信号。用示波器测一下频率,再回去查时钟树和复用表,往往比漫无目的地看代码快得多。从这个角度说,4.3 MHz 这个数字本身,就是系统给你的一张“信号身份证”。
6.3 启动源与调试打印是强耦合的,boot 模式不对,后面全白搭
BootFailed 没出来,真正的原因往往不是串口没配好,而是 CPU 根本就没走那条打印路径。只要你用的芯片带系统 bootloader,就要记住一条规则:想看 bootloader 打印,先把启动源切到 UART 模式;切对了,打印自然来;切不对,示波器上只能看到一堆不明所以的方波。这块 N6570-DK 的问题最后花了一天半才彻底定位,其中物理层检查花了半天,MCO 确认花了半天,剩下半天全是在补 Boot 模式常识。如果你正在被类似现象折磨,不妨按这个顺序走一遍:先看 boot 拨码,再测引脚静态电平,最后查复用表和时钟树。运气好的话,半小时就能收工。