跳转至

应用程序快速开发*


1. LVP SDK 软件框架*

2. SDK 目录结构*

2.1 顶层目录结构*

lvp_tws/
▸ 3rdparty/             # 第三方代码
▸ app/                  # 应用代码 *
▸ arch/                 # 启动代码,应用开发无需关心此目录
▸ base/                 # 基础公用代码
▸ boards/               # 板级配置代码,根据硬件进行配置,一般无需修改 *
▸ configs/              # 默认配置
▸ docs/                 # 说明文档
▸ drivers/              # 驱动代码
▸ include/              # 公共头文件
▸ lib/                  # 库文件
▸ lvp/                  # 语音处理及框架调用 *
▸ output/               # 输出路径
▸ scripts/              # 公共脚本
▸ tools/                # 辅助工具 *
▸ utility/              # 公用代码
  Kconfig               # 编译配置源码
  Makefile

2.2 LVP 核心代码目录结构*

lvp_tws/lvp/
▾ lvp/                  # 语音处理框架与应用框架core
  ▸ app_core/           # 应用core,详见下一节
  ▾ common/             # 公共功能接口
      lvp_audio_in.c    # audio in 模块
      lvp_audio_in.h
      lvp_buffer.c      # 流水线buffer管理接口
      lvp_i2c_msg.c     # i2c通信模块
      lvp_i2c_msg.h
      lvp_queue.c       # 队列管理接口
      lvp_queue.h
      lvp_uart_record.c # 录音模块
      lvp_uart_record.h
      Makefile
      uart_message_v2.c # message 2.0 协议异步接口
      uart_message_v2.h
  ▾ vui/                # 语音处理模型
    ▸ kws/
    ▸ vpr/
      Kconfig
      Makefile
    Kconfig
    lvp_kws.c           # 语音处理接口
    lvp_kws.h
    lvp_mode.c          # 模式管理接口
    lvp_mode.h
    lvp_mode_idle.c     # idle模式
    lvp_mode_record.c   # 录音模式
    lvp_mode_tws.c      # 语音处理模式,该模式调用app core接口驱动app框架
    lvp_mode_tws.h
    main.c              # 系统初始化与主循环
    Makefile

2.3 板级相关修改*

2.3.1 板级目录结构*

lvp_tws/boards/
▾ boards/                       # 板级配置全部是与硬件相关的
  ▾ nationalchip/
    ▸ grus_gx8002_fpga_1v/
    ▸ grus_gx8002a_fan_1v/
    ▾ grus_gx8002b_dev_1v/
      ▾ include/
          board_config.h        # 一些数值配置
          board_misc_config.h   # 杂项配置,各板级可能不同
        audio_board.c           # audio in/out 配置
        board.name              # 暴露到编译配置中的使能开关
        boot_board.c
        clock_board.c           # 时钟配置
        Kconfig
        Makefile
        misc_board.c            # 引脚复用等杂项配置与接口
    ▸ grus_gx8002b_gsensor_1v/
    ▸ grus_gx8002t_test_1v/
      vendor.name
    Kconfig
    Makefile

2.3.2 增益调整*

增益调整,增益可以在编译配置里面进行调整,执行命令make menuconfig,进入Board Options,在最末端可配置增益;

  • 模拟增益(pga)

    • 可调节范围:0~48dB
    • 调节步进:0-18dB,Step 6dB; 19-48dB,Step 1dB
  • 数字增益(audio in)

    • 可调节范围:0~10
    • 调节步进:采用映射的方式,0-10 mapping 0-54dB, Step 6dB
  • 代码参考

    • 实际修改的参数代码在对应板级路径下

      /lvp_tws/boards/nationalchip/grus_gx8002b_dev_1v/audio_board.c
      .pga_gain           = CONFIG_BOARD_MIC_GAIN_A, // pga_gain 只支持模拟麦, 0-18dB,步进6dB, 19-48dB,步进1dB
      .audio_in_gain      = CONFIG_BOARD_MIC_GAIN_B, // GX_AUDIO_IN_GAIN
      

2.3.3 GPIO配置*

GPIO配置在misc_board.c中,有一个可配置的二维数组,id不需要修改,func表示对应的功能的编号,编号从0开始,对应的功能可以参考每个id后面的的注释,可以根据板级的需求修改对应的功能。

