杰理 SDK 文档中心
首页
首页
  • 概述

    • SDK 概览与产品定位
    • 支持芯片平台与蓝牙认证
    • SDK 架构与目录分层
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建系统
    • 板级工程与配置
    • 烧录与固件升级工具
  • 应用工程

    • 应用选择与工程总览
    • SPP + BLE 数传应用框架
    • 透传与 AT 指令示例
    • BLE 广播/中心与定位示例
    • 2.4G 私有协议与 Dongle 示例
    • 云平台接入示例
    • HID 人机交互应用框架
    • HID 示例工程(键盘/鼠标/遥控器/手柄)
    • Bluetooth Mesh 应用框架
    • Mesh 模型与 Mesh DFU 固件升级
    • Mesh 音频编解码演示
  • 芯片平台与硬件抽象

    • 芯片平台总览与差异
    • 音频编解码与时钟管理
    • 外设驱动接口(ADC/IIC/SPI/PWM/LED/充电)
    • 芯片配置工具与下载支持
  • 蓝牙协议栈

    • 蓝牙控制器层(btctrler)
    • 蓝牙协议栈与 Profile(btstack)
    • 蓝牙模块选择与配置
  • 媒体与音频框架

    • 音频流框架
    • 音频编解码与 A2DP 媒体
    • 音频效果处理(EQ/频谱/变调/环绕/超低音)
    • 本地 TWS 与音频同步
  • 系统服务与运行时

    • 实时操作系统与任务调度
    • 消息事件机制
    • 电源管理与低功耗
    • 存储与配置系统
    • 设备驱动框架(USB/RTC)
  • 应用公共组件

    • 音频应用组件
    • 设备外设抽象(按键/触摸/传感器/存储)
    • 蓝牙公共模块与消息联动
    • 调试与配置组件
    • 杰理关键词唤醒(jl_kws)
  • 第三方协议与云平台接入

    • 杰理 RCSP 私有协议
    • 低功耗蓝牙 Mesh 方案(llsync_mesh)
    • Sig Mesh 方案
    • 涂鸦协议接入
    • 腾讯连连接入
    • 华为 HiLink 接入
  • 固件升级与维护

    • OTA 升级机制
    • 升级补丁与版本维护
    • 升级工具链(BLE OTA / USB Dongle OTA)
  • 文档与开发资源

    • 数据手册与架构文档
    • 协议与云平台开发文档
    • 常见问题与技术支持

杰理关键词唤醒(jl_kws)

jl_kws 是杰理 AC63 系列蓝牙 SoC SDK 中的关键词唤醒/语音识别公共组件,负责从麦克风采集音频、送入唤醒算法检测,并在命中关键词或触发事件时驱动上层业务动作。该组件以独立内核任务(kws)运行,通过消息队列与信号量实现生命周期管理,并接入系统低功耗(LP)框架。

Purpose and Scope

本页面向工程师完整说明 apps/common/jl_kws/ 目录下关键词唤醒组件的:

  • 模块分层与文件职责(jl_kws_api.h、jl_kws_main.c、jl_kws_common.h 及 algo/audio/event 三个子模块的接口契约);
  • 状态机、任务生命周期与消息驱动的运行机制;
  • 从 MIC 采集到算法检测再到事件上报的完整数据流;
  • 对外 API、错误码、配置开关与低功耗接入方式。

以下内容不属于本页范围,请参见对应目录的独立文档:

  • 具体唤醒词模型的训练与烧录(算法库资源、cpu/*/tools/jl_kws.cfg 配置);
  • 蓝牙协议栈、音频通路(A2DP/HFP)等其他公共组件;
  • 各 CPU 平台(br23/br25/br30)的底层驱动差异。

Overview

关键词唤醒(Keyword Spotting, KWS)是蓝牙耳机/音箱等产品的核心交互入口:设备处于待机或低功耗状态时,持续监听麦克风,检测到特定唤醒词(如“小杰小杰”)后唤醒系统并进入交互流程。

jl_kws 组件把这一能力封装为四个步骤的 API:打开(open)→ 启动识别(start)→ 停止识别(stop)→ 关闭(close)。其内部架构刻意拆分为三个可独立初始化的子模块:

子模块职责接口文件声明
jl_kws_algo唤醒算法:帧缓冲管理、检测执行jl_kws_common.h L46-L51
jl_kws_audio音频采集:MIC 数据获取、启动/停止jl_kws_common.h L56-L60
jl_kws_event识别事件上报与状态更新jl_kws_common.h L65-L68

三者由 jl_kws_main.c 中的 kws 任务统一编排,保证初始化顺序(算法 → 音频 → 事件)与停止顺序(算法 → 音频 → 事件)的一致性,避免出现“算法还在跑但 MIC 已关闭”的竞态。

设计意图上,模块级联采用任务消息 + 状态标志双保险:外部 API 只写 kws_state 并投递消息,真正的资源操作全部在 kws 任务上下文内串行执行,因此上层任意线程调用 API 都是线程安全的。

Architecture

flowchart TD
    subgraph sg_User["用户层"]
        App["应用程序 / 业务代码"]
        Demo["jl_kws_main_user_demo"]
    end

    subgraph sg_API["API 层 (jl_kws_api.h)"]
        Open["jl_kws_speech_recognition_open"]
        Start["jl_kws_speech_recognition_start"]
        Stop["jl_kws_speech_recognition_stop"]
        Close["jl_kws_speech_recognition_close"]
        AecOut["kws_aec_data_output"]
    end

    subgraph sg_Main["主控层 (jl_kws_main.c)"]
        Task["kws 任务<br/>kws_speech_recognition_task"]
        Run["kws_speech_recognition_run"]
        Init["jl_kws_speech_recognition_init"]
        LP["kws_lp_target<br/>REGISTER_LP_TARGET"]
    end

    subgraph sg_Sub["功能子模块 (jl_kws_common.h 接口)"]
        Algo["jl_kws_algo<br/>算法检测"]
        Audio["jl_kws_audio<br/>MIC 采集"]
        Event["jl_kws_event<br/>事件上报"]
    end

    subgraph sg_HW["硬件/系统层"]
        MIC["麦克风 (MIC)"]
        AEC["AEC 回声消除"]
        Sys["os_taskq / 信号量"]
    end

    App --> Open
    App --> Start
    App --> Stop
    App --> Close
    Demo --> Open
    Demo --> Start
    Demo --> Stop
    Demo --> Close
    Open -->|"task_create + ready_sem"| Task
    Start -->|"post_msg(RUN)"| Task
    Stop -->|"post_msg(STOP)"| Task
    Close -->|"post_msg(CLOSE) + del_sem"| Task
    Task --> Init
    Task --> Run
    Run --> Audio
    Run --> Algo
    Run --> Event
    Audio --> MIC
    Audio --> AEC
    AecOut -->|"外部音频输入"| Audio
    Algo -->|"KWS_VOICE_EVENT"| Event
    LP -->|"is_idle 查询"| Task
    Task --> Sys

架构说明:

  • 用户层只接触 jl_kws_api.h 暴露的 4 个生命周期函数;jl_kws_main_user_demo() 是官方给出的调用示例(jl_kws_api.h L9)。
  • 主控层是唯一的“事实来源”:kws_speech_recognition_task 通过 os_taskq_pend 消费消息,按消息类型调用 run/stop/close,所有子模块操作都发生在此任务内,天然串行化。
  • 子模块层彼此解耦:jl_kws_algo 只关心“给我一帧数据,我告诉你有没有命中”;jl_kws_audio 只关心“从 MIC 拿一帧数据”;jl_kws_event 只关心“事件来了,更新状态并通知上层”。三者仅通过 jl_kws_common.h 的函数声明耦合,便于在不通算法/不通硬件上替换实现。
  • 系统层提供任务队列、信号量(ready_sem/del_sem)与低功耗注册表(REGISTER_LP_TARGET),kws 任务因此可被系统在空闲时挂起。

主内容:状态机与运行机制

状态定义

jl_kws_main.c 中定义了两组状态枚举,分别描述对外状态与任务内部状态:

枚举取值含义
KWS_STATE_IDLE0未初始化
KWS_STATE_INIT1任务已创建、子模块初始化完成
KWS_STATE_RUN2识别进行中
KWS_STATE_STOP3已停止
KWS_STATE_CLOSE4已关闭

KWS_TASK_STATE_* 与之一一对应,用于低功耗空闲查询(见下文)。状态切换由外部 API 写入,任务侧只负责执行对应动作,两者通过 struct kws_speech_recognition 共享(jl_kws_main.c L9-L15):

struct kws_speech_recognition {
    u8 task_init;
    u8 kws_state;
    u8 kws_task_state;
};

static struct kws_speech_recognition jl_kws = {0};

来源:jl_kws_main.c L9-L15

任务消息与状态迁移

kws 任务通过 os_taskq_pend 阻塞等待消息,消息类型定义如下:

enum KWS_TASK_MSG {
    KWS_SPEECH_RECOGNITION_RUN = 1,
    KWS_SPEECH_RECOGNITION_STOP,
    KWS_SPEECH_RECOGNITION_CLOSE,
};

来源:jl_kws_main.c L23-L27

stateDiagram-v2
    [*] --> IDLE
    IDLE --> INIT: open() 创建任务并初始化
    INIT --> RUN: start() 投递 RUN 消息
    RUN --> STOP: stop() 投递 STOP 消息
    STOP --> RUN: start() 再次投递 RUN 消息
    RUN --> CLOSE: close() 投递 CLOSE 消息
    STOP --> CLOSE: close() 投递 CLOSE 消息
    CLOSE --> [*]: task_kill + task_init=0

状态迁移要点:

  • 幂等保护:start() 在 kws_state == KWS_STATE_RUN 时直接返回 0;stop()/close() 在对应状态已设置时直接返回,避免重复投递消息(jl_kws_main.c L235-L278)。
  • RUN → STOP → RUN 是合法路径:kws_speech_recognition_run 的 while 循环以 kws_state != KWS_STATE_RUN 为退出条件,停止后再次 start 会重新进入循环。
  • CLOSE 是终态:任务收到 CLOSE 消息后执行资源释放,通过 del_sem 通知外部调用者,随后外部调用 task_kill 销毁任务,task_init 清零(jl_kws_main.c L263-L278)。

任务创建与初始化

jl_kws_speech_recognition_open() 是模块入口,负责创建任务并等待其就绪:

int jl_kws_speech_recognition_open(void)
{
    kws_info("%s", __func__);
    OS_SEM ready_sem;

    if (__this->task_init == 0) {
        __this->task_init = 1;
        os_sem_create(&ready_sem, 0);
        task_create(kws_speech_recognition_task, (void *)&ready_sem, THIS_TASK_NAME);
        //wait task ready
        os_sem_pend(&ready_sem, 20);
    }

    return 0;
}

来源:jl_kws_main.c L219-L233

设计意图:ready_sem 保证 open() 返回时任务已经启动,任务侧在进入消息循环前 os_sem_post 释放该信号量;外部以 20 tick 超时等待,防止任务创建失败导致死等。任务初始化按“算法 → 音频 → 事件”的顺序执行,任一步失败都会走 kws_speech_recognition_close() 清理并 task_kill 自杀(jl_kws_main.c L99-L122):

static int jl_kws_speech_recognition_init(void)
{
    kws_info("%s", __func__);
    //1.算法初始化
    int ret = 0;
    ret = jl_kws_algo_init();
    if (ret != JL_KWS_ERR_NONE) {
        return ret;
    }
    //2.Audio MIC初始化
    ret = jl_kws_audio_init();
    if (ret != JL_KWS_ERR_NONE) {
        return ret;
    }
    //3. event初始化
    ret = jl_kws_event_init();
    if (ret != JL_KWS_ERR_NONE) {
        return ret;
    }
    return JL_KWS_ERR_NONE;
}

来源:jl_kws_main.c L99-L122

识别主循环

kws_speech_recognition_run() 是核心数据通路:先启动音频与算法,然后循环“取帧缓冲 → 填音频数据 → 跑算法检测 → 事件更新”:

static int kws_speech_recognition_run(void)
{
    void *rbuf = 0;
    u32 rbuf_len = 0;
    u32 audio_data_len = 0;
    int ret = JL_KWS_ERR_NONE;
    int event = KWS_VOICE_EVENT_NONE;

    ret = jl_kws_audio_start();
    if (ret != JL_KWS_ERR_NONE) {
        return ret;
    }
    ret = jl_kws_algo_start();
    if (ret != JL_KWS_ERR_NONE) {
        return ret;
    }
    __this->kws_task_state = KWS_TASK_STATE_RUN;

    while (1) {
        if (__this->kws_state != KWS_STATE_RUN) {
            break;
        }
        rbuf = jl_kws_algo_get_frame_buf(&rbuf_len);
        if (rbuf == NULL) {
            ret = JL_KWS_ERR_ALGO_NO_FRAME_BUF;
            break;
        }
        audio_data_len = jl_kws_audio_get_data(rbuf, rbuf_len);
        if (audio_data_len == rbuf_len) {
            /* kws_putchar('r'); */
            event = jl_kws_algo_detect_run(rbuf, rbuf_len);
            if (event != KWS_VOICE_EVENT_NONE) {
                jl_kws_event_state_update(event);
            }
        }
    }
    return ret;
}

来源:jl_kws_main.c L51-L96

关键设计:

  • 帧对齐校验:仅当 audio_data_len == rbuf_len 时才执行检测,保证算法每次拿到完整的一帧,避免半帧数据造成误检。
  • 忙等退出条件:循环体不阻塞,退出靠外部 API 改写 kws_state;因此 stop 消息必须由同任务在下一轮 os_taskq_pend 处理,而 run 循环在状态变化后自行退出,二者无死锁。
  • 事件过滤:KWS_VOICE_EVENT_NONE 不进入事件模块,减少无效开销。

低功耗接入

kws 任务注册为系统低功耗目标,空闲时允许系统挂起:

static u8 kws_idle_query(void)
{
    return !(__this->kws_task_state == KWS_TASK_STATE_RUN);
}

REGISTER_LP_TARGET(kws_lp_target) = {
    .name = "kws",
    .is_idle = kws_idle_query,
};

来源:jl_kws_main.c L206-L214

设计意图:低功耗框架在进入睡眠前遍历所有 LP 目标,kws_idle_query 返回真(即任务不在 RUN 状态)才允许挂起——识别期间必须保持 CPU 运行,否则会漏检唤醒词。同时,KWS_BT_CALL_SYS_FREQUENCE_HZ 定义为 48 MHz(jl_kws_main.c L19),提示调用方在识别期间需保证系统频率满足算法实时性要求。

编译开关

整个模块由 TCFG_KWS_VOICE_RECOGNITION_ENABLE 宏控制:未使能时 jl_kws_main.c 主体不参与编译(jl_kws_main.c L4)。该开关应在 app_config.h 中按产品需求配置。

Core Flow:端到端时序

以下时序图展示了从应用层调用到资源释放的完整交互:

sequenceDiagram
    participant App as 应用层
    participant API as jl_kws_api
    participant Task as kws 任务
    participant Audio as jl_kws_audio
    participant Algo as jl_kws_algo
    participant Event as jl_kws_event

    App->>API: open()
    API->>Task: task_create + ready_sem
    Task-->>API: os_sem_post(ready_sem)
    Task->>Task: init: algo_init → audio_init → event_init

    App->>API: start()
    API->>Task: post_msg(KWS_SPEECH_RECOGNITION_RUN)
    Task->>Audio: audio_start()
    Task->>Algo: algo_start()
    loop 每帧检测
        Task->>Algo: algo_get_frame_buf(&len)
        Task->>Audio: audio_get_data(buf, len)
        Task->>Algo: algo_detect_run(buf, len)
        Algo-->>Task: voice_event (YES/NO)
        Task->>Event: event_state_update(event)
    end

    App->>API: stop()
    API->>Task: post_msg(KWS_SPEECH_RECOGNITION_STOP)
    Task->>Algo: algo_stop()
    Task->>Audio: audio_stop()
    Task->>Event: event_stop()

    App->>API: close()
    API->>Task: post_msg(KWS_SPEECH_RECOGNITION_CLOSE, del_sem)
    Task->>Algo: algo_close()
    Task->>Audio: audio_close()
    Task->>Event: event_close()
    Task-->>API: os_sem_post(del_sem)
    API->>Task: task_kill("kws")

时序要点:

  1. open 是同步就绪的:任务创建后通过 ready_sem 握手,open() 返回即代表 kws 任务已进入消息循环。
  2. start/stop/close 是异步的:API 只投递消息立即返回,实际资源操作在任务上下文执行;唯一例外是 close() 会 os_sem_pend(&del_sem, 0xffff) 无限等待任务完成清理,确保关闭前所有子模块资源已释放(jl_kws_main.c L263-L278)。
  3. 检测循环是单线程自驱动:不存在中断回调抢占算法状态的问题;音频数据以轮询方式(audio_get_data)拉取,与算法帧率严格对齐。

Usage Examples

基础用法:官方演示流程

jl_kws_main_user_demo() 展示了完整的生命周期调用方式——打开后延时启动识别,中途停止再重启,最后关闭:

void jl_kws_main_user_demo(void)
{
    //1.打开jl_kws模块
    jl_kws_speech_recognition_open();

    //2.在某时刻开始识别
    os_time_dly(1000);
    jl_kws_speech_recognition_start();

    //3.在某时刻之后停止识别
    os_time_dly(1500);
    jl_kws_speech_recognition_stop();

    //4.在某时刻之后重新开启识别
    os_time_dly(2000);
    jl_kws_speech_recognition_start();

    //5.在某时刻之后关闭jl_kws模块
    os_time_dly(2500);
    jl_kws_speech_recognition_close();
}

来源:jl_kws_main.c L284-L304

对外 API 一览

上层业务只需包含 jl_kws_api.h 并使用以下接口:

int jl_kws_speech_recognition_open(void);
int jl_kws_speech_recognition_start(void);
void jl_kws_speech_recognition_stop(void);
void jl_kws_speech_recognition_close(void);

void jl_kws_main_user_demo(void);

//for jl_kws audio extern input
u8 kws_aec_get_state(void);
void kws_aec_data_output(void *priv, s16 *data, int len);

来源:jl_kws_api.h L4-L13

其中 kws_aec_get_state() 与 kws_aec_data_output() 用于外部音频通路(如蓝牙通话 AEC)向 KWS 模块注入回声消除后的参考/输出数据,是扩展“非本地 MIC 输入”场景的预留接口。实际业务中,识别命中后应在事件回调(jl_kws_event 内部)里触发 UI 提示、退出低功耗或启动语音助手会话。

Configuration Options

配置项类型默认值说明
TCFG_KWS_VOICE_RECOGNITION_ENABLE宏未定义(需在 app_config.h 打开)总开关;未使能时整个 jl_kws_main.c 不编译(jl_kws_main.c L4)
KWS_BT_CALL_SYS_FREQUENCE_HZ宏(48 * 1000000)识别期间建议的系统 CPU 频率,保证算法实时性(jl_kws_main.c L19)
KWS_DEBUG_ENABLE宏定义使能 kws_debug/kws_putchar 调试输出(jl_kws_common.h L17-L24)
THIS_TASK_NAME宏"kws"kws 内核任务名,用于 os_taskq_post_msg 与 task_kill(jl_kws_main.c L49)
jl_kws.cfg算法配置各平台一份br23/br25/br30 平台工具目录下的算法配置,如 cpu/br23/tools/jl_kws.cfg

说明:jl_kws_algo 的具体算法参数(模型、阈值、增益等)封装在算法库与 jl_kws.cfg 中,其内部解析细节未在本仓库源码中展开,建议按平台工具链的说明进行配置。jl_kws_common.h 顶部被注释的 #pragma bss_seg/.data_seg/.const_seg/.code_seg 表明该模块数据段可单独划分到指定内存区域(如低功耗常驻 RAM),实际链接时可按需放开。

API Reference

int jl_kws_speech_recognition_open(void)

创建并初始化 KWS 识别任务。

  • 说明:首次调用时创建 kws 任务并通过 ready_sem 等待其就绪;任务内依次执行算法/音频/事件子模块初始化。已打开时幂等返回。
  • 返回:0 表示成功(当前实现恒返回 0;初始化失败由任务内部自杀清理)。
  • 调用时机:系统上电初始化阶段,仅需一次。

来源:jl_kws_main.c L219-L233

int jl_kws_speech_recognition_start(void)

启动关键词识别。

  • 说明:将状态置为 KWS_STATE_RUN 并向任务投递 KWS_SPEECH_RECOGNITION_RUN 消息;任务随即启动音频与算法并进入逐帧检测循环。若已在 RUN 状态则直接返回。
  • 返回:0。
  • 注意:调用前必须先 open()。

来源:jl_kws_main.c L235-L248

void jl_kws_speech_recognition_stop(void)

停止识别,释放音频与算法运行资源(不销毁任务)。

  • 说明:状态置为 KWS_STATE_STOP 并投递 STOP 消息;任务内依次执行 algo_stop → audio_stop → event_stop,检测循环随后退出。
  • 注意:停止后可通过 start() 重新启动,无需再次 open()。

来源:jl_kws_main.c L250-L261

void jl_kws_speech_recognition_close(void)

彻底关闭 KWS 模块并销毁任务。

  • 说明:状态置为 KWS_STATE_CLOSE,投递携带 del_sem 的 CLOSE 消息,并阻塞等待任务完成全部资源释放(algo_close → audio_close → event_close),随后 task_kill("kws") 销毁任务、task_init 清零。
  • 注意:该函数会一直等待直到任务清理完毕,不要在中断上下文调用。

来源:jl_kws_main.c L263-L278

子模块接口(jl_kws_common.h 声明,供内部调用/实现替换)

jl_kws_algo(jl_kws_common.h L46-L51):

函数说明
int jl_kws_algo_init(void)算法初始化,失败返回 JL_KWS_ERR_ALGO_NO_BUF 等负值
int jl_kws_algo_start(void)启动算法运行态
void jl_kws_algo_stop(void)停止算法
void jl_kws_algo_close(void)释放算法资源
void *jl_kws_algo_get_frame_buf(u32 *buf_len)获取一帧检测缓冲,失败返回 NULL
int jl_kws_algo_detect_run(u8 *buf, u32 len)对一帧数据执行检测,返回 KWS_VOICE_EVENT_*

jl_kws_audio(jl_kws_common.h L56-L60):

函数说明
int jl_kws_audio_init(void)采集通道初始化
int jl_kws_audio_start(void)启动采集
void jl_kws_audio_stop(void)停止采集
void jl_kws_audio_close(void)关闭采集通道
int jl_kws_audio_get_data(void *buf, u32 len)向缓冲填充一帧 MIC 数据,返回实际长度

jl_kws_event(jl_kws_common.h L65-L68):

函数说明
int jl_kws_event_init(void)事件模块初始化
void jl_kws_event_stop(void)停止事件上报
void jl_kws_event_close(void)关闭事件模块
void jl_kws_event_state_update(u8 voice_event)上报识别事件(KWS_VOICE_EVENT_YES/NO)

Failure Modes, Edge Cases & Concurrency

错误码定义

jl_kws_common.h 统一了模块错误码(jl_kws_common.h L27-L35):

错误码值触发场景
JL_KWS_ERR_NONE0成功
JL_KWS_ERR_AUDIO_MIC_NO_BUF-400音频模块 MIC 缓冲缺失
JL_KWS_ERR_AUDIO_INIT_STATE_ERR-401音频初始化状态异常(重复/顺序错误)
JL_KWS_ERR_AUDIO_MIC_STATE_ERR-402MIC 状态异常(如未初始化即 start)
JL_KWS_ERR_ALGO_NO_BUF-300算法缓冲分配失败
JL_KWS_ERR_ALGO_NO_FRAME_BUF-301取帧缓冲返回 NULL(算法未就绪)

