欢迎光临
我们一直在努力

STM32 + i.MX6ULL CAN 诊断与 OTA(06):Bootloader——A/B 分区、跳转与回滚,以及 SysTick 没关导致的“App 卡死”

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

STM32 + i.MX6ULL CAN 诊断与 OTA(06):Bootloader——A/B 分区、跳转与回滚,以及 SysTick 没关导致的“App 卡死”

系列目录:从“CAN 能通”到“可诊断、可升级” 上一篇:05 UDS DTC:从故障检测到 Flash 掉电保存 仓库:stm32-imx6ull-can-diag-ota 本文对应 tag:a-v0.7-boot

UDS 和 DTC 做完,接下来是 Bootloader。

它解决的问题不是“怎么发固件”,而是:

上电后启动哪个 App;App 坏了怎么退回去;升级中断怎么办。

这一篇做:

A/B 分区
App Header + CRC32
Metadata
trial / rollback / confirm
跳转

以及两个真实踩坑:

  • App 没设 VTOR;
  • Bootloader 没关 SysTick,App 卡在 BSS 清零。

  • TL;DR

    0x08000000 32KB Bootloader
    0x08008000 16KB Metadata A
    0x0800C000 16KB Metadata B
    0x08010000 64KB DTC Config
    0x08020000 256KB App A
    0x08060000 256KB App B
    0x080A0000 384KB free

    App 槽前 0x200 是 Header,真正向量表在 +0x200。

    流程:

    上电
    → 读 Metadata
    → 校验 active 槽
    → trial 槽存在则先试运行
    → App 自检后 Boot_AppConfirm()
    → 未确认则下次复位回滚


    1. 为什么需要 Bootloader

    没有 Bootloader:

    上电直接跑 0x08000000 的 App
    App 刷坏 = 板子变砖

    有了 Bootloader:

    上电先跑 Bootloader
    Bootloader 校验 App
    App 坏 → 换另一个槽

    OTA 只是“把新固件写到非活动槽”的手段。

    Bootloader 本身不负责网络,也不负责业务。


    2. 分区

    STM32F407VGTX 1MB Flash,sector 大小不均匀,必须按 sector 对齐。

    0x08000000 32KB Bootloader sector 0-1
    0x08008000 16KB Metadata A sector 2
    0x0800C000 16KB Metadata B sector 3
    0x08010000 64KB DTC Config sector 4
    0x08020000 256KB App A sector 5-6
    0x08060000 256KB App B sector 7-8
    0x080A0000 384KB free sector 9-11

    这里有一个之前埋下的坑:

    DTC 原来存在 0x08060000,但 Bootloader 分区后那是 App B。

    所以 DTC 必须迁到:

    0x08010000 sector 4


    3. App Header

    每个 App 槽:

    slot base + 0x000 Header (512B)
    slot base + 0x200 向量表 / App

    Header:

    typedef struct {
    uint32_t magic; /* "APP1" */
    uint32_t version;
    uint32_t length;
    uint32_t crc32;
    uint32_t build_id;
    uint32_t reserved[3];
    } boot_app_header_t;

    为什么要 0x200:

    • Cortex-M 的 VTOR 要求向量表 0x200 对齐;
    • 512B Header 正好把 App 向量表推到下一个 0x200 边界。

    App A:

    Header 0x08020000
    App 0x08020200

    App B:

    Header 0x08060000
    App 0x08060200


    4. 链接脚本

    Bootloader:

    FLASH ORIGIN = 0x08000000
    LENGTH = 32K

    App A:

    FLASH ORIGIN = 0x08020200
    LENGTH = 0x3FE00

    App B:

    FLASH ORIGIN = 0x08060200
    LENGTH = 0x3FE00

    App 还要定义:

    App A: VECT_TAB_OFFSET=0x20200
    App B: VECT_TAB_OFFSET=0x60200


    5. 第一个坑:App 没设 VTOR

    一开始 App 能跳进去,但 UDS 一直 timeout。

    用 ST-Link 热连接看 PC:

    PC = 0x08025C98

    已经在 App A 里,但停在 HAL_Delay。

    反汇编 SystemInit,发现没有写 SCB->VTOR。

    原因是 system_stm32f4xx.c 里:

    #if defined(USER_VECT_TAB_ADDRESS)
    SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;
    #endif

    只加了 VECT_TAB_OFFSET,没加:

    USER_VECT_TAB_ADDRESS

    所以 VTOR 还是 0x08000000,中断全跳到 Bootloader 的向量表。

    补上宏之后,SystemInit 才有:

    SCB->VTOR = 0x08020200;


    6. 第二个坑:SysTick 没关

    VTOR 修好后,UDS 还是 timeout。

    GDB attach 看到:

    Reset_Handler +43
    LoopFillZerobss

    栈顶:

    SysTick_Handler -> xTaskIncrementTick

    原因:

    • Bootloader 的 HAL_Init() 打开了 SysTick;
    • boot_jump_to() 跳转前没有关;
    • App 启动到 main 之前要清零 BSS;
    • 此时 App 的 FreeRTOS 还没初始化;
    • SysTick 一进来就访问未初始化任务链表;
    • BSS 清零都走不完,main 根本没到。

    修复:

    __disable_irq();

    SysTick->CTRL = 0;
    SysTick->LOAD = 0;
    SysTick->VAL = 0;
    SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk;

    SCB->VTOR = app_base;
    __DSB();
    __ISB();
    __set_MSP(sp);
    __enable_irq();
    entry();

    注意顺序:

    先关 SysTick
    清 pending
    设 VTOR
    设 MSP
    开中断
    跳转

    只加 __enable_irq() 不够,SysTick pending 仍然会进来。


    7. Metadata

    Metadata 记录:

    active_slot
    trial_slot
    slot_state[2]
    attempts
    version/length/crc
    seq
    CRC

    双记录:

    sector 2: Metadata A
    sector 3: Metadata B

    写入时:

    擦另一份
    写新记录
    seq++

    掉电时至少还有一份旧记录。


    8. trial / rollback

    升级完成后:

    active=A
    trial=B
    B state=trial
    attempts=0

    第一次复位:

    Bootloader 启动 B
    attempts++
    保存 Metadata

    App B 自检通过后:

    Boot_AppConfirm(1);

    Metadata:

    active=B
    trial=NONE
    B=confirmed

    如果没确认就再次复位:

    Bootloader 看到 trial B attempts=1
    → 回滚 A

    这就是 A/B 的核心。


    9. 实测

    App A:

    PC = 0x08025C50
    UDS 50 03

    trial B:

    PC = 0x08065C50

    不确认,再复位:

    PC = 0x08025C50 # 回滚 A

    App B 调 Boot_AppConfirm(1) 后:

    Metadata seq=12 active=B trial=NONE
    再次复位 PC = 0x08068424 # 仍 B

    A/B 启动、trial、回滚、confirm 都通过。


    10. 目录

    firmware/bootloader/
    ├── boot_layout.h
    ├── boot_crc32.c/h
    ├── boot_image.c/h
    ├── boot_meta.c/h
    ├── boot_meta_store.c/h
    ├── boot_flash.h
    ├── boot_flash_stm32.c/h
    ├── boot_jump.c/h
    ├── boot_loader.c/h
    ├── boot_app.c/h
    ├── ota_server.c/h
    ├── linker/
    │ ├── bootloader.ld
    │ ├── app_a.ld
    │ └── app_b.ld
    └── tools/mk_app_image.py

    打包 App:

    python tools/mk_app_image.py app.bin app_a.img –version 1


    11. 下一步

    Bootloader 完成后,OTA 的写入就变成:

    0x34 擦非活动槽
    0x36 逐块写
    0x37 校验 + 写 trial Metadata
    复位
    Bootloader trial 启动
    App confirm

    下一篇写 OTA。


    12. 仓库 / 数据

    • 仓库:https://github.com/longlin05/stm32-imx6ull-can-diag-ota
    • tag:a-v0.7-boot
    • 测试日志:
      • test/records/20261106-boot-core-host.log
      • test/records/20261106-boot-ab-trial.log

    标签:STM32 Bootloader A/B CRC32 i.MX6ULL

    赞(0)
    未经允许不得转载:171主机测评 » STM32 + i.MX6ULL CAN 诊断与 OTA(06):Bootloader——A/B 分区、跳转与回滚,以及 SysTick 没关导致的“App 卡死”
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址