来源:公众号【鱼鹰谈单片机】,ID :emOsprey
大家好,我是鱼鹰。今天写 邪修如何升级 boot?(标题有误,应该是 app)的第二篇,之前这篇文章是通过直接在代码里面塞入另一个设备的 App 的代码,完成对设备的升级,这种方法可以在前期快速验证设备与设备之间的通路,而不需要考虑固件如何下载的问题。
这个方法好是好,就是每次更新固件,都得更新代码,稍微有点麻烦。而且还会占用设备 A 本身的代码区空间(上图有点问题,设备 B APP其实只是设备 A APP 的一个数组,没有专门的区域)。
今天鱼鹰来个更高效的,只要能接入调试器就可以使用,方便快捷,使用脚本就能用。
这个方法就是使用 gdb 完成固件的注入,然后通过设备 A 的通信代码,完成设备B的固件数据传输。
类似下面这种。
关键指令:
restore device_b_app.bin binary 0x20008000 # 加载固件到 ram,注意这个区域应该是没有任何被任何功能使用的。call Upgrade_Device_B(0x20008000, 16384) # 调用设备 A 的升级函数
大概的自动化脚本(可能有问题,需要自行修改,或者让 AI 处理)
# =====================================================================# GDB 自动化固件下载与设备升级脚本# =====================================================================# 1. 连接调试器并复位设备 Afile build/zephyr/zephyr.elftarget remote localhost:50000monitor resetmonitor halt# 2. 配置区域:指定加载地址与固件文件名set $TARGET_ADDR = 0x2400c400set $BIN_FILE = "device_b_app.bin"# 3. 动态获取文件大小 (兼容 Linux / macOS)# 通过 shell 执行命令,将文件大小写入临时 gdb 脚本中,再由 gdb 导入shell if [ -f "$BIN_FILE" ]; then echo "set $FIRMWARE_SIZE = $(stat -c %s "$BIN_FILE" 2>/dev/null || stat -f %z "$BIN_FILE")" > .tmp_size.gdb; else echo "set $FIRMWARE_SIZE = 0" > .tmp_size.gdb; fisource .tmp_size.gdbshell rm -f .tmp_size.gdb# 4. 检查文件大小是否有效if $FIRMWARE_SIZE == 0echo n【错误】找不到固件文件,或者文件大小为 0 字节!退出脚本。nquitend# 打印读取到的配置信息echo n========================================necho >>> 配置就绪:nprintf "固件文件: %sn", $BIN_FILEprintf "目标 RAM 地址: 0x%Xn", $TARGET_ADDRprintf "自动捕获大小: %d 字节 (约 %d KB)n", $FIRMWARE_SIZE, $FIRMWARE_SIZE / 1024echo ========================================n# 5. 下载 bin 文件到设备 A 的 RAMecho n>>> 步骤 1: 正在下载固件到设备 A 的 RAM...nrestore device_b_app.bin binary $TARGET_ADDR# 6. 调用设备 A 的 C 函数,传入变量参数echo n>>> 步骤 2: 正在调用 Upgrade_Device_B 执行升级...ncall Upgrade_Device_B($TARGET_ADDR, $FIRMWARE_SIZE)# 7. 检查并处理返回值 ($ 自动保存了最后一次 call 的结果)echo n>>> 步骤 3: 检查设备 B 的升级返回值...nif $ == 0echo n【升级成功】设备 B 已顺利完成更新,返回值: 0nelseecho n【升级失败】设备 B 升级中断或报错!nprintf "设备 A 返回的错误码 (R0): %d (0x%X)nn", $, $end
如果 gdb 支持Python,大小计算可以换成:
# 直接在 GDB 中用 Python 获取大小并赋值给 GDB 变量python import os; gdb.set_convenience_variable('FIRMWARE_SIZE', os.path.getsize('device_b_app.bin'))
arm-none-eabi-gdb -x auto_upgrade.gdb # 调用脚本
一般情况下,如果 ram 足够大,直接放到ram就行,然后 gdb 调用设备 A 的函数完成升级,如果ram 不够大,可以通过openocd下载到固定区域,或者使用分片传输。
这个方式其实有点像下载器算法了。不过更灵活,不需要制作算法,只是利用你早已经写好的传输协议。
而且这个思想不仅可以用于固件升级,也可以对外部flash进行操作。
需要注意的是,操作完成后,记得重启设备,不然有可能运行不正常。另外 gdb 调用函数需要需要尽可能简单,不能有太多依赖条件,比如操作系统之类的,因为它的本质是在当前 PC 下强行让 CPU 运行你的函数,如果有太多依赖,就可能存在问题。
113