如何把 STM32 固件变成“双击就烧录”的安装包?🔥
你有没有遇到过这样的场景:
客户打来电话:“你们上次给的固件怎么更新啊?我电脑上弹窗说 ST-LINK_CLI.exe 找不到……”
技术支持小李叹了口气,第17次打开远程桌面,一步步指导对方点开文件夹、运行命令行、检查连接状态。
又或者,在产线上,新来的操作工误把旧版本固件刷进了设备,导致功能异常,返修成本蹭蹭上涨……
说实话,这事儿真不能怪用户。让非技术人员去理解 .hex 文件、SWD 接口、Flash 起始地址这些概念,就像让普通人自己拆发动机换机油一样离谱 😅。
那怎么办?
答案是:别让他们懂,直接做成一个“双击就能升级”的 .exe 安装包。
没错,就像你安装微信、QQ 那样,双击运行 → 点“下一步” → 自动完成烧录 → 提示成功。整个过程不需要打开任何 IDE,也不用记命令行参数。
听起来像魔法?其实背后的技术链非常清晰,而且完全可复制。今天我就带你从零开始,手把手构建这样一个“傻瓜式固件更新工具”,让你的客户、产线工人甚至老板都能轻松完成 STM32 固件升级 ✅。
一、我们到底在做什么?目标拆解 🎯
先明确一点:我们要做的不是一个简单的压缩包(比如 .zip ),而是一个真正的 Windows 可执行安装程序 ( .exe ),它具备以下能力:
- 包含最新的固件文件(
.bin) - 内置烧录工具(无需用户安装 STM32CubeProgrammer)
- 自动安装 ST-LINK 驱动(防止“找不到设备”)
- 提供图形界面和进度反馈
- 支持静默模式(用于批量生产)
- 出错时有提示,并能生成日志
最终效果就是——用户拿到这个 .exe 文件,双击运行,按几下鼠标,设备就自动更新好了。
是不是很爽?😎
接下来我们就一步步实现它。
二、第一步:搞定固件输出 —— .bin 文件才是王道 💾
你在 Keil 或 STM32CubeIDE 里编译完工程后,默认生成的是 .elf 或 .axf 文件。这些文件虽然包含了调试信息,但不适合直接用于打包分发。
我们需要的是 纯二进制镜像文件 —— 也就是 .bin 。
为什么选 .bin 而不是 .hex ?
| 格式 | 特点 | 是否推荐 |
|---|---|---|
.hex | ASCII 文本格式,带地址标签,适合人工查看 | ❌ 不适合自动化 |
.bin | 原始二进制流,体积小,加载快 | ✅ 强烈推荐 |
.bin 文件就像一张“裸片照片”,直接对应 Flash 存储内容。只要指定正确的起始地址(通常是 0x08000000 ),就可以准确写入芯片。
如何生成 .bin 文件?
如果你用的是 GCC 工具链 (如 STM32CubeIDE):
arm-none-eabi-objcopy -O binary your_project.elf firmware.bin
如果你用的是 Keil MDK ,可以在“Options for Target” → “User”标签页中添加:
fromelf --bin --output=firmware.bin !L
勾选“After Build/Rebuild”。
⚠️ 小贴士:一定要在 Release 模式下编译!Debug 模式会包含大量调试符号,导致文件变大且不稳定。
关键注意事项 ✅
- 确保链接脚本(
.ld或 scatter file)正确设置了 Flash 起始地址。 -
.bin文件没有元数据,所以烧录时必须手动指定地址(后面会讲)。 - 使用
xxd或hexdump查看前几个字节,确认是否为有效向量表(第一个字通常是栈顶地址,第二个是复位向量)。
xxd -l 16 firmware.bin
# 输出示例:
# 00000000: 20001000 08000181 ...
看到 0x2000... 开头(SRAM 地址)和 0x0800... (Flash 复位地址),基本就没问题了。
三、第二步:让烧录自动化 —— 命令行工具才是灵魂 🛠️
现在有了固件,下一步是怎么把它“灌”进芯片。
大多数人第一反应是打开 STM32CubeProgrammer 图形界面,连上板子,拖拽文件……但这显然没法集成到安装包里。
我们要的是—— 一行命令搞定烧录 。
幸运的是,ST 官方提供了强大的命令行工具: STM32_Programmer_CLI 。
它能干什么?
- 通过 SWD/JTAG 连接目标芯片
- 擦除 Flash
- 下载
.bin或.hex - 校验数据
- 复位并启动程序
- 返回错误码(可用于判断成败)
而且它是独立可执行程序,不依赖完整 IDE 安装!
怎么获取它?
下载并安装 STM32CubeProgrammer ,安装完成后你会在如下路径找到 CLI 工具:
C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\STM32_Programmer_CLI.exe
我们可以把这个 STM32_Programmer_CLI.exe 单独提取出来,和其他资源一起打包进安装包。
💡 高级技巧:其实这个 CLI 工具依赖一些 DLL(如
libusb-1.0.dll),建议把整个bin目录打包进去,确保兼容性。
编写批处理脚本:一键烧录的核心逻辑
新建一个 flash.bat 文件:
@echo off
color 0a
echo.
echo === STM32 固件烧录工具 ===
echo.
REM 设置路径变量
set "PROGRAMMER=.\STM32_Programmer_CLI.exe"
set "FIRMWARE=.\firmware.bin"
REM 检查文件是否存在
if not exist "%PROGRAMMER%" (
echo 错误:未找到烧录工具,请确认文件完整!
pause
exit /b 1
)
if not exist "%FIRMWARE%" (
echo 错误:未找到固件文件 %FIRMWARE%
pause
exit /b 1
)
echo 正在连接 STM32 芯片...
echo 请确保:
echo 1. ST-LINK 已插入电脑
echo 2. 板卡已供电(红灯亮)
echo 3. 连接线无松动
echo.
REM 主要命令
"%PROGRAMMER%" ^
-c port=SWD mode=UR reset=NRST ^
--erase all ^
-w "%FIRMWARE%" 0x08000000 ^
-v ^
-hv all ^
-rst
REM 判断结果
if %errorlevel% == 0 (
echo.
echo ✅ 固件烧录成功!设备已重启。
timeout /t 3 >nul
) else (
echo.
echo ❌ 烧录失败,错误代码:%errorlevel%
echo 请尝试:
echo - 重新插拔 ST-LINK 和电源
echo - 检查板卡是否短路
echo - 确认芯片型号匹配
pause
)
📌 解释几个关键参数:
-
-c port=SWD: 使用 SWD 接口连接 -
mode=UR: 允许访问未初始化的 RAM(Universal Reset) -
reset=NRST: 使用外部复位引脚进行同步 -
--erase all: 全部擦除 Flash -
-w ... 0x08000000: 写入.bin文件到 Flash 起始地址 -
-v: 启用校验 -
-hv all: 烧录后读回验证 -
-rst: 烧录完成后复位并运行
这个脚本已经足够健壮,可以应对大多数常见问题。
四、第三步:封装成“安装包”——用 Inno Setup 打造专业体验 📦
现在我们有三个核心组件:
-
firmware.bin—— 固件本身 -
STM32_Programmer_CLI.exe+ 相关 DLL —— 烧录引擎 -
flash.bat—— 控制逻辑
如果把这些文件打包成 ZIP 发给别人,技术员可能还能应付,但普通用户大概率会懵:“哪个先运行?”
所以我们需要一个真正意义上的“安装包”——双击即运行,自动解压,引导操作,干净收尾。
这就是 Inno Setup 的主场了。
为什么选 Inno Setup?
- 免费开源,持续维护
- 输出单个
.exe,无需安装 .NET 或其他运行库 - 支持自定义页面、图标、协议
- 可以静默安装(
/SILENT) - 支持管理员权限、数字签名、日志记录
- 社区强大,文档齐全
一句话:轻量、灵活、够用。
安装脚本怎么写?
创建一个 installer.iss 文件:
[Setup]
AppName=MyDevice 固件更新工具
AppVersion=1.2.0
AppPublisher=MyCompany Inc.
DefaultDirName={autopf}\MyCompany\FirmwareUpdater
OutputBaseFilename=Firmware_Updater_V1.2.0
Compression=lzma
SolidCompression=yes
PrivilegesRequired=admin
ChangesAssociations=true
AllowNoIcons=yes
DisableReadyPage=yes
ShowLanguageDialog=no
WizardStyle=modern
[Languages]
Name: "default"; MessagesFile: "compiler:Default.isl"
[Files]
; 固件文件
Source: "firmware.bin"; DestDir: "{app}"; Flags: ignoreversion
; 烧录工具及依赖库
Source: "stm32_programmer\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
; 烧录脚本
Source: "flash.bat"; DestDir: "{app}"; Flags: ignoreversion
; 驱动文件(可选)
Source: "drivers\*.inf"; DestDir: "{app}\drivers"; Flags: ignoreversion
Source: "drivers\*.cat"; DestDir: "{app}\drivers"; Flags: ignoreversion
[Icons]
Name: "{autodesktop}\固件更新工具"; Filename: "{app}\flash.bat"
Name: "{group}\固件更新工具"; Filename: "{app}\flash.bat"
[Run]
Filename: "{app}\flash.bat"; \
Description: "立即开始烧录固件"; \
Flags: nowait postinstall skipifsilent
📌 关键配置说明:
-
PrivilegesRequired=admin:必须以管理员身份运行,否则无法访问 USB 设备。 -
OutputBaseFilename中嵌入版本号,方便管理。 -
[Files]把所有必要文件都打包进去。 -
[Run]实现“安装完成后自动运行烧录脚本”。 - 添加桌面快捷方式,提升用户体验。
进阶玩法:加入驱动自动安装 👇
有些用户的电脑没装过 ST-LINK 驱动,第一次插上会提示“未知设备”。我们可以借助 pnputil 在安装过程中预注册驱动。
修改 [Run] 段落:
[Run]
Filename: "{sys}\pnputil.exe"; \
Parameters: "-i -a ""{app}\drivers\stlink_v21_winusb.inf"""; \
StatusMsg: "正在安装 ST-LINK 驱动..."; \
Flags: runhidden waituntilterminated
Filename: "{app}\flash.bat"; \
Description: "立即开始烧录固件"; \
Flags: nowait postinstall skipifsilent
这样即使用户从未接触过嵌入式开发,也能顺利完成首次连接。
五、实战演示:从编译到交付全流程 🚀
让我们走一遍完整的流程。
假设你现在负责一款基于 STM32F407 的工业网关产品,客户遍布全国,每月都要发布一次固件更新。
你的 CI/CD 流程可以设计如下:
Git Tag (v1.3.0)
↓
CI 构建服务器(Jenkins/GitHub Actions)
↓
1. 拉取代码
2. 使用 CMake + GCC 编译生成 firmware.elf
3. objcopy 转换为 firmware.bin
4. 准备资源目录:
├── firmware.bin
├── flash.bat
├── stm32_programmer/
│ ├── STM32_Programmer_CLI.exe
│ ├── libusb-1.0.dll
│ └── ...
└── drivers/
├── stlink_v21_winusb.inf
└── ...
5. 调用 iscc.exe 编译 installer.iss
6. 输出:Firmware_Updater_V1.3.0.exe
7. 自动上传至内部发布服务器或 CDN
然后你只需要告诉客户:“请下载最新版固件更新工具,双击运行即可。”
再也不用挨个远程协助了,省下的时间够你喝三杯咖啡 ☕️。
六、那些你一定会遇到的问题 & 解决方案 🔍
Q1:杀毒软件总报毒?说是“可疑行为”
这是最常见的坑。很多安全软件会把调用 USB 设备、执行 CLI 工具的行为判定为恶意操作。
✅ 解决方案:
- 对最终 .exe 文件进行 数字签名
- 提交白名单至主流厂商(如 Windows Defender SmartScreen)
- 在公司官网提供下载链接,建立信任源
小技巧:使用免费工具如 OSSLSignCode 进行签名测试。
Q2:不同 Windows 版本兼容性差?
尤其是 Win7 或老旧工控机,可能会缺 DLL 或权限控制更严格。
✅ 解决方案:
- 打包时带上所有依赖 DLL(包括 msvcr120.dll 等运行库)
- 测试覆盖 Win10/Win11/x64/x86/虚拟机等环境
- 在脚本开头检测系统版本并给出提示
ver | findstr /i "6.1" >nul && echo 当前系统为 Windows 7,可能存在兼容问题,请联系技术支持。
Q3:如何防止重复烧录?
有时候用户手滑点了两次,导致设备反复重启,影响寿命。
✅ 解决方案:加入“防重锁”机制
在脚本中创建标志文件:
if exist "%TEMP%\firmware_flashing.lock" (
echo 检测到正在进行中的烧录任务,请勿重复运行!
pause
exit /b 1
)
echo 正在启动烧录... > "%TEMP%\firmware_flashing.lock"
烧录完成后删除:
del "%TEMP%\firmware_flashing.lock" >nul 2>&1
简单有效。
Q4:能不能支持串口 ISP 烧录?
当然可以!不只是 ST-LINK,你还可以集成 stm32flash 工具,支持 USART Bootloader。
例如:
stm32flash.exe -w firmware.bin -v -g 0x08000000 COM3
只需在安装包里多放一个工具,再加个选择界面即可。
Q5:能不能做 GUI 界面而不是黑框?
当然可以,但没必要一开始就搞得太复杂。
你可以:
- 用 AutoIt、Python + Tkinter 写个前端
- 或者直接用 NSIS + 自定义页面实现图形化
不过对于大多数场景,一个清晰的 CMD 窗口加上颜色区分(绿色成功 / 红色失败),已经足够直观。
七、高级优化建议 💡
1. 日志记录 —— 故障排查神器
把每次烧录的日志保存下来:
set LOGFILE=%USERPROFILE%\Documents\FirmwareUpdate.log
echo [%date% %time%] 开始烧录 >> "%LOGFILE%"
"%PROGRAMMER%" ... >> "%LOGFILE%" 2>&1
echo 结果:%errorlevel% >> "%LOGFILE%"
下次出问题,直接让用户发日志,效率翻倍。
2. 版本检测 —— 避免降级
想不想知道当前设备运行的是什么版本?
可以在固件中预留一个“版本区域”(比如 Flash 最后一页),写入版本号字符串。
烧录前先读取:
"%PROGRAMMER%" -c port=SWD -r16 0x080FFFF0 32
对比本地版本,决定是否继续。
3. 多设备支持 —— 一套工具打天下
如果你的产品线用了多种 STM32 型号(F1/F4/H7),可以在安装包里内置多个 .bin 文件,让用户选择目标设备。
甚至可以用 PowerShell 写个菜单:
Write-Host "请选择要烧录的设备:" -ForegroundColor Yellow
Write-Host "1) STM32F407VG"
Write-Host "2) STM32H743ZI"
$choice = Read-Host "输入编号"
switch ($choice) {
1 { $firmware = "f407.bin" }
2 { $firmware = "h743.bin" }
}
灵活性瞬间拉满。
4. 静默模式 —— 专为产线准备
在工厂批量生产时,没人愿意点“下一步”。
加个 /SILENT 参数,全自动运行:
[Run]
Filename: "{app}\flash.bat"; Flags: nowait postinstall runmaximized
配合批处理调用:
Firmware_Updater_V1.3.0.exe /SILENT /NORESTART
每分钟刷几十块板子都不是梦。
八、最后一点思考:这不是工具,是产品思维 🤔
把 STM32 工程打包成安装包,表面上是个技术活,实则是一次 用户体验的重构 。
你想,当你的客户收到一封邮件:
“您好,附件是本次固件更新包,请参考《烧录操作手册》第3章,使用 J-Link V9 连接 JTAG 接口,注意跳线设置为 BOOT0=1……”
vs
“您好,双击运行附件中的
UpdateTool.exe,等待30秒即可完成升级。”
哪种体验更好?哪种更容易赢得客户信任?
技术的价值,从来不只是“能不能做”,而是“别人用不用得上”。
当你能把复杂的底层操作封装成一个简单的按钮,你就不再是单纯的开发者,而是产品的塑造者。
九、结语:现在就开始吧 🚪
我已经把这套方案用在了三个实际项目中:
- 一个智能电表产线的自动化烧录站
- 一个高校实验平台的快速部署工具
- 一个海外客户的远程升级包
每一次都显著减少了沟通成本和技术门槛。
你也完全可以做到。
别等了,今晚下班前花半小时试试:
- 导出你的
.bin文件 - 写个最简
flash.bat - 用 Inno Setup 打个包
- 发给同事试用
你会发现,原来“交付”这件事,也可以这么优雅。✨
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
2364

被折叠的 条评论
为什么被折叠?



