耳机充电与通信调试指南
📎 原始文档:https://www.kdocs.cn/l/cvPJzeJTq3Zu 耳机充电与通信调试指南
一、内置充电基本原理
- 充电基本流程 下图只是充电流程的抽象流程,不是完全流程,按照下图流程去看代码,基本可以覆盖充电检测的绝大部分情况,各个SDK也是写法上大同小异
ldo5v_in后,如果是在idle模式下,就会直接进入充电,如果不在idle,就会复位进idle再充电
ldo5v_off后,先关充电,如果支持拔出开机,就会切蓝牙,不支持就会走关机
ldo5v_keep后,先关充电,如果是智能仓或者测试盒连接,会return掉;如果拔出开机会走复位,不支持就会走关机
charge_start后,才会正式进入充电模式抽取电流
charge_full后,会关充电,然后分情况进关机
charge_close后,才会正式停止充电(注意,如果此时5V还在,有些SDK做法是会继续从5V端取电流给系统工作,主要看有没有以下判断)
1.1 智能仓充电流程
①入仓-开盖通信(返回电量)-关盖升压通信(充电&返回电量)-收到充满指令-延时-关机
注意:通信时高电平为3.3V左右,带通信的耳机关机是通信决定的,耳机返回电量100%后,由仓发关机命令,耳机收到指令后,延时指定时间,然后关机,耳机关机后,仓不能再发指令或者充电脚有电平变化,不然耳机会唤醒开机
1.2 非智能仓充电流程
①入仓-升压充电-耳机判满关充电-充电仓关升压(掉保持电压)-耳机关机
②入仓-升压充电-耳机判满关充电-充电仓不关升压(常5V)-耳机关机
注意:掉保持电压的仓,关机是在ldo5v_keep_delay_deal里面关的,而常5V的仓,关机是在eSystemConfirmStopStatus里面关的,如果关机的地方不对,容易造成充满开机等问题,具体见下面经典案例
另外,耳机充满后,要注意看charge_close里面有没有条件限制跑CHGBG_EN(0)和CHARGE_EN(0),如果没跑这两个寄存器,系统会从5V取电,对一些充满后会进行电流检测的仓,可能会有影响
2. 充电关键配置
2.1 电流与电压
//是否支持芯片内置充电
#define TCFG_CHARGE_ENABLE ENABLE_THIS_MOUDLE
//是否支持开机充电
#define TCFG_CHARGE_POWERON_ENABLE DISABLE
//是否支持拔出充电自动开机功能
#define TCFG_CHARGE_OFF_POWERON_NE ENABLE
/*充电截止电压可选配置*/
#define TCFG_CHARGE_FULL_V CHARGE_FULL_V_4199
/*充电截止电流可选配置*/
#define TCFG_CHARGE_FULL_MA CHARGE_FULL_mA_10
/*充电电流可选配置*/
#define TCFG_CHARGE_MA CHARGE_mA_50
TCFG_CHARGE_ENABLE:内置充电开关使能,默认开,不要关,如果使用外挂充电,则参考示例............;
TCFG_CHARGE_POWERON_ENABLE:开机充电,可以边充电边用耳机,默认不开;
TCFG_CHARGE_OFF_POWERON_NE:拔掉充电后,开机还是关机,看需求选择;
TCFG_CHARGE_FULL_V:截止电压,这个实质上是影响5V端和VBAT端的压差,从而影响电流,实际停止充电判断条件是截止电流,一般值的设置比电池的充满电压高一点,具体视电池规格和SDK的CHARGE_FULL_V_X这个宏的档位配置;
TCFG_CHARGE_FULL_MA:截止电流,实际停止充电的判断条件,具体视客户需求以及充电仓特性配置,一般不低于5ma;
TCFG_CHARGE_MA:充电电流,一般是1C,如50mah电池的1C就是配置50ma,如果支持超过1C按电池规格和需求配置。
2.2 充电仓相关配置
#define TCFG_CHARGESTORE_ENABLE DISABLE_THIS_MOUDLE //是否支持智能充点仓
#define TCFG_TEST_BOX_ENABLE 1
#define TCFG_CHARGE_CALIBRATION_ENABLE DISABLE_THIS_MOUDLE //是否支持充电电流校准功能
#if TCFG_AUDIO_ANC_ENABLE
#define TCFG_ANC_BOX_ENABLE 1 //是否支持ANC测试盒
#else
#define TCFG_ANC_BOX_ENABLE 0 //是否支持ANC测试盒
#endif/*TCFG_AUDIO_ANC_ENABLE*/
#define TCFG_CHARGESTORE_PORT IO_PORTP_00 //耳机和充点仓通讯的IO口
#define TCFG_CHARGESTORE_UART_ID IRQ_UART1_IDX //通讯使用的串口号
TCFG_CHARGESTORE_ENABLE:大部分情况下,有通信功能的就打开,没有就关闭;
TCFG_TEST_BOX_ENABLE:测试盒功能,连接测试盒会有用,默认不要关;
TCFG_CHARGE_CALIBRATION_ENABLE:充电校准功能,如果产线上没有这个测试项,以及客户没有对充电电流提出严格的控制范围,这个就默认关闭不要开;
TCFG_ANC_BOX_ENABLE:开了ANC会开这个,默认不用改
TCFG_CHARGESTORE_PORT:耳机与充电仓通信的IO口,不能改;
TCFG_CHARGESTORE_UART_ID:与仓通信用的UART1,智能仓用到,如果有其他外设使用UART,注意不要冲突。
2.3 充电检测参数
//过滤时间 = (filter*2 + 20)ms,
.ldo5v_off_filter = 100,
.ldo5v_on_filter = 50,
.ldo5v_keep_filter = 220,
.ldo5v_pulldown_lvl = CHARGE_PULLDOWN_200K, //下拉电阻档位选择
.ldo5v_pulldown_keep = 0, //在仓软关机是否要保持下拉(ldo5v_pulldown_en使能时有效)
.ldo5v_pulldown_en = 1,
ldo5v_off_filter:5V拔出的滤波时间,默认220ms;
ldo5v_on_filter:5V插入的滤波时间,默认120ms;
ldo5v_keep_filte:判断维持电压的滤波时间,默认460ms;
ldo5v_pulldown_lvl:充电脚下拉电阻档位,有200K、150K、100K和50K四个档位,一般默认200K就行,具体要向充电仓设计那边了解充电仓对负载的要求;
ldo5v_pulldown_keep:软关机时是否保持下拉,具体看充电仓那边对负载的要求和检测逻辑,(注意:复位如cpu reset时不会保持);
ldo5v_pulldown_en:下拉电阻总使能。
注意:上述以AC700v136版本为例,有些参数旧系列或者旧版本SDK没写出来,或者写法有些许差异,没有的按照AC700v136对应地方添加或者修改即可
struct port_wakeup ldoin_port = {
.edge = BOTH_EDGE, //唤醒方式选择,可选:上升沿\下降沿\双边沿
.both_edge = 1,
.filter = PORT_FLT_16ms,
.iomap = IO_LDOIN_DET, //唤醒口选择
};
edge:边沿唤醒的方式;
both_edge:是否需要双边沿唤醒;
filter:唤醒的滤波时间,硬件模块,只能配置对应enum中的可选项
iomap:对应的唤醒脚
各滤波参数关系如下图所示
注意点:
- PORT_FLT为硬件模块唤醒时间,是根据配置固定的,滤波时间过后会起中断ldoin_wakeup_isr,进入ldo5v_detect,关机唤醒情况下才有这个时间,开机状态下没有这个时间;
- ldo5v_XXX_filter是软件滤波时间,可以改,滤波时间过后才会发对应的IN、OFF或KEEP事件,要挂示波器看,充电仓出来的5V、维持电压和0V电压和电压值和持续时间是多少;
- 充电仓提供的维持电压一般上限取电池电压内都行,下限建议要1.2V以上。
2.4 充电常用寄存器
//充电模块使能
CHARGE_EN();
CHGGO_EN();
//下拉电阻档位和使能
L5V_RES_DET_S_SEL();
L5V_LOAD_EN();
注意:非特殊情况不直接操作寄存器
二、耳机通信基本原理
- 通信方式
1.1 UART方式通信
通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作UART,是一种异步收发传输器,是电脑硬件的一部分。它将要传输的资料在串行通信和并行通信之间加以转换。作为把并行输入信号转成串行输出信号的芯片,UART通常被集成于其他通讯接口的连结上。
耳机一般做的都是单线串口,TX和RX共用一根线,通信由充电仓发起,耳机接收回复
1.2 高低电平方式通信
这种仓通过高低电平通信,各家对电平的定义又不同,一般是高电平占多少ms,低电平占多少ms,多个高低电平组合为一个码,具体需要看充电仓的设计,通信实例见demo
【金山文档】 普通高低电平TWS耳机控制通讯
普通高低电平TWS耳机控制通讯.otl
2. 串口通信流程
chargebox_send:充电仓通过串口发送数据
uart_isr:串口中断,串口收到的数据先到这里(切记不要在中断里,做读写VM或者其他耗时较多的操作,这些在下面event_handle处理)
data_deal:数据分析接口,注意如果不是走杰理标准协议,是不会进这里的
event_to_user:事件发送接口,由于中断里不能占用太长时间,耗时多的操作发事件去handle处理
event_handle:消息处理函数,收到的指令实现、组包和回复等,具体在这里处理
earphone_reply:耳机消息发送接口
3. 串口相关接口
//串口中断
static void uart_isr(void)
//数据分析接口
void app_chargestore_data_deal(u8 *buf, u8 len)
//消息处理函数
int app_chargestore_event_handler(struct chargestore_event *chargestore_dev)
//事件发送接口
void chargestore_event_to_user(u8 *packet, u32 type, u8 event, u8 size)
//耳机消息发送接口
extern int chargestore_api_write(u8 *buf, u8 len);
...
4. 自定义串口通信
自定义串口通信的处理方式,完全可以参考默认的串口通信流程,核心思想是
①在uart_isr拦截数据,判断数据是否是自定义的,若不是则返回0执行原流程(避免影响测试盒等默认通信)
②如果数据是自定义的,就进入自己的处理流程,可以借助SDK默认的handle
自定义串口通信简单demo如下,demo借助默认的uart接口,波特率9600
【金山文档】 AC700v135_ldoin自定义串口通信demo_20230419
https://kdocs.cn/l/clL7r0Gq7iIb
充电仓发送:0xaa,0xbb,0xcc,0x01
耳机返回:0xee,0xdd,0xff,0x01,0x00,0x00
可用串口先模拟发送下
三、通信问题通用调试步骤
此步骤适用于debug耳机与充电仓的串口通信
- 调试前准备工作 ①一个可以有串口工具和改代码的电脑
②一套接好打印线的机子
③一个逻辑分析仪
2. 搭好测试环境
①先用公版SDK,开启智能充电仓的宏
②在uart_isr打印接收到的数据
③在自定义data_deal或者默认data_deal里打印数据
④在自定义event_handle或者默认event_handle里打印数据
3. 调试步骤
①先确认uart_isr有没有收到数据,如果没有收到数据,确认下有没有充电仓发或者接触好不好(有条件用逻辑分析仪看看数据)
②如果收到,确认下数据跟发送的对不对的上,对不上检查一下接触和波特率,波特率9600
③如果走默认充电仓指令,需要满足杰理通信协议,才会进入data_deal,否则需要走自定义
④确认data_deal里面数据接收对不对,一般参数传递没问题不会有错
⑤在data_deal里面发送事件给event_handle处理,默认协议用chargestore_api_write()发,自定义的参考上述demo
⑥如果event_handle收不到数,说明接口使用错误,或者程序其他地方有东西把线程占满了(先用公版对比看看改了哪里导致)
四、充电问题通用调试步骤
- 调试前准备工作 准备以下可能用到的东西
①一个可以有串口工具和改代码的电脑
②一套接好打印线的机子
③一个在pogopin正负端接好杜邦线的充电仓
④一个示波器
⑤一个直流稳压源
⑥一个或多个万用表(最好用福禄克大表)
⑦一个公版SDK
⑧与充电仓方面支持的沟通渠道
2. 搭好测试环境
①耳机端接好打印线,确保能正常打印
②先用公版SDK测试,若公版SDK有问题,直接下一步,若公版SDK没问题,先找改了哪里导致的,再进行下一步
③示波器挂在充电仓pogopin正负端
④打开与充电相关的打印
const char log_tag_const_v_APP_CHARGE AT(.LOG_TAG_CONST) = TRUE;
const char log_tag_const_i_APP_CHARGE AT(.LOG_TAG_CONST) = TRUE;
const char log_tag_const_d_APP_CHARGE AT(.LOG_TAG_CONST) = TRUE;
const char log_tag_const_w_APP_CHARGE AT(.LOG_TAG_CONST) = TRUE;
const char log_tag_const_e_APP_CHARGE AT(.LOG_TAG_CONST) = TRUE;
int __attribute__((weak)) eSystemConfirmStopStatus(void)
{
/* 系统进入在未来时间里,无任务超时唤醒,可根据用户选择系统停止,或者系统定时唤醒(100ms),或自己指定唤醒时间 */
//1:Endless Sleep
//0:100 ms wakeup
//other: x ms wakeup
if (get_charge_full_flag()) {
printf("Endless Sleep"); //打开这个
power_set_soft_poweroff();
return 1;
} else {
/* log_i("100 ms wakeup"); */
return 0;
}
}
static void ldo5v_detect(void *priv)
{
...
if (LVCMP_DET_GET()) {
putchar('X'); //打开这个
if (ldo5v_on_cnt < __this->data->ldo5v_on_filter) {
...
} else {
printf("ldo5V_IN\n"); //打开这个
...
}
} else if (LDO5V_DET_GET() == 0) {
putchar('Q'); //打开这个
if (ldo5v_off_cnt < (__this->data->ldo5v_off_filter + 20)) {
...
} else {
printf("ldo5V_OFF\n"); //打开这个
...
}
} else {
putchar('E'); //打开这个
if (ldo5v_keep_cnt < __this->data->ldo5v_keep_filter) {
...
} else {
printf("ldo5V_ERR\n"); //打开这个
...
}
}
}
static void charge_err_delay_deal(void *priv)
or
static void ldo5v_keep_delay_deal(void *priv)
{
...
//打开这个
printf("get_charge_online_flag:%d %d\n", get_charge_online_flag(), get_ldo5v_online_hw());
if (get_ldo5v_online_hw() && get_charge_online_flag()) {
power_set_soft_poweroff();
} else {
...
}
}
⑤按问题测试手法,复现问题,抓取波形和log,具体问题具体分析,见经典案例分析
五、充电问题经典案例
进入这里排查问题前,先把《充电问题通用调试步骤》看了,准备好先
- 非智能仓,耳机入仓,充电没反应 非智能仓,充电仓检测入仓一般两种方式,负载检测和电流检测,向充电仓的支持咨询确认
情况1:针对负载检测
①耳机端可选下拉电阻档位为200K、150K、100K和50K,一般配置200K可检测插入,如果不行,充电仓端若可调分压,则调节充电仓端分压;
情况2:针对电流检测
①抓取充电仓pogopin波形,看耳机入仓后,充电仓有没有升5V,如果没有升5V,则要先向充电仓的支持咨询下了
②如果有升5V,向充电仓的支持咨询下,电流检测从什么时间点开始?共检测多长时间?需要持续多长时间,示波器测量下5V持续的时间,充电流程默认5V起来唤醒x ms+5V IN的滤波120ms+复位起来充电耗时100~250ms
③如果入仓抽电流达不到电流检测标准,可以参考下面文档加大电流
【金山文档】 开始充电时加大充电电流方法
开始充电时加大充电电流方法.otl
④如果电流检测窗口很短,则尝试在检测到5V就开始加大电流
2. 非智能仓,耳机充满会开机
情况1:耳机先判断充满然后关机,充电仓后再掉保持电压
现象:耳机log出现charge_full,过几秒后出现Endless Sleep,但是此时用示波器看,充电仓充电脚两端5V电压还在,过一段时间后才会掉5V下来,
原因:因为充电仓充满掉电压时间比较长,耳机先关机了,后面充电仓掉电压的时候,就有一个边沿变化,把耳机唤醒了
解决办法:在eSystemConfirmStopStatus里面先不关机,等充电仓掉电压,跑流程在ldo5v_keep_delay_deal里面再关机

