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/caFGRJUNrPNp TWS配对常见应用

  1. AC697X/700x左右耳同时双击发起配对 20210415 HJY 700N V135以及更新版本、697N V236以及更新版本请看第4点
    修改方法:选择双击配对方式,耳机是根据配对码是否一致进行配对的,配对时将两个耳机的配对码改为一致,超时后重新生成新的随机配对码。
    配对方式要选择按键双击发起配对、声道选择不能选择CONFIG_TWS_LEFT_START_PAIR和
    lmp_hci_write_local_address
    CONFIG_TWS_RIGHT_START_PAIR
    imageimage 在bt_tws_start_search_sibling函数中按上图加如下代码:
    u8 mac_addr[6];
    syscfg_read(CFG_BT_MAC_ADDR, mac_addr, 6);
    lmp_hci_write_local_address(mac_addr);
    bt_set_tws_device_indicate(dk_tws_pair_code);
    tws_api_set_quick_connect_addr(tws_set_auto_pair_code());
    r_printf("tws_set_auto_pair_code=%x\n",tws_set_auto_pair_code());
    bt_tws_connect_sibling(3);
    printf("bt_cfg.tws_device_indicate = %x\n",bt_get_tws_device_indicate(NULL));
    sys_timeout_add(NULL,chang_pair_code,3000);
    image void chang_pair_code(void *p)
    {​
    u16 code;
    u8 temp[2];
    get_random_number(temp,2);
    code = temp[1];
    code = ((code<<8) | temp[0]);
    if((tws_api_get_tws_state() & TWS_STA_SIBLING_DISCONNECTED)){​
    bt_set_tws_device_indicate(code);
    g_printf("code=%x,%x\n",temp[0],temp[1]);
    bt_tws_search_sibling_and_pair();
    ​}
    ​}

image u16 dk_tws_pair_code = 0xdc81;
void bt_set_tws_device_indicate(u16 tws_pair_code)
{​
bt_cfg.tws_device_indicate = tws_pair_code;
​}
可配合第2点修改
2. AC697X/700x 左耳与多个右耳配对过,同时开机只有最后配对的耳机能连接成功20201224 HJY 使用场景:左耳分别与多只右耳连接过,下一次连接只允许最后连接的一对耳机回连成功。
imageimage 3. 对耳连接时根据需要固定主机

普通SDK:

下图可以根据自己的需要修改判断条件(这里demo是左耳做主机)
image 关闭主从切换
image

可视化SDK:

按键发起配对的耳机做主机,记忆主从

这里demo是按键发起配对的耳机做主机,能够记忆主从角色,下次开机主从与最后一次连接一样
700N_1T2_V202_patch_06_02_固定主从.rar

功能实现方法

  1. 按键配对,关闭主从切换 image
#if TCFG_TWS_AUTO_ROLE_SWITCH_ENABLE
            tws_api_auto_role_switch_enable();
#else
            tws_api_auto_role_switch_disable();
#endif

imageimageimage 2. 按键操作,发起配对和清除配对记录 image 3. 重点:添加下面的代码到apps\earphone\mode\bt\bt_tws.c

/**
 * @brief 最后一次对耳连接时,本地主从角色
 * @param TWS_ROLE_MASTER:上一次连接是做主机
 * @param TWS_ROLE_SLAVE:上一次连接是做从机
 */
u8 tws_last_role = TWS_ROLE_MASTER;

/**
 * @brief 标记是否本地按键触发配对
 * 
 */
u8 local_start_pair = 0;

//本地主从角色选择,两个耳机会互通彼此的角色选择
int tws_host_get_local_role()
{
    printf("%s %x\n", __func__, tws_api_get_tws_state());
    //本地按键触发配对或者手机已连接,做主机
    if((tws_api_get_tws_state() & TWS_STA_PHONE_CONNECTED)){
        printf("%s M\n", __func__);
        return 0xff;
    }
    if(local_start_pair){
        printf("%s m\n", __func__)
        return TWS_ROLE_MASTER;
    }

    printf("%s %d\n", __func__, tws_last_role);
    return tws_last_role;//使用上一次的角色
}

/**
 * @brief TWS连接过程中,TWS流程根据此函数返回值判断是否进行主从切换
 * 
 * @param remote_role_req 远端耳机请求的角色
 * @param local_role_req 本地耳机请求的角色
 * @return true 切换
 * @return false 不切换
 */
