KDocs 研发资料库
  • 音频技术应用文档入口汇总
  • 杰理WINDOWS环境安装及其他事项
  • LINUX安装和编译
  • Linux环境编译SDK教程(ubuntu18.04)
  • 音频类补丁汇总表
  • 认证资料获取汇总(最新资料后台会更新)
  • 《Hi-Res申请指南v1.0》
  • FreeRTOS 开源协议版本
  • uCOS 开源协议版本信息
  • 各个模块关键资料汇总
  • 软件调试方法
  • Makefile编译
  • 提bug的规范
  • 常规设备使用指导
  • 对外特别发布开发注意点信息汇总
  • AD697X软件问题整理
  • AC700N软件问题整理
  • AC701X耳机软件问题整理
  • 951&961软件问题整理
  • AC104X软件问题整理
  • AC128N问题整理文档
  • 可视化工具调试问题整理
  • 耳机资料汇总
  • AC704N、 AC705N相关应用
  • AC695N&AC696N&AC608N音箱问题整理
  • JL701N音箱问题整理
  • AC692X软件问题整理
  • AC706N音箱问题整理
  • JL701N音箱问题整理(可视化工具SDK)
  • JL703N音箱问题整理
  • JL709N(JL689N)音箱问题整理
  • JL7094系列-入门到进阶开发指导
  • AC210N音箱问题整理
  • 无线麦系列版本支持指南
  • AW32N无线麦问题整理
  • lAD697X&JL701X 无线麦软件共性问题整理
  • AC706N无线麦问题整理
  • AW36N无线麦问题整理
  • AC710X无线麦问题整理
  • 唤醒源获取及唤醒IO设置
  • 电源
  • LVD
  • 长按复位配置方法
  • 低功耗应用
  • TIMER
  • UART
  • SPI
  • 内部FLASH空间添加用户自定义区域
  • IIC
  • GPIO
  • MCPWM
  • SD&U盘
  • 时钟&晶振
  • GPCNT
  • IO口crossbar映射功能
  • 文件系统使用总结
  • 复用应用
  • LINEIN_AUX相关应用
  • IIS音频接口应用
  • SPDIF模式相关(HDMI_ARC、光纤、同轴)
  • PDM LINK
  • 外设相关应用
  • 蓝牙相关
  • 杰理蓝牙距离调试文档
  • TWS耳机配对方式使用及生产把控指南
  • GFP谷歌快连及其认证流程指引
  • 蓝牙发射器(bt_emitter)相关
  • BLE应用
  • 关于提升BLE传输速率说明
  • BLE测试数据
  • MUSIC 模式相关应用
  • MIDI音乐实现
  • PC相关应用
  • FM相关应用
  • AC695N_AC696N FM天线ESD静电防护措施说明V2.0_20250107
  • fm应用详细设计说明书
  • 杰理FM硬件指南-20230315
  • ADC模块
  • AUDIO相关应用
  • 新Tools的音效调试使用
  • 混响应用使用说明
  • 音效调式
  • 音箱耳机低音小音量增强实现方法
  • DAC输出功率调试
  • DAC性能测试指南
  • 针对各类底噪、电流声分析流程
  • 录音相关应用
  • OPUS编解码相关
  • RTC相关应用
  • 升级相关
  • UI相关应用
  • 通话调试流程指南
  • 通话相关应用
  • 省电容mic使用手册
  • 如何测试验证手表通话系统的语音失真 V1.0
  • AC695X_AC696X_AD697X 常见通话问题处理参考资料
  • 通话调试问题确定步骤
  • 内置触摸调试指南
  • 低功耗内置触摸介绍V2.2
  • 触摸相关文档
  • AC697X、AC698X内置触摸调试及整改方法V1.0
  • 产测相关应用
  • AC63X BLE DUT测试介绍
  • 兆华序列使用简易说明
  • JL_1T2蓝牙测试盒音频文件更换说明
  • ENC相关应用入口汇总
  • ANC相关应用入口汇总
  • JL DMS通话调试手册-V3.9.4
  • ANC-ENC结构设计要点-20240407
  • 半入耳式ANC耳机器件选型与结构设计指南V1.0
  • JL 耳道自适应调试指南
  • ANC相关问题处理
  • ANC调试文档
  • 评估蓝牙耳机通话上行降噪性能的方法 V1.0 (2)
  • FCC 或者 BQB 认证相关应用
  • BQB profile 测试中常遇到的问题点汇总
  • 蓝牙参数整理20210811
  • 杰理科技认证问题参考
  • 无线音频产品开发指南
  • 无线麦系列从入门到进阶开发指导
  • 量产烧录相关说明
  • 一拖八烧录器远程过程调用说明
  • 量产相关建议
  • EffectTool(eq)调试工具说明文档
  • AC897AD697系列的EQ调试方法-V3
  • 杰理调音指南-20220622
  • 音效模块总结
  • 简要调音步骤指引(可视化)
  • 耳机充电与通信调试指南
  • 充电仓硬件调试指南
  • 充电仓软件问题整理
  • AC951常用说明
  • AC951_AC961_开发板使用说明V1.0
  • 彩屏仓AC982 入门指导及FAQ
  • 蓝牙音频APP相关应用
  • 手机APP的简介和开发流程
  • 蓝牙音频系列-硬件设计指南
  • 静电测试及整改方法-20220706
  • TWS耳机底噪整改指南V1.2
  • 杰理蓝牙距离调试文档
  • 蓝牙RF硬件设计与调试V1.1-20250219
  • AC695N_AC696N FM天线ESD静电防护措施说明V2.0_20241228
  • 音效调试指南
  • 无线话筒K歌音箱标准开发流程
  • 杰理科技 700N TWS耳机产品标准-202303
  • 杰理科技 701N 广播产品标准-202303
  • JL-HAT ANC高效产测自适应
  • 无线话筒测试板使用介绍
  • 一拖八频偏校准使用说明
  • 领夹麦产测工具使用介绍
  • Dongle量产测试说明
  • Dongle+Tws或头戴式 SDK通过工具绑定配对功能说明文档
  • 双麦ENC产测宏配置验证方法 V1.0
  • JL耳机方案麦克风声学测试指令_V1.0
  • JL耳机方案麦克风声学测试指令_V2.0
  • JL_ENC自动测试命令_V0.0.1
  • JL蓝牙测试盒用例说明
  • LDAC自测环境搭建_ubuntu
  • 延迟小板使用手册
  • 杰理库文件的制作及添加实现过程
  • 关于 AC635N&AC636N系列免晶振兼容性补丁 说明
  • 关于 AC63系列VM兼容性修复补丁_20250224 说明
  • 关于 AC632N &AE632N_ 蓝牙& 低功耗兼容性修复补丁 说明
  • AD698N和AC638N电压优化补丁V3说明
  • 关于 AC635N&AC636N漏电和E&G版兼容联合补丁 说明
  • AD698N和AC638N电压优化补丁V2说明
  • 702相关应用
  • 2024-4-30 发布歌词显示SDK开发注意事项
  • BQB RF & FCC 认证测试固件问题汇总及源码
  • BQB profile 测试中常遇到的问题点汇总
  • 进dut测试模式方法及固件
  • 704-频偏修改bin文件合集
  • 701手表BQB PROFILE认证注意事项
  • 认证的干扰规避整改
  • 海外CRA认证报告生成说明
  • JL700N可视化工具问题处理文档
  • JL701X可视化工具问题处理文档
  • JL701N_earphone_release_v1.6.0_ANC 低功耗问题补丁
  • JL708X可视化工具问题处理文档
  • JL709N可视化工具问题处理文档
  • JLstudio可视化工具使用说明
  • 一拖二-串口_蓝牙_app在线EQ、通话参数及调试方式和内置触摸调试
  • 可视化JL701M_DONGLE_SDK添加MIC为发射源
  • 可视化JL701M_DONGLE_SDK添加按键切换usb audio与MIC发射源
  • JL701M_DONGLE_SDK_1.0.0_dongle输入linein输出dac
  • 701可视化_a2dp_to_le_demo说明
  • 耳机系列demo汇总
  • JL7018F&JL7006F_7003F AUX IIS
  • 一键连接应用
  • 普通SDK动态开关一拖二功能
  • 蓝牙分析仪(EllisysBluetoothAnalyzer)抓包例程
  • TWS配对常见应用
  • APP自定义按键的demo
  • AC700N 播歌双EQ DEMO示例及说明
  • AC698V164 MIC与蓝牙音乐共同输出DAC demo
  • 添加第三方算法以及多算法授权流程
  • 空间音频配置方法
  • 普通高低电平TWS耳机控制通讯
  • 入耳检测状态更新demo解析
  • 资源优化(非可视化的SDK)
  • 资源优化(可视化SDK)
  • 翻译耳机、语音识别应用demo
  • 耳机抗干扰问题排查和优化
  • AC698D2 抗干扰优化
  • AC696X软件问题整理
  • AC695X软件问题整理
  • AC695X软件问题整理_1722249702
  • AC608X软件问题整理
  • JL701N_2T1_DUPLEX 应用软件问题汇总
  • le_audio_1.0.0_patch05 以及之前的版本IRkey 功能异常修改
  • AC706N-le_audio-200SDK有线_无线麦录音demo
  • jl701n_le_audio_v200patch02 IO串口升级修改串口单次发送字节长度的修改办法
  • 703创建IIS 输入dac输出音频流demo
  • 703将媒体流音频同时上传PC_mic录音demo实现
  • 703四路IIS输入输出demo
  • 底噪调试
  • AW328A烧录说明
  • 文档合集
  • AC706N_wireless_mic_release_PATCH_20240712 补丁 说明
  • AC706N_wireless_mic_release_PATCH_20240726 补丁 说明
  • AC706N_wireless_mic_release_V1.0.0、1.0.1、1.0.2 补丁 说明
  • AC706N_wireless_mic_release_V1.1.2 中的2T1应用 RX端不绑定配对,TX端实现绑定配对的修改方法
  • 各系列省晶振实现方法
  • iis tx rx说明文档
  • spdif信号
  • 蓝牙电话本
  • 安卓手机同步时间补丁
  • CDC配置
  • PC模式下增加Teams唤起功能
  • 杰理HDMIARC开发实现过程
  • AC701N patch06之前的版本用第三方算法节点实现新版本的pcm delay节点功能
  • (K歌音箱)各SDK音效配置相关说明
  • 长文件录音
  • 点阵屏歌词字库相关(蓝牙歌词,普通歌词)
  • 字模提取工具说明
  • 音箱可视化彩屏-入门到进阶开发指导
  • 彩屏LVGL UI设计及工具使用
  • 使用内部UI框架进行UI调试的系列步骤
  • AD101C使用强制升级工具spi 烧录不了问题处理(测试版本)
  • AD161扩展灯板说明文档
  • 歌词显示器开发注意项
  • 使用硬件数字增益控制通话下行音量(可视化)
  • 杰理小牛产测资料
  • ENC配置说明
  • 通话调试流程指引
  • ANC相关功能demo
  • 无线麦硬件资料汇总
  • 无线麦相关应用
  • 无线麦复杂功能补丁包汇总
  • 无线话筒形态SDK程序&开发经验&问题点分享
  • 双模式结合应用问题汇总
  • 无线麦距离优化案例
  • 无线麦系列中的双工形态(duplex)演变开发对讲机方案开发资料
  • 音箱入门到进阶指导文档(编写中)
  • 蓝牙耳机品控和生产技巧
  • 982过_欠压标志获取异常
  • 如何获取最新涂鸦SDK及库文件
  • 无线话筒K歌音响-调音标准流程
  • 无线话筒产测流程

经典蓝牙和TWS产测常见功能

📎 原始文档:https://www.kdocs.cn/l/csQkHIpOSLZd 经典蓝牙和TWS产测常见功能
产测的其他文档产测相关应用.otl

  1. 进入蓝牙模式
//一般在idle模式下用
//普通SDK
void enter_bt_mode(void)
{
    struct application *app = get_current_app();
    if (app) {
        if (strcmp(app->name, APP_NAME_BT)) {
            if (!app_var.goto_poweroff_flag) {
                app_var.play_poweron_tone = 0;
                task_switch_to_bt();
            }
        }
    }
}
//可视化SDK
void enter_bt_mode(void)
{
    if (!app_in_mode(APP_MODE_BT)) {
        if(!app_var.goto_poweroff_flag) {
            app_var.play_poweron_tone = 0;
            app_send_message(APP_MSG_GOTO_MODE, APP_MODE_BT);
        }
    }
}
  1. 进入dut模式 基础接口
extern void bredr_set_dut_enble(u8 en, u8 phone);

进dut常用操作:耳机先取消回连流程(包括回连TWS和回连手机),取消蓝牙BLE广播,再进入dut模式,进入dut后不能再发起任何回连。
普通SDK
image 可视化SDK
image

//普通SDK
void bt_bredr_enter_dut_mode(u8 mode, u8 inquiry_scan_en)
{
    u8 local_addr[6];
    puts("<<<<<<<<<<<<<bt_bredr_enter_dut_mode>>>>>>>>>>>>>>\n");
    clk_set("sys", SYS_48M);
    u8 chargestore_set_ex_enter_dut_flag(u8 en);
    chargestore_set_ex_enter_dut_flag(1);//把测试盒进dut标志位置1,避免走回连
    extern void bt_ble_adv_enable(u8 enable);
    bt_ble_adv_enable(0);//关BLE广播,如果有其他地方还会打开,那些地方要加标志位屏蔽掉,总之就是不能开ble广播
    bredr_set_dut_enble(1, 1);
    if (mode) {
        bt_user_priv_var.auto_connection_counter = 0;
        //下面是断开tws和手机,取消回连,开可发现可连接
#if    TCFG_USER_TWS_ENABLE
        tws_cancel_state();
        user_send_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
        user_send_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);
        if (0 == get_total_connect_dev()) {
            log_info("<<<<<no phone connect,instant page_scan,inquiry_scan:%x>>>>>\n", inquiry_scan_en);
            bt_get_vm_mac_addr(local_addr);

            //log_info("local_adr\n");
            //put_buf(local_addr,6);
            lmp_hci_write_local_address(local_addr);
            if (inquiry_scan_en) {
                user_send_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
            }
            user_send_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
        }

#else
        if (bt_user_priv_var.auto_connection_timer) {
            sys_timeout_del(bt_user_priv_var.auto_connection_timer);
            bt_user_priv_var.auto_connection_timer = 0;
        }        
        user_send_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
        user_send_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);      
        if (inquiry_scan_en) {
            user_send_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
        }
        user_send_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);        
#endif
        link_task_idle_disable();
    }
}
//可视化SDK
void bt_bredr_enter_dut_mode(u8 mode, u8 inquiry_scan_en)
{
    puts("<<<<<<<<<<<<<bt_bredr_enter_dut_mode>>>>>>>>>>>>>>\n");
    clock_alloc("DUT", SYS_48M);
    u8 testbox_set_ex_enter_dut_flag(u8 en);
    testbox_set_ex_enter_dut_flag(1);//把测试盒进dut标志位置1,避免走回连
    extern void bt_ble_adv_enable(u8 enable);
    bt_ble_adv_enable(0);//关BLE广播,如果有其他地方还会打开,那些地方要加标志位屏蔽掉,总之就是不能开ble广播
    bredr_set_dut_enble(1, 1);
    if (mode) {
        clr_device_in_page_list();
        dut_idle_run_slot(2);
        g_bt_hdl.auto_connection_counter = 0;
#if TCFG_USER_TWS_ENABLE
        tws_dual_conn_close();
        bt_page_scan_for_test(inquiry_scan_en);
#else
        dual_conn_close();
        bt_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);
        u8 local_addr[6];
        if (0 == bt_get_total_connect_dev()) {
            bt_get_vm_mac_addr(local_addr);
            lmp_hci_write_local_address(local_addr);
            if (inquiry_scan_en) {
                bt_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
            }
            bt_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
        }
#endif
    }
}

//调用
bt_bredr_enter_dut_mode(1, 1);
  1. 进入定频测试
void bt_fix_fre_api(u8 fre);//fre: 0 ~ 78,对应2402——2480

如果开了BLE,需要先关闭BLE广播
普通SDK:

bt_ble_exit();
bt_fix_fre_api(39);//2441

可视化SDK:

void bt_fix_fre_api(u8 fre);
void tws_dual_conn_close();
tws_dual_conn_close();//非tws用dual_conn_close();
bt_ble_adv_enable(0);
bt_ble_exit();
multi_protocol_bt_exit();
bt_fix_fre_api(39);//2441
  1. 进入配对模式 最终实现就是取消所有回连流程,断开手机,开可发现可连接
//普通SDK
void bt_enter_pairing(void)
{    
    bt_user_priv_var.auto_connection_counter = 0;
#if    TCFG_USER_TWS_ENABLE
    gtws.state &= ~BT_TWS_POWER_ON;
    if(tws_api_get_tws_state() & TWS_STA_PHONE_CONNECTED){
        if(tws_api_get_role() == TWS_ROLE_MASTER){
            printf("------------------------------------------------------------\n");
            user_send_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);
            user_send_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);
        }
    }
    else{
        if(get_curr_channel_state() || hci_standard_connect_check()){
            if(tws_api_get_role() == TWS_ROLE_MASTER){
                printf("tttttttttttttttttttttttttttttttttttttttttttttttttttttttt\n");
                user_send_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);
                user_send_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
                user_send_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);
            }
        }
        void bt_tws_connect_and_connectable_switch();
        bt_tws_connect_and_connectable_switch();
    }
#else
    if (bt_user_priv_var.auto_connection_timer) {
        sys_timeout_del(bt_user_priv_var.auto_connection_timer);
        bt_user_priv_var.auto_connection_timer = 0;
    }        
    user_send_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
    if(get_total_connect_dev() || hci_standard_connect_check()){
        user_send_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);  
    }
    else{
        user_send_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
        user_send_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
    }
#endif
}
//可视化SDK
void bt_enter_pairing(void)
{
    clr_device_in_page_list();
    g_bt_hdl.auto_connection_counter = 0;
#if    TCFG_USER_TWS_ENABLE
    if(tws_api_get_tws_state() & TWS_STA_PHONE_CONNECTED){
        if(tws_api_get_role() == TWS_ROLE_MASTER){
            printf("------------------------------------------------------------\n");
            bt_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);
            bt_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);
        }
    }
    else{
        if(bt_get_curr_channel_state() || bt_get_total_connect_dev()){
            if(tws_api_get_role() == TWS_ROLE_MASTER){
                printf("tttttttttttttttttttttttttttttttttttttttttttttttttttttttt\n");
                bt_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);
                bt_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
                bt_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);
            }
        }
        bt_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
        bt_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
    }
#else     
    bt_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
    if(bt_get_curr_channel_state() || bt_get_total_connect_dev()){
        bt_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);  
    }
    else{
        bt_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
        bt_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
    }
#endif
}
  1. tws进入单耳配对模式 最终实现断开tws和手机,取消回连,开可发现可连接
//普通SDK
void tws_enter_single_pairing(void)
{
    bt_user_priv_var.auto_connection_counter = 0;
    u8 chargestore_set_ex_enter_dut_flag(u8 en);
    chargestore_set_ex_enter_dut_flag(1);//把测试盒进dut标志位置1,避免走回连
    //下面是断开tws和手机,取消回连,开可发现可连接
    tws_cancel_state();
    user_send_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
    user_send_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);
    if (0 == get_total_connect_dev()) {
        bt_get_vm_mac_addr(local_addr);
        //log_info("local_adr\n");
        //put_buf(local_addr,6);
        lmp_hci_write_local_address(local_addr);
        user_send_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
        user_send_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
    }
}
//可视化SDK
void tws_enter_single_pairing(void)
{
    clr_device_in_page_list();
    g_bt_hdl.auto_connection_counter = 0;
    u8 testbox_set_ex_enter_dut_flag(u8 en);
    testbox_set_ex_enter_dut_flag(1);//把测试盒进dut标志位置1,避免走回连
    //下面是断开tws和手机,取消回连,开可发现可连接
    tws_dual_conn_close();
    bt_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
    tws_api_detach(TWS_DETACH_BY_POWEROFF, 5000);
    bt_cmd_prepare(USER_CTRL_POWER_OFF, 0, NULL);
    if (0 == get_total_connect_dev()) {
        bt_get_vm_mac_addr(local_addr);
        //log_info("local_adr\n");
        //put_buf(local_addr,6);
        lmp_hci_write_local_address(local_addr);
        bt_cmd_prepare(USER_CTRL_WRITE_SCAN_ENABLE, 0, NULL);
        bt_cmd_prepare(USER_CTRL_WRITE_CONN_ENABLE, 0, NULL);
    }
}
  1. 清除手机配对记录 此操作需要在蓝牙模式下进行,清除设备记录之前先要进入配对模式
//先进蓝牙模式
enter_bt_mode();
//然后进配对模式
bt_enter_pairing();
//然后清除记录
user_send_cmd_prepare(USER_CTRL_DEL_ALL_REMOTE_INFO, 0, NULL);//可视化用bt_cmd_prepare

//如果原来在idle,通过enter_bt_mode()切到蓝牙模式,可以在BT_STATUS_INIT_OK处添加处理
//普通SDK
static int bt_connction_status_event_handler(struct bt_event *bt)
{
    STATUS *p_tone = get_tone_config();
    u8 *phone_number = NULL;

#if CONFIG_BT_BACKGROUND_ENABLE
    int ret = bt_background_event_probe_handler(bt);
    if (ret == -EINVAL) {
        return 0;
    }
#endif
    //log_info("bt_conn_event:%d", bt->event);

    switch (bt->event) {
    case BT_STATUS_INIT_OK:
    //......前面维持公版,省略
        if(clean_all_info){//enter_bt_mode()时添加标志位处理
            clean_all_info = 0;
            user_send_cmd_prepare(USER_CTRL_DEL_ALL_REMOTE_INFO, 0, NULL);
            clear_current_poweron_memory_search_index(1);
        }
        else{
            bt_init_ok_search_index();//不是清除配对才执行
        }
    //......后面维持公版,省略
}

//可视化SDK
static int bt_connction_status_event_handler(struct bt_event *bt)
{
    switch (bt->event) {
    case BT_STATUS_INIT_OK:
    //......前面维持公版,省略
        if(clean_all_info){//enter_bt_mode()时添加标志位处理
            clean_all_info = 0;
            bt_cmd_prepare(USER_CTRL_DEL_ALL_REMOTE_INFO, 0, NULL);
            clear_current_poweron_memory_search_index(0);
        }
        else{
            bt_init_ok_search_index();//不是清除配对才执行
        }
    //......后面维持公版,省略
}
static int dual_conn_btstack_event_handler(int *_event)
{
    struct bt_event *event = (struct bt_event *)_event;
    int state = tws_api_get_tws_state();

    printf("dual_conn_btstack_event_handler:%d\n", event->event);
    switch (event->event) {
    case BT_STATUS_INIT_OK:
        if(clean_all_info == 0){
            dual_conn_page_devices_init();
        }
        return 0;
    //......后面维持公版,省略
}
  1. 清除TWS连接记录
//对耳连接状态下使用
tws_api_remove_pairs();
//普通SDK
//蓝牙模式其他状态下使用
/*
 * 解除配对,清掉对方地址信息和本地声道信息
 */
void bt_tws_remove_pairs()
{
    puts("<<<<<<<<<<<<<<<<<<bt_tws_remove_pairs err>>>>>>>>>>>>>>>>>>\n");
    /*正常情况下不必要清除tws相关配对信息,防止用户误操作。只删除手机配对信息即可*/
    /* u8 del_phone_remote_info=1; */
    /* user_send_cmd_prepare(USER_CTRL_DEL_ALL_REMOTE_INFO, 0, &del_phone_remote_info); */
#if 1
    u8 mac_addr[6];
    char channel = 'U';
    char tws_channel = 0;

    gtws.state &= ~BT_TWS_REMOVE_PAIRS;

    memset(mac_addr, 0xFF, 6);
    syscfg_write(CFG_TWS_COMMON_ADDR, mac_addr, 6);
    syscfg_write(CFG_TWS_REMOTE_ADDR, mac_addr, 6);
    syscfg_read(CFG_BT_MAC_ADDR, mac_addr, 6);
    lmp_hci_write_local_address(mac_addr);

#if (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_LEFT_START_PAIR) ||\
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_RIGHT_START_PAIR) ||\
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_MASTER_AS_LEFT)

#if CONFIG_TWS_SECECT_CHARGESTORE_PRIO
    syscfg_read(CFG_CHARGESTORE_TWS_CHANNEL, &tws_channel, 1);
#endif
    if ((tws_channel != 'L') && (tws_channel != 'R')) {
        syscfg_write(CFG_TWS_CHANNEL, &channel, 1);
        tws_api_set_local_channel(channel);
    }
#endif

    tws_api_clear_connect_aa();

    bt_tws_wait_pair_and_phone_connect();
#endif
}
//非蓝牙模式下使用
void idle_tws_remove_pairs()
{
    puts("<<<<<<<<<<<<<<<<<<idle_tws_remove_pairs err>>>>>>>>>>>>>>>>>>\n");
    /*正常情况下不必要清除tws相关配对信息,防止用户误操作。只删除手机配对信息即可*/
    /* u8 del_phone_remote_info=1; */
    /* user_send_cmd_prepare(USER_CTRL_DEL_ALL_REMOTE_INFO, 0, &del_phone_remote_info); */
#if 1
    u8 mac_addr[6];
    char channel = 'U';
    char tws_channel = 0;

    memset(mac_addr, 0xFF, 6);
    syscfg_write(CFG_TWS_COMMON_ADDR, mac_addr, 6);
    syscfg_write(CFG_TWS_REMOTE_ADDR, mac_addr, 6);

#if (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_LEFT_START_PAIR) ||\
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_RIGHT_START_PAIR) ||\
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_MASTER_AS_LEFT)

#if CONFIG_TWS_SECECT_CHARGESTORE_PRIO
    syscfg_read(CFG_CHARGESTORE_TWS_CHANNEL, &tws_channel, 1);
#endif
    if ((tws_channel != 'L') && (tws_channel != 'R')) {
        syscfg_write(CFG_TWS_CHANNEL, &channel, 1);
    }
#endif
#endif
}
//可视化SDK
//蓝牙模式其他状态下使用
void bt_tws_remove_pairs()
{
    u8 mac_addr[6];

    gtws.state &= ~BT_TWS_REMOVE_PAIRS;
    gtws.state &= ~BT_TWS_PAIRED;
    gtws.state |= BT_TWS_UNPAIRED;

    memset(mac_addr, 0xFF, 6);
    syscfg_write(CFG_TWS_COMMON_ADDR, mac_addr, 6);
    syscfg_write(CFG_TWS_REMOTE_ADDR, mac_addr, 6);
    syscfg_read(CFG_BT_MAC_ADDR, mac_addr, 6);
    lmp_hci_write_local_address(mac_addr);

#if (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_START_PAIR_AS_LEFT) ||\
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_START_PAIR_AS_RIGHT) ||\
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_MASTER_AS_LEFT) || \
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_MASTER_AS_RIGHT) && \
    CONFIG_TWS_SECECT_CHARGESTORE_PRIO
    char channel = 'U';
    char tws_channel = 0;
    syscfg_read(CFG_CHARGESTORE_TWS_CHANNEL, &tws_channel, 1);
    if ((tws_channel != 'L') && (tws_channel != 'R')) {
        syscfg_write(CFG_TWS_CHANNEL, &channel, 1);
        tws_api_set_local_channel(channel);
    }
#endif
}
//非蓝牙模式下使用
void idle_tws_remove_pairs()
{
    u8 mac_addr[6];
    memset(mac_addr, 0xFF, 6);
    syscfg_write(CFG_TWS_COMMON_ADDR, mac_addr, 6);
    syscfg_write(CFG_TWS_REMOTE_ADDR, mac_addr, 6);

#if (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_START_PAIR_AS_LEFT) ||\
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_START_PAIR_AS_RIGHT) ||\
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_MASTER_AS_LEFT) || \
    (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_MASTER_AS_RIGHT) && \
    CONFIG_TWS_SECECT_CHARGESTORE_PRIO
    char channel = 'U';
    char tws_channel = 0;
    syscfg_read(CFG_CHARGESTORE_TWS_CHANNEL, &tws_channel, 1);
    if ((tws_channel != 'L') && (tws_channel != 'R')) {
        syscfg_write(CFG_TWS_CHANNEL, &channel, 1);
    }
#endif
}
  1. tws组队 拿到左右耳的local地址,然后想办法互相交换,写到remote vm
//获取左右耳的local地址
syscfg_read(CFG_TWS_LOCAL_ADDR, local_addr, 6);
//通过串口或者spp等方式左右互换local地址,比如左边local给右边,右边local给左边
//给过来就得到remote,然后写VM
//比如左边local发给右边,这个local就变成了右边的remote
syscfg_write(CFG_TWS_REMOTE_ADDR, mac_remote, 6);
//设置耳机声道
syscfg_write(CFG_TWS_CHANNEL, &channel, 1);
  1. 烧写mac地址 注意:此烧写方式跟一拖八或者一拖二烧录mac地址是一样的,mac写在flash最后4K,每次写mac地址都会把最后4K擦除,所以要注意在写mac地址之前备份最后4K里面的其他数据(如果有的话,比如认证码三元组之类的)
#define MAC_PAGE_OFFSET     64

typedef enum _FLASH_ERASER {
    CHIP_ERASER = 0,
    BLOCK_ERASER,
    SECTOR_ERASER,
    PAGE_ERASER,
} FLASH_ERASER;

extern bool sfc_erase(int cmd, u32 addr);
extern u32 sfc_write(const u8 *buf, u32 addr, u32 len);

/**
 * @brief 将蓝牙地址写入保留区最后256字节
 * 
 * @param mac 蓝牙地址
 * @param len 
 * @return int 
 */
int set_mac_addr_to_flash(const u8 *mac, u16 len)
{
#if (CONFIG_DEVELOPER_MODE)
    return 256;//开发板不支持烧写
#endif
    u8 buffer[256];
    u32 flash_capacity = sdfile_get_disk_capacity();
    u32 sector = flash_capacity - 4 * 1024;
    u32 page_addr = flash_capacity - 256;
    u8 *auif_ptr = (u8 *)sdfile_flash_addr2cpu_addr(page_addr);
    u8 write_mac[6];
    u16 crc;
    u32 wlen;

    ///save last 256 byte
    /* memset(buffer, 0xff, sizeof(buffer)); */
    memcpy(buffer, auif_ptr, sizeof(buffer));
    // auif_ptr += MAC_PAGE_OFFSET;

    void swapX(const uint8_t *src, uint8_t *dst, int len);
    swapX(mac, write_mac, 6);

    memcpy(&buffer[MAC_PAGE_OFFSET], write_mac, sizeof(write_mac));
    crc = CRC16(&buffer[MAC_PAGE_OFFSET], sizeof(write_mac));
    printf("crc:0x%x", crc);
    buffer[MAC_PAGE_OFFSET+sizeof(write_mac)] = crc>>8;
    buffer[MAC_PAGE_OFFSET+sizeof(write_mac)+1] = crc & 0xff;
    put_buf(&buffer[MAC_PAGE_OFFSET], 8);

    if(!memcmp(buffer, auif_ptr, sizeof(buffer))) {
        printf("same , return");
        return 256;
    }

    printf("erase license sector \n");
    sfc_erase(SECTOR_ERASER, sector);

    printf("write license to flash \n");
    wlen = sfc_write(buffer, page_addr, 256);
    printf("wlen:%d \n", wlen);

    return wlen;
    // return 0;
}

//读取烧录的MAC地址
u8 mac_addr[6];
syscfg_read(CFG_BT_MAC_ADDR, mac_addr, 6);
  1. 获取tws配对记录
    u8 local_addr[6];
    u8 remote_addr[6];
    u8 common_addr[6];
    u8 channel = 0;
    bt_get_tws_local_addr(local_addr);
    syscfg_read(CFG_TWS_REMOTE_ADDR, remote_addr, 6);
    syscfg_read(CFG_TWS_COMMON_ADDR, common_addr, 6);
    syscfg_read(CFG_TWS_CHANNEL, &channel, 1);
  1. 获取耳机检测的电池电压和百分比
u16 get_vbat_value(void);//电压:注意返回值是u16
u8 get_vbat_percent(void);//百分比,返回80表示80%
  1. 获取蓝牙名
const char *bt_get_local_name();
  1. 获取SDK版本信息
const char *sdk_version_info_get(void);
  1. 获取烧录校验码
extern u8 *sdfile_get_burn_code(u8 *len);//len为返回值指向内存的长度
  1. 获取是否与当前手机配对过
extern int get_link_key(u8 *bd_addr, u8 *link_key, u8 id);
bool get_linkkey_exist_state(const uint8_t *bt_address)
{
    u8 link_key[16] = {0};
    printf("%s\n", __func__);
    put_buf((u8 *)bt_address, 6);
    if (get_link_key((u8 *)bt_address, (u8 *)&link_key[0],  0)) {
        printf("have link key\n");
        return true;
    } else {
        printf("no link key\n");
        return false;
    }
}
  1. 写入自定义数据 用于例如固定BLE地址、或者做SN数据等
    此方式受“擦除VM和BT配置区“的影响。想要不受影响请使用模拟烧录器写认证码
    1、先定义一个VM索引(注意不要和SDK已有的冲突,SDK已有索引见syscfg_id.h和user_cfg_id.h)
    image 2、将该索引放到蓝牙配置区(蓝牙配置区只允许写一次,要重复写的话请忽略本步骤,但重复写时会受到VM地址的影响,需要固定VM地址,固定VM地址的方式见关于VM、蓝牙配置区和4K预留区.otl)
    image 3、代码上使用syscfg_write写入,使用syscfg_read读取
    禁止在中断中写入数据,判断是否在中断中见CPU&OS相关应用.otl
syscfg_write(CFG_FACTORY_INFO, factory_info, 6);
syscfg_read(CFG_FACTORY_INFO, factory_info, 6);
  1. 模拟烧录器写认证码 注意:如果需要烧写mac地址,则此步骤必须放在烧写mac地址之后,保险起见,生产前要模拟产线流程,看看写完认证码之后耳机的MAC地址有没有问题。
    此方式是将数据写入flash最后4K的倒数256字节偏移80字节处,认证码最长支持
    256-80-4=172字节
    此方式不受”擦除VM和BT配置区”的影响。
    烧录器烧认证码参考:蓝牙音频APP相关应用.otl
#define LIC_PAGE_OFFSET         80

typedef enum _FLASH_ERASER {
    CHIP_ERASER = 0,
    BLOCK_ERASER,
    SECTOR_ERASER,
    PAGE_ERASER,
} FLASH_ERASER;

typedef struct __flash_of_lic_para_head {
    s16 crc;
    u16 string_len;
    const u8 para_string[];
} __attribute__((packed)) _flash_of_lic_para_head;

extern bool sfc_erase(int cmd, u32 addr);
extern u32 sfc_write(const u8 *buf, u32 addr, u32 len);

//将认证码信息保存到flash里面
int license2flash(const u8 *data, u16 len)
{
    _flash_of_lic_para_head header;
    u8 buffer[256];
    u32 flash_capacity = sdfile_get_disk_capacity();
    u32 sector = flash_capacity - 4 * 1024;
    u32 page_addr = flash_capacity - 256;
    u8 *auif_ptr = (u8 *)sdfile_flash_addr2cpu_addr(page_addr);

    ///save last 256 byte
    /* memset(buffer, 0xff, sizeof(buffer)); */
    memcpy(buffer, auif_ptr, sizeof(buffer));
    auif_ptr += LIC_PAGE_OFFSET;

    header.string_len = len;
    ///length
    memcpy(&buffer[LIC_PAGE_OFFSET + sizeof(header.crc)], &(header.string_len), sizeof(header.string_len));
    ///context
    memcpy(&buffer[LIC_PAGE_OFFSET + sizeof(header)], data, len);
    ///crc
    header.crc = CRC16(&buffer[LIC_PAGE_OFFSET + sizeof(header.crc)], sizeof(header.string_len) + header.string_len);
    memcpy(&buffer[LIC_PAGE_OFFSET], &(header.crc), sizeof(header.crc));

    ///check if need update data to flash,erease license erea if there are some informations in license erea
    if (!memcmp(auif_ptr, buffer + LIC_PAGE_OFFSET, len + sizeof(_flash_of_lic_para_head))) {
        printf("flash information the same with license\n");
        return 0;
    }

    printf("erase license sector \n");
    sfc_erase(SECTOR_ERASER, sector);

    printf("write license to flash \n");
    sfc_write(buffer, page_addr, 256);

    return 0;
}

static bool license_para_head_check(u8 *para)
{
    _flash_of_lic_para_head *head;

    //fill head
    head = (_flash_of_lic_para_head *)para;

    ///crc check
    u8 *crc_data = (u8 *)(para + sizeof(((_flash_of_lic_para_head *)0)->crc));
    u32 crc_len = sizeof(_flash_of_lic_para_head) - sizeof(((_flash_of_lic_para_head *)0)->crc)/*head crc*/ + (head->string_len)/*content crc,include end character '\0'*/;
    s16 crc_sum = 0;

    crc_sum = CRC16(crc_data, crc_len);

    if (crc_sum != head->crc) {
        printf("license crc error !!! %x %x \n", (u32)crc_sum, (u32)head->crc);
        return false;
    }

    return true;
}

//读取写入的认证码信息,返回值是认证码信息内存地址
const u8 *get_license_ptr(void)
{
    u32 flash_capacity = sdfile_get_disk_capacity();
    u32 flash_addr = flash_capacity - 256 + LIC_PAGE_OFFSET;
    u8 *lic_ptr = NULL;
    _flash_of_lic_para_head *head;

    printf("flash capacity:%x \n", flash_capacity);
    lic_ptr = (u8 *)sdfile_flash_addr2cpu_addr(flash_addr);

    //head length check
    head = (_flash_of_lic_para_head *)lic_ptr;
    if (head->string_len >= 0xff) {
        printf("license length error !!! \n");
        return NULL;
    }

    ////crc check
    if (license_para_head_check(lic_ptr) == (false)) {
        printf("license head check fail\n");
        return NULL;
    }

    //put_buf(lic_ptr, 128);

    lic_ptr += sizeof(_flash_of_lic_para_head);
    return lic_ptr;
}