杰理 SDK 文档中心
首页
首页
  • 概述与快速入门

    • 芯片平台与 SDK 概述
    • 环境搭建与编译工具链
    • 快速开始:选型、编译与烧录
    • 烧录与量产工具
  • 构建系统与板级工程

    • 顶层 Makefile 与编译目标
    • 板级工程与配置
    • 后处理与配置工具
  • HID 人机交互应用

    • HID 应用架构总览
    • 键盘、翻页器与遥控应用
    • 鼠标应用:单模、双模与低延迟
    • 空闲应用与初始化流程
  • BLE 透传与数传应用

    • 透传应用总览
    • 多连接与无连接传输
    • AT 命令模组应用
    • Dongle 适配器应用
  • BSP 公共模块

    • 蓝牙公共处理
    • 按键、LED 与红外
    • 传感器与编码器
    • 存储、VM 与文件系统
    • 电源管理与低功耗
    • 消息调度与通信外设
  • 协议栈与预编译库

    • 蓝牙协议栈库
    • 设备驱动与文件系统库
    • 音频、升级与其他库
  • 开发资料与补丁发布

    • 文档资料中心
    • 版本补丁与兼容性修复

空闲应用与初始化流程

空闲应用(idle app)是 AW31N BLE SDK 中最精简的应用分支:它关闭 BLE 功能,仅保留系统时钟、定时器、按键事件与消息循环,主要用于验证 SDK 启动链路、低功耗(LP)休眠策略以及最基础的外设初始化是否正常。本文档从系统上电后的 system_init() 出发,完整梳理从芯片启动、应用注册、应用分发到 idle 主循环与低功耗查询的端到端流程。

Purpose and Scope

本文档覆盖以下内容:

  • 系统级初始化:apps/app/bsp/start/init.c 中 system_init() 的初始化序列及其设计意图;
  • 应用注册与分发机制:REGISTER_APPLICATION / REGISTER_LP_TARGET 宏、main_app_get_name() 的分支选择、main_application_operation_state() 的状态机调用链;
  • idle 应用的完整实现:状态机、主循环、事件处理、软关机按键、定时器、低功耗查询(app_idle_query);
  • 配置开关与编译约束:CONFIG_APP_IDLE、TCFG_USER_BLE_ENABLE 等宏的作用。

以下主题属于兄弟页面,本文不做展开:BLE/蓝牙协议栈细节、外设驱动实现(key/led/gpio)、升级流程(app_update_init)、电源管理内部的休眠策略(app_power_mg)仅在与 idle 交互处提及。另注意 apps/demo/transfer/examples/idle/app_idle.c 与 apps/demo/hid/examples/idle/app_idle.c 内容一致,本文以 hid demo 为准。

Overview

为什么需要 idle 应用

在 SDK 中,apps/demo/hid/app_main.c 通过预编译宏从多种应用(键盘、钥匙扣、翻页器、遥控器、鼠标、idle)中选择唯一一个运行。idle 应用的价值在于:

  1. 最小可运行验证:不依赖 BLE 协议栈,仅用时钟、定时器、消息队列和按键事件即可跑通完整的"启动 → 应用分发 → 状态机 → 主循环"框架;
  2. 低功耗调试入口:通过 REGISTER_LP_TARGET 注册 app_idle_query,让系统知道当前应用是否允许进入休眠,是验证 LP 链路的最简场景;
  3. CPU 空转兜底:app_comm_proc.c 中的 __asm__ volatile("idle") 注释明确说明其作用是"避免非低功耗模式下CPU满载运行"。

关键术语

术语含义
REGISTER_APPLICATION将应用描述符(名称、动作、操作函数集)放入链接段,供 list_for_each_app_main 遍历
struct intent应用意图,携带 name 与 action,决定启动哪个应用
APP_STA_*应用状态枚举:CREATE / START / PAUSE / RESUME / STOP / DESTROY
REGISTER_LP_TARGET注册低功耗目标,is_idle 回调决定系统是否可休眠
MSG_TYPE_EVENT系统事件消息,由 post_msg 投递到应用消息队列