情况2:耳机关机的时候,充电脚电平被扯低
现象:充电仓充电脚电压从5V掉到维持电压,此时耳机log打印在ldo5v_keep_delay_deal里面关机了,随后波形出现一小段被拉低接近0V的波形,耳机被唤醒,而且延时关机没用,什么时候关机,就什么时候被拉低
原因:非智能仓默认开下拉,默认关机的时候是不操作下拉的,有些仓充电脚可能被拉低,导致耳机被边沿唤醒
解决办法:①ldo5v_pulldown_keep置0(没有这个变量就参考AC700v136修改),在仓关机关掉下拉
②在①的基础上,在charge start就把下拉关掉,视仓的特性而定,比如在5v off的地方把下拉开起来(避免影响出入仓检测)
具体还是跟充电仓特性有关,放的位置可能有差异,最好问清楚充电仓那边逻辑
情况3:充电仓判满检测的电流条件较低,判断大于电流阈值又开始充电
现象:耳机打印charge_full,充电仓5V还没掉,过一会儿又重新充电,且SDK上charge_close对关充电做了限制
原因:如上图所示,如果5V还在,charge_close没有真关电,芯片还会从ldoin取电,电流可能大于充电仓的检测阈值,如4ma,视充电仓检测机制而定,可能导致充电仓判断又充电
解决办法:注释掉限制,每次都把电关掉
3. 非智能仓,耳机关机后,充电仓那边睡眠功耗高
现象:耳机关机后,充电仓那边也进入睡眠,测睡眠功耗会比正常高几十个ua
原因:如果耳机关机保持了下拉,而仓充电脚有保持电压的话,下拉电阻是会耗电的
解决办法:ldo5v_pulldown_keep置0(没有这个变量就参考AC700v136修改),在仓关机关掉下拉
4. 智能仓,耳机充满会开机
现象:耳机关机后,又被唤醒开机,示波器看充电脚波形,充电仓发完关机码后,耳机延时后进入关机,随后充电仓又发码或者有电平变化
原因:耳机关机后,发码也相当于是有一个电平变化,就会边沿唤醒耳机
解决办法:尝试调节关机延时,并问清楚充电仓设计逻辑
case CMD_SHUT_DOWN:
log_info("shut down\n");
__this->power_status = 0;
__this->cover_status = 0;
__this->close_ing = 0;
chargestore_api_write(send_buf, 1);
//改下面时间
__this->shutdown_timer = sys_hi_timer_add(NULL, chargestore_shutdown_do, 1000);
break;