CSR8675项目实战:BlueEarphone 左右声道各10个Speaker EQ

Posted NiceBT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSR8675项目实战:BlueEarphone 左右声道各10个Speaker EQ相关的知识,希望对你有一定的参考价值。

为了让CSR867x的开发更容易,现与思度科技联合推出CSR867x学习板【淘宝链接:思度科技CSR开发板】

技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠PPT、项目源码、视频教程)
——————————正文分割线———————————–

1. 引言

熟悉CSR867X芯片方案的开发人员一定对UFE工具印象深刻。UFE(universal front end)工具是ADK自带的声学调音工具,能够满足音乐播放、免提通话等场景的声学调音的一般需求。其音乐播放调音界面如下:

从上图可以看出,音频数据从右侧天线输入,其音频编码格式为APTX_HD,采样率44.1k,采样精度24位。在经过Decoder解码器解码后转成双声道的PCM格式音频数据,经过Compander(压扩器)、User EQ(用户均衡器)、Bass Enhancement(低音增强)、Speaker EQ(扬声器均衡器)、Stereo Enhancement(立体声增强)、Crossover(分频器)、Main Volume(主音量控制)等模块的处理后通过模拟通道输出。其中除了Main Volume和Crossover模块以外,所有模块对音频信号的处理都是不区分左右通道的,例如Speaker EQ模块:

可以看到Speaker EQ模块有10个点的滤波器,每个滤波器的参数有Gain(增益)、Freq(频率)、Q值(滤波器品质因数)和带宽(一个计算值,不能设置)。这10个滤波器的参数会同时作用在左右声道。

本文给出了一种支持左右声道各10个Speaker EQ的方案,并给出一个基于matlab GUI的调音工具设计。

2. 主要功能

  • 左右通道各10个Speaker EQ(支持任何蓝牙音频解码格式、任何音源)
  • 可在VM层读写每个Speaker EQ的每个参数(滤波器类型、增益、频率、品质因数)
  • 可通过matlab GUI实时调节每个Speaker EQ的每个参数

修改后的系统框图如下:

3. 项目难点

3.1. 改造音频链路

基本思路是,在现有音频链路的基础上,将整个Speaker EQ模块改为左通道独享,然后拷贝一个完整的Speaker EQ模块单独给右通道使用。首先创建修改原有的speaker eq的数组定义,使其只处理左通道数据:

// music_example_config.asm

    .VAR SpkrEqDefnTable[$user_eq.DEFINITION_TABLE_SIZE] =
        MAX_NUM_SPKR_EQ_BANKS,
        MAX_NUM_SPKR_EQ_STAGES,
        &spkr_eq_left_dm2,
        &spkr_eq_left_dm2,
        &SpkrEqCoefsA,
        &SpkrEqCoefsB,
        &$audio_proc.hq_peq.initialize,
        &$audio_proc.hq_peq.zero_delay_data,
        &$audio_proc.hq_peq.process;

接着创建一个新的R speaker eq数组定义,其只处理右通道的数据:

// music_example_config.asm

    .VAR SpkrEqDefnTable2[$user_eq.DEFINITION_TABLE_SIZE] =
        MAX_NUM_SPKR_EQ_BANKS,
        MAX_NUM_SPKR_EQ_STAGES,
        &spkr_eq_right_dm2,
        &spkr_eq_right_dm2,
        &SpkrEqCoefsC,
        &SpkrEqCoefsD,
        &$audio_proc.hq_peq.initialize,
        &$audio_proc.hq_peq.zero_delay_data,
        &$audio_proc.hq_peq.process;

然后为R speaker eq创建滤波器系数存储空间,并将此系数关联到右通道的音频数据流:

// music_example_config.asm

   .VAR/DM2 spkr_eq_right_dm2[MAX_SPKR_EQ_OBJECT_SIZE] =
        &stream_map_right_in,                   // PTR_INPUT_DATA_BUFF_FIELD
        &stream_map_right_in,                   // PTR_OUTPUT_DATA_BUFF_FIELD
        MAX_NUM_SPKR_EQ_STAGES,                 // MAX_STAGES_FIELD
        &SpkrEqCoefsC,                          // PARAM_PTR_FIELD
        0 ...;

