概念先知道
- 分区表(Partition Table):一张“地图”,告诉芯片 Flash 里每一块区域叫什么、干什么用(固件、分区表本身、PSM 掉电保存、OTA 备份等),以及每块的地址和大小。
- 双备份机制:Flash 中存有两份分区表(表 0 / 表 1),启动时选择其中有效的一份,防止擦写中断导致“地图”损坏。
- 激活分区(active partition):当前实际使用的那份分区表;例程用
pt_table_get_active_partition_need_lock获取并打印它。 - 条目字段:每条分区记录包含
type(类型)、device(设备号)、active_index(激活索引)、name(名称)、start_address[2]/max_len[2](两个备份区地址与长度)、age(老化计数,用于双表切换)。
例程功能简介
本页对应博流官方 SDK 的 partition 例程(examples/partition),演示读取并打印激活分区表:
- 注册 Flash 擦除/写/读回调(
pt_table_set_flash_operation),分区表组件通过这些回调访问 Flash; - 调用
pt_table_get_active_partition_need_lock读取激活分区表(自动完成双表校验与切换); _dump_partition打印表头与每个分区条目,包含 magicCode、version、entryCnt、age、crc32 及各条目地址/长度/名称。- 该例程输出与烧录进 Flash 的
partition*.bin(由分区配置文件生成)对应,可用于核对实际分区布局。
提示
分区表的实际布局由构建配置(partition_cfg / pt 文件)决定,本页例程只是“读出来打印”。修改分区布局属于高风险操作:改错会让固件找不到启动分区,一般保持官方默认分区即可。
操作步骤
本页不需要额外接线。在终端进入 SDK 的分区表例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/partition执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可(该例程依赖 CONFIG_PARTITION,SDK 已在其 defconfig 中打开):
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000)。程序读取激活的分区表并打印:表头信息(magicCode、version、entryCnt、age、crc32)和每一行的分区条目(序号、type、device、active_index、name、两个地址、两个长度、age)。
代码执行流程
例程从启动到打印分区表的完整流程如下:
例程调用的 API 介绍
pt_table_set_flash_operation(erase, write, read)
把分区表组件读写 Flash 的操作函数注册为 SDK 的 bflb_flash_erase / bflb_flash_write / bflb_flash_read。
参数:
erase:擦除回调write:写入回调read:读取回调
返回值:无
pt_table_get_active_partition_need_lock(ptStuff[2])
读取激活的分区表(内部带锁,线程安全)。读取时自动校验两份表,返回有效的那份。
参数:
ptStuff:pt_table_stuff_config数组(2 份表的空间)
返回值:pt_table_id_type(PT_TABLE_ID_0 / PT_TABLE_ID_1;失败为 PT_TABLE_ID_INVALID)
pt_table_get_active_entries_by_name(pt_stuff, name, pt_entry)
按名称查找分区条目(例程未调用,实际项目常用):如按 "PSM"、"FW" 等名称获取对应分区地址与大小。
参数:
pt_stuff:分区表内容name:分区名称pt_entry:接收条目信息
返回值:pt_table_error_type(成功为 PT_SUCCESS)
完整代码
以下为 partition/main.c 完整源码,与官方示例(examples/partition)逐字一致,默认折叠,点击展开:
📜 点击展开 partition/main.c 完整代码
#include "bflb_flash.h"
#include "partition.h"
#include "board.h"
#define DBG_TAG "MAIN"
#include "log.h"
#define USER_UNUSED(a) ((void)(a))
static void _dump_partition(pt_table_stuff_config *part)
{
int i;
USER_UNUSED(i);
USER_UNUSED(part);
printf("======= PtTable_Config @%p=======\r\n", part);
printf("magicCode 0x%08X;", (unsigned int)(part->pt_table.magicCode));
printf(" version 0x%04X;", part->pt_table.version);
printf(" entryCnt %u;", part->pt_table.entryCnt);
printf(" age %lu;", part->pt_table.age);
printf(" crc32 0x%08X\r\n", (unsigned int)part->pt_table.crc32);
printf("idx type device active_index name Address[0] Address[1] Length[0] Length[1] age\r\n");
for (i = 0; i < part->pt_table.entryCnt; i++) {
printf("[%02d] ", i);
printf(" %02u", part->pt_entries[i].type);
printf(" %u", part->pt_entries[i].device);
printf(" %u", part->pt_entries[i].active_index);
printf(" %8s", part->pt_entries[i].name);
printf(" 0x%08lx", (part->pt_entries[i].start_address[0]));
printf(" 0x%08lx", (part->pt_entries[i].start_address[1]));
printf(" 0x%08lx", (part->pt_entries[i].max_len[0]));
printf(" 0x%08lx", (part->pt_entries[i].max_len[1]));
printf(" %lu\r\n", (part->pt_entries[i].age));
}
}
int main(void)
{
board_init();
pt_table_set_flash_operation(bflb_flash_erase, bflb_flash_write, bflb_flash_read);
pt_table_stuff_config ptstuff[2];
pt_table_id_type active_id;
active_id = pt_table_get_active_partition_need_lock(ptstuff);
if (active_id < PT_TABLE_ID_INVALID) {
_dump_partition(&ptstuff[active_id]);
} else {
printf("partition init fail!\r\n");
}
while (1) {
}
}FAQ
打印 partition init fail
分区表读取失败。常见原因:Flash 中分区表被破坏(两份都无效)、分区表地址与固件不匹配,或工程没有启用 CONFIG_PARTITION。重新完整烧录固件(含 partition*.bin)可恢复。
打印的条目与期望布局不一致
打印内容来自实际烧入 Flash 的分区表。核对构建用的分区配置文件(partition_cfg / pt 文件)与烧录的 partition*.bin 是否一致;修改分区配置后必须重新生成并烧录分区表。
如何按名称找到某个分区
使用 pt_table_get_active_entries_by_name(pt_stuff, "PSM", &entry),通过返回的 pt_entry 拿到 start_address 与 max_len,再配合 MTD 接口读写该分区。
遇到问题?
如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