bool tws_host_role_switch(int remote_role_req, int local_role_req)
{
    printf("%s %d %d %x\n", __func__, remote_role_req, local_role_req, tws_api_get_tws_state());
    if(tws_api_get_tws_state() & TWS_STA_PHONE_CONNECTED){
        return true;//已经连接了手机,切换
    }
    if(remote_role_req == 0xff){
        return false;//remote已连接手机,不切换
    }
    //当本地请求做主机,并且是因为触发按键配对或者上一次是主机,就执行切换
    //返回true之后TWS会进行主从切换,让本地切换为主机
    if(local_role_req == TWS_ROLE_MASTER && (local_start_pair == 1 || tws_last_role == TWS_ROLE_MASTER))
    {
        return true;
    }

    return false;
}
  1. 开机读上一次主从角色 image
  2. 对耳连上清标志位,存主从角色 image
  3. 主从切换,存主从角色 image
  4. 按键发起配对置标志位,配对超时清标志位 image
  5. VM索引(不要与SDK已有索引重复)
#define     CFG_TWS_LOCAL_ROLE               7

image

其他相关问题修改

  1. 清除配对流程的bug导致清除对耳配对后主机搜不到从机,修改如下 imageimage
  2. 按键配对不能与第三个耳机配对(交叉配对),或同一对耳机重复发起配对会配不上,需要打开下面的功能 image 要提高交叉配对成功率,需要加长配对时间
    image
  3. 无蓝牙模式提示音导致蓝牙流程异常(比如开机对耳不回连) image
  4. 单耳连上手机之后,还能被其他耳机搜索配对上的问题 image
  5. 单耳连接了手机,发起TWS配对超时后,还能被其他耳机搜索配对上的问题 image
  6. 主动断开手机
//新增断连函数
void bt_disconn_all_phone_dev(void)
{
    void *device;
    void *devices[2];
    int num = btstack_get_conn_devices(devices, 2);
    if(num == 0){
        return;
    }
    for (u8 i = 0; i < num; i++)
    {
        device = devices[i];
        btstack_device_detach(device);
    }
}

//按键执行下面的函数去断连,进入手机配对模式
void bt_tws_enter_pairing(void)
{
    clr_device_in_page_list();
    bt_cmd_prepare(USER_CTRL_PAGE_CANCEL, 0, NULL);
    page_mode_active = 0;
    bt_disconn_all_phone_dev();
}

自定义条件固定主机

修改如图的两个函数内容
图中tws_host_role_switch是从机调用
remote_info = 主机的tws_host_get_local_role返回值
local_info = 从机的tws_host_get_local_role返回值
注意:要关闭自动主从切换,参考功能实现方法
image 4. AC700N_V1.3.5、697N_V236,tws同时按按键进行配对修改 支持单耳连接了手机的状态下进行对耳配对,可使用V1.3.5的btctrler.a替换到旧版本(V1.3.x)修改
【金山文档】 tws同时按按键进行配对修改
https://kdocs.cn/l/culxpi7I3u9Z
按键调用bt_tws_search_sibling_and_pair()函数进行配对。
5. 关闭自动配对,只能通过测试盒进行配对 修改方法:

  1. 关闭自动配对:
#define CONFIG_TWS_PAIR_BY_CLICK            0      /* 按键发起配对 */
#define CONFIG_TWS_PAIR_BY_AUTO             1      /* 开机自动配对 */
#define CONFIG_TWS_PAIR_BY_FAST_CONN        2      /* 开机快速连接,连接速度比自动配对快,不支持取消配对操作 */
#define CONFIG_TWS_PAIR_MODE                3//配置为3,只是为了不让这个宏等于0、1、2,配置为4也可以达到目的,只要不是0、1、2就行
  1. 关闭交叉配对
#define  CONFIG_DEVELOPER_MODE                0 //开发者模式debug ,开启后可以进行按键双击配对
//#define CONFIG_TWS_AUTO_PAIR_WITHOUT_UNPAIR     /* 不取消配对也可以配对新的耳机 */
  1. 关闭或不使用清除配对功能
//#define CONFIG_TWS_REMOVE_PAIR_ENABLE              /* 不连手机的情况下双击按键删除配对信息 */

image 4. TWS未配对,不允许执行tws_api_create_connection函数 imageimage 6. 加快交叉配对速度(未连接手机时) image

        if(bt_user_priv_var.auto_connection_counter || !(gtws.state & BT_TWS_POWER_ON)){
            connect_and_connectable_switch(0);
        }
        else{
            connect_and_connectable_switch(2);
        }

