应用开发*
1. 配置菜单介绍*
执行 make menuconfig 打开配置界面,顶层菜单结构如下:
.config - NationalChip Offline Voice Process Configuration
───────────────────────────────────────────────────────────────────────────────
┌───────────── NationalChip Offline Voice Process Configuration ──────────────┐
│ Arrow keys navigate the menu. <Enter> selects submenus ---> (or empty │
│ submenus ----). Highlighted letters are hotkeys. Pressing <Y> includes, │
│ <N> excludes, <M> modularizes features. Press <Esc><Esc> to exit, <?> for │
│ Help, </> for Search. Legend: [*] built-in [ ] excluded <M> module < > │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Chipset Family selects: (Fornax Family [GX8005/GX8006]) ---> │ │
│ │ Chipset Type selects: (Fornax Type GX8005A) ---> │ │
│ │ Vendor selects: (NationalChip) ---> │ │
│ │ Board selects: (Fornax GX8005(6) Module) ---> │ │
│ │ Board version selects: (V1.0) ---> │ │
│ │ Board Pin Configuration ---> │ │
│ │ │ │
│ │ MCU settings ---> │ │
│ │ Time Profiler settings ---> │ │
│ │ │ │
│ │ OVP Workmode Settings ---> │ │
│ │ I/O Buffer Settings ---> │ │
│ │ Voice Process Algorithm select: (Olab Panda) ---> │ │
│ │ Olab Panda Settings ---> │ │
│ │ │ │
│ │ OVP Application Settings ---> │ │
│ │ Common Function Settings ---> │ │
│ │ │ │
│ │ Object Info ---> │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ <Select> < Exit > < Help > < Save > < Load > │
└─────────────────────────────────────────────────────────────────────────────┘
1.1. 板级选项*
板级说明
当前板级代码位于 boards/nationalchip/fornax_module_1v/ 目录。若需新建板级,可参考此目录结构创建:
boards/<vendor>/<board_name>/
├── board.c # 管脚复用表、板级初始化
├── board.h # 板级头文件
├── board_config.h # 板级配置宏
├── board.name # Kconfig 板型声明
├── clock_board.c # 时钟配置
├── Kconfig # 板级配置选项(UART 管脚选择等)
├── Makefile # 构建配置
└── sample_config.[ch] # 采样率配置
boards/<vendor>/ 下创建 vendor.name、在板级目录下创建 board.name,并在 boards/Kconfig 中通过 source 引入即可。
| 选项 | 说明 | 默认值 |
|---|---|---|
| Chipset Family | 芯片系列选择:Fornax (GX8005/GX8006) | Fornax |
| Chipset Type | 芯片型号:GX8005A 或 GX8006A | GX8005A |
| Vendor | 厂商选择:NationalChip | NationalChip |
| Board | 板型选择:Fornax GX8005(6) Module | Fornax GX8005(6) Module |
| Board version | 板卡版本:V1.0 | V1.0 |
| Board Pin Configuration | 管脚复用配置(详见1.4节) | — |
1.2. MCU settings*
注意事项
- 当前浮点打印功能存在缺陷,若需输出带有浮点数运算结果的日志,建议将浮点数乘以 10000 后强转为整型输出,例如:
float val = 1.23456; printf("val = %d\n", (int)(val * 10000)); // 输出 val = 12345 - Timer ID 默认配置5,若硬件设计需要此 Timer,注意将其配置成未使用的 Timer ID
- 不建议使用双备份,推荐裸片升级
| 选项 | 说明 | 默认值 |
|---|---|---|
| Enable UART printf | 使能UART打印输出 | y |
| Enable Float printf | 使能浮点数打印 | y |
| UART Port For Print | 打印使用的串口号 (0/1/2),需与启用的UART对应 |
0 |
| UART Baud Rate For Print | 打印串口波特率 | 921600 |
| Timer ID For gx_get_time_ms | gx_get_time_ms() 使用的硬件定时器ID,范围0~5,注意不要与应用冲突 |
5 |
| Enable dual bank upgrade | 双Bank升级,升级失败可回滚(需GX8006A) | n |
| Build Schema | 编译优化: Debug(-O0 -g) Release(-O2) Speed(-O3) Size(-Os) |
Release |
| MAIN Stack Minimum Size (KBytes) | 主栈最小大小,范围3~40 | 3 |
| Enable Stack Monitoring | 使能栈溢出监控 | n |
1.3. Time Profiler settings*
用于算法和NPU模型的性能分析,开发过程中验证是否满足算力时使用,一般用户无需关注:(若同时运行多个 NPU 模型任务,需要每个模型单独运行查看算力)
| 选项 | 说明 |
|---|---|
| Enable VPA Process Profile | 使能VPA处理流程耗时统计 |
| Enable NPU Profile | 使能NPU推理耗时统计 |
| Enable KWS Decoder Profile | 使能关键词解码器耗时统计 |
1.4. Board Pin Configuration*
Fornax 系列芯片共有 17 个管脚(PIN0 ~ PIN16),每个管脚通过配置 Function 值选择复用功能。
Kconfig 配置选项*
| 选项 | 说明 | 默认值 |
|---|---|---|
| Enable UART0 | 使能UART0: TX=PIN3(Function 2), RX=PIN2(Function 2) | y |
| Enable UART0 TX Only | 仅使能UART0 TX(仅用于调试打印) | n |
| Enable UART0 TX Open Drain | UART0 TX开漏输出 | n |
| Enable UART1 | 使能UART1: TX=PIN0(Function 2), RX=PIN1(Function 2) | n |
| Enable UART2 | 使能UART2,TX/RX管脚可分别选择 | n |
| UART2 TX Pin | 选择UART2 TX管脚: PIN6 / PIN8 / PIN16 | PIN8 |
| UART2 RX Pin | 选择UART2 RX管脚: PIN5 / PIN7 / PIN15 | PIN15 |
这些选项在 board.c 的 _board_padmux_init() 中生效,将管脚从默认的 IDLE(Function 0) 切换到对应功能。
管脚功能表*
以下为 boards/nationalchip/fornax_module_1v/board.c 中定义的管脚功能映射关系( 加粗 为默认配置)
更多详见:
| gpio-id | Function 0 | Function 1 | Function 2 | Function 3 | Function 4 | Function 5 | Function 6 | Function 7 | Pin |
|---|---|---|---|---|---|---|---|---|---|
| 0 | IDLE(Hi-Z) | PA_N | UART1_TX | PWM0 | I2S_DOUT/LR | I2C_SDA | DAC_P | GPIO0 | 20 |
| 1 | IDLE(Hi-Z) | PA_P | UART1_RX | PWM1 | I2S_BCLK | I2C_SCL | DAC_N | GPIO1 | 18 |
| 2 | IDLE(pull-up) | I2S_DIN | UART0_RX | PWM2 | JTAG_TMS | I2C_SDA | DAC_P | GPIO2 | 11 |
| 3 | IDLE(pull-up) | UART0_TX | PWM3 | JTAG_TCK | I2C_SCL | DAC_N | GPIO3 | 10 | |
| 4 | RSTN(pull-up) | I2S_DIN | JTAG_TMS | IRC_EN | GPIO4 | 9 | |||
| 5 | JTAG_TMS(Hi-Z) | I2S_DIN | UART2_RX | PWM5 | I2S_LR | I2C_SDA | IRC_TX | GPIO5 | 23 |
| 6 | JTAG_TCK(pull-up) | UART2_TX | PWM0 | I2S_MCLK | I2C_SCL | IRC_EN | GPIO6 | 22 | |
| 7 | IDLE(Hi-Z) | SPI2_CLK | UART2_RX | PWM1 | I2S_MCLK | I2C_SDA | IRC_RX | GPIO7 | 21 |
| 8 | IDLE(Hi-Z) | SPI2_CS | UART2_TX | PWM2 | I2S_BCLK | I2C_SCL | IRC_TX | GPIO8 | 24 |
| 9 | IDLE(Hi-Z) | SPI2_MISO | UART2_CTS | PWM3 | I2S_DOUT | IRC_RX | GPIO9 | 1 | |
| 10 | IDLE(pull-up) | SPI2_MOSI | UART2_RTS | PWM4 | I2S_LR | JTAG_TCK | IRC_EN | GPIO10 | 7 |
| 11 | IDLE(Hi-Z) | SPI2_CS | PWM1 | MSPI1_CS | GPIO11 | 8 | |||
| 12 | IDLE(Hi-Z) | SPI2_CLK | PWM2 | MSPI1_CLK | GPIO12 | 6 | |||
| 13 | IDLE(Hi-Z) | SPI2_MOSI | PWM3 | MSPI1_MOSI | GPIO13 | 5 | |||
| 14 | IDLE(Hi-Z) | SPI2_MISO | PWM4 | MSPI1_MISO | JTAG_TCK | GPIO14 | 2 | ||
| 15 | IDLE(Hi-Z) | I2S_LR | UART2_RX | PWM5 | MSPI1_WP | I2C_SDA | IRC_TX | GPIO15 | 4 |
| 16 | BOOT(pull-up) | I2S_MCLK | UART2_TX | PWM0 | MSPI1_HOLD | I2C_SCL | IRC_RX | GPIO16 | 3 |
Function 值范围 0~7,对应 pin_table 中的 Function 列。
Function 0 为 IDLE(并不是都有 IDLE),Function 7 为 GPIO。
PIN4(RSTN)、PIN5(JTAG_TMS)、PIN6(JTAG_TCK)、PIN16(BOOT) 默认配置为 GPIO 而非 IDLE。
默认管脚复用*
系统启动时,board_init() 调用 padmux_init(pin_table, PIN_TABLE_SIZE) 按上表初始化所有管脚。对设置为 GPIO(Function 7) 的管脚,自动设为输入方向。
设置Kconfig中不存在的复用*
方式1:自行新增Kconfig配置
管脚功能由硬件设计确定,软件在预定义的 8 种功能中选择。如需使用某管脚当前未启用的功能:
- 查阅芯片数据手册确认硬件是否支持
- 调用
padmux_set(pin_id, function)切换到目标功能 - 如需通过 menuconfig 控制,在
boards/.../Kconfig添加选项并在_board_padmux_init()增加逻辑
在应用代码中动态切换
运行时通过 padmux_set(pin_id, function) 随时切换:
#include <gx_padmux.h>
padmux_set(0, 7); // PIN0 → GPIO (Function 7)
padmux_set(5, 3); // PIN5 → PWM5 (Function 3)
padmux_set(7, 4); // PIN7 → I2S_MCLK (Function 4)
应用实例:
| 功能 | 代码示例 |
|---|---|
| PWM输出 | padmux_set(5, 3); padmux_set(6, 3); |
| I2S音频 | padmux_set(7, 4); padmux_set(8, 4); padmux_set(10, 4); padmux_set(5, 1); padmux_set(9, 4); |
| DAC输出 | padmux_set(0, 6); padmux_set(1, 6); |
| LED指示 | padmux_set(CONFIG_APP_LED_VAD_PIN, 7); |
高级管脚控制 API*
drivers/drv-fornax/include/gx_padmux.h 提供:
| API | 说明 |
|---|---|
padmux_set(pad_id, function) |
设置管脚复用功能 (0~7) |
padmux_get(pad_id) |
获取当前管脚功能 |
padmux_check(pad_id, function) |
验证管脚是否已设置为指定功能 |
padmux_init(pin_table, size) |
从表初始化所有管脚 |
padmux_set_io_drive_strength(pad_id, strength) |
设置驱动强度 (6.6/10.9/8.8/13.1 mA) |
padmux_get_io_drive_strength(pad_id, *strength) |
获取驱动强度 |
padmux_set_io_pull_up_down(pad_id, pull) |
设置内部上拉/下拉 |
padmux_get_io_pull_up_down(pad_id, *pull) |
获取内部上拉/下拉 |
padmux_set_io_open_drain(pad_id, enable) |
设置开漏(仅 PIN0~3 支持) |
1.5. OVP Workmode Settings*
工作模式选择:
| 选项 | 说明 |
|---|---|
| Has Offline Asr workmode | 离线唤醒+ASR模式(默认开启) |
| Has NN Denoise workmode | 神经网络降噪模式(默认关闭) |
| Has Bypass workmode | 不跑任何算法,用于录制原始音频 |
| Initial Workmode Selects | 启动时初始化的默认工作模式 |
1.6. I/O Buffer Settings*
注意
- REF Channel Source 需使用外部 ADC 做回采(需硬件支持)
音频数据通路配置:
| 选项 | 说明 | 默认值 |
|---|---|---|
| MIC Channel Number | 麦克风通道数(0~2) | 1 |
| REF Channel Number | 参考信号通道数(0~1) | 0 |
| REF Channel Source | 当REF>0时,选择回采源 | ADC(外部ADC采样) |
| FFT Channel Number | FFT通道数(0~1) | 0 |
| Logfbank Channel Number | Logfbank通道数 | 1 |
| Output Channel Number | 输出通道数(0~10) | 0 |
| MIC Gain | 麦克风增益(0~54) | 26 |
| REF Gain | 参考信号增益(0~54) | 0 |
| Sample Rate | 采样率,目前仅16KHz | 16KHz |
| PCM Frame Length | PCM帧长:10ms 或 16ms | 10ms |
| Context Number | Context数量(1~100) | 4 |
| PCM Frame Number in a Context | 每个Context包含的PCM帧数 | 3 |
| SNPU Buffer Number | SNP缓冲区数量 | 3 |
| PCM Frame Number in a Channel | 每个通道中包含的PCM帧数 | 一般为 Context Number * PCM Frame Number in a Context |
| Logfbank Frame Number in a Channel | 每个通道中包含的Logfbank帧数 | 一般等于 PCM Frame Number in a Channel |
| Logfbank Source | Logfbank来源:Hardware(硬件) 或 User PCM | Hardware |
1.7. Olab Panda Settings > VMA Settings*
注意
Enable Opus Codec目前要求每个context中帧长为40msOpus Frame Length设置 20ms 时,内部将 40ms 的音频分成两个 20ms 编码,需要打开40ms divided into two 20 ms encodings
音频算法处理配置:
| 选项 | 说明 | 默认值 |
|---|---|---|
| Denoise Level | 降噪等级(0~3),值越大降噪越强,失真度也越高 | 0 |
| Enable AEC | 使能回声消除 | n |
| AEC Filter Length | AEC滤波器长度 | 1024 |
| Enable Opus Codec | 使能Opus编码(与Speex二选一) | y |
| Opus Bit Rate | Opus编码码率:16kbps 或 24kbps | 16kbps |
| Opus Frame Length | Opus编码帧长:40ms 或 20ms | 40ms |
| Enable DRC | 使能动态范围压缩 | n |
| DRC Amplify dB | DRC增益(dB) | 12 |
1.8. Olab Panda Settings > VUI Settings*
语音交互功能配置:
| 选项 | 说明 | 默认值 |
|---|---|---|
| Enable Keyword Recognition | 使能关键词识别(唤醒词检测) | y |
| Enable VAD | 使能语音活动检测(用于ASR) | n |
| Enable Voiceprint | 使能声纹识别 | n |
| Enable SED | 使能声音事件检测 | n |
| Enable NN Noise | 使能神经网络降噪 | n |
1.9. OVP Application Settings*
应用选择*
| 应用 | 配置选项 | 说明 |
|---|---|---|
| None | OVP_APP_NONE | 不选择任何应用 |
| Hello World | APP_HELLO_WORLD | 最简应用模板 |
| Offline ASR App | APP_OFFLINE_ASR | 离线ASR应用 |
| Smartbot App | APP_SMARTBOT | WiFi大模型应用 |
| NN Denoise App | APP_NN_DENOISE | 神经网络降噪演示 |
| Acoustic Test App | APP_ACOUSTIC_TEST | 声学测试 |
| ... | ... | ... |
Smartbot App 配置(选中Smartbot App后出现)*
| 选项 | 说明 | 默认值 |
|---|---|---|
| Select flow control type | 串口流控方式:ACK流控 或 GPIO流控 | ACK流控 |
| KWS Time Out | 唤醒超时时间(s) | 10 |
| Delay Awake Delay(ms) | 首次唤醒延迟(ms) | 1000 |
| Enable UART transmission | 使能UART音频传输 | y |
| Enable I2S transfer | 使能I2S音频传输 | n |
| Enable LED | 使能LED提示 | y |
| VAD LED Pin | VAD指示LED引脚 | 6 |
| Status LED Pin | 状态指示LED引脚 | 13 |
| Disable Wake Timeout | 调试选项:禁用唤醒超时 | n |
1.10. Common Function Settings*
通用功能设置,包含串口协议、录音、播放器、看门狗等。
串口协议 (UART Message 2.0)*
| 选项 | 说明 | 默认值 |
|---|---|---|
| Has UART protocol Message2.0 | 使能UART协议Message2.0 | y |
| Select UART port for protocol | 协议使用的串口:UART0 / UART1 / Both | Both |
| Send Queue Length | 发送队列长度 | 8 |
| Recv Queue Length | 接收队列长度 | 8 |
UART 录音*
| 选项 | 说明 | 默认值 |
|---|---|---|
| Enable UART record | 使能UART录音 | n |
| UART port | 录音串口号 | 0 |
| UART baudrate | 录音串口波特率 | 500000 |
播放器设置 (Voice Player)*
| 选项 | 说明 | 默认值 |
|---|---|---|
| Has voice player | 使能语音播放器 | n |
| Set volume | 播放音量 | 100 |
| Enable External PA | 使能外部功放 | n |
| Mute Pin | 外部功放静音引脚 | 7 |
| Mute Level | 静音电平:High 或 Low | Low |
| Select DAC channel | 声道模式:立体声/左声道/右声道/单声道 | 立体声 |
| Enable pcm | 使能PCM播放 | y |
| Enable opus decode | 使能Opus解码播放 | y |
其他功能*
| 选项 | 说明 | 默认值 |
|---|---|---|
| Has Viva | 使能Viva交互框架 | n |
| Has watchdog | 使能看门狗 | n |
| Watchdog Reset Time | 看门狗复位时间:1s / 2s / 5s / 10s | 5s |
2. 建立第一个 App*
2.1. App 框架说明*
每个应用需要实现 OVP_APP 结构体(定义在 ovp/app_core/ovp_app.h):
typedef struct {
const char *app_name; // 应用名称
int (*AppInit)(void); // 初始化回调
int (*AppEventResponse)(APP_EVENT *plc_event); // 事件处理回调
int (*AppTaskLoop)(void); // 主循环回调
int (*AppSuspend)(void *priv); // 挂起回调
void *suspend_priv;
int (*AppResume)(void *priv); // 恢复回调
void *resume_priv;
} OVP_APP;
#define OVP_REGISTER_APP(app) \
OVP_APP *app_core_ops = &app
应用通过 OVP_REGISTER_APP(ovp_app) 宏注册到系统中。系统启动后,main() 中的主循环会依次调用:
1. AppInit() — 应用初始化
2. AppEventResponse() — 处理音频/唤醒等事件
3. AppTaskLoop() — while(1)频繁调用
常用事件(定义在 ovp/app_core/ovp_app_core.h):
| 事件ID | 说明 |
|---|---|
EVENT_AUDIO_IN_RECORD_DONE |
录音完成,音频数据到达 |
EVENT_VAD_STATUS |
VAD 状态变化 |
EVENT_WAKEUP_ENTER_RX |
进入唤醒状态 |
EVENT_WAKEUP_EXIT_RX |
退出唤醒状态 |
EVENT_WAKEUP_EXIT |
内部触发唤醒退出 |
2.2. Hello World 示例*
以下为 app/hello_world/app.c 的完整代码,是最简的应用模板:
#include <autoconf.h>
#include <ovp_app.h>
#include <stdint.h>
#define LOG_TAG "[APP_HELLO_WORLD]"
#define LOG_LVL 3
#include <log.h>
static int app_suspend(void *priv)
{
LOG_D(" ---- %s, %d ----", __func__, __LINE__);
return 0;
}
static int app_resume(void *priv)
{
LOG_D(" ---- %s, %d ----", __func__, __LINE__);
return 0;
}
static int app_init(void)
{
LOG_I(" ---- %s, %d ----", __func__, __LINE__);
return 0;
}
static int app_event_response(APP_EVENT *app_event)
{
if (app_event->event_id == EVENT_AUDIO_IN_RECORD_DONE) {
LOG_I("hello world!");
}
return 0;
}
static int app_task_loop(void)
{
return 0;
}
OVP_APP ovp_app = {
.app_name = "app_hello_world",
.AppInit = app_init,
.AppEventResponse = app_event_response,
.AppTaskLoop = app_task_loop,
.AppSuspend = app_suspend,
.suspend_priv = "app_suspend",
.AppResume = app_resume,
.resume_priv = "app_resume",
};
OVP_REGISTER_APP(ovp_app);
2.3. 创建新 App 的步骤*
以创建一个 my_app 为例:
步骤1:创建目录和文件*
app/my_app/
├── app.c # 应用主代码
├── app.name # Kconfig 应用声明
├── app.mk # 构建配置
└── Kconfig # 应用配置选项
步骤2:编写 app.name*
config APP_MY_APP
bool "My App"
help
"My first application"
app.name 用于在 menuconfig 的应用选择列表中注册,被 app/Kconfig 中的 source "app/*/app.name" 引入。
步骤3:编写 Kconfig*
if APP_MY_APP
config MY_APP_SETTING
int "My app setting"
default 1
endif
Kconfig 用于定义该应用的配置选项,被 app/Kconfig 中的 source "app/*/Kconfig" 引入。
步骤4:编写 app.mk*
ifeq ($(CONFIG_APP_MY_APP), y)
APP_DIR := $(dir $(lastword $(MAKEFILE_LIST)))
APP_SRCS := $(wildcard $(APP_DIR)*.c)
app_objs += $(addprefix $(OBJ_DIR),$(APP_SRCS:.c=.o))
endif
app.mk 用于将该应用的源文件加入编译。
步骤5:编写 app.c*
参照 2.2 节的 Hello World 模板编写应用代码。
步骤6:编译*
cp configs/example/app/8006_hello_world_app.config .config
make menuconfig
# 进入 OVP Application Settings → Applications Selection → 选择 "My App"
# 按需配置应用相关选项
# 保存退出
make
编译完成后,固件中即包含 my_app 应用,可通过串口0,921600的波特率查看日志,确定是否成功。
当你了解熟悉了
相信你对 SDK 的使用,已经可以基本掌握。