当前页面为 开发中 版本,查看特定版本的文档,请在页面左下角的下拉菜单中进行选择。

Bootloader (Solutions HID)

1 功能概述

此sample为pan107/pan101上演示HID设备专用Bootloader,支持USB DFU和2.4G PRF OTA双模固件升级,专为鼠标、键盘等HID设备优化。

核心特性:

  • 双模升级: 同时支持USB DFU(有线)和2.4G PRF OTA(无线)两种升级方式

  • Flash分区管理: Boot/App/Backup/KV Store五分区布局,支持安全回滚

  • 信号槽机制: 基于事件驱动的模块化设计,易于扩展新的DFU方式

  • Image Header: 支持固件版本管理和CRC校验

  • 自动搬运: Backup分区的固件在下次启动时自动搬运到App分区

  • 硬件清理: 跳转到App前彻底清理硬件状态,避免干扰

与默认Bootloader的区别:

特性

solutions_hid/bootloader

bootloader(默认)

UART DFU

禁用(BOOT_ENABLE_UART_DFU=0)

启用(BOOT_ENABLE_UART_DFU=1)

USB DFU Mode

Mode 3(应用层控制)

Mode 0(GPIO触发)

应用场景

HID设备(鼠标/键盘)

通用EVB演示

进入方式

App设置DFU Flag

GPIO按键触发

配置宏位置

App工程的sdk_config.h

Boot工程的sdk_config.h

2 环境要求

  • board: pan107QFN32核心板/ pan101MSOP10核心板 + evb底板

  • uart0: 设置P16,P17作为默认的LOG输出端口,波特率921600

  • USB: 需要使用MPC版本之后USB修复后的芯片(早期版本USB不稳定)

  • Keil MDK: ARM Compiler 5或6

  • Python环境: 用于后处理脚本(签名、合并等)

3 编译和烧录

3.1 工程位置

nimble/pan10xx_samples/solutions_hid/bootloader/keil_107x/

3.2 编译步骤

重要: Bootloader必须在App工程之前编译,因为App的post_build脚本会自动调用merge_images.py生成merged固件。

步骤1: 编译Bootloader

  1. 打开Keil工程文件bootloader.uvprojx

  2. 选择目标芯片型号(PAN1070/PAN1010)

  3. 修改sdk_config.h中的配置(见第5章)

  4. 点击”Rebuild”编译工程

  5. 编译成功后,在Images/目录下生成:

    • bootloader.bin: 二进制文件

    • bootloader.hex: Intel HEX格式

    • bootloader.disasm: 反汇编文件

步骤2: 编译App并生成Merged固件

  1. 打开App工程(如mult_ms.uvprojx)

  2. 确保CONFIG_ENABLE_BOOTLOADER=1(在App的sdk_config.h中)

  3. 编译App工程

  4. 自动执行后处理脚本(post_build_app.bat):

    • 生成signed app image

    • 调用merge_images.py合并Bootloader + App

    • 生成mult_ms.merged.hex(最终烧录文件)

步骤3: 烧录

  • 首次烧录: 烧录mult_ms.merged.hex(包含Bootloader + App)

  • 后续OTA升级: 只需烧录App固件,Bootloader保持不变

3.3 关键配置

App工程中使能Bootloader:

// mult_ms/keil_107x/configuration/sdk_config.h
#define CONFIG_ENABLE_BOOTLOADER    1  // 必须使能

Bootloader工程配置:

// bootloader/keil_107x/configuration/sdk_config.h
#define BOOT_ENABLE_USB_DFU         1  // 使能USB DFU
#define BOOT_USB_DFU_MODE           0x03  // Mode 3: App控制
#define BOOT_ENABLE_PRF_OTA         1  // 使能2.4G OTA
#define BOOT_ENABLE_UART_DFU        0  // 禁用UART DFU(HID设备不需要)

注意:

  • Bootloader和App的Flash分区配置必须完全一致

  • Image Header大小必须相同(CONFIG_APP_USE_IMAGE_HEADER=1)

  • 分区对齐必须是4KB的倍数

4 演示说明

4.1 Flash分区布局

PAN107x标准分区布局:

Flash Total: 508 KB (0x00000 - 0x7F000)

