11、USB电源管理与充电协议:USB供电标准(BC 1.2、PD、QC)、Android端充电状态检测(BatteryManager)、USB PD协商流程、OTG供电控制(GPIO与PMIC)
说到USB供电,很多做上层应用的同学可能觉得就是个“插电充电”的事。但做过底层你就会知道,这里面的水其实挺深的。我当年第一次调试快充协议时,就被BC 1.2和PD的握手时序折腾了好几个通宵。今天咱们就把这块彻底捋清楚。
11.1 USB供电标准:BC 1.2、PD、QC
先说说这三个最常见的标准。它们解决的核心问题其实都一样:如何让充电器与设备之间“商量”好,到底给多大电流。
11.1.1 BC 1.2(Battery Charging 1.2)
BC 1.2是最基础的协议,说白了就是靠D+和D-两根数据线上的电压来“报暗号”。
- 标准下行端口(SDP):D+/D-都是0V,最大只能给500mA。电脑上的普通USB口就是这种。
- 充电下行端口(CDP):D+/D-短接,最大1.5A。支持数据传输的同时还能快充。
- 专用充电端口(DCP):D+/D-短接,最大1.5A。但不能传数据,就是纯充电口。
核心要点:BC 1.2的检测过程是硬件自动完成的。Android内核里有个叫 charger 的驱动模块,它会读取USB PHY寄存器里的D+/D-状态,然后上报给上层。
我记得有一次,客户反馈说手机插上充电器不识别。查了半天,发现是D+引脚虚焊,导致硬件一直误判成SDP模式。嗯,这种问题用示波器看波形最直接。
11.1.2 USB PD(Power Delivery)
PD协议就高级多了。它通过CC(Configuration Channel)线来通信,可以协商出最高240W的功率(48V/5A)。PD 3.0还加入了PPS(可编程电源),能精细调节电压。
PD的协商过程我后面会详细讲。这里先记住一个关键点:PD需要一颗专门的芯片(比如TI的TPS6598x)或者集成在Type-C控制器里。不是随便一个MCU就能跑的。
11.1.3 QC(Quick Charge)
QC是高通的私有协议,目前主流是QC 3.0/4+。QC 2.0靠D+/D-电压组合来“讨价还价”,QC 3.0则支持以200mV为步进连续调压。
QC 4+其实已经兼容了USB PD,所以现在很多新手机都直接走PD了。但老设备上QC还是很常见的。
| 协议 | 通信方式 | 最大功率 | 典型场景 |
|---|---|---|---|
| BC 1.2 | D+/D-电压 | 7.5W (5V/1.5A) | 老旧设备、电脑USB口 |
| USB PD 3.0 | CC线 BMC编码 | 240W (48V/5A) | 笔记本、旗舰手机 |
| QC 3.0 | D+/D- 连续调压 | 36W (12V/3A) | 高通平台中端机 |
11.2 Android端充电状态检测(BatteryManager)
上层App怎么知道当前在不在充电?充得快不快?答案就是 BatteryManager。
这个类其实是个系统服务,它通过监听 Intent.ACTION_BATTERY_CHANGED 广播来获取实时状态。我建议你直接用 registerReceiver 注册一个动态广播接收器,别用静态注册——因为电池变化太频繁了,静态注册容易漏消息。
// 获取电池状态
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = context.registerReceiver(null, filter);
// 是否在充电
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL;
// 充电方式
int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_AC;
boolean wirelessCharge = chargePlug == BatteryManager.BATTERY_PLUGGED_WIRELESS;
// 当前电量(百分比)
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
float batteryPct = level / (float) scale;
避坑指南:EXTRA_PLUGGED 只能区分USB、AC和无线。它不能告诉你当前是PD快充还是QC快充。要获取更详细的充电协议信息,你得读 /sys/class/power_supply/usb/ 下的节点。比如 usb_type 文件里会写 "PD"、"QC" 或 "SDP"。
我曾经在调试一个充电App时,发现用 BatteryManager 读到的电压永远是5V。后来才明白,它报告的是电池端的电压,不是充电器输出的电压。想拿到真实的VBUS电压?得去读 /sys/class/power_supply/usb/voltage_now。
11.3 USB PD协商流程
PD协商,说白了就是充电器和设备之间的一场“商务谈判”。整个过程分三步:
- 发现对方:设备插上后,CC线上会有上拉/下拉电阻,双方检测到后就建立物理连接。
- 能力声明:充电器通过
Source_Capabilities消息告诉设备:“我能提供5V/3A、9V/3A、15V/2A这些档位”。 - 请求与确认:设备从中选一个,发
Request消息。充电器同意后回Accept,然后切换电压。
这里有个细节:PD协商是双向的。手机也可以作为电源给别的设备充电(比如反向充电)。这时候手机就变成了 Source,外设是 Sink。
注意:PD协商过程中,如果CC线断了或者接触不良,协议会回退到默认的5V/500mA。这就是为什么有些Type-C线质量差,插上去只能慢充。我建议你在产品中加一个 PD协商超时重试 机制,避免一次失败就永久降级。
在Android内核里,PD协议栈通常跑在 drivers/usb/typec/tcpm/ 目录下。核心文件是 tcpm.c,它实现了状态机。你可以通过 /sys/class/typec/ 下的节点来观察协商状态。
11.4 OTG供电控制(GPIO与PMIC)
OTG(On-The-Go)就是让手机能当主机用,比如插U盘、接鼠标。但这里有个供电问题:U盘需要5V供电,手机电池怎么给它供?
答案是通过PMIC(电源管理芯片)里的一个升压电路(Boost Converter),把电池的3.7V升到5V,然后从VBUS输出。
控制这个升压电路的开关,通常有两种方式:
- GPIO直控:系统里有一个GPIO连接到PMIC的
OTG_EN引脚。拉高就开启5V输出,拉低就关闭。 - PMIC寄存器控制:通过I2C读写PMIC的某个寄存器位来使能OTG。比如高通PM8998的
OTG_EN_BIT在寄存器0x1041里。
在Android内核里,OTG的使能逻辑通常在 drivers/usb/dwc3/ 或者 drivers/usb/gadget/ 里。当检测到OTG ID引脚被拉低(比如插了OTG线),内核就会调用PMIC驱动来开启5V输出。
// 伪代码:GPIO控制OTG供电
static int otg_power_on(struct device *dev) {
struct gpio_desc *otg_en_gpio;
// 获取GPIO
otg_en_gpio = gpiod_get(dev, "otg-en", GPIOD_OUT_LOW);
if (IS_ERR(otg_en_gpio))
return PTR_ERR(otg_en_gpio);
// 拉高,开启5V输出
gpiod_set_value(otg_en_gpio, 1);
msleep(10); // 等待升压稳定
return 0;
}
static int otg_power_off(struct device *dev) {
struct gpio_desc *otg_en_gpio;
otg_en_gpio = gpiod_get(dev, "otg-en", GPIOD_OUT_LOW);
if (IS_ERR(otg_en_gpio))
return PTR_ERR(otg_en_gpio);
// 拉低,关闭5V输出
gpiod_set_value(otg_en_gpio, 0);
return 0;
}
关键提醒:OTG供电不是开了就完事了。你得监控VBUS上的电流,如果超过500mA(或者你的设计上限),要立刻关断OTG,否则PMIC会过热甚至烧毁。我见过一个案例,就是因为接了功耗过大的USB风扇,没做限流保护,结果PMIC直接冒烟了。
另外,OTG模式下电池耗电很快。我建议你在驱动里加一个 低电量自动关闭OTG 的逻辑。比如电量低于10%时,强制关掉5V输出,优先保证手机自身能开机。
好了,这一章的内容就到这里。USB电源管理这块,说白了就是“协议握手”加“硬件控制”的组合拳。你只要把BC 1.2/PD/QC的检测逻辑搞懂,再把BatteryManager和sysfs节点用熟,大部分充电相关的问题都能搞定。OTG供电控制虽然看起来简单,但坑不少,尤其是限流和低电量保护,千万别省。