.VAR SpkrEqCoefsC[3+6*MAX_NUM_SPKR_EQ_STAGES] =
        $spkrEq.Fs44.NumBands,
        $spkrEq.Fs44.GainExp,
        $spkrEq.Fs44.GainMant,
        $spkrEq.Fs44.Stage1.b2,  $spkrEq.Fs44.Stage1.b1,  $spkrEq.Fs44.Stage1.b0,  $spkrEq.Fs44.Stage1.a2,  $spkrEq.Fs44.Stage1.a1,
        $spkrEq.Fs44.Stage2.b2,  $spkrEq.Fs44.Stage2.b1,  $spkrEq.Fs44.Stage2.b0,  $spkrEq.Fs44.Stage2.a2,  $spkrEq.Fs44.Stage2.a1,
        $spkrEq.Fs44.Stage3.b2,  $spkrEq.Fs44.Stage3.b1,  $spkrEq.Fs44.Stage3.b0,  $spkrEq.Fs44.Stage3.a2,  $spkrEq.Fs44.Stage3.a1,
        $spkrEq.Fs44.Stage4.b2,  $spkrEq.Fs44.Stage4.b1,  $spkrEq.Fs44.Stage4.b0,  $spkrEq.Fs44.Stage4.a2,  $spkrEq.Fs44.Stage4.a1,
        $spkrEq.Fs44.Stage5.b2,  $spkrEq.Fs44.Stage5.b1,  $spkrEq.Fs44.Stage5.b0,  $spkrEq.Fs44.Stage5.a2,  $spkrEq.Fs44.Stage5.a1,
        $spkrEq.Fs44.Stage6.b2,  $spkrEq.Fs44.Stage6.b1,  $spkrEq.Fs44.Stage6.b0,  $spkrEq.Fs44.Stage6.a2,  $spkrEq.Fs44.Stage6.a1,
        $spkrEq.Fs44.Stage7.b2,  $spkrEq.Fs44.Stage7.b1,  $spkrEq.Fs44.Stage7.b0,  $spkrEq.Fs44.Stage7.a2,  $spkrEq.Fs44.Stage7.a1,
        $spkrEq.Fs44.Stage8.b2,  $spkrEq.Fs44.Stage8.b1,  $spkrEq.Fs44.Stage8.b0,  $spkrEq.Fs44.Stage8.a2,  $spkrEq.Fs44.Stage8.a1,
        $spkrEq.Fs44.Stage9.b2,  $spkrEq.Fs44.Stage9.b1,  $spkrEq.Fs44.Stage9.b0,  $spkrEq.Fs44.Stage9.a2,  $spkrEq.Fs44.Stage9.a1,
        $spkrEq.Fs44.Stage10.b2, $spkrEq.Fs44.Stage10.b1, $spkrEq.Fs44.Stage10.b0, $spkrEq.Fs44.Stage10.a2, $spkrEq.Fs44.Stage10.a1,
      $spkrEq.Fs44.Stage1.scale, $spkrEq.Fs44.Stage2.scale, $spkrEq.Fs44.Stage3.scale, $spkrEq.Fs44.Stage4.scale, $spkrEq.Fs44.Stage5.scale,
      $spkrEq.Fs44.Stage6.scale, $spkrEq.Fs44.Stage7.scale, $spkrEq.Fs44.Stage8.scale, $spkrEq.Fs44.Stage9.scale, $spkrEq.Fs44.Stage10.scale;

   // 48 kHz coefficients if not using coefficient calculation routines
    .VAR SpkrEqCoefsD[3+6*MAX_NUM_SPKR_EQ_STAGES] =
        $spkrEq.Fs48.NumBands,
        $spkrEq.Fs48.GainExp,
        $spkrEq.Fs48.GainMant,
        $spkrEq.Fs48.Stage1.b2,  $spkrEq.Fs48.Stage1.b1,  $spkrEq.Fs48.Stage1.b0,  $spkrEq.Fs48.Stage1.a2,  $spkrEq.Fs48.Stage1.a1,
        $spkrEq.Fs48.Stage2.b2,  $spkrEq.Fs48.Stage2.b1,  $spkrEq.Fs48.Stage2.b0,  $spkrEq.Fs48.Stage2.a2,  $spkrEq.Fs48.Stage2.a1,
        $spkrEq.Fs48.Stage3.b2,  $spkrEq.Fs48.Stage3.b1,  $spkrEq.Fs48.Stage3.b0,  $spkrEq.Fs48.Stage3.a2,  $spkrEq.Fs48.Stage3.a1,
        $spkrEq.Fs48.Stage4.b2,  $spkrEq.Fs48.Stage4.b1,  $spkrEq.Fs48.Stage4.b0,  $spkrEq.Fs48.Stage4.a2,  $spkrEq.Fs48.Stage4.a1,
        $spkrEq.Fs48.Stage5.b2,  $spkrEq.Fs48.Stage5.b1,  $spkrEq.Fs48.Stage5.b0,  $spkrEq.Fs48.Stage5.a2,  $spkrEq.Fs48.Stage5.a1,
        $spkrEq.Fs48.Stage6.b2,  $spkrEq.Fs48.Stage6.b1,  $spkrEq.Fs48.Stage6.b0,  $spkrEq.Fs48.Stage6.a2,  $spkrEq.Fs48.Stage6.a1,
        $spkrEq.Fs48.Stage7.b2,  $spkrEq.Fs48.Stage7.b1,  $spkrEq.Fs48.Stage7.b0,  $spkrEq.Fs48.Stage7.a2,  $spkrEq.Fs48.Stage7.a1,
        $spkrEq.Fs48.Stage8.b2,  $spkrEq.Fs48.Stage8.b1,  $spkrEq.Fs48.Stage8.b0,  $spkrEq.Fs48.Stage8.a2,  $spkrEq.Fs48.Stage8.a1,
        $spkrEq.Fs48.Stage9.b2,  $spkrEq.Fs48.Stage9.b1,  $spkrEq.Fs48.Stage9.b0,  $spkrEq.Fs48.Stage9.a2,  $spkrEq.Fs48.Stage9.a1,
        $spkrEq.Fs48.Stage10.b2, $spkrEq.Fs48.Stage10.b1, $spkrEq.Fs48.Stage10.b0, $spkrEq.Fs48.Stage10.a2, $spkrEq.Fs48.Stage10.a1,
      $spkrEq.Fs48.Stage1.scale, $spkrEq.Fs48.Stage2.scale, $spkrEq.Fs48.Stage3.scale, $spkrEq.Fs48.Stage4.scale, $spkrEq.Fs48.Stage5.scale,
      $spkrEq.Fs48.Stage6.scale, $spkrEq.Fs48.Stage7.scale, $spkrEq.Fs48.Stage8.scale, $spkrEq.Fs48.Stage9.scale, $spkrEq.Fs48.Stage10.scale;

