当前位置: 首页 > news >正文

app_power.c 学习笔记:从端口状态机到 DCDC 调压链路

app_power.c 学习笔记:从端口状态机到 DCDC 调压链路

本文是对 SDK 中app_power.c电源控制逻辑的一次学习整理,重点围绕pwr_process()drv_protocol_power_get()drv_pwr_adjust()三个函数,梳理从协议功率获取到 DCDC 调压执行的完整链路。


一、学习目标

今天主要复习并梳理app_power.c中电源控制主线,目标是搞清楚三个问题:

  1. 系统什么时候开电、关电、进入 5V、安全握手、快充调压?
  2. 协议层协商出来的电压、电流是怎么传到 power 层的?
  3. power 层拿到目标电压电流后,如何加工并下发给 DCDC?

最终形成的完整链路如下:

协议层 / 快充协议 ↓ drv_protocol_power_get() ↓ pwr_process() ↓ drv_pwr_adjust() ↓ dev_ctrl() ↓ DCDC driver ↓ 硬件执行

二、整体电源控制主线

这套 SDK 的电源控制不是某一个函数单独完成的,而是多个模块分层配合:

main.c ↓ 创建任务 / 初始化模块 ↓ app_port.c / app_prot.c ↓ 检测 Type-C / PD / 端口状态 ↓ app_power.c ↓ 根据端口状态控制电源 ↓ app_protect.c ↓ 异常保护 ↓ dev_ctrl() ↓ DCDC / MOS / ADC / GPIO 等底层驱动

可以用一句话概括:

Port 负责判断状态,Power 负责执行电源策略,Protect 负责异常保护,Driver 负责真正操作硬件。


三、pwr_process():app_power 的电源状态机核心

pwr_process()是今天学习的重点之一。它的核心作用是:

根据当前端口状态_status,决定当前端口应该关电、开 5V、等待握手、进入快充调压,还是保护关断。

函数开头会读取几个关键状态:

port_type_t_type=app_port_get_type(hpwr->id);port_state_t_status=app_port_get_status(hpwr->id);port_power_role_t_ppr=app_port_get_ppr(hpwr->id);port_protocol_t_protocol=app_port_get_protocol(hpwr->id);

这几个变量决定了后续动作:

变量作用
_status当前端口状态,例如空闲、拔出、握手、快充、保护挂起
_ppr当前电源角色,例如 Source、Sink、Bypass
_protocol当前协议类型,例如 PD、QC、FCP、SCP、UFCS、VOOC

pwr_process()的核心结构是:

switch(_status){casePORT_STATE_IDLE:break;casePORT_STATE_UNATTACHED:break;casePORT_STATE_VBUS_SAFE:break;casePORT_STATE_HANDSHAKE:break;casePORT_STATE_FAST_CHARGE:break;casePORT_STATE_ATTACHED_OCP_HANG:break;}

也就是说,它本质上是一个电源状态机执行函数


四、pwr_process()中几个重要状态

1.PORT_STATE_IDLE:空闲状态

空闲状态下,主要是清理当前端口的电源记录:

h_power[hpwr->id].curr_pwr.vol=0;h_power[hpwr->id].curr_pwr.curr=0;hpwr->vsafe5v_wait=false;

可以理解为:

端口当前没有电源动作 ↓ 清空电压、电流记录 ↓ 等待下一次插入或状态变化

2.PORT_STATE_UNATTACHED:拔出状态

当端口拔出时,代码会执行:

drv_pwr_vsafe0v(hpwr);app_port_set_status(hpwr->id,PORT_STATE_IDLE);

含义是:

设备拔出 ↓ 关闭 VBUS ↓ 卸放到 0V ↓ 状态回到 IDLE

这里的重点是:拔出后不能让 VBUS 残留高电压,所以要执行关断和卸放。


3.PORT_STATE_VBUS_SAFE:安全 5V 阶段

这个阶段不是快充,而是快充前的安全准备阶段。

Source 放电时

