杰理 SDK 文档中心
首页
首页
  • 项目概览

    • SDK 简介与核心特性
    • 芯片平台与硬件资料
    • SDK 版本与发布信息
  • 快速开始

    • 环境搭建与工具链
    • 编译工程
    • 烧录与量产工具
  • 工程结构与构建系统

    • 工程目录布局
    • 构建与链接配置
  • 应用层开发

    • mbox_flash 应用框架
    • 板级支持包 (BSP)
    • 公共应用模块
    • UI 显示子系统
  • 蓝牙子系统

    • BLE 控制器、链路层与 HCI 传输
    • GATT 服务框架
    • BLE 应用示例:遥控器 / Dongle / 对讲机
    • 经典蓝牙支持
  • 音频子系统

    • 音频编解码器
    • 音频设备接口 (DAC / ADC / APA)
    • 音效处理与 EQ
    • 播放、录音与 MIO 工作流
  • 设备与文件系统

    • 存储设备驱动 (NorFlash / SDMMC / USB)
    • 文件系统 (FAT / nor_fs / SYDF)
    • 设备管理框架 (dev_mg)
  • 系统服务与电源管理

    • 消息机制 (msg / hot_msg)
    • 配置与参数存储 (app_config / VM)
    • 电源管理 (SOFT OFF / POWER DOWN)
  • 固件升级

    • 升级框架总览 (code_v1 / code_v2)
    • 双 Bank 升级机制
    • 升级通道:UART / 测试盒 / BLE OTA / USB / SD
  • 补丁包与版本维护

    • 版本升级补丁链 (v1.1.0 → v1.4.0)
    • 问题修复补丁
    • 固件裁剪与资源优化
  • 开发工具与支持

    • 辅助工具与脚本
    • 文档、配置说明与常见问题

配置与参数存储 (app_config / VM)

本文档介绍 AW30N BLE SDK 中的两大基础机制:app_config 配置体系(编译期宏配置 app_config.h + 运行期全局配置变量 app_config.c)与 VM 参数存储系统(旧 vm 接口与新 new_vm 接口),说明它们如何协作,为整个 SDK 提供"板级参数定义"与"掉电保存的用户参数存储"能力。

Purpose and Scope

本页覆盖以下内容:

  • app_config 配置体系:sdk/apps/app/src/mbox_flash/app_config.h 中成百上千个 TCFG_* / KEY_* 等编译期宏,以及 app_config.c 中定义的运行期全局配置变量(中断优先级、日志开关、升级使能等)。
  • VM 参数存储:包括旧 VM 接口(vm.h / vm_api.c,通过 vm_buff_alloc 向 VM 库提供索引缓冲)与新 VM 接口(new_vm.h / nvm_api.c,由预编译库 new_vm_lib.a 实现),覆盖其缓存结构、双备份区、磨损均衡与掉电保护机制。

本页不展开的内容(属于其它目录页):

  • 具体蓝牙业务的配置变体,如 hid_app_config.h、spple_app_config.h、test_app_config.h,它们只是本页所述配置体系在各自业务模块上的裁剪实例。
  • OTA 升级、测试盒通信等具体业务实现(仅在本页提及 CONFIG_UPDATE_* 使能开关)。
  • 补丁包中针对特定版本的修复细节(如 v1.3.3→v1.3.4 的 new_vm 格式整理修复),仅在本页"故障模式"中作为已知问题引用。

Overview

为什么需要 app_config

AW30N 是一颗低功耗蓝牙 SoC,同一份 SDK 需要适配多种产品形态(不同时钟、按键矩阵、充电方案、音频配置)。app_config.h 将这些可变项收敛为编译期宏:通过修改宏即可裁剪模块、调整 IO 与参数,而无需改动各模块源码。app_config.c 则存放运行期只读的全局配置变量(中断优先级、日志标签开关、升级功能使能等),供 SDK 各库在运行时查询。

为什么需要 VM

VM(Virtual Memory,虚拟内存)是 JieLi SDK 在 SPI NOR Flash 上实现的参数存储系统,用于保存掉电后仍需保留的数据:蓝牙配对信息、音量、EQ、用户自定义参数等。与直接读写 Flash 相比,VM 提供:

  • 按 ID 寻址:应用通过 id 读写参数,无需关心物理地址。
  • 磨损均衡:参数频繁改写时分散到不同物理块,延长 Flash 寿命。
  • 双备份区与掉电保护:数据区分为 A/B 两块,配合 CRC 与格式整理(format_another),降低掉电导致的数据损坏概率。
  • 缓存加速:通过 NVM_CACHE 在 RAM 中维护 ID→偏移映射,避免每次读写都扫描 Flash。

