当前位置:首页 > 消费电子 > 消费电子
[导读]TWS双耳连不上、单耳出声、手机里冒出两个设备条目,根因大多不是射频芯片坏了,而是左右耳内部没同步成一对。 手机只认主耳,副耳靠对耳链路跟主耳对齐状态;只要配对表、角色、时钟、重连逻辑有一处乱,双耳就失步。C代码里要把“配对同步”和“手机连接”切成两层。


TWS双耳连不上、单耳出声、手机里冒出两个设备条目,根因大多不是射频芯片坏了,而是左右耳内部没同步成一对。 手机只认主耳,副耳靠对耳链路跟主耳对齐状态;只要配对表、角色、时钟、重连逻辑有一处乱,双耳就失步。C代码里要把“配对同步”和“手机连接”切成两层。

一、上电先选主副耳

出盒别两只都去抢手机连接,先在对耳私有通道(NFMI/2.4G私有BLE/BR-EDR ACL)里选举:

typedef enum { ROLE_NONE, ROLE_MASTER, ROLE_SLAVE } tws_role_t;


tws_role_t elect_role(int batt_pct, int rssi_to_phone){

   /* 电量高、离手机近的当主耳 */

   if(batt_pct >= 50 && rssi_to_phone > -75)

       return ROLE_MASTER;

   return ROLE_SLAVE;

}

工程做法一般是交换:电量、RSSI、链路质量、是否先出盒。 规则要加迟滞——两耳差距小的时候沿用上一次角色,别每毫秒切一次,否则主副来回跳就是断连源头。

二、配对信息双耳同步

最常见“两个蓝牙条目”就是左右耳存了不一致的bonding key。 主耳和手机完成配对后,把LTK、IRK、设备地址、服务UUID同步给副耳:

typedef struct {

   uint8_t  phone_addr[6];

   uint8_t  ltk[16];

   uint8_t  irk[16];

   uint8_t  bonded;

} tws_pair_info_t;


void master_sync_pairinfo(const tws_pair_info_t*p){

   tws_peer_send(PEER_PAIR_SYNC, (uint8_t*)p, sizeof(*p));

}


void slave_on_peer_msg(uint8_t cmd, uint8_t*buf, int len){

   if(cmd==PEER_PAIR_SYNC && len==sizeof(tws_pair_info_t)){

       memcpy(&g_pair, buf, len);

       bt_stack_load_bond(&g_pair);   // 副耳装入同一份密钥

   }

}

副耳不自己走配对流程,只等主耳派发;这样手机侧看到的是“一个设备”,不会出两个条目。

三、主耳建链,副耳监听同步

经典BR/EDR A2DP原生是一对一,常见架构是手机↔主耳建ACL,副耳走监听或主耳选择性转发补齐丢包。 控制面(音量、播放状态、电量上报)走对耳链路:

void tws_connect_flow(void){

   if(local_role()==ROLE_MASTER){

       bt_connect_phone(g_pair.phone_addr);

   }else{

       peer_wait_master_ready();   // 副耳不主动连手机

       audio_sync_clock(base_clock());

   }

}

副耳千万别在bt_connect_phone里也调一次——双连模式要看芯片是否支持TWS+/双路直连,否则就是“两个设备”。

四、断线重连状态机

单耳掉线后无脑重连=重连风暴,控制器假死、功耗炸。 区分“用户主动断开”和“异常掉线”:

typedef enum { LINK_IDLE, LINK_ON,

              LINK_RECONN, LINK_USER_OFF } link_st_t;


#define MAX_RETRY 5

static int retry;

static uint32_t delay_ms = 1000;


void on_phone_disconnect(int reason){

   if(reason==USER_DISCONNECT){

       link_st = LINK_USER_OFF; return;

   }

   link_st = LINK_RECONN;

   retry = 0; delay_ms = 1000;

   schedule_reconnect();

}


void schedule_reconnect(void){

   if(retry++ >= MAX_RETRY){ link_st=LINK_IDLE; return; }

   tws_timer(delay_ms, [](){

       if(phone_in_range())            // 先确认可见

           bt_connect_phone(g_pair.phone_addr);

       delay_ms = delay_ms*2>30000 ? 30000 : delay_ms*2;

   });

}

退避1/2/4/8/16s,超5次放弃等用户。 副耳不单独重连手机,只跟主耳重新同步;主耳重连成功再把新信道映射/时钟基准发给副耳。

五、主副切换不停流

主耳放回盒子/低电,副耳接管:

void on_master_in_case(void){

   if(local_role()==ROLE_SLAVE){

       local_role_set(ROLE_MASTER);

       bt_connect_phone(g_pair.phone_addr); // 用同一bond

       audio_continue();

   }

}

切换前提是对耳链路已把LTK和当前ACL状态同步过,否则副耳连手机会走“新配对”,手机弹配对框。

六、落坑清单

左右耳各自存bond→手机里两个条目

副耳也调bt_connect_phone→单耳抢占

断连while重试→重连风暴

主副角色无迟滞→每秒切换一次

出盒不重新选举→低电耳当主耳,很快掉

对耳通道不通就连手机→副耳永久沉默

用户点断开还自动重连→语义错误

时钟漂移不重同步→副耳音画错位

盒内不停重连→耗电发热

七、落地顺序

定义对耳私有通道(NFMI或BLE对端GATT)

上电交换电量/RSSI选主副,加迟滞

主耳配对,LTK/IRK同步副耳

主耳连手机A2DP/AVRCP,副耳监听

控制指令走对耳:音量/播放/电量

断连状态机分USER_OFF/RECONN

指数退避+扫描到再连

主耳入盒→副耳用同bond接管

盒内清除pending_reconnect,复位角色

TWS稳定连接的本质不是“两只都连上手机”,是双耳先内部成组、再统一对手机出场。主耳管外链、副耳跟状态、对耳通道做同步、重连走退避——这四件事在C状态机里拆开,单耳掉线/两个蓝牙条目/反复重连基本就消失了。量产项目里80%的“连接不稳”都出在配对信息没双耳对齐,不是天线问题。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