/lvp_tws/boards/nationalchip/grus_gx8002b_dev_1v/misc_board.c
static const GX_PIN_CONFIG pin_table[] = {
/*   id  func     func0                 | func1          |  func2              | func3 ... */
    { 0,  1},  // 0:JTAG_TMS            | 1:GPIO_00      | 2:I2S_IN_BCLK_OUT_M | 3:I2S_OUT_BCLK_OUT_M
               // 4:SPI_CSn_M           | 5:SPI_CSn_S    | 6:REC_BCLK_OUT      | 7:PDM_DATA
               // 8:I2S_OUT_BCLK_IN_S   | 9:REC_BCLK_IN  | 10:I2S_IN_BCLK_IN_S
    { 1,  1},  // 0:JTAG_CK             | 1:GPIO_01      | 2:I2S_IN_DATA_IN     | 3:I2S_OUT_DATA_OUT
               // 4:SPI_MISO_M          | 5:SPI_MISO_S   | 6:REC_DATA_OUT       | 7:PDM_CLK_IN
               // 8:PWM_CLK_IN          | 9:PDM_CLK_OUT
    { 2,  0},  // 0:GPIO_02             | 1:PWM_CLK_IN    //i2c addr decision: low->0x35, high->0x36
    { 3,  1},  // 0:I2C0_SDA            | 1:GPIO_03      | 2:PDM_CLK_IN         | 3:PDM_CLK_OUT
    { 4,  1},  // 0:I2C0_SCL            | 1:GPIO_04      | 2:PDM_DATA
    { 5,  1},  // 0:UART0_TX            | 1:GPIO_05      | 2:I2C1_SDA           | 3:PDM_CLK_IN
               // 4:DAC_OUT_L           | 5:PDM_CLK_OUT
    { 6,  1},  // 0:UART0_RX            | 1:GPIO_06      | 2:I2C1_SCL           | 3:PDM_DATA
               // 4:DAC_OUT_R           | 5:PWM_CLK_IN
    { 7,  1},  // 0:PDM_CLK_IN          | 1:GPIO_07      | 2:I2S_IN_MCLK_OUT_M  | 3:I2S_OUT_MCLK_OUT_M
               // 4:SPI_SCLK_M          | 5:SPI_SCLK_S   | 6:REC_MCLK_OUT       | 7:DAC_OUT_L
               // 8:I2S_OUT_MCLK_IN_S   | 9:REC_MCLK_IN  | 10:I2S_IN_MCLK_IN_S  | 11:UART0_CTS
               // 12:PDM_CLK_OUT
    { 8,  1},  // 0:PDM_DATA            | 1:GPIO_08      | 2:I2S_IN_LRCLK_OUT_M | 3:I2S_OUT_LRCLK_OUT_M
               // 4:SPI_MOSI_M          | 5:SPI_MOSI_S   | 6:REC_LRCLK_OUT      | 7:DAC_OUT_R
               // 8:I2S_OUT_LRCLK_IN_S  | 9:REC_LRCLK_IN | 10:I2S_IN_LRCLK_IN_S | 11:UART0_RTS
               // 12:PWM_CLK_IN
    { 9,  1},  // 0:I2C1_SDA            | 1:GPIO_09      | 2:I2S_IN_BCLK_OUT_M  | 3:I2S_OUT_BCLK_OUT_M
               // 4:SPI_CSn_M           | 5:SPI_CSn_S    | 6:REC_BCLK_OUT       | 7:UART1_CTS
               // 8:I2S_OUT_BCLK_IN_S   | 9:REC_BCLK_IN  | 10:I2S_IN_BCLK_IN_S
    {10,  1},  // 0:I2C1_SCL            | 1:GPIO_10      | 2:I2S_IN_DATA_IN     | 3:I2S_OUT_DATA_OUT
               // 4:SPI_MISO_M          | 5:SPI_MISO_S   | 6:REC_DATA_OUT       | 7:UART1_RTS
               // 8:PWM_CLK_IN
    {11,  1},  // 0:UART1_TX            | 1:GPIO_11      | 2:I2S_IN_MCLK_OUT_M  | 3:I2S_OUT_MCLK_OUT_M
               // 4:SPI_SCLK_M          | 5:SPI_SCLK_S   | 6:REC_MCLK_OUT       | 7:DAC_OUT_L
               // 8:I2S_OUT_MCLK_IN_S   | 9:REC_MCLK_IN  | 10:I2S_IN_MCLK_IN_S
    {12,  1},  // 0:UART1_RX            | 1:GPIO_12      | 2:I2S_IN_LRCLK_OUT_M | 3:I2S_OUT_LRCLK_OUT_M
               // 4:SPI_MOSI_M          | 5:SPI_MOSI_S   | 6:REC_LRCLK_OUT      | 7:DAC_OUT_R
               // 8:I2S_OUT_LRCLK_IN_S  | 9:REC_LRCLK_IN | 10:I2S_IN_LRCLK_IN_S
};
一般上面的二维数组不需要去改,除非有特殊的需求;一般的功能只需要在编译配置Board Options中使能对应的功能,代码就会去自动配置GPIO的功能,修改GPIO的功能也只需要在BoardSetUserPinMux接口中去修改。

编译配置中的选项对应代码中的引脚配置