Architecture

flowchart TD
    subgraph sg_Config["配置层 (app_config)"]
        H["app_config.h<br/>TCFG_* 编译期宏"]
        C["app_config.c<br/>const 全局配置变量"]
    end

    subgraph sg_App["应用层"]
        APP["业务模块<br/>配对 / 音量 / 自定义参数"]
    end

    subgraph sg_VM["参数存储层 (VM)"]
        OLD["旧 VM 接口<br/>vm.h + vm_api.c (vm_buff_alloc)"]
        NEW["新 VM 接口<br/>new_vm.h + nvm_api.c (nvm_*_api)"]
        CACHE["NVM_CACHE / NVM_ENTRY<br/>RAM 索引缓存"]
    end

    subgraph sg_Lib["预编译库"]
        LIB["new_vm_lib.a<br/>nvm_init / nvm_read / nvm_write ..."]
    end

    subgraph sg_Flash["硬件层"]
        SFC["SFC 控制器 (vm_sfc.h)"]
        FLASH[("SPI NOR Flash<br/>双备份区 A/B")]
    end

    H -->|"裁剪/定义"| C
    C -->|"提供常量"| APP
    APP -->|"nvm_read_api / nvm_write_api"| NEW
    APP -->|"vm_read / vm_write"| OLD
    NEW --> CACHE
    NEW --> LIB
    OLD --> LIB
    LIB --> SFC
    SFC --> FLASH

架构说明:

  • 配置层:app_config.h 的宏在编译期决定模块是否编译、参数默认值;app_config.c 的 const 变量在运行期被 SDK 库读取。两者共同构成"板级配置"的唯一入口。
  • 参数存储层:新 VM(new_vm)是当前推荐的参数存储方案,提供 nvm_*_api 应用接口;旧 VM(vm)为兼容接口,vm_api.c 通过 vm_buff_alloc 为 VM 库分配索引缓冲(含 CRC 空间)。两者底层都经由预编译库 new_vm_lib.a 访问 SFC 控制器,最终读写 SPI NOR Flash。
  • 缓存:NEW_VM_OBJ 内嵌 NVM_CACHE,保存 ID→偏移的映射,避免每次读写都全盘扫描。

核心数据结构

new_vm.h 定义了 VM 的核心数据结构,是整个参数存储系统的基础:

typedef struct __nvm_entry {
    u16 id;
    u16 rw_cnt;
    u32 offset;
} NVM_ENTRY;

typedef struct __nvm_cache {
    u16 rw_cnt;
    u16 number_entry;
    NVM_ENTRY *entries;
} NVM_CACHE;

Source: new_vm.h

  • NVM_ENTRY:缓存表项,id 为参数 ID,rw_cnt 为该 ID 的读写计数(用于磨损均衡/整理判断),offset 为参数在当前块中的偏移。
  • NVM_CACHE:缓存整体,rw_cnt 为总读写计数,number_entry 为表项个数,entries 指向 NVM_ENTRY 数组。

NEW_VM_OBJ 是 VM 运行期对象,通过位域压缩存储关键状态:

typedef struct __new_vm_obj {
    void *device;        // 底层存储设备句柄
    NVM_CACHE *cache;    // RAM 缓存
    u32 addr;            // VM 区域起始地址
    u32 reserve : 7;     // 保留位
    u32 bool_block : 1;  // 当前使用块标记
    u32 block_size : 24; // 块大小(bitfield 24 位)
    u32 area_len;        // VM 区域总长度
    u32 w_offset;        // 当前写偏移
    u16 pre_sec_a;       // 预擦除区 A
    u16 pre_sec_b;       // 预擦除区 B
    u16 id;              // 最近操作 ID
    u16 offset;          // 最近操作偏移
} NEW_VM_OBJ;

Source: new_vm.h

设计意图:VM 区域被划分为两块(由 bool_block 标记当前使用块),pre_sec_a / pre_sec_b 记录两块的"预擦除"状态,w_offset 追踪当前写位置。这种双块设计配合 nvm_format_another 实现磨损均衡与掉电安全(详见下文核心流程)。

app_config 配置体系详解

编译期宏配置 (app_config.h)

app_config.h 是 SDK 顶层板级配置头文件,位于 sdk/apps/app/src/mbox_flash/app_config.h。它通过大量 #define 提供两类开关:

  • 模块使能开关:ENABLE_THIS_MOUDLE(1) / DISABLE_THIS_MOUDLE(0),以及通用 ENABLE / DISABLE;NO_CONFIG_PORT 定义为 (-1),表示"无对应端口"的占位值。
  • 参数宏:以 TCFG_ 前缀命名的具体配置项。