+------------------+----------+------------+---------------------------+
| Partition        | Start    | Size       | Description               |
+------------------+----------+------------+---------------------------+
| Bootloader       | 0x00000  | 40 KB      | 引导程序(USB DFU + OTA)   |
+------------------+----------+------------+---------------------------+
| App              | 0x0A000  | 220 KB     | 应用程序(mult_ms等)       |
+------------------+----------+------------+---------------------------+
| Backup           | 0x40800  | 220 KB     | OTA备份分区               |
+------------------+----------+------------+---------------------------+
| KV Store         | 0x77000  | 16 KB      | 键值存储(配对信息等)      |
+------------------+----------+------------+---------------------------+
| User Custom      | 0x7B000  | 12 KB      | 用户自定义数据            |
+------------------+----------+------------+---------------------------+

分区计算公式:

// bootloader/src/flash_manager.h
#define FLASH_AREA_BACK_UP_START    CONFIG_FLASH_PARTITION_APP_BACKUP_ADDR
#define FLASH_AREA_IMAGE_START      CONFIG_FLASH_PARTITION_APP_ADDR
#define FLASH_IMAGE_MAX_SIZE        CONFIG_FLASH_PARTITION_APP_SIZE

// App地址 = Bootloader起始地址 + Bootloader大小
CONFIG_FLASH_PARTITION_APP_ADDR = 0x00000 + 40KB = 0x0A000

// Backup地址 = App起始地址 + App大小
CONFIG_FLASH_PARTITION_APP_BACKUP_ADDR = 0x0A000 + 220KB = 0x40800

验证规则:

// sdk_config.h 中的编译期检查
#if CONFIG_FLASH_PARTITION_BOOTLOADER_SIZE % 0x1000
#error "Bootloader Partition size should be multiple of 4KB!"
#endif

#if (CONFIG_FLASH_PARTITION_BOOTLOADER_SIZE + \
     CONFIG_FLASH_PARTITION_APP_SIZE + \
     CONFIG_FLASH_PARTITION_APP_BACKUP_SIZE + \
     CONFIG_FLASH_PARTITION_KVSTORE_SIZE + \
     CONFIG_FLASH_PARTITION_USER_CUSTOM_SIZE > CONFIG_FLASH_SIZE)
#error "The size of all flash partitions exceeds the total flash size!"
#endif

4.2 启动流程

正常启动(App有效):

上电 → Bootloader启动 → 检查App分区Magic → 有效 → 硬件清理 → 跳转到App

OTA升级启动(Backup有效):

上电 → Bootloader启动 → 检查Backup分区 → 有效 → 搬运到App分区 → 清除Backup标志 → 跳转到App

USB DFU启动:

上电 → Bootloader启动 → 检测DFU Flag → 有Flag → 进入USB DFU模式 → 接收新固件 → 写入Backup → 重启

2.4G OTA启动:

上电 → Bootloader启动 → 监听2.4G OTA包 → 收到开始包 → 进入OTA模式 → 接收固件 → 写入Backup → 重启

详细代码流程(main.c):

int main(void)
{
    APP_LOG("Bootloader in..\n\n");

    // 1. 连接信号槽: Backup有效时搬运固件
    ss_connect(0, sig_back_up_is_completed_image, on_image_load_enter);

#if BOOT_ENABLE_UART_DFU
    // 2. UART DFU: Key1按下时进入
    ss_connect(3, sig_key1_push_down, on_uart_dfu_enter);
#endif

#if BOOT_ENABLE_USB_DFU
#if BOOT_USB_DFU_MODE == 0x00
    // 3a. USB DFU Mode 0: Key2按下时进入
    ss_connect(1, sig_key2_push_down, on_usb_dfu_enter);
#endif
    // 3b. USB DFU Mode 1/2/3: 检测DFU Flag时进入
    ss_connect(2, sig_usb_dfu_enter_check, on_usb_dfu_enter);
#endif

#if BOOT_ENABLE_PRF_OTA
    // 4. 2.4G OTA: 收到OTA开始包时进入
    ss_connect(4, sig_ota_start_received, on_prf_ota_enter);
#endif

    // 5. 处理所有信号事件
    ss_events_handle();

    // 6. 检查App分区是否有效
    if (fm_image_magic_check(FLASH_AREA_IMAGE_START) != true) {
        APP_LOG("\nWARNING: Cannot find a valid APP image!\n");
        SYS_ABORT();  // App无效,停止运行
    }

    // 7. 硬件清理(中断、外设、Cache等)
    boot_cleanup();

    // 8. 跳转到App
    jump_to_app();
    
    return 0;
}

