MTK8765Q平台关机充电卡在第一帧画面、同时电池完全无法充电,这类问题在嵌入式终端开发里非常典型,也特别容易让人一头雾水。现象看起来是“开机/充电流程死在最早期”,但只要把关机充电的完整链路拆开看,你会发现卡帧和不充电其实是两个独立又互相缠住的故障,排查思路一旦理清,解决起来并不玄学。这篇文章就从实际项目复盘的角度,把整个排查过程、根因定位、代码改动和硬件验证完整拆给你,适合正在调试MTK类平台充电功能的BSP工程师,也适合刚接手充电子系统、想搞懂关机充电流程的入门开发者。
1. 问题现象与初始现场
拿到问题机的时候,现象很明确:插上充电器,机器屏幕亮起,停在厂商Logo的第一帧画面,再也不往下走。放个半小时再摸机器,温热但不至于发烫,电池电量没有抬升的迹象,系统也没法通过按键唤醒到充电动画或正常开机。
1.1 先明确“卡帧”意味着什么
在MTK平台这种以Linux内核为底的系统里,关机充电的显示并不是一个“全屏应用”,而是分阶段渲染的。插上充电器后最早由BootROM启动预加载器,随后进入LK阶段,LK会初始化显示面板、点亮背光,画出第一张静态图——就是你看到的那个Logo画面。正常情况下,这个画面会持续几百毫秒到一两秒,然后进入内核,内核起来后由充电相关的服务接管屏幕,切换到充电百分比动画。
卡在第一帧画面,意即LK阶段的启动是成功的,至少显示驱动、背光、初始化内存这些环节都过了。但往后的路径被什么东西堵住,系统既没有继续引导内核,也没有退出LK进入下一阶段。这个观察非常重要,它直接排除了“硬件完全没电启动不了”的可能。
同理,“不能充电”也分两种:一种是充电器插着但电流完全没有流入电池,另一种是电池在充但是幅度极慢,看起来像没充。这两种情况的根因路径完全不同,前者多半是充电握手失败或通路被切断,后者则要重点怀疑电流设置、温控保护或电量计配置。
1.2 为什么这两个问题会同时出现
调试现场最容易犯的一个错,就是把“卡帧”和“不充电”当成同一个问题去查。实际上在关机充电场景里,二者往往互为因果。
如果充电通路异常,系统检测不到足够电流,就会一直停留在“等待稳定充电条件”的状态;而LK阶段如果认为充电条件不满足,它不会继续往下走,因为内核起来后如果电量过低、无充电保障,系统可能在几分钟内再次掉电关机,反而带来更差体验。于是Lk卡住画面等待,其实是设计上的一种保护行为。反过来,如果卡帧的根本原因是某个驱动在等待I2C应答超时,充电管理IC也可能一直没能被正确初始化,充电自然就停摆。
所以遇到这类组合故障,第一步不是急着改代码,而是分清层次:硬件充电链路有没有物理问题,LK阶段的状态机卡在哪一个判断条件,内核是否根本没进得去。这三个层次用一个串口日志就能拆得七七八八。
2. 用日志和寄存器先把故障范围钉死
这一节是整个排查过程的地基,也是最容易被忽略的环节。我见过不少同事拿到问题机直接开始翻物料、换充电IC,结果换了三片板子问题依旧,最后发现是LK状态机因为一个读不到的寄存器值在死循环。所以请先把工具备齐:串口线(最好是带调试口的工程板)、逻辑分析仪、可调电源,以及一块能确认是好的同型号主板。
2.1 串口日志里如何区分LK阶段和内核阶段
MTK平台的LK日志和Kernel日志通常会打在不同的物理串口或同一串口的不同前缀段。连接串口后插上充电器,重点观察日志停在哪一行。正常的关机充电流程中,插充电器后会看到LK阶段的充电检测打印,比如vbat、vbus、充电IC的ID读取等;随后会出现Kernel解压启动的信息。
如果日志的最后停在一句类似charger detection fail或者反复出现reboot原因带入的打印,基本说明LK阶段没完成充电条件判断。如果日志里已经出现了内核早期的banner,却仍然停在第一帧,说明显示画面是内核节点渲染的,系统不是卡在LK。这两个方向后续排查的侧重点完全不同,前者继续查LK的充电状态机,后者转去查内核drivers的初始化顺序。
现场记录日志时,建议用带时间戳的串口工具连续抓取至少30秒,因为有些板子在反复软复位,每个周期只有几百毫秒的打印,不仔细看会误以为日志卡住了。
2.2 用I2C实测充电IC到底醒没醒
日志只能判断软件流程走到哪里,硬件上充电IC有没有正常起来,必须用I2C读写来确认。大多数MTK平台的关机充电会使用一颗独立的充电管理芯片,比如常见的中功率快充IC或PMIC内部集成的充电路径。先在原理图上找到它的I2C地址和供电脚,再在LK阶段启动的早期代码里临时加打印,把读到的寄存器原始值打出来。
具体做法是在LK的充电检测函数初始化处,对充电IC的Device ID寄存器做一次读操作。如果返回0xFF或者超时NoAck,说明I2C通信都不通,问题直接在硬件链路或芯片供电上。如果能读到ID,但状态寄存器显示VBUS不在位或充电使能位未置起,那就继续查PMIC对VBUS的检测、充电器插入识别电阻等外围。
这一步看似基础,却能节省后面大量猜谜时间。记住一个原则:如果I2C不通,先不要改任何软件配置,回头检查芯片的复位引脚、供电电压、I2C总线上拉和地址冲突。寄存器能读再谈软件流程。
3. 充电路径和状态机:为什么会停在第一帧
把故障范围压缩到LK阶段之后,接下来要面对的就是关机充电状态机本身。MTK的LK在充电场景下并不是一笔糊涂账,它遵循一条非常明确的线路:充电器插入检测、VBUS稳定判定、电池电压与温度读取、选择充电模式、初始化充电IC、显示反馈。
3.1 充电器插入检测与VBUS判定
现在很多终端用的都是Type-C接口,别忘了还有一个CC检测芯片或者PMIC内部的CC检测逻辑。如果CC检测电阻配置不对,或者检测脚被拉死,系统根本不会认为有充电器插入。我遇到过一台机器,现象完全吻合卡第一帧不充电,查到最后是Type-C座子的CC1/CC2到检测脚之间的滤波电容虚焊,导致电平抖动,系统反复进入“插入-拔出”的死循环。
判断方法很简单:看PMIC的VBUS中断寄存器,插上充电器后该位是否稳定置位。如果看到该位在0和1之间跳变,基本就是物理链路接触不良或者检测电路上的阻容参数有问题,不要纠结软件了。另一个常见坑是充电器的协议握手,部分快充充电器默认不会直接输出5V,而是等待DP/DM上的手信号才抬升电压。如果LK阶段的充电路径不支持或者没有触发那一刻的handshake,VBUS检测也会不通过。
3.2 温度与电压阈值保护让系统“不敢充”
即便VBUS正常、充电器也识别了,如果电池温度读出来超出范围,或者电池电压低到某个预设值以下,充电IC也会拒绝开启充电通路。MTK平台这类保护参数通常放在LK的充电配置里,常见的有:
- 充电电压下限:比如低于3.0V时,先用小电流预充
- 温度范围:一般0到45度,超出就停止
- 充电安全定时器:连续充电超过某个时间,强制断开
如果是温度读出来异常,基本可以锁定在NTC热敏电阻的检测电路上。NTC通常挂在电池包里,通过一个分压电路连到PMIC或充电IC的TS脚。我调试时常用一个土办法:用可调电阻模拟NTC在不同温度下的阻值,直接接到TS脚上,看看系统读数是否跟着变化。如果读数固定在一个异常值,就得检查分压电阻是否贴错、NTC的料号是否选型错误。
还有一个小细节:电池电压采样脚和电池正极之间的走线电阻。有些低成本的板子采样线走得很细很长,导致实际电池电压3.6V,芯片读回来只有3.4V,直接触发低压保护。这个问题在低电量关机充电的场景下特别致命。
3.3 低电量“死区”导致卡帧不动的典型机制
有一个非常典型的场景值得单独拿出来说:电池电量低到关机之后,电压还会缓慢回弹一点。如果回弹值恰好在充电IC的唤醒阈值附近,就可能在“检测到电池存在”和“电压不足无法开启充电通路”之间死锁。
具体表现就是,LK读电池电压发现低于充电阈值,于是尝试给电池先做预充,但预充通路又因为没有满足某一路检测条件而无法导通;系统不会继续走正常开机流程,也不会显示充电动画,因为LK认为此时应该安静等待。表现出来就是插充电器亮第一帧Logo,然后一切静止。
这种场景的排查重点在于区分“预充条件”是什么。不同充电IC的预充启动条件差异很大,有的只需VBUS有效,有的还要求电池电压大于某个硬阈值(比如2.8V),有的要求电池温度在范围内。最直接的做法是查阅对应充电IC的数据手册里state machine那一章,把你板子上读到的所有关键寄存器值列出来,跟手册里的状态跳转条件逐一对照,一定会有某个不满足的条件被揪出来。
4. 修改代码与配置:从LK到内核的实践记录
经过上面的排查,硬件链路基本确认没有虚焊、短路、漏件的问题后,就可以进入纯软件修改环节。这里分享三个我在这个项目上真实动过的代码位置和修改思路,它们都属于那种“文档里不太会写,但调试时大概率要碰”的地方。
4.1 充电安全定时器引发的“假死”
很多充电IC内部都有一个安全定时器,防止电池因软件失控被无限充电。默认值通常是几个小时,但在关机调试阶段由于反复重启,累计充电时间很容易撞上这个定时器,充电IC会锁死充电通路,必须重新插拔充电器或者给IC复位才能解除。
如果在串口日志里发现了类似safety timer expired的字段,解决思路不是把定时器改成一个极大值,那会让量产产品失去保护。更合理的是在LK初始化阶段每次都重置这个定时器,而不是依赖IC的上一次状态;同时检查软件里是否有代码在异常分支提前返回,绕过了重置动作。我当时就是在一个提前返回的error path里,把charge_enable置位忘了写,导致每次走到低电量分支就跳过使能。
4.2 低温保护误触发导致不充电
有一块测试板低温保护频频误触发,查了一圈,发现是NTC分压电路的上拉电阻用了精度5%的普通电阻,温度读数偏差比较大。软件上无法彻底解决硬件精度问题,但可以在LK配置里把温度检测的滤波深度调大,连续多次读到超温才真正触发保护,避免瞬时毛刺误伤。另外也可以增加一个校准偏移量,把读到的ADC值映射到实际温度曲线上。
这种改动要注意:低温保护是安全项,不能为了提高充电成功率粗暴关掉。量产版本上最好保留保护功能,只在调试版本里放宽范围,并显著打印log,方便对比。
4.3 内核阶段充电驱动挂死导致系统的连锁反应
这个项目到后期还遇到一个情况:LK已经顺利通过,内核也启动了,但显示画面依然停在第一帧。查内核日志发现是charge driver在probe阶段尝试请求一个中断等待回应,而对应的硬件事件永远没来,驱动线程阻塞,连带开机动画服务无法加载。
这类问题我惯用的处理方法是给中断等待加超时保护。与其让驱动无限等一个外部事件,不如在超时后先用默认配置把充电IC初始化一遍,再异步去校正状态。系统的开机流程不应当被充电子系统的初始化完全阻塞,充电功能晚几百毫秒就绪完全可以接受,但开机卡死在第一帧是不可接受的。
5. 硬件侧排查要点:不要忽视示波器的作用
软件改了一大轮之后,如果问题依然复现,那就要回到硬件测量上。充电问题最终一定会反映在电压和电流的物理波形上,用示波器抓几个关键点,通常比纯看代码定位更快。
5.1 关键测量点与预期波形
重点是三个位置:VBUS输入、电池端电压、充电IC的开关节点。插上充电器的瞬间,正常板子的VBUS应该迅速抬升到5V左右并保持稳定;电池端电压则会缓慢上升,如果充电通路没开,电池端电压会维持原样甚至缓慢下跌;开关节点则能看到开关动作的方波。
用示波器抓波形的时候,记得把探头地线夹到尽量靠近测量点的地方,避免引入噪声。我看到太多人因为这个原因误判波形异常,白白浪费半天。另一个小技巧是使用直流耦合,把垂直档位放到1V/格附近,能够同时观察到信号中的直流电平变化和纹波形态。
5.2 电池连接器接触电阻的坑
还有一次很隐蔽的问题:电池连接器弹片因为氧化导致接触电阻偏大,正常电流下压降不明显,但关机充电这种大电流场景下,电池端电压被拉得很低,系统判定电池不在位。这种问题用万用表量静态电阻是量不出来的,必须带负载测。实际手法是给电池充电回路串一个电子负载,设置一个恒流,再看连接器两端的压降是否超过几十毫伏。
如果发现接触电阻偏大,先尝试清洗连接器触点并重新插拔几次。如果依旧偏大,可能要评估更换连接器方案。这个跟软件完全无关,但会伪装成“系统不充电”,直接把调试方向带偏。
6. 复盘与后续建议
这台机器的最终修复,实际上是多个小问题的叠加:Type-C检测脚滤波电容虚焊导致插入不稳定、电池NTC分压电阻精度不足导致低温误保护、以及LK异常分支漏写充电使能。三个问题单独拎出来都不算复杂,但它们交织在一起,就让现象变得非常具有迷惑性。
6.1 排查顺序是最大的效率杠杆
再复盘整个流程,我认为最值得分享的经验就是排查顺序。在这个案例里,如果一开始就埋头看LK状态机代码,不去用示波器抓VBUS波形、不去实测I2C通信,很可能要多花两三天才能定位到硬件问题。反过来,如果一上来就怀疑硬件,而忽略状态机中的软件死分支,也会绕远。
比较合理的顺序是先确认VBUS物理层正常,再通过I2C确认充电IC能与主控正常通信,然后看状态寄存器与预期是否一致,最后才回到软件代码里找逻辑漏洞。每一层都用实测数据说话,不要跳步。
6.2 对同类型平台项目的建议
给正在做类似终端项目的人几条实用建议:第一,原理图阶段就把充电检测相关信号留出测试点,量产的测试工装很难操作BGA底下的过孔。第二,LK阶段的充电日志要打全,特别是关键寄存器值,后期调试能省很多时间。第三,硬件和软件同事要一起看波形和日志,不要各查各的。最后,所有充电保护相关的阈值改动,一定要在需求文档里留记录,免得验证人员测到一半发现行为不一致,又花时间排查是不是版本问题。
说到底,关机充电这种看起来不起眼的功能,在项目开发最后阶段往往是最折磨人的。它牵涉到硬件电路、底层引导、内核驱动和系统服务,任何一个环节的缺陷都会被放大。但只要链路清晰、数据充分,绝大部分问题都能在几天内收敛。希望这篇记录能让你少踩几个坑。