如果还不够快就把这个值加大
image

  1. 可视化SDK两边耳机同时操作按键发起配对 修改代码
    imageimageimageimageimageimageimageimage 配置工具配置按键功能
    image 或者tws未连接状态下执行函数
app_send_message(APP_MSG_TWS_START_PAIR, 0);
  1. 实现TWS右耳分时和两个左耳连接 功能描述:有A耳机、B耳机、C耳机,取出A、B时,AB连接后去连接手机,然后B关机,取出C,AC能回连上并且正常连接同一台手机播歌,AC关机后再开机也能正常回连手机。
    典型应用:AB是对耳,C是mic,AC连接时使用C的mic通话
    实现方法:将B的TWS配对信息通过测试盒同步给C,同步的信息包含:TWS local地址和remote地址,通过代码区分B和C。
    在user_cfg.c添加代码缓存remote地址
static u8 remote_addr[6];
void bt_get_tws_remote_addr(u8 *addr)
{
    memcpy(addr, remote_addr, 6);
}

void bt_set_tws_remote_addr(u8 *addr)
{
    memcpy(remote_addr, addr, 6);
}

image 在tws配对时记录remote地址
image 修改测试盒配对函数

#define TWS_TYPE_EAR                0
#define TWS_TYPE_MIC                1
#define CURR_TWS_TYPE               TWS_TYPE_MIC//选择当前耳机的角色(也就是上面提到的B、C)
bool chargestore_set_tws_remote_info(u8 *data, u8 len)
{
    u8 i;
    bool ret = true;
    int w_ret = 0;
    u8 remote_addr[6];
    u8 common_addr[6];
    u8 local_addr[6];

    CHARGE_STORE_INFO *charge_store_info = (CHARGE_STORE_INFO *)data;
    if (len > sizeof(CHARGE_STORE_INFO)) {
        log_error("len err\n");
        return false;
    }
    //以下,新增
    u8 *mutil_data = (u8 *)&charge_store_info->search_aa;
    if(mutil_data[0] == TWS_TYPE_EAR && charge_store_info->local_channel == 'L' && CURR_TWS_TYPE == TWS_TYPE_MIC){
        //本机是mic,跟左耳进测试盒配对,复制左耳的local mac和remote mac
        //左耳的local mac --> mic的local mac
        //左耳的remote mac --> mic的remote mac
        if (sizeof(local_addr) != syscfg_write(CFG_TWS_LOCAL_ADDR, charge_store_info->tws_local_addr, sizeof(local_addr))) {
            w_ret = -1;
        }
        if (sizeof(remote_addr) != syscfg_write(CFG_TWS_REMOTE_ADDR, mutil_data + 1, sizeof(remote_addr))) {
            w_ret = -1;
        }
        goto __pair_over;
    }
    if(mutil_data[0] == TWS_TYPE_EAR && charge_store_info->local_channel == 'R' && CURR_TWS_TYPE == TWS_TYPE_MIC){
        //本机是mic,跟右耳进测试盒配对,复制右耳的remote mac和local mac
        //右耳的local mac --> mic的remote mac
        //右耳的remote mac --> mic的local mac
        if (sizeof(local_addr) != syscfg_write(CFG_TWS_REMOTE_ADDR, charge_store_info->tws_local_addr, sizeof(local_addr))) {
            w_ret = -1;
        }
        if (sizeof(remote_addr) != syscfg_write(CFG_TWS_LOCAL_ADDR, mutil_data + 1, sizeof(remote_addr))) {
            w_ret = -1;
        }
        goto __pair_over;
    }
    if(mutil_data[0] == TWS_TYPE_MIC){
        //耳机跟mic配对,不操作,直接报成功
        goto __pair_over;
    }
    //以上,新增
    //set remote addr
    syscfg_read(CFG_TWS_REMOTE_ADDR, remote_addr, sizeof(remote_addr));


    if (memcmp(remote_addr, charge_store_info->tws_local_addr, sizeof(remote_addr))) {
        ret = false;
    }

    //新增:正常的对耳配对,记录remote地址
    void bt_set_tws_remote_addr(u8 *addr);
    bt_set_tws_remote_addr(charge_store_info->tws_local_addr);

    if (sizeof(remote_addr) != syscfg_write(CFG_TWS_REMOTE_ADDR, charge_store_info->tws_local_addr, sizeof(remote_addr))) {
        w_ret = -1;
    }

    bt_get_tws_local_addr(local_addr);


#if (CONFIG_TWS_COMMON_ADDR_SELECT != CONFIG_TWS_COMMON_ADDR_USED_LEFT)
    //set common addr
    syscfg_read(CFG_TWS_COMMON_ADDR, common_addr, sizeof(common_addr));

    for (i = 0; i < sizeof(common_addr); i++) {
        if (common_addr[i] != (u8)(local_addr[i] + charge_store_info->tws_local_addr[i])) {
            ret = false;
        }
        common_addr[i] = local_addr[i] + charge_store_info->tws_local_addr[i];
    }

    if (sizeof(common_addr) != syscfg_write(CFG_TWS_COMMON_ADDR, common_addr, sizeof(common_addr))) {
        w_ret = -3;
    }
#endif

#ifndef CONFIG_NEW_BREDR_ENABLE
    if (__this->channel == 'L') {
        if (tws_le_get_connect_aa() != tws_le_get_pair_aa()) {
            ret = false;
        }
        tws_api_set_connect_aa_allways(tws_le_get_pair_aa());
    } else {
        if (tws_le_get_connect_aa() != charge_store_info->pair_aa) {
            ret = false;
        }
        tws_api_set_connect_aa_allways(charge_store_info->pair_aa);
    }
#endif
__pair_over://新增
    if (__this->testbox_status && (0 == w_ret)) {
        u8 cmd = CMD_BOX_TWS_REMOTE_ADDR;
        chargestore_api_write(&cmd, 1);
        __this->tws_paired_flag = 1;
        /* r_printf("tws_paired_flag=%d\n",__this->tws_paired_flag ); */
    }
    return ret;
}

