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 |
禁用( |
启用( |
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
打开Keil工程文件
bootloader.uvprojx选择目标芯片型号(PAN1070/PAN1010)
修改
sdk_config.h中的配置(见第5章)点击”Rebuild”编译工程
编译成功后,在
Images/目录下生成:bootloader.bin: 二进制文件bootloader.hex: Intel HEX格式bootloader.disasm: 反汇编文件
步骤2: 编译App并生成Merged固件
打开App工程(如
mult_ms.uvprojx)确保
CONFIG_ENABLE_BOOTLOADER=1(在App的sdk_config.h中)编译App工程
自动执行后处理脚本(
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作用:
Magic校验: Bootloader通过magic判断App是否有效
CRC校验: 确保固件完整性
版本管理: 支持版本比较,防止降级
大小信息: 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):
宏定义 |
默认值 |
说明 |
|---|---|---|
|
0 |
使能UART DFU(XMODEM协议) |
|
1 |
使能USB DFU |
|
0x03 |
USB DFU模式(0/1/2/3) |
|
1 |
使能2.4G PRF OTA |
|
0x7F000 |
Flash总大小(508KB) |
|
40 |
Bootloader分区大小 |
|
220 |
App分区大小 |
|
220 |
Backup分区大小 |
|
16 |
KV Store分区大小 |
|
12 |
用户自定义分区大小 |
|
1 |
使用Image Header |
|
0 |
RF产测模式 |
App配置(sdk_config.h):
宏定义 |
默认值 |
说明 |
|---|---|---|
|
1 |
使能Bootloader(必须与Boot工程一致) |
|
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 |
|
|
Backup有效,搬运固件 |
1 |
|
|
Key2按下,进入USB DFU(Mode 0) |
2 |
|
|
检测到DFU Flag,进入USB DFU |
3 |
|
|
Key1按下,进入UART DFU |
4 |
|
|
收到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% ⚠️ |
关键差异:
Flash代码大小相同: 两个平台的Bootloader代码完全一样(31.4KB)
RAM差异巨大:
PAN107有48KB RAM,使用率仅31.92%
PAN101只有16KB RAM,使用率高达94.87%
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平台(推荐配置):
✅ 关闭UART DFU: 已默认关闭,节省~3KB Flash
✅ 关闭日志:
APP_LOG_EN=0可节省~2KB Flash✅ 精简PRF OTA: 如不需要2.4G升级,
BOOT_ENABLE_PRF_OTA=0节省~8KB✅ 最小配置: 只保留USB DFU,Flash可降至~20KB
PAN101平台(RAM紧张,必须优化): ⚠️
🔴 必须关闭日志:
APP_LOG_EN=0节省~2KB Flash + ~1KB RAM🔴 必须关闭PRF OTA:
BOOT_ENABLE_PRF_OTA=0节省~8KB Flash + ~2KB RAM🔴 减小USB缓冲区: 从3KB减至2KB,节省1KB RAM
🔴 优化全局变量: 检查并减少静态变量使用,目标节省1-2KB RAM
🟡 考虑移除部分功能: 如不需要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文件
解决方法:
先编译Bootloader工程(
bootloader.uvprojx)确认
Images/bootloader.hex存在再编译App工程
7.2 烧录后无法启动App¶
可能原因:
App分区Magic无效
Flash分区配置不一致
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无法进入¶
可能原因:
DFU Flag未设置
USB枚举失败
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升级失败¶
可能原因:
射频干扰
Backup分区空间不足
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 |
说明 |
|---|---|---|---|
|
0(禁用) |
1(启用) |
HID设备不需要UART |
|
1 |
1 |
都支持USB DFU |
|
0x03(Mode 3) |
0x00(Mode 0) |
HID用App控制,EVB用按键 |
|
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作为备用升级方式
✅ 快速原型验证
✅ 调试阶段