Application Development*
1. Configuration Menu Introduction*
Run make menuconfig to open the configuration interface. The top-level menu structure is as follows:
.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. Board-Level Options*
Board Notes
The current board-level code is located in the boards/nationalchip/fornax_module_1v/ directory. To create a new board, refer to this directory structure:
boards/<vendor>/<board_name>/
├── board.c # Pin mux table, board-level initialization
├── board.h # Board-level header file
├── board_config.h # Board-level configuration macros
├── board.name # Kconfig board type declaration
├── clock_board.c # Clock configuration
├── Kconfig # Board-level configuration options (UART pin selection, etc.)
├── Makefile # Build configuration
└── sample_config.[ch] # Sample rate configuration
vendor.name under boards/<vendor>/, create board.name in the board directory, and include it via source in boards/Kconfig.
| Option | Description | Default Value |
|---|---|---|
| Chipset Family | Chipset family selection: Fornax (GX8005/GX8006) | Fornax |
| Chipset Type | Chipset model: GX8005A or GX8006A | GX8005A |
| Vendor | Vendor selection: NationalChip | NationalChip |
| Board | Board type selection: Fornax GX8005(6) Module | Fornax GX8005(6) Module |
| Board version | Board version: V1.0 | V1.0 |
| Board Pin Configuration | Pin mux configuration (see Section 1.4) | — |
1.2. MCU Settings*
Notes
- The current floating-point printing feature has defects. If you need to output logs with floating-point calculation results, it is recommended to multiply the floating-point number by 10000 and cast it to an integer for output, for example:
float val = 1.23456; printf("val = %d\n", (int)(val * 10000)); // Outputs val = 12345 - The Timer ID defaults to 5. If the hardware design requires this Timer, be sure to configure it to an unused Timer ID.
- Dual-bank upgrade is not recommended; it is recommended to use bare-chip upgrade
| Option | Description | Default Value |
|---|---|---|
| Enable UART printf | Enable UART print output | y |
| Enable Float printf | Enable floating-point printing | y |
| UART Port For Print | UART port used for printing (0/1/2), must correspond to an enabled UART |
0 |
| UART Baud Rate For Print | Print UART baud rate | 921600 |
| Timer ID For gx_get_time_ms | Hardware timer ID used by gx_get_time_ms(), range 0~5, ensure no conflict with the application |
5 |
| Enable dual bank upgrade | Dual-bank upgrade, allows rollback on upgrade failure (requires GX8006A) | n |
| Build Schema | Compilation optimization: Debug(-O0 -g) Release(-O2) Speed(-O3) Size(-Os) |
Release |
| MAIN Stack Minimum Size (KBytes) | Main stack minimum size, range 3~40 | 3 |
| Enable Stack Monitoring | Enable stack overflow monitoring | n |
1.3. Time Profiler Settings*
Used for performance analysis of algorithms and NPU models, to verify whether computational requirements are met during development. General users do not need to pay attention to this: (if multiple NPU model tasks are running simultaneously, each model needs to be run separately to check computational load)
| Option | Description |
|---|---|
| Enable VPA Process Profile | Enable VPA processing flow timing statistics |
| Enable NPU Profile | Enable NPU inference timing statistics |
| Enable KWS Decoder Profile | Enable keyword decoder timing statistics |
1.4. Board Pin Configuration*
The Fornax family chips have a total of 17 pins (PIN0 ~ PIN16). Each pin selects its multiplexed function by configuring the Function value.
Kconfig Configuration Options*
| Option | Description | Default Value |
|---|---|---|
| Enable UART0 | Enable UART0: TX=PIN3(Function 2), RX=PIN2(Function 2) | y |
| Enable UART0 TX Only | Enable UART0 TX only (for debug printing only) | n |
| Enable UART0 TX Open Drain | UART0 TX open-drain output | n |
| Enable UART1 | Enable UART1: TX=PIN0(Function 2), RX=PIN1(Function 2) | n |
| Enable UART2 | Enable UART2, TX/RX pins can be selected independently | n |
| UART2 TX Pin | Select UART2 TX pin: PIN6 / PIN8 / PIN16 | PIN8 |
| UART2 RX Pin | Select UART2 RX pin: PIN5 / PIN7 / PIN15 | PIN15 |
These options take effect in _board_padmux_init() in board.c, switching pins from the default IDLE(Function 0) to the corresponding function.
Pin Function Table*
The following is the pin function mapping defined in boards/nationalchip/fornax_module_1v/board.c ( bold indicates the default configuration)
For more details, see:
| 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 |
The Function value ranges from 0 to 7, corresponding to the Function column in the pin_table.
Function 0 is IDLE (not all have IDLE), Function 7 is GPIO.
PIN4(RSTN), PIN5(JTAG_TMS), PIN6(JTAG_TCK), PIN16(BOOT) are configured as GPIO by default rather than IDLE.
Default Pin Mux*
At system startup, board_init() calls padmux_init(pin_table, PIN_TABLE_SIZE) to initialize all pins according to the table above. Pins configured as GPIO(Function 7) are automatically set to input direction.
Setting Mux Functions Not Available in Kconfig*
Method 1: Add a New Kconfig Configuration
Pin functions are determined by the hardware design, and the software selects from 8 predefined functions. To use a function not currently enabled for a pin:
- Refer to the chip manual to confirm hardware support
- Call
padmux_set(pin_id, function)to switch to the target function - To control via menuconfig, add an option in
boards/.../Kconfigand add logic in_board_padmux_init()
Dynamic Switching in Application Code
Switch at runtime via 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)
Application examples:
| Function | Code Example |
|---|---|
| PWM output | padmux_set(5, 3); padmux_set(6, 3); |
| I2S audio | padmux_set(7, 4); padmux_set(8, 4); padmux_set(10, 4); padmux_set(5, 1); padmux_set(9, 4); |
| DAC output | padmux_set(0, 6); padmux_set(1, 6); |
| LED indication | padmux_set(CONFIG_APP_LED_VAD_PIN, 7); |
Advanced Pin Control API*
drivers/drv-fornax/include/gx_padmux.h provides:
| API | Description |
|---|---|
padmux_set(pad_id, function) |
Set pin mux function (0~7) |
padmux_get(pad_id) |
Get current pin function |
padmux_check(pad_id, function) |
Verify whether the pin is set to the specified function |
padmux_init(pin_table, size) |
Initialize all pins from a table |
padmux_set_io_drive_strength(pad_id, strength) |
Set drive strength (6.6/10.9/8.8/13.1 mA) |
padmux_get_io_drive_strength(pad_id, *strength) |
Get drive strength |
padmux_set_io_pull_up_down(pad_id, pull) |
Set internal pull-up/pull-down |
padmux_get_io_pull_up_down(pad_id, *pull) |
Get internal pull-up/pull-down |
padmux_set_io_open_drain(pad_id, enable) |
Set open-drain (only supported on PIN0~3) |
1.5. OVP Workmode Settings*
Work mode selection:
| Option | Description |
|---|---|
| Has Offline Asr workmode | Offline wake-up + ASR mode (enabled by default) |
| Has NN Denoise workmode | Neural network denoising mode (disabled by default) |
| Has Bypass workmode | Runs no algorithms, used for recording raw audio |
| Initial Workmode Selects | Default work mode initialized at startup |
1.6. I/O Buffer Settings*
Note
- REF Channel Source requires an external ADC for loopback sampling (requires hardware support)
Audio data path configuration:
| Option | Description | Default Value |
|---|---|---|
| MIC Channel Number | Microphone channel count (0~2) | 1 |
| REF Channel Number | Reference signal channel count (0~1) | 0 |
| REF Channel Source | When REF>0, select the loopback source | ADC (external ADC sampling) |
| FFT Channel Number | FFT channel count (0~1) | 0 |
| Logfbank Channel Number | Logfbank channel count | 1 |
| Output Channel Number | Output channel count (0~10) | 0 |
| MIC Gain | Microphone gain (0~54) | 26 |
| REF Gain | Reference signal gain (0~54) | 0 |
| Sample Rate | Sample rate, currently only 16KHz | 16KHz |
| PCM Frame Length | PCM frame length: 10ms or 16ms | 10ms |
| Context Number | Context count (1~100) | 4 |
| PCM Frame Number in a Context | Number of PCM frames per Context | 3 |
| SNPU Buffer Number | SNP buffer count | 3 |
| PCM Frame Number in a Channel | Number of PCM frames per channel | Typically Context Number * PCM Frame Number in a Context |
| Logfbank Frame Number in a Channel | Number of Logfbank frames per channel | Typically equal to PCM Frame Number in a Channel |
| Logfbank Source | Logfbank source: Hardware or User PCM | Hardware |
1.7. Olab Panda Settings > VMA Settings*
Note
Enable Opus Codeccurrently requires a frame length of 40ms per context- When
Opus Frame Lengthis set to 20ms, the internal 40ms audio is split into two 20ms encodings;40ms divided into two 20 ms encodingsmust be enabled
Audio algorithm processing configuration:
| Option | Description | Default Value |
|---|---|---|
| Denoise Level | Denoise level (0~3), higher values provide stronger denoising but also higher distortion | 0 |
| Enable AEC | Enable echo cancellation | n |
| AEC Filter Length | AEC filter length | 1024 |
| Enable Opus Codec | Enable Opus encoding (mutually exclusive with Speex) | y |
| Opus Bit Rate | Opus encoding bit rate: 16kbps or 24kbps | 16kbps |
| Opus Frame Length | Opus encoding frame length: 40ms or 20ms | 40ms |
| Enable DRC | Enable dynamic range compression | n |
| DRC Amplify dB | DRC gain (dB) | 12 |
1.8. Olab Panda Settings > VUI Settings*
Voice interaction function configuration:
| Option | Description | Default Value |
|---|---|---|
| Enable Keyword Recognition | Enable keyword recognition (wake word detection) | y |
| Enable VAD | Enable voice activity detection (for ASR) | n |
| Enable Voiceprint | Enable voiceprint recognition | n |
| Enable SED | Enable sound event detection | n |
| Enable NN Noise | Enable neural network denoising | n |
1.9. OVP Application Settings*
Application Selection*
| Application | Configuration Option | Description |
|---|---|---|
| None | OVP_APP_NONE | No application selected |
| Hello World | APP_HELLO_WORLD | Minimal application template |
| Offline ASR App | APP_OFFLINE_ASR | Offline ASR application |
| Smartbot App | APP_SMARTBOT | WiFi large model application |
| NN Denoise App | APP_NN_DENOISE | Neural network denoising demo |
| Acoustic Test App | APP_ACOUSTIC_TEST | Acoustic testing |
| ... | ... | ... |
Smartbot App Configuration (appears after selecting Smartbot App)*
| Option | Description | Default Value |
|---|---|---|
| Select flow control type | UART flow control type: ACK flow control or GPIO flow control | ACK flow control |
| KWS Time Out | Wake-up timeout (s) | 10 |
| Delay Awake Delay(ms) | First wake-up delay (ms) | 1000 |
| Enable UART transmission | Enable UART audio transmission | y |
| Enable I2S transfer | Enable I2S audio transmission | n |
| Enable LED | Enable LED indication | y |
| VAD LED Pin | VAD indication LED pin | 6 |
| Status LED Pin | Status indication LED pin | 13 |
| Disable Wake Timeout | Debug option: disable wake-up timeout | n |
1.10. Common Function Settings*
General function settings, including UART protocol, recording, player, watchdog, etc.
UART Protocol (UART Message 2.0)*
| Option | Description | Default Value |
|---|---|---|
| Has UART protocol Message2.0 | Enable UART protocol Message2.0 | y |
| Select UART port for protocol | UART port used by the protocol: UART0 / UART1 / Both | Both |
| Send Queue Length | Send queue length | 8 |
| Recv Queue Length | Receive queue length | 8 |
UART Recording*
| Option | Description | Default Value |
|---|---|---|
| Enable UART record | Enable UART recording | n |
| UART port | Recording UART port number | 0 |
| UART baudrate | Recording UART baud rate | 500000 |
Player Settings (Voice Player)*
| Option | Description | Default Value |
|---|---|---|
| Has voice player | Enable voice player | n |
| Set volume | Playback volume | 100 |
| Enable External PA | Enable external power amplifier | n |
| Mute Pin | External PA mute pin | 7 |
| Mute Level | Mute level: High or Low | Low |
| Select DAC channel | Channel mode: Stereo/Left channel/Right channel/Mono | Stereo |
| Enable pcm | Enable PCM playback | y |
| Enable opus decode | Enable Opus decode playback | y |
Other Functions*
| Option | Description | Default Value |
|---|---|---|
| Has Viva | Enable Viva interaction framework | n |
| Has watchdog | Enable watchdog | n |
| Watchdog Reset Time | Watchdog reset time: 1s / 2s / 5s / 10s | 5s |
2. Creating Your First App*
2.1. App Framework Description*
Each application needs to implement the OVP_APP structure (defined in ovp/app_core/ovp_app.h):
typedef struct {
const char *app_name; // Application name
int (*AppInit)(void); // Initialization callback
int (*AppEventResponse)(APP_EVENT *plc_event); // Event handling callback
int (*AppTaskLoop)(void); // Main loop callback
int (*AppSuspend)(void *priv); // Suspend callback
void *suspend_priv;
int (*AppResume)(void *priv); // Resume callback
void *resume_priv;
} OVP_APP;
#define OVP_REGISTER_APP(app) \
OVP_APP *app_core_ops = &app
The application registers itself to the system via the OVP_REGISTER_APP(ovp_app) macro. After system startup, the main loop in main() calls in sequence:
1. AppInit() — Application initialization
2. AppEventResponse() — Handles audio/wake-up events
3. AppTaskLoop() — Called repeatedly in while(1)
Common events (defined in ovp/app_core/ovp_app_core.h):
| Event ID | Description |
|---|---|
EVENT_AUDIO_IN_RECORD_DONE |
Recording complete, audio data arrived |
EVENT_VAD_STATUS |
VAD status change |
EVENT_WAKEUP_ENTER_RX |
Entering wake-up state |
EVENT_WAKEUP_EXIT_RX |
Exiting wake-up state |
EVENT_WAKEUP_EXIT |
Internal trigger for wake-up exit |
2.2. Hello World Example*
The following is the complete code of app/hello_world/app.c, which is the minimal application template:
#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. Steps to Create a New App*
Taking creating a my_app as an example:
Step 1: Create Directory and Files*
app/my_app/
├── app.c # Application main code
├── app.name # Kconfig application declaration
├── app.mk # Build configuration
└── Kconfig # Application configuration options
Step 2: Write app.name*
config APP_MY_APP
bool "My App"
help
"My first application"
app.name is used to register the application in the menuconfig application selection list and is included by source "app/*/app.name" in app/Kconfig.
Step 3: Write Kconfig*
if APP_MY_APP
config MY_APP_SETTING
int "My app setting"
default 1
endif
Kconfig is used to define configuration options for the application and is included by source "app/*/Kconfig" in app/Kconfig.
Step 4: Write 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 is used to add the application source files to the build.
Step 5: Write app.c*
Refer to the Hello World template in Section 2.2 to write the application code.
Step 6: Build*
cp configs/example/app/8006_hello_world_app.config .config
make menuconfig
# Navigate to OVP Application Settings → Applications Selection → Select "My App"
# Configure application-related options as needed
# Save and exit
make
After the build completes, the firmware will include the my_app application. You can check the logs via UART0 at 921600 baud to verify success.
Once you are familiar with
You should have a basic grasp of SDK usage.