u16 chargestore_get_tws_remote_info(u8 *data)
{
    u16 ret_len = 0;
    u16 device_ind;
    CHARGE_STORE_INFO *charge_store_info = (CHARGE_STORE_INFO *)data;
#if TCFG_USER_TWS_ENABLE
    bt_get_tws_local_addr(charge_store_info->tws_local_addr);
    if (__this->keep_tws_conn_flag) {
        memcpy(charge_store_info->tws_mac_addr, bt_get_mac_addr(), 6);
    } else {
        bt_get_vm_mac_addr(charge_store_info->tws_mac_addr);
    }
#ifndef CONFIG_NEW_BREDR_ENABLE
    charge_store_info->search_aa = tws_le_get_search_aa();
    charge_store_info->pair_aa = tws_le_get_pair_aa();
#endif
    //以下,新增
    u8 *mutil_data = (u8 *)&charge_store_info->search_aa;
    mutil_data[0] = CURR_TWS_TYPE;
    void bt_get_tws_remote_addr(u8 *addr);
    bt_get_tws_remote_addr(mutil_data + 1);
    //以上,新增

#if (CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_AS_LEFT_CHANNEL) \
    ||(CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_AS_RIGHT_CHANNEL) \
    ||(CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_EXTERN_DOWN_AS_LEFT) \
    ||(CONFIG_TWS_CHANNEL_SELECT == CONFIG_TWS_EXTERN_UP_AS_LEFT)
    charge_store_info->local_channel = tws_api_get_local_channel();
#else
    charge_store_info->local_channel = 'U';
#endif
    charge_store_info->device_ind = bt_get_tws_device_indicate(NULL);
    if (__this->testbox_status) {
        charge_store_info->reserved_data = 0;
    } else {
        charge_store_info->reserved_data = chargestore_crc8(data, sizeof(CHARGE_STORE_INFO) - 2);
    }
#else
    bt_get_vm_mac_addr(charge_store_info->tws_mac_addr);
#endif
    ret_len = sizeof(CHARGE_STORE_INFO);

    return ret_len;
}

  1. 单耳连接了手机的情况下发起TWS配对 普通SDK参考:
    AC700N软件问题整理.doc 第86点
    可视化SDK:
    连接了手机的那个耳机执行:
void tws_api_set_pair_code(u16 pair_code);//指定配对用的配对码
int tws_api_wait_pair_when_phone_connect(int timeout_ms);//timeout_ms:0是不超时,非0是指定等待ms

没连手机的耳机执行:

void tws_api_set_pair_code(u16 pair_code);//指定配对用的配对码
int tws_api_auto_pair(int timeout);//timeout:0是不超时,非0是指定等待ms

例:

//如果使用tws_api_set_pair_code重新设置了配对码,则需要左右耳都执行
void tws_enter_pair_mode(void)
{
    tws_api_set_pair_code(0x1234);
    if(tws_api_get_tws_state() & TWS_STA_PHONE_CONNECTED){
        tws_api_wait_pair_when_phone_connect(5000);
    }
    else{
        tws_api_auto_pair(5000);
    }
}
  1. tws连接成功后,修改TWS的声道 可视化SDK:
//channel:'L'、'R'
syscfg_write(CFG_TWS_CHANNEL, &channel, 1);
tws_api_set_local_channel(channel);
//如果当前在播放音乐,还要修改音频声道
if(g_a2dp_player) {
    a2dp_player_set_audio_channel(g_a2dp_player);
}
  1. tws连接成功后,解除tws配对
tws_api_remove_pairs();

12、主动进行TWS主从切换

添加下面的代码,调用user_start_role_switch();进行主从切换
普通SDK接口

//非通话状态下用
void tws_conn_switch_role();
//通话状态下用
void test_esco_role_switch(u8 flag);

void tws_role_switch_api(void)
{
    if (get_call_status() != BT_CALL_HANGUP) {
        test_esco_role_switch(1);
    }
    else{
        tws_conn_switch_role();
    }
}

static int role_sw_check_timer = 0;
void user_ctrl_tws_role_switch(void *p)
{
    if(role_sw_check_timer){
        sys_timer_del(role_sw_check_timer);
        role_sw_check_timer = 0;
    }
    printf("role_switch_check");
    if(app_var.goto_poweroff_flag){
        puts("goto_poweroff ing\n");
        return;
    }
    if(get_tws_phone_connect_state() == 0 || get_tws_sibling_connect_state() == 0){
        puts("phone or tws disconnected\n");
        return;
    }
    if(tws_api_get_role() == TWS_ROLE_SLAVE){
        puts("TWS_ROLE_SLAVE\n");
        return;
    }
    int deal_state = tws_api_get_tws_state();
    if (bt_is_sniff_close()) {
        user_send_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);
        role_sw_check_timer = sys_timer_add(NULL, user_ctrl_tws_role_switch, 500);
    } else {
        tws_role_switch_api();
    }
}

void user_start_role_switch(void)
{
    user_ctrl_tws_role_switch(NULL);
}

可视化SDK接口

void tws_api_role_switch();
static int role_sw_check_timer = 0;
void user_ctrl_tws_role_switch(void *p)
{
    if(role_sw_check_timer){
        sys_timer_del(role_sw_check_timer);
        role_sw_check_timer = 0;
    }
    printf("role_switch_check");
    if(app_var.goto_poweroff_flag){
        puts("goto_poweroff ing\n");
        return;
    }
    int deal_state = tws_api_get_tws_state();
    if((deal_state & TWS_STA_PHONE_DISCONNECTED) || get_tws_sibling_connect_state() == 0){
        puts("phone or tws disconnected\n");
        return;
    }
    if(tws_api_get_role() == TWS_ROLE_SLAVE){
        puts("TWS_ROLE_SLAVE\n");
        return;
    }
    if (bt_is_sniff_close()) {
        bt_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);
        role_sw_check_timer = sys_timer_add(NULL, user_ctrl_tws_role_switch, 500);
    } else {
        tws_api_role_switch();
    }
}

void user_start_role_switch(void)
{
    user_ctrl_tws_role_switch(NULL);
}

13、TWS根据信号强弱进行主从切换(非可视化仅播歌状态下支持)的切换阈值修改

添加下面的函数到SDK的bt_tws.c文件中,底层会调用,先看看a2dp_role_switch_check_debug打印的rssi情况,再去修改a2dp_role_switch_check_rssi和tws_esco_rs_rssi_check函数中的判断条件。
调整判断条件可以让耳机更容易或者更难进行主从切换

u8 a2dp_role_switch_check_debug(int master_rx_num, int slave_rx_num, int master_rssi, char slave_rssi, int tws_rssi, int msec)
{
#if 0//def CONFIG_DEBUG_ENABLE//需要查看实时信号强度时打开,用于调试切换阈值
    static int rx_rssi_print_cnt = 0;

    if (++rx_rssi_print_cnt == 10) {

        rx_rssi_print_cnt = 0;

        printf("rx: %d, %d, rssi: %d, %d, %d, msec:%d\n",

               master_rx_num, slave_rx_num,

               master_rssi, slave_rssi, tws_rssi, msec);

    }

    return 1;
#endif
    return 0;
}

u8 a2dp_role_switch_check_rssi(int master_rssi, char slave_rssi)
{
    if (master_rssi < -58 && slave_rssi > -70 && master_rssi + 13 <= slave_rssi) {
        return 1;//返回1进行主从切换
    }

    return 0;
}

bool tws_esco_rs_rssi_check(char m_rssi, char s_rssi)
{
    static char old_s_rssi, old_m_rssi;

    if (((old_s_rssi - s_rssi) < 20 && (old_s_rssi - s_rssi) > -20) && ((old_m_rssi - m_rssi) < 20 && (old_m_rssi - m_rssi) > -20)) {
        if (m_rssi < -60 && s_rssi > -70 && m_rssi + 25 <= s_rssi) {
            return TRUE;
        }
    }
    old_s_rssi = s_rssi;
    old_m_rssi = m_rssi;

    return FALSE;
}

14、默认不配对 tws ,按键手动自由配对与删除

该应用需要关闭主从自动切换。
可视化 SDK:
添加 APP_MSG_TWS_START_REMOVE_PAIR // 如果没有配对则发起配对,如果配对了则取消配对

static int dual_conn_app_event_handler(int *msg) {
    // ..... 此处省略其他代码
    case APP_MSG_TWS_START_REMOVE_PAIR:
        if (msg[1] == APP_KEY_MSG_FROM_TWS) {
            break;
        }
        g_printf("tws_api_get_tws_state:%x", tws_api_get_tws_state());
        if ((tws_api_get_tws_state() & TWS_STA_TWS_PAIRED)
            && (tws_api_get_tws_state() & TWS_STA_SIBLING_CONNECTED)) {
            g_printf("tws_api_remove_pairs");
            tws_api_remove_pairs();
        } else { // 没有连接也要发起配对
            g_printf("tws_api_wait_pair_by_code");
            tws_api_search_sibling_by_code();
            if (g_dual_conn.timer) {
                sys_timeout_del(g_dual_conn.timer);
            }
            g_dual_conn.timer
                = sys_timeout_add(NULL, msg_tws_start_pair_timeout, TCFG_TWS_PAIR_TIMEOUT * 1000);
        }
        break;
     // ..... 此处省略其他代码
}

配对连接后,主从关系控制,(注:A、B 两个都连接了手机情况下禁止配对,或者断开其中一台后再配对):

/* 从机跑这里,返回 ture 为需要进行 role_switch 变成主机 */
bool tws_host_role_switch(int remote_info, int local_info)
{
    g_printf("remote_info:0x%x local_info:0x%x", remote_info, local_info);

    if (remote_info & TWS_STA_PHONE_CONNECTED && !(local_info & TWS_STA_PHONE_CONNECTED)) {       /*对方连手机, 本地没连手机*/
        g_printf("remote connect phone, local not connect phone");
        return FALSE;
    } else if (!(remote_info & TWS_STA_PHONE_CONNECTED) && (local_info & TWS_STA_PHONE_CONNECTED)) { /*对方没连手机,本地连手机*/
        g_printf("remote not connect phone, local connect phone");
        return TRUE;
    } else if (remote_info & TWS_STA_PHONE_CONNECTED && (local_info & TWS_STA_PHONE_CONNECTED)) { /*对方连手机,本地连手机, 需要断开*/
        //do detach, 两个设备分别都连接了手机需要断开
        g_printf("remote connect phone, local connect phone");
        return FALSE;
    }
    if (remote_info & TWS_STA_LE_AUDIO_CONNECTED) { //对方已连手机le_audio
        g_printf("remote is le_audio connected");
        return FALSE;
    }
    if ((local_info & TWS_STA_HAVE_PAGE_INFO) && !(remote_info & TWS_STA_HAVE_PAGE_INFO)) {
        //tws超时断开同时与手机也超时断开,再次tws连接上后,还需要继续回连手机,有回连信息端要继续做回主机
        g_printf("local have page info");
        return TRUE;
    }
    return FALSE;

}

维持设备一直处于 tws wait pair 状态:
BT_STATUS_FIRST_CONNECTED 事件后打开 tws wait pair
image TWS_EVENT_CONNECTION_DETACH 事件后,即 tws 断开后打开 tws wait pair:
image

本文档包含以下子页面

Prev
蓝牙分析仪(EllisysBluetoothAnalyzer)抓包例程
Next
APP自定义按键的demo