☰
ESP32 独立串口工具烧录 bin 文件:地址、参数与故障排查
2026/10/2 8:55:48 网站建设 项目流程

一个做硬件的朋友上周给我发来一个压缩包,里面躺着三个文件:bootloader.bin、partition-table.bin、myapp.bin,外加一句"麻烦帮我烧到这批板子上"。他手上有二十块刚贴片回来的 ESP32 模块,工作在客户现场,既没装 Arduino,也没配 ESP-IDF,只有一个 Windows 笔记本和一根 USB 线。这种场景下,IDE 那一套一键下载的流程完全用不上,能救场的只有官方那个独立的串口烧录工具。这篇文章就把这条路走通:从工具的获取、bin 文件和烧录地址的对应关系,到点击 START 之后每一步在发生什么,再到烧录失败时怎么一层层往外剥。关键词就三个——ESP32、串口工具、bin 文件烧录,摸清这三样,你拿到任何一份别人编译好的固件包都能自己灌进板子。

1. 明明 IDE 能一键下载,为什么还要折腾独立的烧录工具

1.1 三种下代码路径的真实差异

先说清楚一件事:不管你是用 Arduino IDE 点那个向右的箭头,还是在 ESP-IDF 里敲idf.py flash,或者用本文说的这个独立工具,底层跑的都是同一个东西——esptool。它做的事情本质上是:通过串口把芯片拉进下载模式(Download Mode),然后按地址往 Flash 里写数据,写完再复位。区别只在于"谁帮你在前面把参数填好了"。

  • Arduino IDE:编译完自动生成一份 esptool 命令行,参数是 IDE 根据你选的开发板型号推出来的,你基本看不见也改不了。
  • ESP-IDF / platformio:编译目录里会留下flash_args文件,里面列着每个 bin 的名字和地址,烧录命令就是照着它拼的。
  • Flash Download Tool(官方独立烧录工具):所有参数都摊在你面前,地址、波特率、SPI 模式、Flash 容量全靠手填。灵活,但也意味着填错就是你的锅。

理解了这层关系,你就不会觉得独立工具"原始"了。它只是把自动化那一层的假设全部拿掉,换成了手动控制。

1.2 只有独立烧录工具才顺手的三类场景

第一类是批量烧录。工厂里一台电脑带多个 USB 口,同时开几个工具实例,各自占一个 COM 口,互相不干扰。IDE 那边就不行了,工程目录、编译缓存、串口占用全搅在一起,开第二个实例都费劲。

第二类是只有 bin 没有源码。客户给你一个编译好的固件包,或者你从别人的板子上读出来一份完整镜像,这时候你根本没有工程可以打开,唯一的路就是拿工具按地址往里写。

第三类是要精确定位每一个 bin 的位置。比如你想把应用固件放在 0x10000 以外的地址,或者要往 NVS 分区、SPIFFS 镜像里塞数据,这些操作在 IDE 里要靠改分区表、重新编译才能实现,而在独立工具里就是表格里多加一行的事。

注意:独立工具不会帮你编译,也不会帮你校验分区表和 bin 是否匹配。它只负责"照着地址写"。所以前面那些信息必须你自己准备好,这也是后面几节的重点。

2. Flash Download Tool 的获取与串口链路自查

2.1 版本选择与目录结构

这个工具在官方文档站的"工具"栏目下能直接下到,是一个免安装的压缩包,解压后双击flash_download_tool_*.exe就能跑。版本上,我一般建议用较新的几版,原因很现实:新版本才认新芯片。你拿一个老版本去烧 ESP32-C3、C6 这类较新的型号,工具界面里压根就没有对应的芯片选项,硬选 ESP32 会直接烧废。

解压后的目录大致是这几块:

  • 主程序 exe 和几个 dll,别单独把 exe 拎出来用,缺文件会报错。
  • 一个bin子目录,里面放着官方的出厂固件或者一些辅助镜像。
  • 部分版本还带一个combine目录,里面是 bin 合并相关的小工具。

我踩过的坑是:把 exe 从压缩包里双击打开运行,看起来能用,但因为没有正常解压,某些动态库没释放出来,烧到一半工具直接闪退。所以老老实实先完整解压到一个纯英文路径下,别放在桌面中文目录里,某些版本的路径解析对中文支持不好。

2.2 驱动、线材与板上那两个按键