Source 表示当前端口对外供电。流程大致是:

进入 VBUS_SAFE ↓ DCDC 先建立安全 5V ↓ 检测 VBUS 是否达到安全范围 ↓ 打开 Source MOS ↓ 进入 HANDSHAKE

核心动作包括:

drv_pwr_vsafe5v_src(hpwr);app_power_src_sw_on(hpwr->id);app_port_set_status(hpwr->id,PORT_STATE_HANDSHAKE);

这里不是一上来就快充,而是先保证 5V 安全输出。

Sink 充电时

Sink 表示当前设备被外部充电器供电。流程大致是:

进入 VBUS_SAFE ↓ 准备接收外部 5V ↓ 确认 VBUS 没有异常高压 ↓ 打开 Sink MOS ↓ 进入 HANDSHAKE

核心动作包括:

drv_pwr_vsafe5v_snk(hpwr);app_power_snk_sw_on(hpwr->id);app_port_set_status(hpwr->id,PORT_STATE_HANDSHAKE);

总结一句话:

VBUS_SAFE 阶段的核心作用是先建立安全 5V,为后续协议握手和快充调压做准备。


4.PORT_STATE_HANDSHAKE:协议握手阶段

进入HANDSHAKE时,端口已经有了安全 5V,但协议不一定已经完成快充协商。

因此这里通常先使用默认 5V 和默认电流:

_power.vol=POWER_VSAFE_5V;

不同角色下默认电流不同:

if(_ppr==PPR_SRC_SINGLE){_power.curr=POWER_CURR_3A3;}elseif(_ppr==PPR_SNK_SINGLE){_power.curr=POWER_CURR_1A5;}

可以理解为:

HANDSHAKE 阶段 ↓ 快充还没真正开始 ↓ 先用默认 5V 维持供电或充电

这里也可能调用drv_pwr_adjust(),但目的不是进入快充,而是维持默认 5V 电源状态。


5.PORT_STATE_FAST_CHARGE:快充调压阶段

这个阶段才是真正进入快充调压。

流程如下:

进入 FAST_CHARGE ↓ 根据 _protocol 判断当前协议类型 ↓ drv_protocol_power_get() 获取协议目标电压电流 ↓ 如果获取成功 ↓ drv_pwr_adjust() 调整 DCDC 电压电流

核心代码逻辑是:

_ret=drv_protocol_power_get(hpwr,(port_protocol_t)(1<<i),_ppr,&_power);if(_ret==ERR_OK){drv_pwr_adjust(hpwr,_ppr,&_power);}

所以快充阶段的关键链路是:

协议层协商结果 ↓ drv_protocol_power_get() ↓ _power.vol / _power.curr ↓ drv_pwr_adjust() ↓ DCDC 调压调流

6. 保护挂起状态

例如:

casePORT_STATE_ATTACHED_OTP_HANG:casePORT_STATE_ATTACHED_UVP_HANG:casePORT_STATE_ATTACHED_OCP_HANG:{app_power_sw_off(hpwr->id);break;}

这些状态表示端口处于保护挂起:

状态含义
OTP_HANG过温保护挂起
UVP_HANG欠压 / 过放保护挂起
OCP_HANG过流 / 短路保护挂起

这说明app_protect.capp_power.c之间不是孤立的。

典型流程是:

app_protect 检测到异常 ↓ 设置端口状态为 xxx_HANG ↓ pwr_process() 看到 HANG 状态 ↓ 关闭电源开关

五、drv_protocol_power_get():从协议层获取目标功率

drv_protocol_power_get()的作用可以简单理解为:

问协议层一句话:当前应该使用几伏几安?

它不负责协议协商,也不直接调 DCDC,只负责把协议层已经得到的结果取出来,填到:

power->vol power->curr

函数接口如下:

staticerrno_tdrv_protocol_power_get(app_power_t*hpwr,port_protocol_t_prot,port_power_role_t_ppr,app_bus_power_t*power)

参数含义:

参数含义
hpwr当前端口的 power 句柄
_prot当前协议类型,例如 PD、QC、FCP、SCP、UFCS
_ppr当前电源角色,Source 或 Sink
power输出参数,用来保存协议目标电压、电流

1. PD 协议分支

PD 分支的核心流程是:

读取 PD 当前电源切换状态 ↓ 如果正在切换电压 / 电流,返回 ERR_ERR ↓ 如果状态可用,读取 PD 协商功率 ↓ 填入 power->vol / power->curr ↓ 根据 Source Fixed PDO 情况做电流补偿 ↓ 返回 ERR_OK

核心代码是:

_ps_status=ps_pd_power_transition_status_get(id2pdport(hpwr->id));if(_ps_status==PS_SRC_PRE_TRANSITION||_ps_status==PS_SNK_TRANSITION){returnERR_ERR;}ps_pd_power_transition_get(id2pdport(hpwr->id),(power_pd_t*)power);

其中:

ps_pd_power_transition_get(...,(power_pd_t*)power);

可以理解为:

从 PD 协议层拿出已经协商好的电压、电流 ↓ 写入 power 结构体

例如 PD 协商结果是 9V / 3A,则:

power->vol = 9000 power->curr = 3000

如果当前是 Source 且 PD 是 Fixed PDO,还会执行:

power->curr+=400;

这属于电流 offset 补偿,用来应对硬件限流误差,避免过早触发限流。


2. 非 PD 协议分支

QC、FCP、SCP、UFCS、VOOC 等协议,大多通过:

prot_mp_ctrl(id2mpport(hpwr->id),PROT_IO_CTRL_CMD_MP_DISCHRG_POWER_TRANSITION,(void*)power);

来获取协议目标电压电流。

可以理解为:

向多协议 MP 模块询问: 当前协议协商出来的是几伏几安?

不同协议还会根据自身特点做限流或补偿,例如:

协议处理逻辑
QC/QC30Sink 最大 3A,Source 电流加 offset 后封顶
AFC/FCP12V 档限制电流,避免功率过高
SCP高压档限制电流,低压档允许更大电流,接近恒功率控制
UFCS高压档电压补偿,电流小幅补偿
VOOC电流最大限制到 5.6A

六、drv_pwr_adjust():把目标功率加工成 DCDC 设置值

drv_pwr_adjust()的作用是:

把协议或策略给出的目标电压、电流,结合 Source/Sink 角色进行加工,然后通过dev_ctrl()下发给 DCDC。

它和drv_protocol_power_get()的区别是:

函数作用
drv_protocol_power_get()从协议层拿目标电压电流
drv_pwr_adjust()把目标电压电流加工成 DCDC 实际设置值

函数中有两个重要变量:

app_bus_power_t*_p_pwr_info=(app_bus_power_t*)arg;app_bus_power_t_pwr_info;

含义如下:

变量含义
_p_pwr_info原始目标值,来自协议或策略
_pwr_info加工后的实际 DCDC 设置值

七、Source 放电:电压往上补

Source 表示当前端口对外供电。

在 Source 放电时,主要考虑两个问题:

  1. 电流限流补偿
  2. IR Drop 线损补偿

1. 电流限流补偿

代码中会根据平台和电流档位对目标电流进行补偿,例如:

_pwr_info.curr=_p_pwr_info->curr;_pwr_info.curr+=500;

这类补偿的目的不是随意超协议输出,而是为了修正硬件限流误差,避免实际电流还没达到协议目标就提前限流。


2. IR Drop 电压补偿

Source 对外供电时,线材、MOS、PCB 走线都会产生压降。

例如协议目标是 9V,但由于线损,负载端可能只收到 8.8V。

因此代码会根据电流计算补偿电压:

_vol_irdrop0=app_power_get_ibus(PORT_USB_PORT_1)/10;_vol_irdrop1=app_power_get_ibus(PORT_USB_PORT_2)/10;

注释中说明:

1A 补 100mV

所以:

1A → 补 100mV 2A → 补 200mV 3A → 补 300mV

最终:

_pwr_info.vol=_p_pwr_info->vol+_vol_irdrop;

即:

协议目标电压 + 线损补偿 = DCDC 实际设置电压

例如:

协议目标:9V 线损补偿:0.2V DCDC 设置:9.2V

八、Sink 充电:电压往下让,电流慢慢升

Sink 表示当前设备被外部充电器供电。

Sink 充电时和 Source 放电完全不同。

1. 充电自适应电压

Sink 充电时,DCDC 不会直接设置成外部输入电压,而是设置得低一点:

if(_p_pwr_info->vol>POWER_VOL_15V){_pwr_info.vol=_p_pwr_info->vol-POWER_VOL_1V5;}elseif(_p_pwr_info->vol>POWER_VOL_5V5){_pwr_info.vol=_p_pwr_info->vol-POWER_VOL_1V;}elseif(_p_pwr_info->vol>POWER_VOL_3V){_pwr_info.vol=_p_pwr_info->vol-POWER_VOL_0V5;}else{_pwr_info.vol=POWER_VOL_4V4;}

可以理解为:

外部输入电压要比内部 DCDC 设置电压高一点,这样 DCDC 才有调节空间。

例如:

外部协议电压DCDC 实际设置留出调节余量
20V18.5V1.5V
12V11V1V
9V8V1V
5V4.5V0.5V

核心理解:

Source 放电:电压往上补,保证负载端电压够 Sink 充电:电压往下让,保证 DCDC 有调节空间

2. 充电电流缓增

Sink 充电时,电流也不会一下子拉满,而是通过operate_curr慢慢爬升:

_operate_curr=hpwr->operate_curr;if((_operate_curr<_pwr_info.curr)&&(ticker_out(hpwr->pwr_timer,TIME_PWR_CURRENT_STEP)==true)){hpwr->pwr_timer=ticker_read();_operate_curr+=CFG_PWR_CURRENT_STEP;}

例如目标电流是 3000mA,实际过程可能是:

500mA ↓ 600mA ↓ 700mA ↓ ... ↓ 3000mA

这样做的目的:

避免输入电压被瞬间拉低 避免 DCDC 冲击 避免 MOS 电流冲击 避免误触发 OCP 避免充电器误判异常

总结一句话:

Sink 充电不是暴力拉电流,而是先留电压余量,再让电流慢慢爬升。


九、dev_ctrl():从 app 层到 driver 层的命令通道

drv_pwr_adjust()算出最终要设置的电压、电流后,会调用:

dev_ctrl(hpwr->fd_dev_dcdc,DEV_IO_CTRL_CMD_DCDC_SET_VBUS,(void*)&_pwr_info.vol);dev_ctrl(hpwr->fd_dev_dcdc,DEV_IO_CTRL_CMD_DCDC_SET_IBUS,(void*)&_operate_curr);

这里的含义是:

找到当前端口绑定的 DCDC 设备 ↓ 发送 SET_VBUS / SET_IBUS 命令 ↓ 由 DCDC driver 真正执行

所以dev_ctrl()不是最终控制硬件的函数,而是 app 层通向 driver 层的统一命令入口。


十、今天打通的完整链路

今天最终理解的完整链路如下:

PD / QC / FCP / SCP / UFCS / VOOC 协议层 ↓ 协议层得到目标电压电流 ↓ drv_protocol_power_get() ↓ 写入 power->vol / power->curr ↓ pwr_process() ↓ 判断当前端口状态是否允许调电 ↓ drv_pwr_adjust() ↓ 根据 Source / Sink 做补偿、限流、缓启动 ↓ dev_ctrl() ↓ 下发 SET_VBUS / SET_IBUS ↓ DCDC driver ↓ 真正控制硬件

也可以压缩成一句话:

协议层决定目标功率,pwr_process 判断状态,drv_protocol_power_get 获取目标,drv_pwr_adjust 加工目标,dev_ctrl 下发命令,DCDC 驱动执行硬件动作。


