在产品开发中使用 STM32G070 时,一直无法通过串口进行 ICP (In-Circuit Programming) 升级。常规操作是,需要在上电前将 BOOT0 引脚拉高才能进入 ISP (系统编程) 模式。在我们的电路板上,将 MCU 的 USART1 连接到一颗 CH340N USB 转串口芯片上。

这里存在一个问题:BOOT0 引脚与 SWD 接口的 SWCLK 是复用的。为了实现在设备未上电时插入 USB 即进入 ICP 模式,上电后插入 USB 则正常进行串口通信的功能,我们设计了一个电路,利用两个电阻 (50K 和 100K) 对 USB 的 VBUS (5V) 进行分压,在插入 USB 时在 BOOT0 引脚上产生约 3.3V 的高电平。
理想很丰满,现实却很骨感。如此配置后,只有当 MCU 是全新的空片时,才能成功进入 ICP 模式。一旦程序被烧录进去,就无法再通过这种方式进入 ICP 了。
问题的根源:Option Bytes 配置
翻阅 STM32G070 的用户手册后,我发现了关键所在。要可靠地通过 BOOT0 引脚进入系统存储器启动模式,不仅需要硬件拉高引脚,还需要配置芯片内部的 Boot 配置选项。
查阅手册中的 Boot 模式表格,要进入系统存储器 (System memory) 进行串口 ISP,需要满足特定的条件组合。

从上表可以看出,当 BOOT0 引脚为 1 (高电平) 时,具体启动哪个区域还受到内部选项字节 nBOOT1 位和 nBOOT_SEL 位的控制。要让芯片在 BOOT0=1 时无条件进入系统存储器,至少需要将 nBOOT1 这个比特位设置为 1。
这些选项字节存储在芯片内部一块特定的存储区域。其组织结构如下:

地址 0x1FFF7800 处的双字 (Double Word) 就包含了 RDP (读保护)、nBOOT0、nBOOT1 等关键配置位。我们需要修改的就是这里的值。
解决方案一:修改 Option Bytes
修改方法并不复杂,使用 J-Flash 工具即可完成。
-
连接与读取:使用 J-Flash 新建一个工程,选择目标 MCU 为 STM32G070,通过 SWD 接口连接成功后。在 Target 菜单下选择 Manual Programming -> Read back -> Range...。

-
定位数据:在弹出的对话框中输入选项字节的起始地址 0x1FFF7800,点击 OK。工具会读取并显示该地址的内容。对于一颗新片,读出的数据通常是 AA E1 FF DF。

-
计算与修改:我们需要将最后一个字节 DF 修改为 DE。DF 的二进制是 1101 1111,DE 是 1101 1110。这个变化就是将 nBOOT1 位(从 LSB 即 bit0 开始数的 bit1)从 0 改成了 1。如果你对 C/C++ 中的位操作熟悉,可以很容易理解这一步。
-
烧写与验证:在 J-Flash 的数据窗口手动将 DF 改为 DE,然后选择 Target -> Manual Programming -> Program 进行烧写。

烧写完成后,再次执行 Read back 操作,确认数据已成功更改为 AA E1 FF DE。
完成以上操作后,芯片理论上应该能响应 BOOT0 引脚进入 ICP 模式了。实际测试中,使用 FlyMcu 工具确实可以偶尔烧写 HEX 文件,但极不稳定,成功率很低。
深入排查:电源倒灌问题
为什么还是不稳定?经过一番分析,我将怀疑的目光投向了硬件电路,特别是 UART 通信引脚上的电源倒灌问题。
在我们的设计中,CH340N 和 STM32G070 是分别独立供电的。CH340N 由 USB VBUS 供电,而 MCU 由板载的 3.3V LDO 供电。当设备未上电(即 MCU 的 3.3V 电源断开)时,如果插入 USB,CH340N 的 TXD 引脚会输出一个高电平(通常为 VCC 电压,约 3.3V)。这个高电平会通过直连的导线“倒灌”进 MCU 的 RXD 引脚,并通过 MCU 内部 I/O 的保护二极管给 MCU 的 VDD 网络进行微弱的供电。
这种微弱的供电可能会扰乱 MCU 内部的上电复位时序,导致其无法正确识别 BOOT0 引脚的状态,从而进入错误的启动模式。
解决方案二:改造 UART 电路
为了解决倒灌问题,必须对 网络/系统 通信中常见的电平匹配电路进行改造。我们需要一个“双向防灌电流”的电路。参考 CH340 芯片手册和应用笔记,理想的改造方案如下:
-
TX 路径 (CH340 -> MCU):在 CH340 的 TXD 引脚串联一个肖特基二极管(如 BAS70),方向为阴极朝向 MCU。这样,当 CH340 的 TXD 输出高电平时,如果 MCU 未供电,其 RXD 引脚为悬空或低电平,二极管因反偏而截止,阻止了电流灌入 MCU。如果 MCU 已供电,其 RXD 引脚通过内部或外部上拉为高电平,二极管正偏,信号可以正常传递高电平;当 TXD 输出低电平时,会将 MCU 的 RXD 引脚钳位到低电平。
-
RX 路径 (MCU -> CH340):在 MCU 的 TXD 引脚和 CH340 的 RXD 引脚之间,加入一个低阈值电压 (Vth) 的 N-MOSFET(如 2SK3018)。MCU 的 TXD 连接 MOSFET 的栅极 (G),源极 (S) 接地,漏极 (D) 连接 CH340 的 RXD 并通过电阻上拉到 3.3V。当 MCU 未供电时,其 TXD 为低,MOSFET 关闭,CH340 的 RXD 被上拉为高,不会产生灌电流。当 MCU 已供电并输出高电平时,MOSFET 导通,将 CH340 的 RXD 拉低。


需要注意的是,MOSFET 内部存在一个体二极管(寄生二极管)。在上述 RX 路径的电路中,这个体二极管的方向(源极到漏极)并不能防止 MCU 对未供电的 CH340 灌电流(因为电流方向相反)。不过在这种应用下,主要矛盾是防止 USB 设备对未上电的主设备灌电,所以这个电路是有效的。
最终验证与工具选择
按照上述方案修改硬件电路并重新配置 Option Bytes 后,进入 ICP 模式的稳定性问题得到了彻底解决,每次都能可靠触发。
然而,在最后一步烧写时又遇到了新问题:使用 FlyMcu 工具,虽然界面显示连接成功、擦除成功、编程成功、校验成功,整个过程毫无报错,但程序实际上并没有被真正写入芯片。
最终,更换为 ST 官方的 STM32CubeProgrammer 工具的最新版本后,所有问题迎刃而解,可以稳定、可靠地通过串口完成 STM32G070 的固件升级。
这段经历告诉我们,嵌入式开发中,软件配置和硬件设计必须紧密结合。一个简单的串口升级功能,背后可能涉及到 Boot 配置、电源时序、接口电气特性等多个层面的知识。希望我的这些“踩坑”经验,能为你在 云栈社区 的嵌入式开发之路提供一些参考。