Skip to content

LV030-FLASH APP设计 ​

一、 定位与功能 ​

1. 它解决什么问题 ​

flash-app 是 IAP 方案里"被升级的那一方",也是整个方案的主线:它从内部 Flash 第 2 块(0x08020000)运行,因此可以通过网络或串口把新的镜像写进同一块区域来替换自己。所有"下载、校验、落盘"的设计都是为了让这个目标成立。

它与 Bootloader 的关系是一次性的交接:Bootloader 在 0x08000000 完成初始化、判断 APP 区有没有合法镜像,然后把控制权交出去,此后不再参与运行。

2. 功能清单 ​

功能实现位置说明
串口日志输出fputc() 重定向到 USART1115200,中断接收、轮询发送
行命令 rebootapp_cmd_reboot()打印后等 TC,软复位回 Bootloader
串口回显usart_echo_test()把收到的整行原样回发,用于验证链路
看门狗iwdg_start() + 主循环喂狗3 秒超时,初始化完成后使能
网络无flash-app 不含 lwIP 与 DM9000 驱动

一句话概括:它是个最小可运行的应用,只保留"证明自己活着"和"能被重启回升级流程"两件事。业务代码是后续往上加的,升级通道与启动衔接已经打通。

3. 与其它镜像的关系 ​

镜像落盘位置运行位置交付方式
Bootloader0x080000000x08000000SWD 下载
flash-app0x080200000x08020000app.img,经串口/TCP 写回
sram-app0x08040000 暂存0x20007000app.img,再 go sram 搬运

sram-app 与 flash-app 源码同源、链接地址不同,功能集合基本一致;差别在于 sram-app 掉电即失、需要额外的装载步骤(见 LV035-SRAM-APP设计)。

二、 链接地址 ​

1. IROM1 与 IRAM1 的取值 ​

项取值含义
IROM10x08020000,大小 0x20000(128KB)代码与只读数据的起始地址与容量
IRAM10x20000000,大小 0x10000(64KB)全局变量、堆、栈;此时 Bootloader 已退出,整块 RAM 可用

内部 FLASH 与 SRAM 地址分区

2. 为什么必须是 0x08020000 ​

三个原因叠加,缺一不可:

  • Bootloader 自己占第 1 块(0x08000000 ~ 0x08020000),APP 必须让开;
  • IAP 的帧头只接受两个容器基址,0x08020000 是其中之一(另一个是 0x20007000),别的地址直接拒绝,不做"就近对齐"这种猜测;
  • 页擦除以 2KB 为粒度,128KB 块边界正好是页边界,擦除时不会碰到 Bootloader。

3. 向量表与 APP_BASE ​

APP 自己的向量表就放在镜像起始处,main() 开头、HAL_Init() 之前先重定位一次:

c
// flash-app/Core/Src/main.c
#ifndef APP_BASE
#define APP_BASE  0x08020000U     /* IAP_APP_BASE: 与 IROM1 / pack_iap.py 一致 */
#endif

/* 向量表重定位到 APP 基址: 必须早于 HAL_Init(), 否则中断仍会走 Bootloader 的向量表 */
SCB->VTOR = APP_BASE;

这一步在"由 Bootloader 跳转"时是冗余的(跳转前已经设过),但它让镜像具备"独立启动"的能力:只要把 APP_BASE 覆盖成实际链接地址,同一个工程既能被 IAP 装载,也能被调试器直接下载运行。

三、 工程与构建 ​

1. 两个 Target ​

flash-app.uvprojx 里有两个 Target,源码完全共享,只有宏定义不同:

Target宏链接地址用途
flash-app默认,APP_BASE=0x080200000x08020000交付镜像,走 app.img
flash-app-debugFLASH_APP_BASE=0x080000000x08000000直跑调试,SWD 下载后直接启动

flash-app-debug 的存在解决了一个具体问题:IAP 链路没跑通时,仍然需要一个"能独立上电运行"的版本去验证业务代码。它覆盖掉 Bootloader 的地址,因此只能配合整片擦除的下载方式——用调试器下载它会把 Bootloader 覆盖掉。

构建脚本也据此加了门禁:-d 只对 -t debug 生效,release 目标带 -d 只提示不下载(见 build_flash.ps1)。

2. 构建产物 ​

一次 release 构建会产出三个文件,各有明确用途:

产物生成方式用途
flash-app.axfARMCC 链接输出调试符号,J-Link 下载用
flash-app.binfromelf --bin裸镜像,给 pack_iap.py 打包
app.img构建后自动执行 tools/pack_iap.pyIAP 的传输格式

.bin 与 app.img 的关系见 LV040-image镜像文件打包原理与生成。

3. 构建命令 ​

powershell
# release: 生成 app.img,供串口/TCP 下载
.\build_flash.ps1 -b

# 调试: 链接到 0x08000000,可 SWD 直接下载
.\build_flash.ps1 -b -t debug -d

四、 上电到运行 ​

1. Bootloader 的判定 ​

Bootloader 启动后不会无条件跳转,先看 0x08020000 处的向量表:

  • 初始栈顶必须落在 0x20000000 ~ 0x20010000;
  • 复位向量必须落在 0x08020000 ~ 0x08040000 内,且最低位为 1(Thumb 地址)。

两条都过才跳,否则停在命令行等待下载。所以"APP 区是空的"不会导致跑飞,只会看到 Bootloader 的命令提示。

2. 运行时的内存分配 ​

flash-app 运行期间独占内部 SRAM:.data 由 __main 从 Flash 拷贝到 RAM,.bss 清零,栈顶由向量表第 1 项给出(初始值 0x20010000)。Bootloader 此时已经交出控制权,它的 RW/ZI 不再被访问,因此不存在抢占 RAM 的问题。

3. 交给它的硬约束 ​

  • 链接地址一旦改动,pack_iap.py 的 APP_BASE、iap.h 的 IAP_APP_BASE、向量表合法性检查三处都要同步;
  • 镜像不能超过 128KB,pack_iap.py 在打包阶段就会拒绝超限;
  • 中断向量表必须随镜像一起落盘(它在镜像头部),否则跳转后第一次中断就会取到 Bootloader 的处理函数。

五、 常见问题 ​

现象原因处理
下载后敲 go flash 没反应,日志说 no appAPP 区未写入或向量表不合法先发 flash-app 的 app.img,确认 flash write OK
跳转后立刻 HardFault镜像链接地址与帧头地址不一致用 -t debug 编出的镜像走 IAP 就会这样,改用 release 构建
串口无输出flash-app-debug 覆盖了 Bootloader,但没整片擦除重新整片擦除后再下载,或先下载 Bootloader
复位后回到 Bootloader 而不是 APP正常运行行为Bootloader 每次上电都要重新判定,用 go flash 或等自动跳转

本文档由 markdowncli 技能辅助生成