#define ENABLE_THIS_MOUDLE				    1
#define DISABLE_THIS_MOUDLE				    0

#define ENABLE							    1
#define DISABLE							    0

#define NO_CONFIG_PORT						(-1)

/*---------系统时钟配置---------------------*/
#define TCFG_SYS_PLL_CLK                    96000000
#define TCFG_PLL_SEL                        PLL_D1p0_192M
#define TCFG_PLL_DIV                        PLL_DIV2
#define TCFG_HSB_DIV                        HSB_DIV1

Source: app_config.h

配置项按功能分区组织,典型的区段包括:

区段关键宏示例作用
系统时钟TCFG_SYS_PLL_CLK=96000000、TCFG_PLL_SEL=PLL_D1p0_192M、TCFG_PLL_DIV=PLL_DIV2、TCFG_HSB_DIV=HSB_DIV1配置 PLL 频率与分频,决定 CPU 主频
CacheCPU_USE_CACHE_WAY_NUMBER=4配置 Cache 路数(2~4),影响取指/数据缓存容量
UARTTCFG_UART_TX_PORT=IO_PORTA_05、TCFG_UART_BAUDRATE=1000000串口打印引脚与波特率(默认 1Mbps)
按键KEY_DOUBLE_CLICK_EN、KEY_IO_EN=0、KEY_AD_EN=1、AD_KEY_IO=IO_PORTA_08 等使能按键类型并指定引脚
低功耗TCFG_CONFIG_LOWPOWER_SEL=ENABLE低功耗功能总开关
软关机TCFG_SHUTDOWN_TIME=5000软关机判定时间(单位 ms)
充电TCFG_CHARGE_ENABLE=0、TCFG_CHARGE_FULL_V=CHARGE_FULL_V_4199、TCFG_CHARGE_MA=CHARGE_mA_90充电使能与满电电压/充电电流挡位
音频SR_DEFAULT=32000、AMM_RS_INSIDE_ENABLE、TCFG_AUDIO_AUTO_MUTE_ENABLE=0默认采样率、MIC 选择、自动 mute
DAC 音量TCFG_DAC_VOL_FADE_EN、TCFG_DAC_VOL_FADE_STEP、TCFG_DAC_VOL_FADE_SLOW=15音量淡入淡出开关与步进
EQ 调音TCFG_CFG_TOOL_ENABLE=DISABLE、TCFG_COMM_TYPE=TCFG_USB_COMM在线调音工具使能与通信方式(USB)
RF PARF_PA_EN=DISABLE、RF_PA_POWER_SUPPLY=DISABLE外置 PA 使能与供电控制

设计意图:所有可变硬件参数集中在一个头文件,使同一 SDK 可以仅通过改宏适配不同硬件方案;同时宏在编译期求值,不产生运行时开销,便于编译器优化裁剪。

运行期全局配置变量 (app_config.c)

app_config.c 定义 SDK 各库在运行期读取的 const 全局变量,典型内容:

  1. 中断优先级表:为系统各外设/蓝牙中断分配固定优先级(数值越小优先级越高,IRQ_DECODER_IP=0 最高):
//系统使用到的
const int IRQ_IRTMR_IP   = 6;
const int IRQ_AUDIO_IP   = 5;
const int IRQ_DECODER_IP = 0;
const int IRQ_WFILE_IP   = 2;
const int IRQ_RF_QUEUE_IP   = 2;
const int IRQ_ADC_IP     = 3;
const int IRQ_ENCODER_IP = 1;
const int IRQ_TICKTMR_IP = 3;
const int IRQ_USB_IP	 = 4;
const int IRQ_SD_IP		 = 3;
const int IRQ_CTMU_IP	 = 2;
const int IRQ_STREAM_IP  = 4;
const int IRQ_SPEAKER_IP = 1;
const int IRQ_LEDC_IP    = 1;
const int IRQ_SLCD_IP    = 2;
const int IRQ_PMU_TMR1_IP = 3;
const int IRQ_ALINK_IP	 = 3;
//BT
const int IRQ_BT_TIMEBASE_IP  = 6;   //BT TIMEBASE
const int IRQ_BLE_EVENT_IP    = 6;   //BT RX_EVT
const int IRQ_BLE_RX_IP       = 6;   //BT RX
const int IRQ_BTSTACK_MSG_IP  = 3;   //BT STACK

Source: app_config.c

  1. 系统行为开关:内存断言、内核异常打印、SFC 代码缓存、音频 ADC、RTC 等:
//内存管理malloc内部断言
const char MM_ASSERT    = TRUE;
//内核异常打印
const u8 config_asser = 1;

const u8 config_spi_code_user_cache = 1;//sfc放code区

const u8 config_audio_adc_enable = 1;

#if RTC_EN
const int config_rtc_enable = 1;
#else
const int config_rtc_enable = 0;
#endif

Source: app_config.c

  1. 升级/产测功能使能:CONFIG_UPDATE_STORAGE_DEV_EN(存储设备升级)、CONFIG_UPDATE_TESTBOX_UART_EN(测试盒 UART)、CONFIG_UPDATE_APP_OTA_EN(手机 App OTA)、CONFIG_UPDATE_TESTBOX_BLE_EN(测试盒 BLE):
////////////////////////////update control///////////////////////////////////////////
const int CONFIG_UPDATE_STORAGE_DEV_EN = 1;
const int CONFIG_UPDATE_TESTBOX_UART_EN = 1;
const int CONFIG_UPDATE_APP_OTA_EN = 1;
const int CONFIG_UPDATE_TESTBOX_BLE_EN = 1;
/////////////////////////////////////////////////////////////////////////////////////

Source: app_config.c

  1. 日志总开关与标签:libs_debug 配合 CONFIG_DEBUG_LIBS(X) 宏控制各日志标签(如 MAIN、KEYM、PMU、WKUP、RTC、P33、LRC)的 i/d/e/c 级打印是否生效,存放在 .LOG_TAG_CONST 段以便链接器统一管理:
const char libs_debug AT(.LOG_TAG_CONST) = TRUE; //打印总开关

#define  CONFIG_DEBUG_LIBS(X)   (X & libs_debug)

const char log_tag_const_i_MAIN AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(1);
const char log_tag_const_d_MAIN AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(1);
const char log_tag_const_e_MAIN AT(.LOG_TAG_CONST) = CONFIG_DEBUG_LIBS(1);

Source: app_config.c

设计意图:这些变量以 const 存储于 Flash(而非 RAM),避免占用稀缺的 RAM;运行时只需读取常量即可获得全部板级信息。中断优先级集中定义则保证了系统中断(如音频、USB、RF)与用户中断之间的确定性抢占关系。

各业务模块的配置变体

SDK 在每个业务模块目录下提供独立的 app_config 变体头文件,例如:

  • sdk/apps/app/bsp/common/bt_common/hid/include/hid_app_config.h — HID 业务配置
  • sdk/apps/app/bsp/common/bt_common/spp_and_le/include/spple_app_config.h — SPP/LE 业务配置
  • sdk/apps/app/bsp/common/bt_common/ble_test/test_app_config.h — BLE 测试配置

它们遵循与本页相同的"宏裁剪"模式,但只涉及各自业务参数,细节请参见对应业务功能页。

VM 参数存储系统详解

旧 VM 接口 (vm.h / vm_api.c)

旧 VM 接口由头文件 sdk/apps/include_lib/common/vm.h 声明(定义 VM_INDEX_BUFF 等索引缓冲类型),sdk/apps/app/bsp/common/vm/vm_api.c 负责为 VM 库提供内存缓冲回调:

#define LABEL_INDEX_LEN_CRC_SIZE        (4)
static u8 vm_buff[sizeof(VM_INDEX_BUFF) + LABEL_INDEX_LEN_CRC_SIZE];
u16 vm_buff_alloc(u8 **buf)
{
    if (buf == NULL) {
        return 0;
    }

    *buf = vm_buff;
    return sizeof(vm_buff);
}

Source: vm_api.c

设计意图:VM 库需要在 RAM 中维护一份"索引缓冲"(VM_INDEX_BUFF),用于记录 Flash 上各参数的位置。vm_buff_alloc 是库回调(nvm_buf_for_lib 的同源模式),由应用层静态分配缓冲区以避免运行时 malloc;缓冲区末尾额外预留 LABEL_INDEX_LEN_CRC_SIZE(4 字节)存放 CRC,用于校验索引数据的完整性。

新 VM 接口 (new_vm.h / nvm_api.c)

新 VM 是当前推荐方案,接口全部声明在 sdk/apps/include_lib/common/new_vm.h,实现位于预编译库 new_vm_lib.a(源码路径 sdk/apps/include_lib/liba/bd49/mbox_flash/new_vm_lib.a),应用层适配在 sdk/apps/app/bsp/common/vm/nvm_api.c。

新 VM 的设计参数:

#define BIT_MAP  (32 * 16)      // 512 bit 位图
#define BIT_MAP_SIZE  (BIT_MAP / 8)  // 64 字节

#define NVM_MAX_LEN     128     // 单条参数最大长度(字节)

#define NVM_BUFF_SIZE   (NVM_MAX_LEN + BIT_MAP_SIZE)  // 192 字节

Source: new_vm.h

  • 每条参数最大 128 字节(NVM_MAX_LEN);块内以位图(BIT_MAP)记录每个槽位的占用/有效状态,位图本身占 64 字节,与单条参数合计为 192 字节的内部缓冲。
  • NVM_MULTIPLE_READ 宏默认被注释,即默认关闭多读路径,NEW_VM_OBJ 中的 id/offset 字段在关闭时仍保留(由 // #ifdef 注释可见),便于调试。

接口分三层:

库接口(由 new_vm_lib.a 实现,应用通常不直接调用):

u32 nvm_init(NEW_VM_OBJ *p_nvm, u32 addr, u32 size);
u32 nvm_format_another(NEW_VM_OBJ *p_nvm);
u32 nvm_read(NEW_VM_OBJ *p_nvm, u32 id, u8 *buf, u32 len);
u32 nvm_write(NEW_VM_OBJ *p_nvm, u32 id, u8 *buf, u32 len);
void nvm_pre_erasure_next(NEW_VM_OBJ *p_nvm, u16 using_next, u16 idle_next);

u32 nvm_format_another_ignore(NEW_VM_OBJ *p_nvm, u32 *ignore_map, u32 ignore_bits);
u32 nvm_get_half_addr(NEW_VM_OBJ *p_nvm);
u32 nvm_get_half_len(NEW_VM_OBJ *p_nvm);
u32 nvm_get_cur_date_len(NEW_VM_OBJ *p_nvm);

Source: new_vm.h

库回调(由应用层实现,库在需要缓冲时调用):

void *nvm_buf_for_lib(NEW_VM_OBJ *p_nvm, u32 *p_len);

Source: new_vm.h

应用接口(最常用,由 nvm_api.c 适配层封装):

u32 nvm_init_api(u32 addr, u32 size);
u32 nvm_format_anotheri_api(void);
u32 nvm_read_api(u32 id, u8 *buf, u32 len);
u32 nvm_write_api(u32 id, u8 *buf, u32 len);
void nvm_erasure_next_api(void);
void nvm_format_another_ignore_api(u32 *delete_map, u32 delete_bits);

Source: new_vm.h

缓存辅助接口(供上层维护 ID→偏移映射):

u32 nvm_cache_cnt(NVM_ENTRY *entries, u32 len);
u32 nvm_write_cache(NVM_CACHE *cache, u16 id, u32 offset);
u32 nvm_read_cache(NVM_CACHE *cache, u16 id);
u32 nvm_clear_cache(NVM_CACHE *cache);

Source: new_vm.h

设计意图:接口分层使预编译库与工程代码解耦——库只依赖 NEW_VM_OBJ 结构与 nvm_buf_for_lib 回调,内存由应用层供给;_api 后缀的适配函数隐藏了对象实例管理细节,业务代码只需"初始化一次,之后按 ID 读写"。

参数写入与磨损均衡流程

flowchart TD
    Start([应用调用 nvm_write_api]) --> Lookup["查 NVM_CACHE<br/>找到/分配 NVM_ENTRY"]
    Lookup --> Space{"当前块<br/>剩余空间足够?"}
    Space -->|"是"| Write["在当前块 w_offset 处写入数据<br/>更新位图与 cache"]
    Space -->|"否"| Format["nvm_format_another<br/>把有效数据整理到另一块"]
    Format --> Erase["nvm_pre_erasure_next<br/>预擦除空闲块"]
    Erase --> Write2["写入新块<br/>切换 bool_block"]
    Write --> Check{"该 ID 旧记录<br/>需要清理?"}
    Write2 --> Check
    Check -->|"是"| Mark["标记旧记录失效(位图置 0)"]
    Check -->|"否"| Done
    Mark --> Done([返回写入结果])
  • 按 ID 追加写:VM 采用"追加写 + 位图标记"策略,每次写不覆盖旧位置,而是在当前块末尾追加新记录并更新 cache,旧记录通过位图标记为失效。这避免了 Flash 的擦-写-擦循环,是磨损均衡的基础。
  • 块切换:当当前块写满时,nvm_format_another 将仍有效的记录(根据 cache 与位图)整理到另一块,随后 nvm_pre_erasure_next 预擦除旧块,bool_block 翻转表示当前使用块切换。若某些 ID 需要被删除,可调用 nvm_format_another_ignore 并传入 ignore_map/ignore_bits,在整理时直接丢弃这些 ID。
  • 掉电安全:由于两块中始终有一块是完整可用的,即使整理中途掉电,下次 nvm_init 也能基于位图/CRC 恢复到一致状态。这也是仓库中"new_vm 格式整理失败导致概率数据错误"补丁(v1.3.3→v1.3.4)所针对的关键路径——整理过程本身的原子性直接影响数据可靠性。

读写时序

sequenceDiagram
    participant App as 业务模块
    participant Api as nvm_api.c (应用接口)
    participant Lib as new_vm_lib.a
    participant Cache as NVM_CACHE (RAM)
    participant SFC as SFC 控制器
    participant F as SPI NOR Flash (A/B 区)

    App->>Api: nvm_write_api(id, buf, len)
    Api->>Lib: nvm_write(p_nvm, id, buf, len)
    Lib->>Cache: nvm_read_cache 查映射 / 新建 NVM_ENTRY
    Lib->>SFC: 在当前块 w_offset 追加写数据(含位图更新)
    SFC->>F: 页编程
    F-->>SFC: 写完成
    SFC-->>Lib: 成功
    Lib->>Cache: nvm_write_cache 更新 id→offset
    Lib-->>Api: 返回结果
    Api-->>App: 返回结果

    App->>Api: nvm_read_api(id, buf, len)
    Api->>Lib: nvm_read(p_nvm, id, buf, len)
    Lib->>Cache: nvm_read_cache 查 id→offset
    Cache-->>Lib: offset
    Lib->>SFC: 读取该偏移数据
    SFC->>F: 页读
    F-->>SFC: 数据
    SFC-->>Lib: 数据
    Lib-->>Api: 返回结果
    Api-->>App: 数据写入 buf

配置选项

app_config 关键配置项

配置宏类型/取值默认值说明
TCFG_SYS_PLL_CLK数值(Hz)96000000系统 PLL 输出频率
TCFG_PLL_SEL枚举PLL_D1p0_192MPLL 档位选择
TCFG_PLL_DIV枚举PLL_DIV2PLL 分频(决定 CPU 主频)
TCFG_HSB_DIV枚举HSB_DIV1HSB 总线分频
CPU_USE_CACHE_WAY_NUMBER数值4Cache 路数,范围 2~4
TCFG_UART_TX_PORTIO 枚举IO_PORTA_05串口打印 TX 引脚
TCFG_UART_BAUDRATE数值(bps)1000000串口打印波特率
KEY_DOUBLE_CLICK_EN0/1DISABLE按键多击使能
KEY_IO_EN / KEY_AD_EN0/10 / 1IO 键 / AD 键使能
AD_KEY_IOIO 枚举IO_PORTA_08AD 键采样引脚
TCFG_CONFIG_LOWPOWER_SEL0/1ENABLE低功耗功能开关
TCFG_SHUTDOWN_TIME数值(ms)5000软关机判定时间
TCFG_CHARGE_ENABLE0/10充电功能使能
TCFG_CHARGE_FULL_V枚举CHARGE_FULL_V_4199满电电压挡位
TCFG_CHARGE_MA枚举CHARGE_mA_90充电电流挡位
SR_DEFAULT数值(Hz)32000默认音频采样率
TCFG_DAC_VOL_FADE_EN0/1DISABLE硬件 DAC 音量淡入淡出
TCFG_DAC_VOL_FADE_STEP数值(0~15)1淡入淡出步进
TCFG_DAC_VOL_FADE_SLOW数值(0~15)15淡入淡出慢速系数
TCFG_CFG_TOOL_ENABLE0/1DISABLE在线调音工具使能
TCFG_COMM_TYPE枚举TCFG_USB_COMM调音通信方式(仅 USB)
RF_PA_EN0/1DISABLE外置 RF PA 使能

来源:app_config.h(配置宏均为编译期常量,修改后需重新编译)。

运行期全局配置变量

变量类型默认值说明
IRQ_IRTMR_IP / IRQ_AUDIO_IP / IRQ_DECODER_IP 等const int0~6各中断优先级(0 最高)
IRQ_BT_TIMEBASE_IP / IRQ_BLE_EVENT_IP / IRQ_BLE_RX_IPconst int6蓝牙相关中断优先级
MM_ASSERTconst charTRUEmalloc 内部断言开关
config_asserconst u81内核异常打印开关
config_spi_code_user_cacheconst u81SFC 是否放 code 区
config_audio_adc_enableconst u81音频 ADC 使能
config_rtc_enableconst int随 RTC_ENRTC 使能(宏条件编译)
CONFIG_UPDATE_STORAGE_DEV_ENconst int1存储设备升级使能
CONFIG_UPDATE_TESTBOX_UART_ENconst int1测试盒 UART 升级使能
CONFIG_UPDATE_APP_OTA_ENconst int1App OTA 升级使能
CONFIG_UPDATE_TESTBOX_BLE_ENconst int1测试盒 BLE 升级使能
libs_debugconst charTRUE日志打印总开关

来源:app_config.c。

VM 存储参数

参数值说明
NVM_MAX_LEN128 字节单条参数最大长度
BIT_MAP512 bit块内槽位占用位图
BIT_MAP_SIZE64 字节位图占用的存储大小
NVM_BUFF_SIZE192 字节内部缓冲(参数+位图)
LABEL_INDEX_LEN_CRC_SIZE4 字节旧 VM 索引缓冲 CRC 长度
双备份区A/B 两块由 bool_block 标记当前使用块,nvm_format_another 切换

来源:new_vm.h、vm_api.c。

API 参考

新 VM 库接口 (new_vm.h)

u32 nvm_init(NEW_VM_OBJ *p_nvm, u32 addr, u32 size)

初始化 VM 对象,绑定 Flash 起始地址 addr 与区域长度 size;首次使用或检测到数据不一致时自动完成格式整理。

参数:

  • p_nvm (NEW_VM_OBJ*):VM 对象指针,内存由调用方提供。
  • addr (u32):VM 区域在 Flash 中的起始地址。
  • size (u32):VM 区域大小(应为两块的总长)。

返回: 0 表示成功,非 0 表示初始化/整理出错。

u32 nvm_read(NEW_VM_OBJ *p_nvm, u32 id, u8 *buf, u32 len)

按 ID 读取参数到 buf。

参数: p_nvm 为对象指针;id 为参数 ID;buf 为输出缓冲区;len 为期望读取长度(≤ NVM_MAX_LEN)。

返回: 读取的字节数;未找到返回 0(或错误码)。

u32 nvm_write(NEW_VM_OBJ *p_nvm, u32 id, u8 *buf, u32 len)

按 ID 写入参数,内部执行"追加写 + 位图更新 + cache 更新",必要时触发 format_another 块切换。

返回: 写入的字节数;失败返回 0。

u32 nvm_format_another(NEW_VM_OBJ *p_nvm) / u32 nvm_format_another_ignore(NEW_VM_OBJ *p_nvm, u32 *ignore_map, u32 ignore_bits)

将有效数据整理到另一块并预擦除旧块;_ignore 变体在整理时丢弃 ignore_map 指定的 ID(按位标记,共 ignore_bits 位)。

返回: 0 表示成功。

void nvm_pre_erasure_next(NEW_VM_OBJ *p_nvm, u16 using_next, u16 idle_next)

预擦除下一个空闲块,由库在块切换前后调用,把耗时的擦除操作提前,减少写入时延。

u32 nvm_get_half_addr(NEW_VM_OBJ *p_nvm) / u32 nvm_get_half_len(NEW_VM_OBJ *p_nvm) / u32 nvm_get_cur_date_len(NEW_VM_OBJ *p_nvm)

查询单块地址、单块长度与当前块有效数据长度,用于上层判断空间与统计。

新 VM 应用接口 (nvm_api.c 封装)

函数说明
u32 nvm_init_api(u32 addr, u32 size)初始化全局 VM 实例(内部管理 NEW_VM_OBJ)
u32 nvm_read_api(u32 id, u8 *buf, u32 len)按 ID 读参数
u32 nvm_write_api(u32 id, u8 *buf, u32 len)按 ID 写参数
void nvm_erasure_next_api(void)主动触发下一块预擦除
void nvm_format_another_ignore_api(u32 *delete_map, u32 delete_bits)整理并删除指定 ID
void *nvm_buf_for_lib(NEW_VM_OBJ *p_nvm, u32 *p_len)向库提供 RAM 缓冲(应用层回调实现)

缓存辅助接口

函数说明
u32 nvm_cache_cnt(NVM_ENTRY *entries, u32 len)统计给定 entry 数组的有效项数
u32 nvm_write_cache(NVM_CACHE *cache, u16 id, u32 offset)写入/更新 id→offset 映射
u32 nvm_read_cache(NVM_CACHE *cache, u16 id)查询 id 对应的 offset
u32 nvm_clear_cache(NVM_CACHE *cache)清空整个缓存

旧 VM 缓冲回调 (vm_api.c)

u16 vm_buff_alloc(u8 **buf)

为旧 VM 库分配索引缓冲。

参数: buf (u8**):输出参数,接收静态缓冲指针。

返回: 缓冲长度(sizeof(VM_INDEX_BUFF) + 4);buf 为 NULL 时返回 0。

说明: 缓冲为静态数组,无 malloc 开销,末尾 4 字节供 CRC 使用。

故障模式、边界情况与并发

掉电与数据一致性

VM 的双备份区 + 位图 + CRC 设计旨在保证掉电安全,但格式整理(format_another)过程本身是脆弱的:整理需要"读有效记录 → 写入另一块 → 擦除旧块",若中途掉电,两块都可能处于中间状态。仓库补丁目录 补丁包/v1.3.3升级至v1.3.4相关new_vm和uart问题修复补丁 中即包含"解决 new_vm 格式整理失败导致的概率数据错误"的 new_vm_lib.a 替换包,说明该路径曾出现概率性数据错误,升级固件时需同步替换预编译库。

参数长度与数量边界

  • 单条参数最大 128 字节(NVM_MAX_LEN),超长写入会被截断或失败。
  • 位图 512 bit,即每块最多可标记 512 个槽位;参数数量与每条占用长度共同决定块容量。
  • w_offset 到达块尾时触发 format_another,频繁写满会引入擦除/整理开销,应合理规划参数 ID 与写入频率。

并发与中断上下文

app_config.c 中的配置变量均为 const,运行期只读,无并发问题。VM 读写接口未在头文件中体现互斥保护,意味着调用方应避免在多个任务/中断中并发读写同一 VM 实例(或由 nvm_api.c 适配层加锁);蓝牙中断(如 IRQ_BLE_RX_IP)与主循环同时访问 Flash 时,应确保 SFC 访问串行化,否则可能出现索引与数据不一致。

已知约束

  • 旧 VM(vm.h)与新 VM(new_vm.h)使用不同的缓冲与索引格式,二者不可混用于同一 Flash 区域。
  • NVM_MULTIPLE_READ 默认关闭,NEW_VM_OBJ 中 id/offset 字段仍存在,但多读路径未启用。
  • VM 区域地址 addr/size 必须与链接脚本中的 Flash 分区一致,错误配置会破坏其它分区(如固件区、资源区)。

性能与运维考虑

  • RAM 占用小:VM 索引缓冲由静态数组提供(vm_buff_alloc / nvm_buf_for_lib),避免堆分配;NVM_ENTRY 单表项仅 8 字节。
  • 读路径快:NVM_CACHE 将 ID→offset 映射常驻 RAM,读操作无需扫描 Flash;nvm_read_cache 命中后直接按偏移页读。
  • 写路径优化:追加写避免频繁擦除;nvm_pre_erasure_next 将擦除操作提前到空闲时刻,降低写时延抖动。
  • 升级维护:new_vm_lib.a 为预编译库,修复(如格式整理问题)通过替换 .a 文件发布;升级时应核对补丁包版本与 SDK 版本匹配(仓库中存在多个版本补丁目录)。
  • 调试:通过 app_config.c 的日志总开关(libs_debug)与各模块 log_tag_const_* 标签可单独开关 VM/SFC 相关打印,便于定位读写异常。

扩展点

  • 新增参数 ID:应用层在 nvm_init_api 之后即可使用任意 u32 作为 ID 调用 nvm_read_api/nvm_write_api,无需注册表;ID 的分配约定由业务层维护。
  • 自定义缓冲:通过实现 nvm_buf_for_lib 回调可替换 VM 库的 RAM 缓冲来源(静态数组或专用内存池)。
  • 整理策略定制:nvm_format_another_ignore 允许在整理时按位图丢弃指定 ID(例如恢复出厂设置、删除配对信息),无需先读后删。
  • 旧/新 VM 并存:vm_buff_alloc(旧)与 nvm_*_api(新)接口可分别服务不同参数集合,但 Flash 分区必须隔离。

相关链接

  • app_config.h(主配置头文件)
  • app_config.c(运行期配置变量)
  • new_vm.h(新 VM 接口定义)
  • vm_api.c(旧 VM 缓冲回调)
  • nvm_api.c(新 VM 应用层适配)
  • vm.h(旧 VM 头文件)
  • vm_sfc.h(SFC 控制器相关)
  • new_vm_lib.a(预编译库)
  • new_vm 格式整理问题修复补丁(示例:补丁说明 PDF)
Prev
消息机制 (msg / hot_msg)
Next
电源管理 (SOFT OFF / POWER DOWN)