Architecture

下图展示 idle 应用与初始化流程的整体架构,从 system_init() 到 idle 主循环及低功耗查询:

flowchart TD
    subgraph sg_Boot["启动初始化 init.c"]
        SYS_INIT["system_init()"]
        TICK["tick_timer_set_state(STATE_SFC)"]
        MSG_INIT["message_init() / event_pool_init()"]
        DEV_INIT["devices_init_api()"]
        PWR_INIT["app_power_init() / trim_timer_add()"]
        CFG_INIT["cfg_bin_init() / cfg_file_parse(0)"]
        SYS_INIT --> TICK
        SYS_INIT --> MSG_INIT
        SYS_INIT --> DEV_INIT
        SYS_INIT --> PWR_INIT
        SYS_INIT --> CFG_INIT
    end

    subgraph sg_AppDispatch["应用分发 app_main.c"]
        GET_NAME["main_app_get_name(&it)"]
        GET_NAME -->|"CONFIG_APP_IDLE 编译分支"| IDLE_NAME["name = \"idle\"<br/>action = ACTION_IDLE_MAIN"]
        LIST["list_for_each_app_main 遍历注册表"]
        GET_NAME --> LIST
        LIST -->|"名称匹配"| STATE_MACHINE["ops->state_machine(app, state, &it)"]
    end

    subgraph sg_IdleApp["idle 应用 app_idle.c"]
        REG_APP["REGISTER_APPLICATION(app_app_idle)"]
        REG_APP --> OPS["app_idle_ops<br/>state_machine / event_handler"]
        STATE_MACHINE -->|"APP_STA_START + ACTION_IDLE_MAIN"| MAIN_LOOP["idle_app_start()"]
        MAIN_LOOP --> CLK["clk_set(sys/lsb)"]
        MAIN_LOOP --> TIMER["sys_timer_add(2000ms 唤醒)"]
        MAIN_LOOP --> MSG_LOOP["get_msg / app_comm_process_handler"]
    end

    subgraph sg_LP["低功耗链路"]
        REG_LP["REGISTER_LP_TARGET(app_idle_lp_target)"]
        REG_LP --> QUERY["app_idle_query() 返回 !idle_is_active"]
    end

    EVT["系统事件<br/>按键/蓝牙/设备"] -->|"main_application_operation_event 投递"| OPS
    TIMER -->|"回调置位 idle_is_active"| QUERY

架构说明

  • 启动层(init.c):system_init() 是 SDK 初始化入口,按"定时器 → 消息池 → 设备 → 电源 → 配置"的顺序建立系统运行基座;其中写保护、温度 trim、配置解析等耗时操作被集中在此阶段完成。
  • 分发层(app_main.c):main_app_get_name() 用 #elif 链把编译宏映射为应用名与动作;随后遍历应用注册表,找到名称匹配的描述符并调用其 state_machine。
  • 应用层(app_idle.c):idle 应用通过 REGISTER_APPLICATION 把自己注册进注册表,状态机在 APP_STA_START 时进入 idle_app_start() 主循环。
  • 低功耗层:REGISTER_LP_TARGET 注册的 app_idle_query 使系统可根据 idle 状态决定是否休眠,这是 idle 应用区别于其他应用的重要扩展点。

系统初始化序列