Windows 下,绝大多数 ESP32 开发板用的是 USB 转串口芯片,常见的是 CP2102、CH340/CH343 这两类。CH34x 需要单独装驱动,装完设备管理器里才会出现 COM 口。判断驱动装没装好有个很直观的办法:插上板子看设备管理器里有没有新增的端口,拔掉再插上,端口号跟着消失又出现,就说明链路通了。

线材这块我要单独强调。很多人烧录失败排查半天,最后发现是那根线只能充电不能传数据。这种线外表和正常线一模一样,唯一的区别是里面少了 D+ 和 D- 两根数据线。判断方法很简单,换一根平时给手机传文件的线试一下,如果突然就通了,那就是线的问题。

再说板子上那两个按键。ESP32 芯片进下载模式靠的是几个 strapping 引脚的组合状态,而绝大多数开发板把它们接到了两个按键上:一个标着BOOT(或者IO0),一个标着EN(或者RST)。手动进下载模式的标准动作是:

  1. 按住BOOT不放。
  2. 短按一下EN,然后松开。
  3. 松开BOOT。

这套动作做完,芯片就被锁在下载模式里等待接收数据了。有些板子在 USB 转串口电路上做了自动复位(用 DTR/RTS 控制那两个引脚),工具点 START 时它自己会完成这一套动作,你就什么都不用按。但自动复位不是所有板子都有,尤其是自己画的板子或者便宜的模块,经常需要手动来一遍。

3. 搞错地址是烧录失败的头号原因:先认清楚 bin 和它的地址

3.1 一个固件工程到底会产出哪几个 bin

很多人第一次打开工具,看到表格里可以填好几行,就懵了——我到底该填几个文件?答案取决于你的固件结构,但一个标准的、带 OTA(空中升级)功能的 ESP32 应用,通常会产出一组固定的 bin:

  • bootloader.bin:二级引导程序。芯片上电后,ROM 里的固化代码先跑起来,然后把这段 bootloader 加载到 RAM 里执行,由它负责初始化 Flash、加载分区表和主应用。它必须放在 Flash 最前面。
  • partition-table.bin:分区表。它是一张"地图",告诉系统 Flash 的哪一段是应用区、哪一段是 NVS 参数区、哪一段是文件系统。它占 0x1000 大小,紧跟 bootloader 之后。
  • boot_app0.bin:这段小镜像配合 OTA 机制工作,通常和 OTA data 初始化数据一起烧。如果你不用 OTA,有些流程可以省掉,但省掉之后某些 IDF 版本启动会报错,所以我一般建议留着。
  • ota_data_initial.bin:OTA 数据分区的初始内容。它决定芯片第一次启动时从哪个 app 分区引导。
  • 主应用 bin:名字可能是firmware.bin、myapp.bin、project.bin,也可能是 Arduino 自动命名的一长串。这是你真正的业务代码,体积最大。

五个文件、五个地址,少一个、错一位,板子要么启动不了,要么启动起来跑的是旧代码。

3.2 各型号芯片的地址速查

先上表,再讲怎么验证。下表是按官方模板工程在常见 ESP-IDF 版本下的默认值整理的:

文件ESP32ESP32-S2ESP32-S3 / C3 / C6 / H2
bootloader.bin0x10000x10000x0
partition-table.bin0x80000x80000x8000
boot_app0.bin0xe0000xe0000xe000
ota_data_initial.bin0xf0000xf0000xd000
主应用 bin0x100000x100000x10000

注意:这张表只是"常见默认值",不是真理。自定义分区表会把应用区起点改掉,不同 IDF 版本对 bootloader 偏移也可能调整。动手前一定用下面说的方法拿到你自己工程的真实地址,别照抄。

表里有两个地方特别容易翻车。一个是 bootloader 的偏移,老 ESP32 是 0x1000,而 S3、C3 这一代变成了 0x0,把 0x1000 的规则套到 S3 上,bootloader 就写到空白区去了,芯片上电找不到引导程序,串口只会吐一串乱码或者干脆没输出。另一个是ota_data_initial.bin的地址,ESP32 是 0xf000,S3/C3 是 0xd000,这两个值互换了的话,OTA 分区数据会覆盖到别的东西上。

3.3 不靠记忆,从编译产物里精确拿到地址

记不住没关系,工程自己会告诉你。如果你是 ESP-IDF 编译的,打开编译输出目录(一般是工程根目录下的build文件夹),里面有个叫flash_args的文件,内容长这样:

--flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 bootloader/bootloader.bin 0x8000 partition_table/partition-table.bin 0xf000 ota_data_initial.bin 0x10000 myapp.bin

这个文件就是给 esptool 用的参数清单,每一行前面的十六进制数就是地址,后面是相对路径。照着它往工具的表格里填,一个字都不会错。有的 IDF 版本还会生成一个flash_project_args,内容更全,包含 boot_app0,同样可以用。

如果你是 Arduino IDE 编译的,打开的姿势是:文件 > 首选项,把"显示详细输出"里的"上传过程"勾上,然后正常点一次上传。IDE 下方的输出窗口会打印出完整的 esptool 命令行,里面带write_flash和后面一长串地址 文件名,把它们抄下来就行。最后面的--flash_mode dio --flash_freq 40m --flash_size detect这几个参数也别丢,它们对应工具界面右侧的 SPI 配置。

还有一种情况,就是你手上只有 bin 文件,没有任何工程。这时候只能靠经验判断文件类型:体积在 4KB 到 8KB 之间、文件名带 bootloader 的,基本都是引导程序;大小正好 3KB 左右的,大概率是分区表;剩下体积最大的那个就是应用。地址按芯片型号套上面那张表,再用串口日志验证结果。

4. 界面上的每一项参数到底在决定什么

4.1 从串口号到波特率的填写顺序

打开工具,第一件事是选芯片类型。这个下拉框必须和你的板子严格对应,选错不是"烧不进去",而是"能烧进去但跑不起来",更隐蔽。ESP32 和 ESP32-S3 的 bootloader 偏移不同,选错了地址也跟着错。

接下来选工作模式,绝大多数情况就是Develop加UART。Factory模式是给产线做整片烧录用的,配合合并后的单个 bin;HSPI模式在现在的工具版本里基本已经废弃了。

然后填 COM 口和波特率。COM 口在设备管理器里看,注意插拔板子前后对比,确认哪个是新增的。波特率默认 115200,这个值偏保守,是为了兼容性。实际用起来,我一般直接上 921600,速度快接近八倍,烧一个 1MB 的固件一两秒就完事。如果你的板子或线材质量一般,高波特率下容易报校验错误,那就退回 460800 或者 115200。

4.2 多文件填写的行顺序与 DoNotChgBin

表格部分是最关键的。左边有个复选框,中间是文件路径,右边是地址。每一行都要勾上复选框,否则那一行会被忽略,这一点新手极容易漏——文件填了、地址填了,就是没勾,然后烧录"成功"了但板子跑不起来,因为根本就没写进去。

行顺序本身不影响烧录结果,esptool 内部会按地址排序,但我习惯按地址从小到大排,方便核对:bootloader、partition-table、boot_app0、ota_data、app。

右侧的 SPI 配置区有三项要填:

参数推荐值说明
SPI SPEED40MHz与固件编译时的 flash_freq 保持一致
SPI MODEDIO与 flash_mode 一致,QIO 更快但需硬件支持
Flash SIZE与芯片实际容量一致常见 4MB(32Mbit),看模块丝印或手册

这里有个非常重要的勾选项:DoNotChgBin。它的作用是告诉工具"别动我的 bin 文件头"。默认不勾的情况下,工具会根据你选的 SPI SPEED / MODE / SIZE 去修改第一个 bin(也就是 bootloader)的头部字节,把参数写进去。如果你勾上,工具就原样写入,使用 bin 里自带的参数。

那到底勾不勾?我的经验是:如果你是从flash_args里抄来的参数,并且已经在工具里填了同样的值,勾不勾都一样,因为两者一致。但如果你不清楚 bin 里原本的参数是什么,那就勾上 DoNotChgBin,让 bin 自己说了算。反过来,如果 bin 是别人给的、参数未知,而你知道自己的板子必须用 DIO 40MHz,那就不勾,让工具强行覆盖。

4.3 点 START 之后,日志里该出现什么

点下 START,右侧的日志区会开始滚动。正常的流程是这样几步:

  1. 工具尝试连接串口,打印Connecting...。
  2. 芯片被复位进下载模式,日志里出现SYNC字样。如果这一步卡住不动,说明没进下载模式,需要手动按 BOOT 和 EN。
  3. 读取芯片信息,显示 MAC 地址、芯片型号、晶振频率。这一步是验证你芯片类型选对了没有的关键,如果工具显示的 MAC 是乱码或者读不到,说明通信有问题。
  4. 开始逐段写入,每写一段打印一个进度条和校验结果。
  5. 全部写完,显示FINISH,然后自动复位芯片。