4.3 USB DFU四种模式详解

Mode 0: GPIO触发(默认bootloader使用)

行为: 
  a. 跳转到App (cmd 0x04 DFU End, param 0x00)
  b. DFU结束

特点:
  - Bootloader自己负责提供进入DFU的方式(如按键)
  - App无法主动触发DFU
  - 适合EVB演示,不适合产品

配置:
  #define BOOT_USB_DFU_MODE    0x00
  #define BOOT_ENABLE_USB_DFU  1

Mode 1: 停留在Bootloader

行为:
  a. 擦除DFU Flag (cmd 0x06 DFU Force Upgrade, param 0x01)
  b. 停留在Bootloader,除非复位
  c. DFU结束

特点:
  - App需要设置DFU Flag来指示Bootloader进入DFU
  - 升级后停留在Bootloader,需手动复位才能进入新App
  - 适合调试场景

配置:
  #define BOOT_USB_DFU_MODE    0x01

Mode 2: 擦除Flag后跳转

行为:
  a. 擦除DFU Flag (cmd 0x06 DFU Force Upgrade, param 0x01)
  b. 跳转到App (cmd 0x04 DFU End, param 0x00)
  c. DFU结束

特点:
  - App需要设置DFU Flag
  - 升级后自动跳转到新App
  - 适合大多数产品场景

配置:
  #define BOOT_USB_DFU_MODE    0x02

Mode 3: App控制(推荐,solutions_hid使用)

行为:
  a. 跳转到App (cmd 0x04 DFU End, param 0x00)
  b. 等待App USB枚举完成
  c. App擦除DFU Flag (cmd 0x06 DFU Force Upgrade, param 0x01)
  d. DFU结束

特点:
  - App必须支持USB HID,并支持DFU cmd 0x06
  - App可以决定何时擦除Flag,灵活性最高
  - 适合HID设备(鼠标/键盘),可通过组合键触发

配置:
  #define BOOT_USB_DFU_MODE    0x03
  
App端实现(示例):
  // mult_ms/src/app_mouse_key.c
  void check_bootloader_entry(void)
  {
      // 检测到特定按键组合(如左键+右键+滚轮按下3秒)
      if (key_combo_detected()) {
          // 设置DFU Flag
          fm_set_dfu_flag();
          // 重启系统
          NVIC_SystemReset();
      }
  }

模式对比表:

模式

触发方式

升级后行为

App支持要求

适用场景

Mode 0

GPIO按键

跳转App

EVB演示

Mode 1

App设Flag

停留Boot

设Flag

调试

Mode 2

App设Flag

跳转App

设Flag

通用产品

Mode 3

App设Flag

跳转App+等待

USB HID+Cmd 0x06

HID设备

4.4 2.4G PRF OTA流程

OTA升级完整流程:

1. App端触发OTA:
   App → 发送OTA开始包(固定频点2412MHz,固定地址) → 重启

2. Bootloader接收:
   Bootloader → 监听2412MHz → 收到开始包 → 进入OTA模式

3. 固件传输:
   Dongle → 分包发送固件(每包256字节) → Bootloader接收 → 写入Backup分区

4. 校验完成:
   Bootloader → CRC校验 → 成功 → 设置Backup有效标志 → 重启

5. 固件搬运:
   Bootloader → 检测Backup有效 → 搬运到App分区 → 清除Backup标志 → 跳转到新App

关键代码(prf_ota.c):