system_init() 位于 apps/app/bsp/start/init.c,它是 BLE SDK 上电后执行的第一个业务初始化函数。其调用顺序并非随意排列,而是遵循严格的依赖关系:

  1. tick_timer_set_state(STATE_SFC):先设定系统节拍定时器状态,保证后续所有需要时间基准的模块可用;
  2. message_init() 与 event_pool_init():建立消息队列与事件池——这是应用主循环 get_msg() 和系统事件投递(post_msg/event_pool_alloc)的前提;
  3. devices_init_api():初始化设备抽象层(vfs、flash 等设备驱动接口);
  4. norflash_set_write_protect(1):打开 flash 写保护,注释明确提示"写保护耗时较多",故放在启动阶段统一处理;
  5. app_power_init():电源管理初始化,为后续 app_power_set_soft_poweroff() 与低功耗休眠做准备;
  6. trim_timer_add():温度 trim 定时器,补偿时钟漂移;
  7. 打印 SDK 版本信息(SDK_VERSION_CFG_DEFINE / SDK_VERSION_DATE_DEFINE);
  8. cfg_bin_init() 与 cfg_file_parse(0):配置二进制初始化并解析配置文件,idle 应用运行时读取的用户配置(如按键值 TCFG_ADKEY_VALUE3)依赖此步骤;
  9. 若开启 UPDATE_V2_EN,调用 app_update_init() 启动升级初始化(注意原文件中 rcsp_init() 被 #if 0 注释掉,属于预留分支)。

设计意图:初始化顺序把"时间、消息、设备、电源、配置"这些基础设施放在最前,应用层(idle 或其他)启动时可以直接使用,从而让每个应用保持精简、聚焦业务逻辑。

应用注册与分发机制

REGISTER_APPLICATION:链接段注册

idle 应用通过宏 REGISTER_APPLICATION(app_app_idle) 把自己写入链接段,描述符包含应用名、入口动作与操作函数集:

static const struct application_operation app_idle_ops = {
    .state_machine  = idle_state_machine,
    .event_handler 	= idle_event_handler,
};

REGISTER_APPLICATION(app_app_idle) = {
    .name 	= "idle",
    .action	= ACTION_IDLE_MAIN,
    .ops 	= &app_idle_ops,
    .state  = APP_STA_DESTROY,
};

Source: apps/demo/hid/examples/idle/app_idle.c

这种"链接段注册表"模式(linker-section registry)的好处是:新增应用只需实现 struct application_operation 并调用 REGISTER_APPLICATION,分发层用 list_for_each_app_main 统一遍历,无需修改分发代码。.state = APP_STA_DESTROY 表示应用初始状态为销毁,等待状态机驱动。

应用名与动作解析

main_app_get_name() 是唯一的分支决策点,它用编译宏链决定运行哪个应用:

#elif(CONFIG_APP_IDLE)
    it->name = "idle";
    it->action = ACTION_IDLE_MAIN;

#else
    ASSERT(0, "no app!!!");
#endif

Source: apps/demo/hid/app_main.c

设计意图:应用选择被设计为"编译期决定",而不是运行时菜单。这样每个产品只编译一个应用,RAM/ROM 占用最小,且 ASSERT(0, "no app!!!") 能在未选择任何应用时立即暴露配置错误——这对嵌入式固件很重要,因为运行时才发现"没有应用"代价极高。

状态机分发

main_application_operation_state() 完成"按名查找 → 调用状态机":

static struct application *main_application_operation_state(struct application *app, enum app_state state)
{
    struct intent it;
    const struct  application *dev = NULL;

    main_app_get_name(&it);
    log_info("run app>>> %s", it.name);

    list_for_each_app_main(dev) {
        if (memcmp(dev->name, it.name, strlen(it.name)) == 0) {
            if (dev->ops) {
                dev->ops->state_machine(app, state, &it);
            }
        } else {
            log_info("no app run");
        }
    }
    return NULL;
}

Source: apps/demo/hid/app_main.c

注意这里 memcmp 比较的是注册表项 dev->name 与意图 it.name 的前缀,因此应用名必须全局唯一。分发层不直接持有应用实例,而是通过 ops 函数指针间接调用,这正是"策略模式"——分发逻辑与具体应用解耦。

事件投递路径

与状态机同步调用不同,系统事件走异步消息通道。main_application_operation_event() 找到匹配应用后,通过 post_msg 把事件连同操作函数集一起投递到消息队列:

list_for_each_app_main(dev) {
    if (memcmp(dev->name, it.name, strlen(it.name)) == 0) {
        if (dev->ops) {
            post_msg(3, MSG_TYPE_EVENT, event, dev->ops);
        }
    }
}

Source: apps/demo/hid/app_main.c

消费端 main_sys_event_msg_handle() 从消息中取出事件指针与 ops,调用 ops_ptr->event_handler(NULL, event_ptr) 后释放事件池内存:

void main_sys_event_msg_handle(int *msg)
{
    struct sys_event *event_ptr = (struct sys_event *)msg[1];
    const struct application_operation *ops_ptr = (const struct application_operation *)msg[2];
    ops_ptr->event_handler(NULL, event_ptr);
    event_pool_free(event_ptr);
}

Source: apps/demo/hid/app_main.c

事件异步化的设计意图:事件源(按键中断、蓝牙回调)运行在中断/高优先级上下文,不能直接调用应用逻辑;通过消息队列解耦后,事件在应用主循环的上下文中被顺序处理,天然避免并发竞争。

idle 应用状态机

idle 的状态机覆盖 SDK 定义的全部六个状态,但大部分是空实现,只有 START 和 DESTROY 有实际行为:

static int idle_state_machine(struct application *app, enum app_state state,
                              struct intent *it)
{
    switch (state) {
    case APP_STA_CREATE:
        break;
    case APP_STA_START:
        if (!it) {
            break;
        }
        switch (it->action) {
        case ACTION_IDLE_MAIN:
            log_info("ACTION_IDLE_MAIN\n");
            idle_app_start();
            break;
        }
        break;
    case APP_STA_PAUSE:
        break;
    case APP_STA_RESUME:
        break;
    case APP_STA_STOP:
        break;
    case APP_STA_DESTROY:
        sys_timer_del(timer_wakeup_id);// 关闭idle定时器
        break;
    }
    return 0;
}

Source: apps/demo/hid/examples/idle/app_idle.c

关键行为:

  • APP_STA_START + ACTION_IDLE_MAIN:进入 idle_app_start() 主循环。注意该函数内部是 while(1),正常情况下不会返回,因此 PAUSE/RESUME/STOP 对 idle 无实际意义;
  • APP_STA_DESTROY:删除 2000ms 唤醒定时器 timer_wakeup_id,释放资源。这是状态机框架要求每个应用必须执行的清理动作;
  • if (!it) 空指针保护:若意图为空则跳过,防止未初始化意图导致解引用崩溃。

idle 主循环

idle_app_start() 是 idle 应用的核心执行体,展示了极简应用的最小骨架:

static void idle_app_start()
{
    log_info("=======================================");
    log_info("---------idle demo---------");
    log_info("app_file: %s", __FILE__);

    clk_set("sys", TCFG_CLOCK_SYS_HZ);
    clk_set("lsb", TCFG_CLOCK_LSB_HZ);

    timer_wakeup_id = sys_timer_add(NULL, (void *)idle_timer_handle_test, 2000);

    int msg[4] = {0};
    while (1) {
        if (idle_is_active) {
            idle_is_active = 0;
            log_info("wakeup");
        }

        get_msg(sizeof(msg) / sizeof(int), msg);
        app_comm_process_handler(msg);
    }
}

Source: apps/demo/hid/examples/idle/app_idle.c

执行步骤与设计意图:

  1. 打印横幅:输出应用名与源文件路径,便于在串口日志中快速确认固件运行的是哪个应用分支;
  2. 设置系统时钟:clk_set("sys", TCFG_CLOCK_SYS_HZ) 与 clk_set("lsb", TCFG_CLOCK_LSB_HZ) 将主时钟与低功耗慢时钟配置为用户定义值——idle 不依赖 BLE 的时钟需求,因此可自由降频以降低功耗;
  3. 注册 2000ms 周期定时器:sys_timer_add 注册 idle_timer_handle_test,回调仅置位 idle_is_active = 1。该定时器承担双重角色——演示"定时唤醒"机制,同时作为低功耗查询的"忙碌信号";
  4. 消息循环:get_msg 阻塞等待消息,app_comm_process_handler 处理通用命令(其中包含 __asm__ volatile("idle") 空转指令,避免非低功耗模式下 CPU 满载)。

主循环采用"阻塞取消息 + 统一处理"模型:CPU 在无消息时休眠,有消息时唤醒处理,这是低功耗嵌入式系统的标准事件循环范式。

Core Flow:从上电到 idle 主循环

sequenceDiagram
    participant HW as CPU/启动代码
    participant INIT as system_init()<br/>(init.c)
    participant MAIN as app_main<br/>(app_main.c)
    participant REG as 应用注册表<br/>(REGISTER_APPLICATION)
    participant IDLE as idle 应用<br/>(app_idle.c)
    participant LP as 低功耗调度<br/>(REGISTER_LP_TARGET)

    HW->>INIT: 上电/复位
    INIT->>INIT: tick_timer_set_state / message_init / event_pool_init
    INIT->>INIT: devices_init_api / app_power_init / trim_timer_add
    INIT->>INIT: cfg_bin_init / cfg_file_parse(0)
    INIT->>MAIN: 进入应用层
    MAIN->>MAIN: main_app_get_name(&it)<br/>CONFIG_APP_IDLE → name="idle"
    MAIN->>REG: list_for_each_app_main 遍历
    REG-->>MAIN: 匹配 "idle" → app_idle_ops
    MAIN->>IDLE: ops->state_machine(APP_STA_START, ACTION_IDLE_MAIN)
    IDLE->>IDLE: clk_set(sys/lsb) + sys_timer_add(2000ms)
    IDLE->>IDLE: while(1) get_msg / app_comm_process_handler
    IDLE-->>LP: app_idle_query() 查询是否可休眠
    LP-->>IDLE: 返回 !idle_is_active
    Note over IDLE,LP: 定时器回调置位 idle_is_active → 查询返回 false → 系统保持唤醒

时序要点:

  1. 初始化阶段(system_init)完全发生在应用分发之前,保证消息池、设备、电源、配置都可用;
  2. 分发阶段是"编译期选择 + 运行期查表":main_app_get_name 决定目标,list_for_each_app_main 查表获得 ops;
  3. idle 的 state_machine 在 START 时同步进入 idle_app_start(),之后控制权不再返回分发层;
  4. 低功耗查询 app_idle_query() 由系统电源管理按需调用(而非 idle 主动调用),它根据 idle_is_active 标志判断 2 秒唤醒周期内系统是否处于"活跃"状态。

低功耗目标:REGISTER_LP_TARGET

idle 应用是演示 SDK 低功耗接口的最简案例:

//system check go sleep is ok
static uint8_t app_idle_query(void)
{
    return !idle_is_active;
}

REGISTER_LP_TARGET(app_idle_lp_target) = {
    .name = "app_idle_deal",
    .is_idle = app_idle_query,
};

Source: apps/demo/hid/examples/idle/app_idle.c

工作机制:REGISTER_LP_TARGET 把低功耗目标描述符放入链接段,系统电源管理在进入休眠前会遍历所有 LP 目标,调用各自的 is_idle 回调。app_idle_query() 返回 !idle_is_active——当 2000ms 定时器回调刚触发(idle_is_active == 1)时返回 false,表示"应用刚被唤醒、尚不允许休眠";其余时间返回 true,允许系统休眠。

设计意图:通过"应用主动报告自己是否空闲"而非"系统强制休眠",把低功耗决策权下放给应用。idle 的 idle_timer_handle_test 只置位标志不做事,本质上是一个"周期心跳",用于验证唤醒路径(定时器唤醒 → 主循环检测 → 日志打印 wakeup → 标志清除 → 再次允许休眠)的闭环。

事件处理与软关机

idle 的事件处理器把系统事件分发给各子处理器:

static int idle_event_handler(struct application *app, struct sys_event *event)
{
    switch (event->type) {
    case SYS_KEY_EVENT:
        idle_key_event_handler(event);
        return 0;
    case SYS_BT_EVENT:
        return 0;
    case SYS_DEVICE_EVENT:
        return 0;
    default:
        return false;
    }
}

Source: apps/demo/hid/examples/idle/app_idle.c

idle 只对按键事件感兴趣(SYS_BT_EVENT/SYS_DEVICE_EVENT 直接忽略),因为 idle 场景下唯一的用户交互入口就是按键。按键处理器实现"三击关机":

static void idle_key_event_handler(struct sys_event *event)
{
    log_info("idle_key_evnet: %d,%d\n", event->u.key.event, event->u.key.value);

    uint8_t key_type = event->u.key.event;
    uint8_t key_value = event->u.key.value;

    if (key_type == KEY_EVENT_TRIPLE_CLICK
        && (key_value == TCFG_ADKEY_VALUE3 || key_value == TCFG_ADKEY_VALUE0)) {
        idle_set_soft_poweroff();
        return;
    }
}

Source: apps/demo/hid/examples/idle/app_idle.c

设计意图与约束:

  • 仅响应 KEY_EVENT_TRIPLE_CLICK(三击)而非单击/双击,是为了防止正常握持或误触导致关机;
  • 按键值限定为 TCFG_ADKEY_VALUE3 或 TCFG_ADKEY_VALUE0,这两个值来自用户配置(user_cfg.h),与实际板级 AD 按键分压相关,确保只有特定物理按键能触发;
  • idle_set_soft_poweroff() 最终调用 app_power_set_soft_poweroff(NULL),走电源管理模块的软关机通道(而非直接断电),让系统有机会完成状态保存与外围下电序列。

编译约束与配置选项

idle 应用的编译开关及其约束如下:

宏默认值/位置说明
CONFIG_APP_IDLE0,apps/demo/hid/include/app_config.h#L28置 1 时编译 idle 应用;同一 demo 中键盘/鼠标等宏必须为 0
TCFG_USER_BLE_ENABLE由用户配置idle 源码在启用 BLE 时主动 #error "confirm, need disable !!!!!!" 强制编译失败,防止误配
TCFG_CLOCK_SYS_HZ用户配置主系统时钟频率,idle_app_start 中通过 clk_set("sys", ...) 生效
TCFG_CLOCK_LSB_HZ用户配置低功耗慢时钟频率,clk_set("lsb", ...) 生效
TCFG_ADKEY_VALUE3/0用户配置触发三击软关机的 AD 按键值
UPDATE_V2_EN由升级配置决定开启后在 system_init() 末尾执行 app_update_init()
CONFIG_APP_OTA_EN0(#if 0 注释)预留的 OTA/rcsp 初始化分支,当前未启用

关键约束说明:TCFG_USER_BLE_ENABLE 的 #error 是"防御式编译"的典型用法——idle 是刻意剥离 BLE 的最小验证环境,若同时打开 BLE 配置,内存布局和时钟需求都会改变,idle 的"最小验证"意义即被破坏,因此 SDK 选择在编译期直接报错而非运行期告警。

Failure Modes、边界情况与并发

编译期失败(最优先的防线)

  1. TCFG_USER_BLE_ENABLE 被打开:app_idle.c 第 28-31 行直接触发 #error,编译终止。这是刻意设计——idle 环境不允许 BLE,防止开发者误以为 idle 已包含蓝牙能力;
  2. 没有任何应用宏被定义:app_main.c 的 #else 分支执行 ASSERT(0, "no app!!!"),在运行期断言失败。若多个应用宏同时为 1,#elif 链只取第一个匹配分支,其余被静默忽略,配置时需注意互斥。

运行时边界

  1. idle_app_start() 不返回:状态机在 APP_STA_START 中进入 while(1) 后永久占用执行上下文,因此 PAUSE/RESUME/STOP 状态对 idle 是"可达但无行为"的空实现。若未来需要挂起 idle,必须重构主循环(如把 get_msg 改为带超时版本);
  2. main_application_operation_event 未匹配到应用:遍历注册表找不到 it.name 时打印 "no event run" 且事件被丢弃。若注册表因链接段裁剪(如优化器删除未引用段)丢失 idle 描述符,会出现"应用启动成功但按键无响应"的隐蔽故障;
  3. 事件池耗尽:bt_event_update_to_user 中 event_pool_alloc() 返回 NULL 时打印 "Memory allocation failed for sys_event" 并直接返回——事件丢失但不崩溃,属于可容忍的降级行为。

并发与竞态

  • 事件投递是异步的:事件源(按键/蓝牙回调)通过 post_msg 入队,idle 主循环 get_msg 取消息后调用 app_comm_process_handler,事件处理与主循环在同一线程上下文串行执行,不存在自旋锁竞争;
  • idle_is_active 标志由系统定时器回调(中断/高优先级上下文)写入、主循环读取清除,是典型的"标志位通信":写方只置 1、读方清零,单字节原子操作,无需临界区;但若未来在回调中读取复杂结构,则必须加临界保护;
  • sys_timer_del(timer_wakeup_id) 在 DESTROY 状态执行,若定时器回调与销毁并发(极端情况下),需依赖系统定时器 API 自身的安全删除语义。

扩展点

idle 应用刻意保持最小,但框架为其预留了清晰的扩展路径:

  • 新增应用:仿照 app_idle.c 实现 struct application_operation(state_machine + event_handler),用 REGISTER_APPLICATION 注册,并在 app_main.c 的 main_app_get_name() 中增加 #elif 分支——无需改动分发框架;
  • 自定义低功耗策略:仿照 REGISTER_LP_TARGET(app_idle_lp_target) 注册新的 LP 目标,通过 is_idle 回调向电源管理声明本模块的空闲状态;idle 的 app_idle_query 可作为"定时心跳保活"模板;
  • 替换主循环:idle_app_start() 中的 app_comm_process_handler(msg) 是通用命令处理器,可替换为业务专属处理;sys_timer_add 的 2000ms 周期可改为业务节拍(如轮询传感器);
  • 按键扩展:idle_key_event_handler 目前只响应三击,可扩展 KEY_EVENT_SINGLE_CLICK/KEY_EVENT_DOUBLE_CLICK 到其他 TCFG_ADKEY_VALUE* 值,实现不同的动作分发。

测试与验证建议

仓库内 idle 相关测试没有独立的测试套件,其正确性依赖以下运行时观测点:

  • 串口日志:启动后应依次出现 >>>AW31N_SDK INFO:(init.c 版本打印)、run app>>> idle(app_main.c)、---------idle demo---------(app_idle.c)——三段日志即验证了"初始化 → 分发 → 应用启动"全链路;
  • 唤醒闭环:每 2 秒出现一次 wakeup 日志,证明定时器 → 标志置位 → 主循环检测的唤醒路径正常;
  • 按键验证:三击 TCFG_ADKEY_VALUE3/0 对应物理按键后出现 set_soft_poweroff 日志并进入软关机,验证事件投递与电源管理联动。

Related Links

  • app_idle.c(hid demo idle 应用实现)
  • app_main.c(应用分发与状态机调度)
  • init.c(系统初始化序列)
  • app_config.h(CONFIG_APP_IDLE 等应用宏定义)
  • app_comm_proc.c(通用命令处理与 CPU idle 指令)
  • transfer demo idle 应用(与 hid 版本一致)
  • 相关兄弟页面:BLE 协议栈与应用、电源管理(app_power_mg)、升级初始化流程(app_update_init)、按键驱动(key.c)
Prev
鼠标应用:单模、双模与低延迟