整个过程里,我最关注的是第 3 步读出来的 MAC 地址。能正常读出 MAC,说明串口链路、芯片型号选择、下载模式这三个环节全都对了,后面的写入基本不会出问题。如果这一步就失败,别急着点第二次 START,先回去检查前面那些设置。

提示:工具里有个STOP按钮,注意不要把它和START搞混。我见过有人点错了,然后盯着屏幕纳闷为什么没反应。

5. 把一堆 bin 压成一个文件:量产和远程升级的省事做法

5.1 merge_bin 与 flash_args

烧一块板子填五行还行,烧二十块板子就是重复劳动,而且每换一台电脑都要重新填一遍,很容易填错。这时候就该把多个 bin 合并成一个整片镜像。

ESP-IDF 里自带了这个能力,两种用法。一种是用 esptool 的 merge_bin 子命令:

esptool.py --chip esp32 merge_bin \ -o merged.bin \ --flash_mode dio \ --flash_freq 40m \ --flash_size 4MB \ 0x1000 bootloader/bootloader.bin \ 0x8000 partition_table/partition-table.bin \ 0xf000 ota_data_initial.bin \ 0x10000 myapp.bin

另一种更省事,直接让 IDF 帮你合:

idf.py merge-bin

它会把build目录下的镜像按正确地址合成一个merged-flash.bin。合完之后,你在烧录工具里就只需要填一行:地址填0x0,文件选这个合并后的镜像,勾上复选框,点 START。合并镜像的起始地址永远是 0x0,因为它内部已经包含了从 bootloader 开始的全部内容。这一点搞错的话,比如你填了 0x1000,整个镜像会往后平移 4KB,bootloader 就跑偏了。

5.2 合并后的坑:地址偏移与 flash 参数头

合并镜像有两个隐蔽的坑。

第一个是地址必须严格递增且不重叠。merge_bin 会按你给的顺序往里拼,如果你把 0x8000 的分区表写在 0x1000 的 bootloader 前面,工具不会报错,但生成的文件内容是错乱的。

第二个是合并后的文件体积。merged.bin 的大小等于"最后一个镜像的地址 + 它的长度",不是所有文件大小简单相加。比如应用在 0x10000,大小 700KB,那合并文件就是 0x10000 + 700KB 左右,中间 bootloader 到分区表之间的空白区域会用 0xFF 填充。这意味着合并镜像会比分散烧录多占一点传输时间,但换来的是流程上的一步到位,量产时非常值得。

第三个,合并时给的那些--flash_mode、--flash_size参数,会被写进合并文件头部。所以合的时候用什么参数,板子上就必须用什么参数,两者对不上会出现"能烧进去、开机就崩"的情况。我自己的习惯是:合之前先确认板子手册上的 Flash 型号,是 DIO 还是 QIO、多大容量,然后命令行参数和烧录工具里的下拉框保持完全一致。

6. 烧录不成功的完整排查链路

6.1 现象一:串口列表里根本没有 COM 口

这是最外层的问题,压根没到芯片那一层。排查顺序我一般这样走:

先换 USB 口,尤其是台式机前面板的 USB 口,供电和数据质量都不稳定,直接插到主板后面板。然后换线,用一根确定能传数据的线。再然后装驱动,CH34x 和 CP210x 各装一次,装完拔插板子看设备管理器有没有反应。

还有一种情况是设备管理器里出现了一个带黄色感叹号的设备。这说明硬件层面认到了,但驱动不对或者安装失败。右键卸载设备,拔插一次,重新装驱动,通常能解决。

最后一种比较少见但确实存在的:板子的 USB 转串口芯片坏了,或者 USB 口虚焊。判断方法是拿万用表量一下 USB 座的 VBUS 有没有 5V,或者换一块同款板子对比。

6.2 现象二:一直卡在等待上电同步

串口能选到,但点 START 之后日志停在Connecting...或者一直重试SYNC。这说明串口是通的,但芯片没被拉进下载模式。手动按 BOOT + EN 那个序列来一遍,注意顺序和时序:先按住 BOOT,短按 EN 并松开,再松开 BOOT。有些板子的按键手感很硬,短按可能没触发,用力一点、按住时间稍长一点再试。