void on_prf_ota_enter(void)
{
    APP_LOG_INFO("Enter 2.4G PRF OTA mode..\n");
    
    // 1. 初始化PRF射频
    prf_ota_rf_init();
    
    // 2. 擦除Backup分区
    FMC_EraseCodeArea(FLCTL, FLASH_AREA_BACK_UP_START, BACKUP_SIZE);
    
    // 3. 接收固件包
    while (1) {
        uint8_t pkt[PRF_OTA_PKT_LEN];
        prf_receive_packet(pkt);
        
        // 解析包类型
        if (pkt[0] == OTA_PKT_TYPE_DATA) {
            // 数据包: 写入Backup分区
            uint32_t offset = pkt[1] * PRF_OTA_PKT_LEN;
            FMC_WriteStream(FLCTL, 
                           FLASH_AREA_BACK_UP_START + offset, 
                           &pkt[2], 
                           PRF_OTA_PKT_LEN - 2);
        } else if (pkt[0] == OTA_PKT_TYPE_END) {
            // 结束包: CRC校验
            if (verify_crc()) {
                fm_image_make_valid(FLASH_AREA_BACK_UP_START);
                APP_LOG_INFO("OTA success, rebooting..\n");
                NVIC_SystemReset();
            } else {
                APP_LOG_ERR("OTA failed, CRC error!\n");
                SYS_ABORT();
            }
        }
    }
}

注意事项:

  • OTA期间不能断电,否则固件损坏

  • Backup分区必须有足够空间(≥App固件大小)

  • 建议在OTA前备份重要数据(KV Store)

4.5 Image Header结构

CONFIG_APP_USE_IMAGE_HEADER=1时,App固件开头包含Image Header:

// soc/img_hdr.h
typedef struct img_hdr {
    uint32_t magic;          // 0x4F544148 ("HTAO") - Magic Number
    uint32_t header_size;    // Header大小(通常32字节)
    uint32_t firmware_size;  // 固件大小(不含Header)
    uint32_t firmware_crc;   // 固件CRC32校验值
    struct image_version {
        uint8_t major;       // 主版本号
        uint8_t minor;       // 次版本号
        uint16_t revision;   // 修订版本号
        uint32_t build_num;  // 构建号
    } version;
    uint32_t reserved[4];    // 保留字段
} image_header_t;

Header作用:

  1. Magic校验: Bootloader通过magic判断App是否有效

  2. CRC校验: 确保固件完整性

  3. 版本管理: 支持版本比较,防止降级

  4. 大小信息: Bootloader知道需要搬运多少数据

Jump到App时的偏移:

static void jump_to_app(void)
{
    // 跳过Image Header,从Header后的代码开始执行
    uint32_t msp = *(volatile uint32_t*)(FLASH_AREA_IMAGE_START + APP_IMG_HEADER_SIZE);
    uint32_t addr = *(uint32_t*)(FLASH_AREA_IMAGE_START + APP_IMG_HEADER_SIZE + 4);
    
    FLCTL->X_FL_REMAP_ADDR = FLASH_AREA_IMAGE_START + APP_IMG_HEADER_SIZE;
    __set_MSP(msp);
    
    void (*app_init)() = (void(*)())(uint32_t*)addr;
    app_init();
}

5 开发说明

5.1 关键配置宏

Bootloader配置(sdk_config.h):

宏定义

默认值

说明

BOOT_ENABLE_UART_DFU

0

使能UART DFU(XMODEM协议)

BOOT_ENABLE_USB_DFU

1

使能USB DFU

BOOT_USB_DFU_MODE

0x03

USB DFU模式(0/1/2/3)

BOOT_ENABLE_PRF_OTA

1

使能2.4G PRF OTA

CONFIG_FLASH_SIZE

0x7F000

Flash总大小(508KB)

CONFIG_FLASH_PARTITION_BOOTLOADER_SIZE_KB

40

Bootloader分区大小

CONFIG_FLASH_PARTITION_APP_SIZE_KB

220

App分区大小

CONFIG_FLASH_PARTITION_APP_BACKUP_SIZE_KB

220

Backup分区大小

CONFIG_FLASH_PARTITION_KVSTORE_SIZE_KB

16

KV Store分区大小

CONFIG_FLASH_PARTITION_USER_CUSTOM_SIZE_KB

12

用户自定义分区大小

CONFIG_APP_USE_IMAGE_HEADER

1

使用Image Header

CFG_RF_MANUFACTOR_TEST

0

RF产测模式

App配置(sdk_config.h):

宏定义

默认值

说明

CONFIG_ENABLE_BOOTLOADER

1

使能Bootloader(必须与Boot工程一致)

CONFIG_APP_CID

0x1234

客户ID(防误升级)

重要提示:

  • Bootloader和App的Flash分区配置必须完全一致

  • CONFIG_ENABLE_BOOTLOADER只在App工程中配置,Boot工程中不需要

  • 修改分区大小后,必须重新编译Bootloader和App

5.2 信号槽机制

Signal-Slot架构:

Bootloader使用信号槽机制实现事件驱动,便于扩展新的DFU方式。

核心组件:

// signal_slot_manager.h
typedef void (*signal_handler_t)(void);

// 连接信号和处理函数
void ss_connect(uint8_t signal_id, bool (*condition)(void), signal_handler_t handler);

// 处理所有信号事件
void ss_events_handle(void);

预定义信号:

信号ID

条件函数

处理函数

说明

0

sig_back_up_is_completed_image()

on_image_load_enter()

Backup有效,搬运固件

1

sig_key2_push_down()

on_usb_dfu_enter()

Key2按下,进入USB DFU(Mode 0)

2

sig_usb_dfu_enter_check()

on_usb_dfu_enter()

检测到DFU Flag,进入USB DFU

3

sig_key1_push_down()

on_uart_dfu_enter()

Key1按下,进入UART DFU

4

sig_ota_start_received()

on_prf_ota_enter()

收到OTA开始包,进入OTA

添加自定义信号示例:

// 1. 在signal.c中添加条件函数
bool sig_custom_trigger(void)
{
    // 例如: 检测特定GPIO状态
    return (GPIO_ReadPin(GPIOA, PIN_5) == 0);
}

// 2. 在main.c中连接信号
void on_custom_enter(void)
{
    APP_LOG_INFO("Custom DFU mode entered\n");
    // 自定义DFU逻辑
}

int main(void)
{
    // ... 其他信号连接 ...
    
    // 连接自定义信号
    ss_connect(5, sig_custom_trigger, on_custom_enter);
    
    ss_events_handle();
    // ...
}

5.3 硬件清理机制

跳转到App前的清理工作(boot_cleanup):

static void boot_cleanup(void)
{
    // 1. 关闭所有中断
    __disable_irq();

    // 2. 恢复信号模块使用的硬件
    sig_hardware_recovery();

    // 3. 重置所有外设(除了eFuse和GPIO)
    CLK->IPRST0 = 0x1CC;  // 重置特定模块
    CLK->IPRST0 = 0x0;
    CLK->IPRST1 = 0x17FFF; // 重置更多模块
    CLK->IPRST1 = 0x0;

    // 4. 禁用并清除所有NVIC中断
    NVIC->ICER[0U] = 0xFFFFFFFF;  // Disable all IRQs
    NVIC->ICPR[0U] = 0xFFFFFFFF;  // Clear pending IRQs

    // 5. Flash workaround(避免x4模式切换失败)
    FMC_ReadByte(FLCTL, 0x0, CMD_DREAD);

    // 6. 禁用I-Cache(App的SystemInit会重新启用)
    CR->X_CACHE_EN = 0x00;
    
    __enable_irq();
}

为什么需要清理:

  • 避免Bootloader的中断干扰App

  • 防止外设状态不一致导致App异常

  • 确保App从干净的状态启动

5.4 Merged固件生成流程

post_build_app.bat脚本流程:

:: 1. 生成binary和disassembly文件
fromelf.exe --bin --output "mult_ms.bin" "mult_ms.axf"
fromelf.exe --text -acd --output "mult_ms.disasm" "mult_ms.axf"

:: 2. 复制hex文件
copy "mult_ms.hex" "Images\mult_ms.hex"

:: 3. 解析配置宏
armcc.exe -P "soc_config.h" -I"configuration" --list-macros -o "final_config.h"

:: 4. 如果使能加密,生成加密固件
if CONFIG_FIRMWARE_ENCRYPTION == 1:
    python encode_yaml.py -i "." -o "Images"
    python encrypt_tool.py "mult_ms.hex" "encrypt_info.yaml"

:: 5. 生成签名固件
python sign_image.py "mult_ms.hex"
:: 生成 mult_ms.signed.hex

:: 6. 合并Bootloader + App
python merge_images.py -u "mult_ms.uvprojx" -c "final_config.h" -a "mult_ms.signed.hex"
:: 生成 mult_ms.merged.hex

:: 7. 打印资源使用情况
python flash_ram_usage.py "mult_ms.map"

merge_images.py工作流程:

def main():
    # 1. 解析App配置文件
    macros = parse_c_header_macros(config_file_path)
    
    # 2. 检查是否使能Bootloader
    enable_bootloader = get_macro_value(macros, "CONFIG_ENABLE_BOOTLOADER")
    
    if not enable_bootloader:
        print("Bootloader is disabled, skip merging.")
        return
    
    # 3. 查找Bootloader工程路径
    bl_project_path = find_bootloader_project(uvision_file_path)
    
    # 4. 检查Bootloader是否已编译
    bl_hex_file = f"{bl_project_path}Images/bootloader.hex"
    if not os.path.exists(bl_hex_file):
        print("Error: Bootloader not compiled yet!")
        print("Please compile bootloader project first.")
        sys.exit(1)
    
    # 5. 读取Bootloader和App的hex文件
    bl_data = read_hex_file(bl_hex_file)
    app_data = read_hex_file(app_image_file_path)
    
    # 6. 合并数据(App覆盖Bootloader之后的区域)
    merged_data = merge_hex_data(bl_data, app_data)
    
    # 7. 生成merged hex文件
    output_file = app_image_file_path.replace(".hex", ".merged.hex")
    write_hex_file(output_file, merged_data)
    
    print(f"Merged image generated: {output_file}")

重要提醒:

  • 必须先编译Bootloader,再编译App

  • 如果Bootloader未编译,merge_images.py会报错退出

  • Merged固件包含完整的Bootloader + App,可直接烧录

  • 后续OTA升级只需烧录App固件

5.5 日志与调试

Bootloader日志级别:

// app_log.h 中配置
#define APP_LOG_EN    1  // 使能日志
#define APP_LOG_LVL   3  // 0:None, 1:Error, 2:Warning, 3:Info, 4:Debug

典型日志输出:

// 正常启动
Bootloader in..

Clean up and try to jump into app image..
- app partition start addr: 0xA000
- app partition size      : 220 KB
- app image header size   : 32 B

// Backup有效,搬运固件
Try to move image from App Backup Partition to App Partition..
Image moved successfully.

// USB DFU进入
Enter USB DFU mode..
Waiting for DFU tool connection...

// 2.4G OTA进入
Enter 2.4G PRF OTA mode..
Receiving firmware packets...

// App无效
WARNING: Cannot find a valid APP image in App Flash Partition!
[System Abort]

调试建议:

  • 首次使用时使能日志(APP_LOG_LVL=3)

  • 量产时可关闭日志(APP_LOG_EN=0)节省Flash

  • 通过UART日志监控Bootloader状态

6 RAM/Flash资源使用情况

6.1 PAN107x资源配置

Flash Total: 508 KB (0x7F000)
SRAM Total:  48 KB (0xC000)

Bootloader实际使用情况(PAN107):

Memory Region

Start Address

Used Size

Region Size

%age Used

FLASH_TEXT

0x00000000

32,158 B

40.00 KB

78.51%

SRAM_DATA_TEXT

0x20000000

15,688 B

48.00 KB

31.92%

说明:

  • Flash占用: 32,158字节 ≈ 31.4 KB (Bootloader代码和数据)

  • RAM占用: 15,688字节 ≈ 15.3 KB (系统栈、堆、USB/PRF缓冲区)

  • Flash分区: Bootloader占用40KB分区中的78.51%,剩余空间可用于扩展

  • RAM充裕: SRAM使用率仅31.92%,有充足空间用于DFU缓冲区

6.2 PAN101x资源配置

Flash Total: 252 KB (0x3F000)
SRAM Total:  16 KB (0x4000)

Bootloader实际使用情况(PAN101):

Memory Region

Start Address

Used Size

Region Size

%age Used

FLASH_TEXT

0x00000000

32,158 B

40.00 KB

78.51%

SRAM_DATA_TEXT

0x20000000

15,544 B

16.00 KB

94.87%

说明:

  • Flash占用: 32,158字节 ≈ 31.4 KB (与PAN107相同)

  • RAM占用: 15,544字节 ≈ 15.2 KB (比PAN107略少144字节)

  • Flash分区: Bootloader占用40KB分区中的78.51%,剩余空间可用于扩展

  • ⚠️ RAM非常紧张: SRAM使用率达94.87%,仅剩约824字节,需特别注意内存优化

6.3 平台对比分析

PAN107 vs PAN101 资源对比:

平台

Flash总大小

SRAM总大小

Bootloader Flash

