共计3459个字符,预计需要花费9分钟才能阅读完成。
ESP32S3 固件烧录地址
在我们用 PlatformIO 的 Upload 自动上传固件的时候,实际上是上传了以下四个文件,每个文件的地址如下:

其中地址为 0x0 的是 bootloader.bin 文件 (引导程序);0x8000 的是 partitions.bin 文件 (分区文件);0x10000 的才是我们的应用程序文件 firmware.bin 文件。最后的 0xe000 文件本身可以不用上传,是用于区分 OTA 分区的,可以参考 ESP32S3 官方文档 - 引导加载 ESP32S3 官方文档 -OTA 分区 。
并且以上的这些文件都存在于项目文件夹下的 .pio\build\env (env 为你的环境名) 里面,我们可以用这些文件当做固件进行上传,但是很麻烦,因为有四个 (也可以只上传三个) 文件:

(而且每次编译完成,都要到这些文件夹里面去找)
那有没有简单的方法呢?有的,有的,往下看。
合并多个 BIN 文件
ESP32 官方提供的 ESPTool 提供了 merge-bin 命令,用于合并多个分区文件,最终变成全量固件,这样就只用在 0x0 地址烧录一个 BIN 文件就行了。官方介绍如下, 点我 。
也就是说,我们只需要用命令 esptool --chip ESP32-S3 merge-bin -o merged-flash.bin 0x0 bootloader.bin 0x8000 partitions.bin 0x10000 firmware.bin 就能把三个不同文件合在一起了,变成 merged-flash.bin。
但是每次都执行这个命令实在是太麻烦了,而且 PlatformIO 下无法直接运行这个命令。
PlatformIO 执行自定义命令
主要意思是,在 platformio.ini 文件中加入:
[env:pre_and_post_hooks]
extra_scripts = post:extra_script.py
并且创建 extra_script.py 文件:
Import("env")
print("Current CLI targets", COMMAND_LINE_TARGETS)
print("Current Build targets", BUILD_TARGETS)
def post_program_action(source, target, env):
print("Program has been built!")
program_path = target[0].get_abspath()
print("Program path", program_path)
# Use case: sign a firmware, do any manipulations with ELF, etc
# env.Execute(f"sign --elf {program_path}")
env.AddPostAction("$PROGPATH", post_program_action)
#
# Upload actions
#
def before_upload(source, target, env):
print("before_upload")
# do some actions
# call Node.JS or other script
env.Execute("node --version")
def after_upload(source, target, env):
print("after_upload")
# do some actions
env.AddPreAction("upload", before_upload)
env.AddPostAction("upload", after_upload)
#
# Custom actions when building program/firmware
#
env.AddPreAction("buildprog", callback...)
env.AddPostAction("buildprog", callback...)
#
# Custom actions for specific files/objects
#
env.AddPreAction("$PROGPATH", callback...)
env.AddPreAction("$BUILD_DIR/${PROGNAME}.elf", [callback1, callback2,...])
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", callback...)
# custom action before building SPIFFS image. For example, compress HTML, etc.
env.AddPreAction("$BUILD_DIR/spiffs.bin", callback...)
# custom action for project's main.cpp
env.AddPostAction("$BUILD_DIR/src/main.cpp.o", callback...)
# Custom HEX from ELF
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf",
env.VerboseAction(" ".join([
"$OBJCOPY", "-O", "ihex", "-R", ".eeprom",
"$BUILD_DIR/${PROGNAME}.elf", "$BUILD_DIR/${PROGNAME}.hex"
]), "Building $BUILD_DIR/${PROGNAME}.hex")
)
可以运行自定义的 python 脚本,当然也可以运行我们刚才提到的 merge-bin 命令。
也就是说,env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", callback...) 能够在生成 firmware.bin 后,执行 callback... 函数。
PlatformIO 自动生成全量固件方法
在项目文件夹下的 platformio.ini 的 PIO 配置文件中加上 (可以在 [env] 或者 [env:xxx] 下面加上 ):
extra_scripts = post:build/build_script.py
其中 build/build_script.py 对应我们想要执行的额外脚本文件。
根据刚才的配置,我们在项目文件夹下创建 build 目录,并创建以下自动合并固件的脚本 build/build_script.py (这个对应刚才的配置):
Import("env")
import os
OUTPUT_DIR = f"build{os.path.sep}firmware.bin"
def merge_bins(source, target, env):
# source 为 ELF 文件路径
# target 为 BIN 文件路径
# 获取除了 APP_BIN 之外的其他需要烧录的 bin 文件列表
flash_images = env.Flatten(env.get("FLASH_EXTRA_IMAGES", []))
# 添加主程序 bin 文件及其偏移地址
flash_images += [env.get("ESP32_APP_OFFSET"),
f"$BUILD_DIR{os.path.sep}${{PROGNAME}}.bin",
]
# 执行合并 bin 文件命令
cmd = " ".join(
[
"$PYTHONEXE",
"$OBJCOPY",
"--chip",
env.get("BOARD_MCU"),
"merge-bin",
"--output",
f"$PROJECT_DIR{os.path.sep}{OUTPUT_DIR}",
# "--flash-mode",
# env.get("BOARD_FLASH_MODE"),
# "--flash-size",
# flash_size,
# "--flash-freq",
# str(int(env.get("BOARD_F_FLASH").replace("L", "")) // 1000000) + "m",
]
+ flash_images
)
env.Execute(cmd)
env.AddPostAction(f"$BUILD_DIR{os.path.sep}${{PROGNAME}}.bin", [merge_bins])
代码里的 OUTPUT_DIR 表示合并好后的输出文件路径,可以修改。其他内容代码里都有注释,应该很好理解。
此时,执行 build 后,在 build/firmware.bin 可以看到我们的全量固件:


这时候我们烧录只用烧录一个文件即可,即烧录到 0x0:
