SDK软件结构¶
1. SDK软件结构图¶

2. SDK目录结构¶
以 CI13XX_SDK_LLM_AIOT_2.1.2 为例,SDK 根目录结构如下:
--components // 功能组件
----RISCV // RISC-V 内核相关定义
----VPR // 声纹识别
----alg // 语音前处理算法库
------aec // 回声消除(AEC)
------agc // 自动增益控制(AGC)
------ai_denoise // AI 深度降噪
------ai_denoise_rtc // AI 深度降噪(实时)
------alc_auto_switch // 自动电平控制(ALC)
------basic_alg // 基础算法
------beamforming // 波束成形(BF)
------common // 算法公共部分
------denoise // 传统降噪
------dereverb // 去混响
------doa // 双麦声源定位(DOA)
------drc // 动态范围控制(DRC)
------eq // 均衡(EQ)
------pwk // 就近唤醒(PWK)
------sound_event_detection // 声音事件检测(SED)
------vp_host // 声纹算法宿主适配
----asr // 语音识别
----assist // 辅助函数,例如:测试算法的计时函数等
----audio_in_manage // 音频采集实时任务
----audio_pre_rslt_iis_out // 语音前处理结果输出组件
----ci_ble // 蓝牙
----ci_cwsl // 自学习
----ci_cwsl_v2 // 自学习(V2)
----ci_key // 按键管理
----ci_nvdm // 用户数据管理
----cias_g722 // G722 编解码
----cias_lib // 通用第三方库(如 cJSON)
----cias_opus // OPUS 编解码
----cias_speex // SPEEX 编解码
----cmd_info // 固件信息解析(命令词信息表)
----codec_manager // codec管理器
----factory_test // 产测
----fft // fft管理器
----flash_control // flash管理器
----flash_encrypt // flash加密策略
----freertos // 操作系统
----ir_remote_driver // 红外遥控驱动
----led // 三色灯控管理器
----log // 日志打印
----msg_com // 串口协议、IIC协议
----nlp // 自然语言处理
----nuclear_com // 双核通信
----ota // OTA升级
----player // 播放器(mp3/aac/adpcm/flac/m4a)
----protocol // 云端对接协议
----simple_audio_player // 精简播放器
----status_share // 状态共享
----sys_monitor // 系统监控器
----tts // 语音合成(TTS)
--driver // 驱动
----boards // 板级支持,例如CI-D06GT01D.c、CI-1306TEST.c
----ci130x_chip_driver // 芯片内部驱动,如IIC驱动
----third_device_driver // 外部驱动,如外部codec驱动
--external // 扩展参考(firmware参考、model算法模型)
--libs // 预编译算法库
--projects // 应用示例代码
----offline_asr_llm_aiot_hpout_sample // 离在线大模型对话模板工程(hpout上传录音)
----offline_asr_llm_aiot_iis_sample // 离在线大模型对话模板工程(iis上传录音)
----offline_asr_llm_aiot_uart_sample // 离在线大模型对话模板工程(uart上传录音)
--startup // 启动代码
--system // 统一使用的头定义等
--tools // 固件构建工具
--utils // 构建脚本与调试辅助工具集
--对接协议 // 离在线应用对接说明文档
备注
driver/boards下的板级文件为.c文件(如CI-D06GT01D.c、CI-1306TEST.c),通过system/sdk_default_config.h中的BOARD_PORT_FILE宏选择当前工程使用的板级文件;projects下的三个模板工程拥有完全一致的目录结构,仅音频上传/播放通路不同,可按应用场景任意选用。
3. SDK用户代码说明¶
本文以 offline_asr_llm_aiot_uart_sample 为例,offline_asr_llm_aiot_hpout_sample、offline_asr_llm_aiot_iis_sample 同样适用。示例工程目录结构如下图3-1所示:

示例工程 projects\offline_asr_llm_aiot_uart_sample 的目录组成如下:
offline_asr_llm_aiot_uart_sample
--app // 应用层代码(用户主要修改区域)
----app_main // 主逻辑:系统启动、消息处理、用户代码
----app_audio_handle // 离在线音频数据上传/下载处理
----app_ble // 蓝牙、射频消息处理
----app_common // 应用层公共定义
----app_cwsl // 自学习应用处理
----app_demo // demo配置
----app_doa // 声源定位应用处理
----app_ir // 红外遥控应用处理
----app_ota // OTA升级应用处理
----app_sed // 声音事件检测应用处理
--firmware // 固件资源(模型、播报音、命令词表等)与打包脚本
----asr // 语言模型(.dat、G.fst.txt)
----dnn // 声学模型及算法模型(.fefixbin、nn_denoise、doa等)
----ota_code // OTA升级代码分区bin
----user_file // 用户文件(命令词表等)
----voice // 播报音(mp3/opus/src)
--lds // 链接脚本(不同算法组合对应不同lds)
--project_file // 工程文件(makefile、source_file.prj)
--.vscode // 编译器配置
下列文件所在目录:CI13XX_SDK_LLM_AIOT_2.1.2\projects\offline_asr_llm_aiot_uart_sample\app\app_main
| 文件名 | 描述 |
|---|---|
| main.c | 主函数所在文件:包含任务创建、平台初始化、系统启动代码 |
| system_hook.c | 事件钩子接口c文件:系统启动、唤醒、退出唤醒、语音识别事件钩子函数 |
| system_hook.h | 事件钩子接口h文件 |
| system_msg_deal.c | 系统消息处理任务c文件 |
| system_msg_deal.h | 系统消息处理任务h文件 |
| user_config.h | 用户配置宏定义.h文件 |
| user_msg_deal.c | 用户代码.c文件:按命令词ID/语义ID响应、串口协议、IIC协议、按键消息等用户处理 |
| user_msg_deal.h | 用户代码.h文件 |
| ci_ssp_config.c | 算法(SSP)注册与参数配置:AEC、降噪、DOA、AGC、SED、PWK等 |
| ci_nlp_user.c | NLP(多意图)用户适配c文件 |
| ci_nlp_user.h | NLP(多意图)用户适配h文件 |
| code_switch.c | 代码切换c文件:ASR/TTS 算法代码动态切换实现 |
| code_switch.h | 代码切换h文件 |
4. 添加代码示例¶
(1)针对唤醒词添加协议等处理代码,找到CI13XX_SDK_LLM_AIOT_2.1.2\projects\offline_asr_llm_aiot_uart_sample\app\app_main,
- 先在user_msg_deal.h里添加处理接口声明,例如:
void wake_up_xxx_deal(void);
- 然后在user_msg_deal.c里添加处理接口定义,例如:
void wake_up_xxx_deal(void)
{
/*处理接口逻辑代码*/
}
- 最后在system_hook.c里调用接口,例如:
__WEAK void sys_weakup_hook(void)
{
#if MSG_COM_USE_UART_EN
vmup_send_notify(VMUP_MSG_DATA_NOTIFY_WAKEUPENTER);
#endif
/*此处添加处理接口调用*/
wake_up_xxx_deal();
}
注意
系统其他状态(系统启动、系统退出唤醒、语音识别),也可参照上述方式添加代码,但需在对应的事件钩子函数中调用。
(2)根据命令词ID添加处理代码,找到CI13XX_SDK_LLM_AIOT_2.1.2\projects\offline_asr_llm_aiot_uart_sample\app\app_main\user_msg_deal.c的deal_asr_msg_by_cmd_id函数。
命令词ID的对应命令词,由CI13XX_SDK_LLM_AIOT_2.1.2\projects\offline_asr_llm_aiot_uart_sample\firmware\user_file\cmd_info[60000]{xxx}.xlsx指定
uint32_t deal_asr_msg_by_cmd_id(sys_msg_asr_data_t *asr_msg, cmd_handle_t cmd_handle, uint16_t cmd_id)
{
uint32_t ret = 1;
int select_index = -1;
uint8_t vol;
switch(cmd_id)
{
case 3://“增大音量”
{
vol = vol_set(vol_get() + 1);
select_index = (vol == VOLUME_MAX) ? 1 : 0;
break;
}
case 103://“XXX”
{
/*此处根据命令词ID添加处理代码*/
break;
}
/*自行添加case处理命令词ID*/
/*省略部分代码*/
default:
ret = 0;
break;
}
/*省略部分代码*/
}
(3)根据语义ID添加处理代码,找到CI13XX_SDK_LLM_AIOT_2.1.2\projects\offline_asr_llm_aiot_uart_sample\app\app_main\user_msg_deal.c的deal_asr_msg_by_semantic_id函数。
更多语义ID信息可以访问 ☞《CI13XX系列芯片语义ID说明文档》页面
uint32_t deal_asr_msg_by_semantic_id(sys_msg_asr_data_t *asr_msg, cmd_handle_t cmd_handle, uint32_t semantic_id)
{
uint32_t ret = 1;
if (PRODUCT_GENERAL == get_product_id_from_semantic_id(semantic_id))
{
uint8_t vol;
int select_index = -1;
switch(get_function_id_from_semantic_id(semantic_id))
{
case VOLUME_UP: //增大音量
vol = vol_set(vol_get() + 1);
select_index = (vol == VOLUME_MAX) ? 1:0;
break;
case XXX_XXX:
/*此处根据语义ID添加处理代码*/
break;
/*自行添加case处理语义ID*/
/*省略部分代码*/
default:
ret = 0;
break;
}
/*省略部分代码*/
}
/*省略部分代码*/
}
5. SDK代码逻辑分析¶
由于语音识别的系统的特定需要,因此SDK中程序已经包含了大量初始化工作,为了帮助用户更快的熟悉代码结构流程,现将程序启动流程和工作状态做以简单说明。

如上图所示,系统上电启动后进入main函数初始化相关硬件(hardware_default_init)、完成平台相关初始化(platform_init),然后通过 xTaskCreate 创建一个启动任务(task_init),随后调用 vTaskStartScheduler 启动FreeRTOS调度器;在启动任务中会依次完成算法模型与算法链初始化、播放器初始化,并创建识别、播报、用户任务(UserTaskManageProcess)等各功能任务,之后系统进入sleep状态并采集语音输入。
当正确的语音输入给识别线程后,识别线程将通过消息队列的机制将消息发送到用户线程中进行处理,在SDK的用户线程中,已经完成了一部分操作比如播放对应的播报语音,切换系统状态,发送串口协议等工作,用户增加代码时一般只需在用户线程中增加代码,因此理解用户线程的中的消息处理机制就可以轻松扩展功能。
当外部通讯串口发送串口协议控制语音板时,将在通讯串口中断中解析串口协议,成功解析后将在中断中调用 vmup_port_send_packet_rev_msg 函数发送串口消息给用户线程,用户线程收到该消息后将在依次调用 deal_userdef_msg >> userapp_deal_com_msg >> userapp_deal_cmd 最终在此处同步或异步(大多数为同步执行,但播放播报词为异步请求)执行功能,完成后发回ACK到通讯串口。