典型失败路径

  1. 初始化失败自清理:任务内 jl_kws_speech_recognition_init() 任一步返回非 0 时,任务调用 kws_speech_recognition_close() 释放已初始化资源、置 task_init = 0 并 task_kill 自杀;但此时 __this->kws_state 尚未置为 INIT,外部再次 open() 可重新创建任务(jl_kws_main.c L174-L183)。
  2. 帧缓冲缺失:jl_kws_algo_get_frame_buf 返回 NULL 时退出检测循环并返回 JL_KWS_ERR_ALGO_NO_FRAME_BUF,需要上层重新 start()。
  3. 数据不足:audio_get_data 返回长度不等于帧长时跳过本轮检测(不产生事件),这是半帧数据的正常降级行为。

并发与竞态控制

  • 单任务串行化:所有子模块操作都发生在 kws 任务上下文,API 与任务之间仅通过 os_taskq_post_msg 传递,不存在多线程同时操作算法/音频资源的问题。
  • 状态标志的可见性:kws_state 由 API 线程写入、任务线程读取。在单核 MCU 且无抢占中断改写的前提下是安全的;若在中断中调用 API,需确认平台 os_taskq_post_msg 的中断安全性。
  • close 的同步语义:close() 通过 del_sem 无限等待,保证“返回后模块完全不可用”,避免任务被杀时仍在访问音频/算法资源。os_sem_pend(&del_sem, 0xffff) 的超时上限同时避免系统异常时永久挂起(jl_kws_main.c L263-L278)。
  • LP 与 RUN 互斥:kws_idle_query 在 RUN 状态返回“忙”,系统低功耗框架不会在识别期间挂起 CPU,这是保证不漏检的关键约束。

Performance & Operational Considerations

  • CPU 频率:识别期间建议将系统频率提升至 KWS_BT_CALL_SYS_FREQUENCE_HZ(48 MHz),否则逐帧 algo_detect_run 可能超时造成帧丢弃(jl_kws_main.c L19)。
  • 低功耗调度:任务注册为 kws_lp_target,非识别态允许系统睡眠;识别态保持唤醒。产品功耗优化应聚焦缩短 RUN 窗口(如无交互超时自动 stop())。
  • 调试手段:使能 KWS_DEBUG_ENABLE 后可用 kws_debug/kws_putchar 打印帧检测节奏(循环中的 kws_putchar('r') 注释);kws_info 恒为 printf,可用于跟踪 open/init/run/stop/close 调用序列(jl_kws_common.h L15-L24)。
  • 内存布局:jl_kws_common.h 预留的 #pragma *_seg 支持将 KWS 数据/常量/代码段放入专用内存段(例如低功耗常驻 SRAM),批量生产时可按需启用。

Extension Points

  • 算法替换:实现 jl_kws_algo_* 六个接口即可替换唤醒算法(含模型),jl_kws_main.c 无需改动;这是组件与算法解耦的直接收益。
  • 音频通路扩展:kws_aec_data_output() / kws_aec_get_state() 允许外部音频模块(如通话 AEC 链路)向 KWS 注入音频数据,适合“唤醒词在通话链路中检测”的产品形态(jl_kws_api.h L12-L13)。
  • 事件消费:命中事件在 jl_kws_event_state_update 内分发(KWS_VOICE_EVENT_YES = 2 / KWS_VOICE_EVENT_NO = 3,jl_kws_common.h L37-L41),业务侧可在此挂接唤醒提示音、LED、蓝牙回连等动作。
  • 平台差异:各平台算法配置独立存放于 cpu/br23/tools/jl_kws.cfg、cpu/br25/tools/jl_kws.cfg、cpu/br30/tools/jl_kws.cfg,移植到新平台时复制并调整对应配置即可。

Related Links

  • jl_kws_api.h — 对外 API 声明
  • jl_kws_main.c — 任务、状态机与生命周期实现
  • jl_kws_common.h — 错误码、事件与子模块接口定义
  • jl_kws_algo.c / jl_kws_audio.c / jl_kws_event.c — 子模块实现
  • br23 平台算法配置 jl_kws.cfg
  • 目录 8-common-components 下其他公共组件文档(音频通路、事件框架等)请参见对应目录页。
Prev
调试与配置组件