十一、今日学习收获

今天主要掌握了以下内容:

  1. pwr_process()app_power.c的电源状态机核心。
  2. VBUS_SAFE阶段是快充前的安全 5V 准备阶段。
  3. HANDSHAKE阶段还不是真正快充,主要是默认 5V 供电或充电。
  4. FAST_CHARGE阶段才会从协议层获取目标功率,并进入真正调压调流。
  5. drv_protocol_power_get()负责从协议层读取目标电压电流。
  6. drv_pwr_adjust()负责把目标电压电流加工成 DCDC 实际设置值。
  7. Source 放电时,电压往上补,主要是补线损和限流误差。
  8. Sink 充电时,电压往下让,主要是给 DCDC 留调节空间。
  9. Sink 充电电流需要缓慢爬升,避免冲击和误触发保护。
  10. dev_ctrl()是 app 层到 driver 层的统一命令入口。

十二、下一步学习方向

下一步可以继续看:

drv_protocol_reset_dtc()drv_ac_adapter_aout_dtc()pwr_telemetry()

重点关注:

协议复位如何检测? 适配器掉压 / 掉档如何判断? power 层如何结合采样值做动态控制? protect 保护状态如何影响 power 状态机?

当前阶段已经基本打通了:

端口状态 → 协议功率 → power 决策 → DCDC 调压 → 底层执行

后面需要继续补强的是:

异常检测 → 协议复位 → 掉档处理 → protect 和 power 的联动
http://www.gsyq.cn/news/1605500.html

相关文章:

  • 防爆电气工程选型 不同供应商产品线定位与场景适配参考
  • 字节跳动Seedance:从“卖Token”到“卖生产力”,多赛道试水开启商业化新征程
  • bilibili视频解析:3分钟学会获取B站高清播放地址的实用指南
  • MSC许可管理系统的选择与使用:优化软件资源管理新途径
  • 城中村出入口改造,让居住更有秩序
  • 人才公寓智慧通行,让安居更安心
  • 2026年跨境电商新机遇:避开这5个坑,中小卖家如何用AI选品月入10万?附最新平台政策解读
  • 实战:从水色到纸币——彩色图像识别模型的双场景应用
  • Claude 4 Opus 评测 2026:200K 上下文与中文创作之王
  • CTF实战:巧用文件结构修复图片宽高
  • Android中App电量优化
  • 防止 iOS 应用被二次打包 代码混淆 和 签名校验的防篡改方案
  • Ryujinx:在PC上免费体验Nintendo Switch游戏的全能模拟器
  • 元器件为什么会失效?
  • 一颗芯片撬动48款爆款产品:杰理2026最新矩阵与尚凌科技供应链布局揭秘
  • 企业微信API开发会话数据进入业务系统时,需要注意哪些边界
  • 《电工学》核心解题思路精讲:从电路定理到暂态分析
  • LoadRunner 11.0 在 Windows 11 上的完整部署与本地化实战
  • 从单线程到多线程 IO,Redis 7.2 到底快了多少?
  • 从0开始学梯形图:10个经典案例,一次讲透!
  • C/C++ 堆与栈的区别——面试完整知识体系
  • 怎么知道供应商在不在行业黑名单里
  • 密码学 | 数字签名进阶:Schnorr签名的线性之美与密钥聚合
  • 为什么 CPU/内存指标不足以支撑真实业务伸缩
  • 软硬一体销售会话分析软硬件一体方案选型与落地参考
  • vitest + vue3 踩坑记录
  • 【课程设计/毕业设计】基于 SpringBoot 的餐厅前台点餐后台管理系统 轻量化餐饮订单服务管理系统设计与实现【附源码、数据库、万字文档】
  • vide coding软件开发流程
  • 2026 私域全面严打,无层级矩阵拼团为什么能安稳做
  • 6个真实用户反馈 森优时铁锌维 白发转黑发 改善周期测评