接着为R speaker EQ创建参数存储空间,用于存储VM发送过来的EQ参数(Type/Gain/Freq/Q),也是R speaker EQ的初始参数:

// music_example_config.asm

   .VAR  SpkrRParams[2*ROUND(0.5*$M.MUSIC_MANAGER.PARAMETERS.SPKR_R_STRUCT_SIZE)] = 
     1, // config
     1,  // bank
     10, // band
     0,  // master gain
     13, 96, 0, 5792, 		// SPRK R EQ : PEQ, 32Hz, 0dB, 1.414
     13, 192, 0, 5792,		// SPRK R EQ : PEQ, 64Hz, 0dB, 1.414
     13, 375, 0, 5792,		// SPRK R EQ : PEQ, 125Hz, 0dB, 1.414
     13, 750, 0, 5792,		// SPRK R EQ : PEQ, 250Hz, 0dB, 1.414
     13, 1500, 0, 5792,		// SPRK R EQ : PEQ, 500Hz, 0dB, 1.414
     13, 3000, 0, 5792,		// SPRK R EQ : PEQ, 1000Hz, 0dB, 1.414
     13, 6000, 0, 5792,		// SPRK R EQ : PEQ, 2000Hz, 0dB, 1.414
     13, 12000, 0, 5792,	// SPRK R EQ : PEQ, 4000Hz, 0dB, 1.414
     13, 24000, 0, 5792,	// SPRK R EQ : PEQ, 8000Hz, 0dB, 1.414
     13, 48000, 0, 5792;	// SPRK R EQ : PEQ, 16000Hz, 0dB, 1.414

再然后需要创建L / R speaker EQ的音频处理模块并插入到整个音频处理流程中:

// music_example_config.asm

#if uses_SPKR_EQ
    $user_eq.eqInitialize,              &SpkrEqDefnTable,     &SpkrEqParams,
    $user_eq.eqInitialize,              &SpkrEqDefnTable2,     &SpkrEqParams2,