Bootloader RAM

RAM使用率

PAN107

508 KB

48 KB

31.4 KB

15.3 KB

31.92%

PAN101

252 KB

16 KB

31.4 KB

15.2 KB

94.87% ⚠️

关键差异:

  1. Flash代码大小相同: 两个平台的Bootloader代码完全一样(31.4KB)

  2. RAM差异巨大:

    • PAN107有48KB RAM,使用率仅31.92%

    • PAN101只有16KB RAM,使用率高达94.87%

  3. PAN101 RAM余量极小: 仅剩约824字节可用

资源占用分析(PAN107/PAN101通用):

  • Flash主要占用:

    • USB DFU协议栈: ~12KB

    • 2.4G PRF OTA: ~8KB

    • Flash管理(FMC): ~4KB

    • 信号槽机制: ~2KB

    • 应用层代码: ~5.4KB

  • RAM主要占用:

    • USB缓冲区: ~3KB

    • PRF接收缓冲: ~2KB

    • 系统栈和堆: ~6KB

    • 全局变量: ~4.3KB

优化建议:

PAN107平台(推荐配置):

  1. 关闭UART DFU: 已默认关闭,节省~3KB Flash

  2. 关闭日志: APP_LOG_EN=0 可节省~2KB Flash

  3. 精简PRF OTA: 如不需要2.4G升级,BOOT_ENABLE_PRF_OTA=0 节省~8KB

  4. 最小配置: 只保留USB DFU,Flash可降至~20KB

PAN101平台(RAM紧张,必须优化): ⚠️

  1. 🔴 必须关闭日志: APP_LOG_EN=0 节省~2KB Flash + ~1KB RAM

  2. 🔴 必须关闭PRF OTA: BOOT_ENABLE_PRF_OTA=0 节省~8KB Flash + ~2KB RAM

  3. 🔴 减小USB缓冲区: 从3KB减至2KB,节省1KB RAM

  4. 🔴 优化全局变量: 检查并减少静态变量使用,目标节省1-2KB RAM

  5. 🟡 考虑移除部分功能: 如不需要2.4G OTA,强烈建议禁用

PAN101优化后预期:

优化前: Flash 31.4KB, RAM 15.2KB (94.87%)
优化后: Flash ~20KB, RAM ~12KB (75%)  ← 更安全

资源对比(PAN107):

配置项

Flash占用

RAM占用

说明

完整配置(USB+PRF)

31.4 KB

15.3 KB

当前默认配置(PAN107)

仅USB DFU

~24KB

~13KB

关闭PRF OTA

最小配置

~20KB

~12KB

关闭日志+UART

含UART DFU

~35KB

~16KB

启用UART XMODEM

资源对比(PAN101): ⚠️

配置项

Flash占用

RAM占用

RAM使用率

可行性

完整配置(USB+PRF)

31.4 KB

15.2 KB

94.87%

❌ 不推荐,RAM太紧张

仅USB DFU

~24KB

~13KB

81.25%

⚠️ 勉强可用

最小配置

~20KB

~12KB

75.00%

✅ 推荐配置

含UART DFU

~35KB

~14KB

87.50%

❌ 不推荐

重要提示:

  • PAN101平台RAM仅16KB,强烈建议使用最小配置(仅USB DFU,关闭日志和PRF OTA)

  • PAN107平台RAM充足(48KB),可使用完整配置

7 常见问题

7.1 Bootloader编译成功,但merge_images.py报错

错误信息:

Error: Bootloader not compiled yet!
Please compile bootloader project first.

原因:

  • App编译时,merge_images.py找不到Bootloader的hex文件

解决方法:

  1. 先编译Bootloader工程(bootloader.uvprojx)

  2. 确认Images/bootloader.hex存在

  3. 再编译App工程

7.2 烧录后无法启动App

可能原因:

  1. App分区Magic无效

  2. Flash分区配置不一致

  3. Image Header损坏

排查步骤:

1. 查看Bootloader日志:
   "Cannot find a valid APP image" → App未正确烧录

2. 检查分区配置:
   Bootloader和App的sdk_config.h中分区大小必须一致

3. 重新烧录merged固件:
   烧录 mult_ms.merged.hex(不是mult_ms.hex)

7.3 USB DFU无法进入

