# spv40-bare **Repository Path**: stevending/spv40-bare ## Basic Information - **Project Name**: spv40-bare - **Description**: No description available - **Primary Language**: C - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2024-08-06 - **Last Updated**: 2024-08-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # sp2cw3 bare SDK快速开发指南 本文介绍珠海普林芯驰sp2cw3平台芯片bare的SDK使用,包括编译环境搭建、下载、驱动及应用开发等,使用SDK进行开发之前,请阅读本文。 ## 1、SOC简介 sp2cw3系列是珠海普林芯驰(www.spacetouch.co)推出的通用音频处理SOC,采用CPU+NPU+uDSP三核异构框架。CPU使用RISC-V架构,支持单精度浮点运算,uDSP能够完成音频信号的特征提取及计算,通过NPU实现神经网络训练和学习,能够实现AI降噪、AEC、语音识别等音频算法应用。 同时内置高性能音频专用 codec,丰富的外设资源,能够应用于各种音频应用领域。 ![spv40](.\figures/spv40.png) ## 2、bare SDK框架说明 bare SDK提供了AI降噪、传统(MMSE)降噪、回声消除、啸叫抑制等工程模版,工程模版已经集成最新的算法库。开发人员只需要根据自己的需求,完成业务逻辑部分即可。 ### 2.1、文件夹目录结构 SDK根目录主控包括如下文件夹及内容: | **文件夹** | **描述** | | ---------- | ------------------------------------------------------------ | | oss | 链接脚本、启动文件、寄存器封装等公共文件用,用户一般不需要改动,如需要改动,请联系原厂技术支持 | | project | 该文件下放置多个demo工程,用以不同的需求开发 | ![spv40](.\figures/oss.png) project是SDK源码目录,开发需要进入该目录进行: | **文件夹** | **描述** | | ------------------------------------- | ------------------------------- | | sp2cw3_demo_for_a2a_ai_aec_v1.0 | 回声消除(AEC)A2A标准参考工程 | | sp2cw3_demo_for_a2a_ai_denoise_v1.0 | AI降噪A2A标准参考工程 | | sp2cw3_demo_for_a2a_mmse_denoise_v1.0 | 传统(MMSE)降噪A2A标准参考工程 | | sp2cw3_demo_for_iis_ai_denoise_v1.0 | AI降噪IIS标准参考工程 | | sp2cw3_demo_for_iis_ai_ahs_v1.0 | AI啸叫抑制A2A标准参考工程 | 工程文件夹目录结构: | **文件夹** | **描述** | | ---------- | ------------------------------------------------------------ | | algo | 算法静态库和算法接口调用逻辑代码 | | board | 硬件平台、系统状态机 | | codec | audio adc(录音),audio dac(播放) ,src(采样率转换)、 IIS等等相关配置代码 | | image | 算法模型(.mod)、参数配置文件(.a)、提示音文件(.dat) | | src | 用户业务逻辑代码相关 | | system | 系统运行、算法执行调用文件,udsp、npu、DMA等 | | debug | 编译过程中间文件 | | output | 编译后输出的烧录文件(.bin) | ![1722505678538](.\figures\sdk.png) ### 2.2、系统状态机说明 | 函数名 | 位置 | 描述 | | ----------------------------------------------- | ------ | ------------------------------------------------------------ | | **void** **user_init**(**void**) | main.c | 用于上电后的外设初始化 | | **void** **user_2ms_handler**(**void**) | mainc | 2ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码 | | **void** **user_10ms_handler**(**void**) | main.c | 10ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码 | | **void** **user_100ms_handler**(**void**) | main.c | 100ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码 | | __IRAM **void** **tick_user_handler**(**void**) | main.c | 500us执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码 | 注释:在增加业务逻辑代码时候,应该评估其执行周期,尽可能不要频繁执行,为CPU跑语音算法预留足够的CPU算力。 ### 2.3核心代码解析 #### 2.3.1 audio adc ```c void audio_adc_init(void) { static uint8_t init_flag = 0; audio_adc_ch_cfg_typedef cfg; cfg.sample_rate = SAMPLE_RATE_16K; /*配置采样率*/ cfg.digit_volume = VOLUME_01X; /*数字音量*/ cfg.hw_pin = AMIC_CH0; /*模拟通道*/ cfg.amic.mode = different; /*差分模式*/ cfg.amic.mic_gain = denoise_param.mic_gain; /*从配置文件获取MIC增益*/ cfg.amic.mix_gain = MADC_MIX_GAIN_N0D0B; /*设置MIX增益*/ cfg.amic.vmic = VOLT_2o59; /*设置VMIC电压*/ hal_audio_adc_ch_cfg(&audio_adc, cfg); cfg.hw_pin = AMIC_CH1; hal_audio_adc_ch_cfg(&audio_adc, cfg); audio_adc.init.data_width = WIDTH_16BIT; /*数据宽度*/ #ifndef ADC_USE_SRC1 audio_adc.init.src_sel = AHB; #else audio_adc.init.src_sel = SRC; debug("in audio_adc_init, src take effect \n\r"); #endif audio_adc.init.channel_id = CHN0 | CHN1; audio_adc.dma_handle = &audio_dma;/*注册DMA句柄*/ hal_audio_init(&audio_adc); #ifndef ADC_USE_SRC1 if (init_flag == 0) { init_flag = 1; adc_dma_init(); } #endif //adc_mic0_AGC_init(); audio_adc_cmd(&audio_adc, ENABLE); } ``` 主要通过2个结构体传入参数,结构体的赋值通过结构体枚举变量成员传入: ```c typedef struct _audio_adc_ch_cfg_typedef { sample_rate_type_def sample_rate; /*采样率*/ volume_type_def digit_volume; /*数字音量*/ microphone_type_def hw_pin; /*pin脚选择*/ audio_adc_dmic_typedef dmic; /*DMIC配置*/ audio_adc_amic_typedef amic; /*AMIC配置*/ } audio_adc_ch_cfg_typedef; typedef struct _audio_adc_amic_typedef { amic_mode_type_def mode; /*输入方式*/ amic_mic_gain_type_def mic_gain; /*mic增益*/ amic_mix_gain_type_def mix_gain; /*mix增益*/ vmic_vol_type_def vmic; /*VMIC电压*/ } audio_adc_amic_typedef; ``` #### 2.3.1 audio dac ```c void audio_dac_init(void) { static uint8_t init_flag = 0; volatile __ALIGNED(4) static uint16_t dac_buff[2][DAC_FRAM_SIZE * 2]; audio_dac_buff[0].addr = (uint8_t *)dac_buff[0]; audio_dac_buff[0].status = BUFF_FREE; audio_dac_buff[1].addr = (uint8_t *)dac_buff[1]; audio_dac_buff[1].status = BUFF_FREE; audio_dac.init.track = DAC_STEREO; audio_dac.init.sample_rate = DAC_SAMPLE_RATE_16K; audio_dac.init.outfrom = HEADPHONE; #ifndef DAC_USE_SRC0 audio_dac.init.src_sel = AHB; audio_dac.init.data_format = width_16bit_useful_16bit; #else audio_dac.init.src_sel = SRC; audio_dac.init.data_format = width_32bit_useful_low_16bit; #endif audio_dac.init.mix_source = MIX_SOURCE_DAC_DETACHED; audio_dac.init.vol = denoise_param.hp_volume; audio_dac.init.eq_type = EQ_NORMAL_TYPE; audio_dac.init.inverse = denoise_param.dac_output_inverse; audio_dac.dma_handle = &dac_dma; hal_audio_dac_init(&audio_dac); audio_dac_EQ_init(); audio_dac_drc_init(); if (init_flag == 0) { init_flag = 1; dac_dma_cfg(); } audio_dac_cmd(&audio_dac, ENABLE); } ``` 主要通过结构体传入参数,结构体的赋值通过结构体枚举变量成员传入: ```c typedef struct dac_init_typedef { adc_track_typedef track; /*声道数*/ dac_sample_rate_typedef sample_rate; /*采样率*/ dac_outfrom_typedef outfrom; /*输出类型*/ src_sel_type_def src_sel; /*AHB或SRC*/ dac_data_format_typedef data_format; /*数据对齐格式*/ dac_mix_sorce_def mix_source; /*mix设置*/ uint32_t vol; /*音量设置*/ eq_param_class_type eq_type; /*EQ类型*/ dac_param_inverse_type inverse; /*输出为差分或正常*/ } dac_init_typedef; ``` ### 2.3音频算法接口说明 ![1722512619376](.\figures\algo.png) ```c /** * 音频算法初始化 * @param: none * @return: none. */ void denoise_init(void) { plxc_set_model_addr((uint32_t)(FW_HEADER->BIN[1].ADDR + 0x4000000));// set ai denoise mod addr plxc_algo_init();//init ai denoise algo plxc_set_denoise_level(250); // set ai denoise level plxc_set_audio_callback(getaudio);// set callback function } /** * 向算法输入原始音频数据 * * @param: audio:原始音频数据的缓存地址 * num_samples:音频数据的点数 (uint32_t)audio_buffer[0]:主通道 (uint32_t)audio_buffer[1]:参考通道 * @return: none. */ void push_primitive_audio_data(int16_t *audio, uint32_t num_samples) { ... if ((gAudioPrimitive != (void *)0) && (gAudioPrimitive->audioListener != (void *)0)) { gAudioPrimitive->audioListener(0, (uint32_t)audio_buffer[0], (uint32_t)audio_buffer[1]); } ... } /** * 音频算法计算完成后回调函数,从该函数获取数据 * @param: audio0:音频数据0buff地址 * audio1:音频数据1buff地址 * num_samples:音频数据的点数 * @return: none. */ void get_audio(int16_t *audio0, int16_t *audio1, uint32_t num_samples) { uint16_t temp_buff[AUDIO_CALLBACK_LEN * 2]; ... for (uint16_t i = 0; i < num_samples; i++)/*输出1路降噪前,1路降噪后的数据*/ { temp_buff[2 * i + 0] = audio1[i]; temp_buff[2 * i + 1] = audio0[i]; } ... src1_write_data((uint8_t *)temp_buff, AUDIO_CALLBACK_LEN);//将降噪后的数据,通过SRC后由DAC播出 } ```