如果手动操作也不行,那就要怀疑串口被占用了。最常见的是同时开了 IDE 的串口监视器或者别的串口工具,它们独占了 COM 口,烧录工具连不上。把所有可能占用串口的软件关掉,包括后台隐藏的。

再往下就得看硬件了。ESP32 有自动下载电路,需要 USB 转串口的 DTR 和 RTS 分别控制 EN 和 IO0,中间通过两个三极管做电平转换。自己画的板子如果这部分电路没做,或者三极管方向焊反了,就只能全程手动按按键。这种情况下有个临时办法:先在按住 BOOT 的状态下点上工具的 START,等日志出现 SYNC 再松开 BOOT,相当于手动接管了自动同步的过程。

6.3 现象三:提示烧录成功,但板子毫无反应

这个最让人抓狂,因为工具给了你成功反馈,但结果不对。按可能性从高到低排:

  • 地址错了。尤其是 bootloader 偏移,S3/C3 用了 0x1000,或者应用固件的地址和分区表里定义的起始地址不一致。回到第 3 节,用flash_args核对一遍。
  • bin 文件不完整或者类型放错。比如把分区表当成了应用烧到 0x10000,或者漏烧了boot_app0.bin导致 OTA 引导标志异常。
  • 复选框没勾。前面提过,最冤的一种。
  • Flash 参数不匹配。固件编译时用的是 QIO 80MHz,而你在工具里选了 DIO 40MHz 并且没勾 DoNotChgBin,工具把参数覆盖写成了错的。

排查这个现象,串口日志是唯一的抓手。把烧录工具的串口监视功能打开,或者用另一个串口工具连上,波特率 115200,然后按一下 EN 复位,看输出什么:

  • 全是乱码:波特率不对,或者晶体频率配置不对。
  • 输出invalid header: 0xffffffff:那个地址上是空的,说明 bootloader 没写进去,回去查地址。
  • 输出invalid header: 0x...后面是一个非 ffffffff 的值:那个地址上有东西但不是引导程序,说明写错文件了。
  • 输出一段正常的启动信息,然后卡住或者反复重启:地址都对,问题在应用本身,比如 NVS 分区数据异常、看门狗超时。这时候可以试着手动擦除整片 Flash 再重烧。工具界面上有个 ERASE 按钮,擦除会花几秒到十几秒,但能排除掉旧数据残留的干扰。

7. 几个我在项目里反复验证过的实用习惯

第一个习惯是在文件夹里留一份地址清单。每次交付固件包给同事或者客户,我都习惯在同一个目录里放一个文本文件,写清楚芯片型号、每个 bin 对应的地址、SPI 参数,最好再附上一条完整的合并命令。这样别人拿到包,不用问就知道怎么烧,也不需要猜参数。这个小动作省下的沟通时间远超写它的成本。

第二个习惯是先烧再测,别先测再烧。有些板子出厂时 Flash 里带着测试固件,直接上电看串口是有输出的,容易让人误以为可以正常烧录。实际上先做一次完整烧录,看到FINISH和下发后的正常日志,再去做功能测试,顺序不能反。

第三个习惯是准备一份最小可用的验证固件。就是那种开机往串口打印一行固定字符串的简单程序,编好之后把 bin 存起来。当你不确定板子到底是硬件坏了还是固件有问题时,烧这个最小固件进去。它如果跑起来能打印,说明芯片、Flash、串口这条链路全部正常,问题就在原来的固件上;如果它也跑不起来,那就是硬件层面的问题,不用再折腾软件了。这份固件我换了好几个项目,一直是排查问题的第一把钥匙。

关于波特率还有个细节值得说:如果你用 921600 烧录成功但偶尔出现校验失败,有时候不是线材问题,而是电脑 USB 口供电波动。换到主机后面板直连的口,或者换一根带磁环的线,往往就稳了。这种偶发性故障最难查,因为它十次里只失败一次,让人总以为是自己的操作问题。

最后,工具版本这件事值得反复提醒。新芯片层出不穷,烧录工具更新也比较频繁,遇到"板子是新买的、工具是两年前下的"这种组合,第一反应应该是去下个新版本,而不是怀疑自己的操作。我自己就曾在 C6 上折腾了一个多小时,最后发现是工具版本太老压根不支持,换了版本之后两分钟搞定。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询