可能原因:

  1. DFU Flag未设置

  2. USB枚举失败

  3. Mode配置错误

解决方法:

// 检查App是否正确设置DFU Flag
void trigger_dfu(void)
{
    // 方式1: 直接写入Flash
    FMC_WriteWord(DFU_FLAG_ADDR, DFU_FLAG_MAGIC);
    
    // 方式2: 通过KV Store
    kv_store_set("dfu_flag", &flag, sizeof(flag));
    
    // 重启
    NVIC_SystemReset();
}

// 检查Bootloader配置
#define BOOT_USB_DFU_MODE    0x03  // 必须是Mode 3
#define BOOT_ENABLE_USB_DFU  1     // 必须使能

7.4 2.4G OTA升级失败

可能原因:

  1. 射频干扰

  2. Backup分区空间不足

  3. CRC校验失败

解决方法:

1. 检查射频环境:
   - 远离WiFi路由器(2.4GHz干扰)
   - 缩短Dongle和设备距离(<5米)

2. 验证分区大小:
   CONFIG_FLASH_PARTITION_APP_BACKUP_SIZE_KB >= App固件大小

3. 查看日志:
   "OTA failed, CRC error" → 传输过程中数据损坏
   重新尝试OTA

7.5 分区大小修改后启动异常

原因:

  • Bootloader和App的分区配置不一致

解决方法:

1. 修改Bootloader的sdk_config.h:
   CONFIG_FLASH_PARTITION_APP_SIZE_KB = 新值
   
2. 修改App的sdk_config.h:
   保持相同的分区配置
   
3. 重新编译Bootloader
4. 重新编译App
5. 烧录新的merged固件

8 与默认Bootloader的详细对比

8.1 配置差异

配置项

solutions_hid

默认bootloader

说明

BOOT_ENABLE_UART_DFU

0(禁用)

1(启用)

HID设备不需要UART

BOOT_ENABLE_USB_DFU

1

1

都支持USB DFU

BOOT_USB_DFU_MODE

0x03(Mode 3)

0x00(Mode 0)

HID用App控制,EVB用按键

BOOT_ENABLE_PRF_OTA

1

1

都支持2.4G OTA

应用场景

HID产品

EVB演示

定位不同

8.2 进入方式差异

solutions_hid(产品级):

触发方式: App设置DFU Flag + 重启
优点: 
  - 可通过按键组合触发(用户体验好)
  - 无需额外硬件(GPIO)
  - 灵活可控
缺点:
  - App必须实现触发逻辑

默认bootloader(演示级):

触发方式: GPIO按键(Key1/Key2)
优点:
  - 简单直接
  - 不依赖App
缺点:
  - 需要预留GPIO
  - 用户操作不便

8.3 代码差异

main.c信号槽连接编号:

// solutions_hid/bootloader/main.c
ss_connect(0, sig_back_up_is_completed_image, on_image_load_enter);
ss_connect(3, sig_key1_push_down, on_uart_dfu_enter);      // UART禁用,但保留
ss_connect(1, sig_key2_push_down, on_usb_dfu_enter);       // Mode 0时使用
ss_connect(2, sig_usb_dfu_enter_check, on_usb_dfu_enter);  // Mode 1/2/3使用
ss_connect(4, sig_ota_start_received, on_prf_ota_enter);

// 默认bootloader/main.c
ss_connect(0, sig_back_up_is_completed_image, on_image_load_enter);
ss_connect(1, sig_key1_push_down, on_uart_dfu_enter);      // 编号不同
ss_connect(2, sig_key2_push_down, on_usb_dfu_enter);       // 编号不同
ss_connect(3, sig_usb_dfu_enter_check, on_usb_dfu_enter);  // 编号不同
ss_connect(4, sig_ota_start_received, on_prf_ota_enter);

注意: 信号槽编号只是内部标识,不影响功能,但两个工程的编号不完全一致。

8.4 选择建议

使用solutions_hid/bootloader的场景:

  • ✅ HID产品(鼠标/键盘/手柄)

  • ✅ 需要通过按键组合触发DFU

  • ✅ 不需要UART调试接口

  • ✅ 追求更好的用户体验

使用默认bootloader的场景:

  • ✅ EVB开发板演示

  • ✅ 需要UART DFU作为备用升级方式

  • ✅ 快速原型验证

  • ✅ 调试阶段