注意:

  • 1.在使用编译配置的选项配置引脚的时候,需要注意,一个功能可能会有多个引脚可以实现,比如I2S和I2C1等都有两组引脚可以实现功能,所以配置的时候需要注意板级使用的引脚。
  • 2.关于I2S和SPI中功能的说明,后缀带_S,代表slave模式,如I2S_IN_BCLK_IN_S,后缀是_M则表示master,如I2S_OUT_MCLK_OUT_M。

2.4 应用软件(app)开发*

2.4.1 app 核心代码目录结构*

lvp_tws/lvp/app_core/
▾ lvp/
  ▾ app_core/
      lvp_app.h
      lvp_app_core.c       # app框架代码,负责调用应用暴露的接口
      lvp_app_core.h
      Makefile

2.4.2 用户 app 开发目录结构 *

lvp_tws/app/
▾ app/
  ▸ neck_fan/               # 挂脖风扇应用
  ▾ sample/                 # 示例,新应用开发只需在此做修改或参考它新建一个
      app.mk                # makefile 文件
      app.name              # 用于在Kconfig系统中暴露该应用使能开关
      Kconfig               # 用于添加需要在编译配置中进行的配置
      lvp_app_sample.c      # 应用代码,如需拆分多个c文件,请置于同级目录
  ▸ voice_controller/       # 语音控制应用
    Kconfig                 # 用于app框架
    Makefile                # 用于app框架

2.4.3 用户 app 开发示例 *

lvp_tws/app/sample/lvp_app_sample.c
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
/* Lower-Power Voice Process
* Copyright (C) 2001-2020 NationalChip Co., Ltd
* ALL RIGHTS RESERVED!
*
* lvp_app_sample.c
*
*/

#include <lvp_app.h>
#include <lvp_buffer.h>

#ifdef CONFIG_LVP_FACTORY_MIC_TEST
#include "lvp_mic_test.h"
#endif

#define LOG_TAG "[SAMPLE_APP]"

//=================================================================================================

// 待机接口,用于实现应用的待机准备工作,在进入低功耗前被调用
static int SampleAppSuspend(void *priv)
{
    printf(LOG_TAG" ---- %s ----\n", __func__);
    printf("%s\n", (unsigned char *)priv);
    return 0;
}

// 唤醒接口,用于实现应用在唤醒后的准备工作,系统唤醒后被调用
static int SampleAppResume(void *priv)
{
    printf(LOG_TAG" ---- %s ----\n", __func__);
    printf("%s\n", (unsigned char *)priv);
    return 0;
}

static int SampleAppInit(void)
{
    printf(LOG_TAG" ---- %s ----\n", __func__);

    return 0;
}

// App Event Process;在触发事件的时候被调用,比如 识别关键词,或其他自定义事件
// 用于响应事件
// 调用app框架中的接口:LvpTriggerPlcEvent,即可触发事件
static int SampleAppEventResponse(APP_EVENT *app_event)
{
#ifdef CONFIG_LVP_FACTORY_MIC_TEST
    // 只是个使用mic测试接口的参考例子,没有考虑会休眠的问题和测试与工作模式切换的问题,需要在实际方案里考虑
    // 使用方式分两步:
    // 1 获取到contex,然后调用LvpMicTestProcess,参数为获取到的contex
    // 2 测试一定的contex后,停止测试,调用LvpMicTestGetResult获取测试结果,结果以字符串显示
    static int count = 0;
    LVP_CONTEXT *context;
    unsigned int ctx_size;
    LvpGetContext(app_event->ctx_index, &context, &ctx_size);
    if(count < 100)
    {
        LvpMicTestProcess(context);
        count++;
    }

    if(count == 100) // 自己定义个测试的时间
    {
        char *str = LvpMicTestGetResult();
        printf("%s\n", str);
        count = 1000;
    }
#else
    if (app_event->event_id < 100)
        return 0;

    LVP_CONTEXT *context;
    unsigned int ctx_size;
    LvpGetContext(app_event->ctx_index, &context, &ctx_size);

    printf(LOG_TAG"event_id %d\n", app_event->event_id);
    printf(LOG_TAG"kws %d\n", context->kws);
    printf(LOG_TAG"vad %d\n", context->fft_vad);
    printf(LOG_TAG"G-vad %d\n", context->G_vad);
#endif
    return 0;
}

// APP Main Loop;在主循环中被调用,用于实现需要循环执行的代码。返回应用状态
static int SampleAppTaskLoop(void)
{
    return 0;
}

// 将上述接口填入app框架,由app core进行调用
LVP_APP sample_app = {
    .app_name = "sample app",
    .AppInit = SampleAppInit,
    .AppEventResponse = SampleAppEventResponse,
    .AppTaskLoop = SampleAppTaskLoop,
    .AppSuspend = SampleAppSuspend,
    .suspend_priv = "SampleAppSuspend",
    .AppResume = SampleAppResume,
    .resume_priv = "SampleAppResume",
};

LVP_REGISTER_APP(sample_app);