#endif

#if uses_SPKR_EQ
    $music_example.peq.zerodelay,      &SpkrEqDefnTable,        0,
    $music_example.peq.zerodelay,      &SpkrEqDefnTable2,        0,
#endif

#if uses_SPKR_EQ
    $music_example.peq.process_left,                 &SpkrEqDefnTable,           $M.MUSIC_MANAGER.CONFIG.SPKR_EQ_BYPASS,
    $music_example.peq.process_right,                &SpkrEqDefnTable2,           $M.MUSIC_MANAGER.CONFIG.SPKR_EQ_BYPASS,
#endif  // uses_SPKR_EQ

//------------------------------------------------------------------------------
process_left:
//------------------------------------------------------------------------------
// peq processing wrapper
// - return without processing if bypassed
// - if running user_eq (BYPASS_BIT_MASK_FIELD == USER_EQ_BYPASS)
//     then check whether user eq bank is 0
//------------------------------------------------------------------------------
// on entry r7 = pointer to filter definition table
//          r8 = bypass mask
//------------------------------------------------------------------------------

    r0 = M[&$M.system_config.data.CurParams + $M.MUSIC_MANAGER.PARAMETERS.OFFSET_CONFIG];

    // check if EQ is bypassed
    null = r0 and r8;
    if NZ rts;

    r3 = m[r7 + $user_eq.EQ_PROC_FUNC];

    $push_rLink_macro;

    pushm <r3,r7>;
    r7 = m[r7 + $user_eq.DEFINITION_TABLE_LEFT_DM_PTR];         // r7 points to left channel EQ memory structure
    // if Parameters is Null then no Peq
    Null = M[r7 + $audio_proc.peq.PARAM_PTR_FIELD];
    if Z jump $pop_rLink_and_rts;
        call r3;
    
    popm <r3,r7>;
    jump $pop_rLink_and_rts;

//------------------------------------------------------------------------------
process_right:
//------------------------------------------------------------------------------
// peq processing wrapper
// - return without processing if bypassed
// - if running user_eq (BYPASS_BIT_MASK_FIELD == USER_EQ_BYPASS)
//     then check whether user eq bank is 0
//------------------------------------------------------------------------------
// on entry r7 = pointer to filter definition table
//          r8 = bypass mask
//------------------------------------------------------------------------------

    r0 = M[&$M.system_config.data.CurParams + $M.MUSIC_MANAGER.PARAMETERS.OFFSET_CONFIG];

    // check if EQ is bypassed
    null = r0 and r8;
    if NZ rts;

    r3 = m[r7 + $user_eq.EQ_PROC_FUNC];

    $push_rLink_macro;

    pushm <r3,r7>;
    r7 = m[r7 + $user_eq.DEFINITION_TABLE_RIGHT_DM_PTR];        // r7 points to right channel EQ memory structure
    if Z jump $pop_rLink_and_rts;                               // if Z there isn't a right channel
        // if Parameters is Null then no Peq
        Null = M[r7 + $audio_proc.peq.PARAM_PTR_FIELD];
        if Z jump $pop_rLink_and_rts;                          // if Z there isn't a right channel
            call r3;
    
    popm <r3,r7>;
    jump $pop_rLink_and_rts;

// music_example_system.asm

#if uses_SPKR_EQ
    $M.system_config.data.SpkrEqDefnTable,
    $M.system_config.data.SpkrEqDefnTable2,
#else
    0,
#endif    

至此音频链路的改造工作已经完成了。

3.2. 打通VM和DSP之间的消息链路

首先我们来看一下设置EQ参数的消息链路:

VM Audio Plugin DSP MessageSendConditionallyOnTask KalimbaSendMessage(msg_id, param_id, param_value,...) 根据参数计算滤波器系数并立即生效 VM Audio Plugin DSP

VM层对应的代码:

void set_sprk_l_eq_param(uint16 band, eq_param_type_t param_type, uint32 value)

    if(sinkAudioGetRoutedAudioTask())
    
        audio_plugin_spkr_eq_param_t param;
        param.id.bank = 1;
        param.id.band = band;
        param.id.param_type = param_type;
        param.value = value;

        AudiosetSpkrLEqParameter(sinkAudioGetRoutedAudioTask(), &param);
    

Audio Plugin对应的代码:

case AUDIO_PLUGIN_SET_SPKR_L_EQ_PARAMETER_MSG:

    AUDIO_PLUGIN_SET_SPKR_L_EQ_PARAMETER_MSG_T* eq_message = (AUDIO_PLUGIN_SET_SPKR_L_EQ_PARAMETER_MSG_T*)message ;
    CsrA2dpDecoderSetSpkrLEqParameter(&eq_message->param);

break;

void CsrA2dpDecoderSetSpkrLEqParameter(const audio_plugin_spkr_eq_param_t* param)

    uint16 param_id;
    uint16 param_value;
    bool recalcalculate_coefficients = TRUE;

    param_id = csrA2dpDecoderMakeParamId(param->id.bank, param->id.band, param->id.param_type);
    param_value = (uint16)param->value;
    
    KALIMBA_SEND_MESSAGE(KALIMBA_SET_SPKR_L_EQ_PARAM, param_id, param_value, (uint16)recalcalculate_coefficients, 0);

DSP工程中对应的代码:

//消息结构体
   .VAR set_spkr_l_eq_param_message_struct[$message.STRUC_SIZE];

//消息处理函数

//------------------------------------------------------------------------------
.module $M.music_example_message.SetSpkrLEqParamMsg;
//------------------------------------------------------------------------------
// receives user eq parameter update message.
//   If "update" field is non-zero, the coefficient calculation routine is run
//------------------------------------------------------------------------------
// Parameter is sent as a short message
//   <msgID> <Param ID> <value> <update> <>
//------------------------------------------------------------------------------
// On entry
//   r0 = message ID (ignored)
//   r1 = parameter ID
//   r2 = value
//   r3 = update
//   r4 = unused
//------------------------------------------------------------------------------

    .datasegment dm ;
    .codesegment VM_MESSAGE_PM ;

func:

    $push_rLink_macro ;

    r0 = r1;                // r0 = paramID
    r7 = &$M.system_config.data.SpkrEqDefnTable;
    call $user_eq.calcParamAddrOffset;          // on exit, r0 = ParamAddrOffset
    r0 = r0 + ($M.system_config.data.CurParams + $M.MUSIC_MANAGER.PARAMETERS.OFFSET_CONFIG);

    r2 = r2 and 0x00ffff;
    m[r0] = r2;

    // if update flag is zero then exit now and don't recalculate coefficients
    null = r3 - 0;
    if eq jump $pop_rLink_and_rts ;

    r0 = r1;
    r1 = &$M.system_config.data.SpkrEqCoefsA;
    r2 = &$M.system_config.data.SpkrEqCoefsB;
    r3 = ($M.system_config.data.CurParams + $M.MUSIC_MANAGER.PARAMETERS.OFFSET_CONFIG);
    call $user_eq.calcBandCoefs;

    jump $pop_rLink_and_rts ;

.endmodule ;
   
//注册消息
   &$M.music_example_message.set_spkr_l_eq_param_message_struct,         $music_example.GAIAMSG.SET_SPKR_L_PARAM,          

接着是读取EQ参数的消息链路:

VM Audio Plugin DSP MessageSendConditionallyOnTask KalimbaSendMessage 读取指定滤波器的系数,准备消息内容 message.send_short MessageSend VM Audio Plugin DSP

VM层的代码:

void get_spkr_l_eq_param(uint16 band, eq_param_type_t param_type)

    audio_plugin_spkr_eq_param_id_t param;
    
    if (sinkAudioGetRoutedAudioTask())
    
        param.bank = 1;
        param.band = band;
        param.param_type = param_type;
        AudioGetSpkrLEqParameter(sinkAudioGetRoutedAudioTask(), &param, &theSink.task);
    


case AUDIO_GET_USER_EQ_PARAMETER_CFM:

    AUDIO_GET_USER_EQ_PARAMETER_CFM_T* get_user_eq_resp_msg = (AUDIO_GET_USER_EQ_PARAMETER_CFM_T *)message;
    GaiaHandleGetUserEqParamResponse(get_user_eq_resp_msg);

break;

Audio Plugin的代码:

void CsrA2dpDecoderGetSpkrLEqParameter(audio_plugin_spkr_eq_param_id_t* param_id)

    uint16 param = csrA2dpDecoderMakeParamId(param_id->bank, param_id->band, param_id->param_type);
    KalimbaSendMessage(KALIMBA_GET_SPKR_L_EQ_PARAM, param, 0, 0, 0);


case KALIMBA_GET_USER_EQ_PARAM_RESP:

    MAKE_AUDIO_MESSAGE(AUDIO_GET_USER_EQ_PARAMETER_CFM, user_eq_message);
    PRINT(("DECODER: User EQ Param from DSP: [%x][%x]\\n", m->a, m->b));
    user_eq_message->data_valid = TRUE;

    user_eq_message->param[0].id.bank = (uint16)((m->a >> 8) & 0x000f);
    user_eq_message->param[0].id.band = (uint16)((m->a >> 4) & 0x000f);
    user_eq_message->param[0].id.param_type = (uint16)(m->a & 0x000f);
    user_eq_message->param[0].value = m->b;

    MessageSend(decoder->app_task, AUDIO_GET_USER_EQ_PARAMETER_CFM, user_eq_message);

break;

DSP工程的代码:

// 创建消息结构体
   .VAR get_spkr_l_eq_param_message_struct[$message.STRUC_SIZE];

// 消息处理函数
//------------------------------------------------------------------------------
.module $M.music_example_message.GetSpkrLEqParamMsg;
//------------------------------------------------------------------------------
// request user eq parameter message.
//   return message contains requested parameter
//------------------------------------------------------------------------------
// Parameter is sent as a short message
//   <msgID> <Param ID> <> <> <>
// Reply is sent as a short message
//   <msgID> <Param ID> <value> <> <>
//------------------------------------------------------------------------------
// On entry
//   r0 = message ID (ignored)
//   r1 = parameter ID
//   r2 = unused
//   r3 = unused
//   r4 = unused
//------------------------------------------------------------------------------

    .datasegment dm ;
    .codesegment VM_MESSAGE_PM ;

func:

    $push_rLink_macro ;

    r3 = r1;          // word 1 of return message (parameter ID)

    r0 = r1;                // r0 = paramID
    r7 = &$M.system_config.data.SpkrEqDefnTable;
    call $user_eq.calcParamAddrOffset;          // on exit, r0 = ParamAddrOffset
    r0 = r0 + ($M.system_config.data.CurParams + $M.MUSIC_MANAGER.PARAMETERS.OFFSET_CONFIG);

    r4 = m[r0];       // word 2 (value)
    r5 = 0;           // word 3
    r6 = 0;           // word 4
    r2 = $music_example.GAIAMSG.GET_SPKR_L_PARAM_RESP;
    call $message.send_short;

    jump $pop_rLink_and_rts ;

.endmodule ;

// 注册消息
   &$M.music_example_message.get_spkr_l_eq_param_message_struct,         $music_example.GAIAMSG.GET_SPKR_L_PARAM,          $M.music_example_message.GetSpkrLEqParamMsg.func,        $message.register_handler,

至此消息链路已准备就绪,调用VM层的Set或Get函数即可读写DSP的EQ参数设置,并能在播放过程中立即生效。

3.3. 开发matlab GUI调音工具

由于UFE工具无法显示我们新增加的R Speaker EQ,因此需要定制一个调音工具,界面如下:

调音界面主要分四部分:

  • 连接区域:有连接DSP,刷新参数,设置参数三个按键
  • log显示区域:用来记录并显示一些操作信息
  • 左声道EQ:用来显示和修改当前L Speaker EQ(之前的Speaker EQ)的所有参数
  • 右声道EQ:用来显示和修改当前R Speaker EQ的所有参数

工具的后台调用了ADK提供的matlab接口库,代码路径是ADK\\tools\\matlab\\win64,有兴趣的读者可以自学一下。

4. 总结

在熟练掌握CSR867X的硬软件架构后,可以快速开发出通用ADK所没有的特色功能,有助于提升产品的竞争力。

以上是关于CSR8675项目实战:BlueEarphone 左右声道各10个Speaker EQ的主要内容,如果未能解决你的问题,请参考以下文章

CSR8675项目实战:BlueAg蓝牙一拖二发射器

CSR8675项目实战:BlueHiFi蓝牙音乐收发器

CSR8675项目实战:BlueHiFi蓝牙音乐收发器

CSR8675的学习笔记:驱动正交编码器

CSR8675的学习笔记:驱动正交编码器

CSR8670项目实战:BlueCar蓝牙音频网关