1. 构建最小Zephyr引导程序

构建最小Zephyr引导程序 学习卡片

任务目标

构建一个最小可用的Zephyr引导程序,实现上电后从固定地址加载并跳转到应用程序。引导程序与应用程序均基于Zephyr RTOS,通过链接脚本控制地址布局,使用sys_reboot或直接函数指针跳转完成切换。

环境依赖

  • Zephyr SDK(v3.4+)及对应工具链
  • 开发板:以 qemu_cortex_m3 为例(无需真实硬件)
  • West 工作区已初始化

实现原理

引导程序本质是一个独立的Zephyr镜像,链接到Flash起始地址(如0x0),应用程序链接到偏移地址(如0x8000)。引导程序通过函数指针跳转到应用复位向量,并设置MSP(主栈指针)。关键点在于两个镜像的链接脚本需互不重叠。

关键完整代码

// boot.c - 最小引导程序
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/linker/sections.h>
#include <stdint.h>

#define APP_FLASH_BASE  0x8000  // 应用程序起始地址

// 应用程序向量表结构(与ARM Cortex-M一致)
typedef void (*vector_entry_t)(void);
typedef struct {
    uint32_t initial_sp;
    vector_entry_t reset;
} app_vector_table_t;

void jump_to_app(void)
{
    // 关闭中断,避免跳转过程中被干扰
    __disable_irq();

    // 获取应用程序向量表
    const app_vector_table_t *vt = (const app_vector_table_t *)APP_FLASH_BASE;

    // 验证栈指针和复位向量合法性(简单检查)
    if (vt->initial_sp == 0xFFFFFFFF || vt->reset == NULL) {
        return;  // 非法向量表,停留在引导程序
    }

    // 设置主栈指针并跳转
    __set_MSP(vt->initial_sp);
    vt->reset();

    // 不应返回
    while (1) {}
}

void main(void)
{
    printk("Bootloader: jumping to app at 0x%x\n", APP_FLASH_BASE);
    jump_to_app();
}

链接脚本配置

在 prj.conf 中设置 CONFIG_FLASH_LOAD_OFFSET=0(引导程序从0x0开始)。应用程序工程需设置 CONFIG_FLASH_LOAD_OFFSET=0x8000。两个工程使用不同的 board.overlay 或直接修改 dts.overlay 定义flash分区。

构建与运行

# 构建引导程序
west build -b qemu_cortex_m3 -d build_boot . -p

# 构建应用程序(需单独工程,设置FLASH_LOAD_OFFSET=0x8000)
# 假设应用工程在 ../app 目录
west build -b qemu_cortex_m3 -d build_app ../app -p

# 合并镜像(引导程序 + 应用程序)
west sign -d build_boot --toolpath /path/to/zephyr/sdk -B build_app/zephyr/zephyr.bin -o merged.bin

# 运行合并镜像
qemu-system-arm -machine mps2-an385 -cpu cortex-m3 -nographic -device loader,file=merged.bin

验证方法

  1. 构建无错误,生成 merged.bin 大小不超过Flash容量。
  2. 运行QEMU后,串口输出 Bootloader: jumping to app at 0x8000,随后应用程序的启动日志出现(如 Hello from app)。
  3. 若跳转失败,程序会卡在引导程序的 while(1) 循环,无应用日志输出。

常见问题

  • 跳转后HardFault:检查应用程序的 CONFIG_FLASH_LOAD_OFFSET 是否与引导程序中的 APP_FLASH_BASE 一致。
  • 向量表未对齐:ARM要求向量表按128字节对齐,确认应用程序链接脚本中 .isr_vector 段对齐。
  • 中断未关闭:跳转前必须 __disable_irq(),否则跳转过程中可能触发中断导致异常。