简介:围绕 USB Type-C 的 CC(Configuration Channel)通道展开的专题 PDF,面向硬件设计、接口协议调试与嵌入式开发人员,帮助理清正反插识别、接口类型判别、供电协商与替代模式背后的电气逻辑。文档以 CC1 与 CC2 两条引脚为主线,从 DFP 上拉 Rp 与 UFP 下拉 Rd 的阻值组合讲起,说明插入检测如何触发 VBUS 使能,以及 MUX 依据 CC 状态翻转 TX/RX 通道、实现正反插自适应;随后梳理 Ra、Rd 取值对应的接口类型判定,非 PD 设备的三档电流能力,以及 PD2.0、PD3.0 到 PD3.1 由 100W 向 240W 的功率演进与 PPS 微调机制;并补充 VCONN 为 Emarker 线缆供电、通过 VDM 协商进入 DP Alt Mode 的激活流程,含 2-Lane 与 4-Lane 两种显示配置。压缩包内为 1 个 PDF,约 878KB,配有大量电路框图与时序示意,便于调试时随手比对查阅。目前已有 6731 人学习。
1. 两根 CC 引脚,决定了这个 C 口能不能用
一根 C to C 线插上去没反应,换一根就好;同一台笔记本,接某个扩展坞能跑 4K,换一个只能点亮 1080p;明明标着 100W 的充电器,插上却只走 45W。这类问题九成不出在 USB 3.0 那几对差分线上,而出在连接器里最窄的两根针——CC1 和 CC2。TYPE-C CC 功能是整个接口的协商底座:正反插靠它识别,VBUS 什么时候给电、敢给多大电流靠它决定,PD 协商、VCONN 取电、DP Alt mode 能不能进,也全挂在它身上。这份材料把 CC 的五类职责拆开讲,从分压计算一路落到 PDO 解析、VCONN 检测和 Alt Mode 激活顺序,适合正在做 C 口硬件、写 PD 固件、或者被扩展坞兼容性反复折腾的人照着复现。
2. CC 的插入检测链路:从 5.1k 分压点到 MUX 翻转
先把结论摆前面:CC 上跑的不是数字协议,是模拟电平。所有后续的协议协商,都建立在“DFP 能正确读出 CC 上的电压落在哪个窗口”这个前提上。这个前提一旦被破坏——电阻选错、走线容性太大、采样点取错位置——后面 PD 谈得再漂亮也没用,因为链路根本没被识别成一条合法的 C 口连接。
2.1 连接器侧的角色划分与 CC 的物理位置
Type-C 插座是 24 针双排对称结构,A 排和 B 排关于中心旋转对称,这是正反插能被“电气识别”而不是靠机械结构硬扛的根本原因。CC1 在 A5,CC2 在 B5,两根针在插座里位置对称,插头正插时 A5 对 A5、B5 对 B5;反插时插头的 A5 落到插座的 B5 上。也就是说,无论怎么插,主机侧总会有一根 CC 被接上,另一根悬空或接到 Ra。
角色定义必须先分清,否则后面所有电阻讨论都是空的:
- DFP(Downstream Facing Port):供电源,也就是 Host 侧,CC 上挂上拉 Rp。
- UFP(Upstream Facing Port):受电端,也就是 Device 侧,CC 上挂下拉 Rd。
- DRP(Dual Role Port):两端都能当,靠 periodically 在 Rp 和 Rd 之间翻转来试探对端,手机、笔记本的 C 口多数属于这一类。
DRP 的存在是很多“插上没反应”问题的根源。两台 DRP 对接时,谁当源谁当宿是协商出来的,不是插上就定的;如果双方翻转周期错开或者固件里把角色写死,就会出现两端都在等对方供电的死锁。
2.2 Rp 与 Rd 的分压模型:5.1k 为什么是硬约束
UFP 侧的 Rd 规范值是 5.1kΩ ±20%,这个数字不是随便定的,它和 DFP 侧的 Rp 一起决定了 CC 上的稳态电压,而这个电压就是 DFP 判断“对面是什么设备、能给多少电”的唯一依据。
DFP 侧有两种等效模型:电流源模型和电阻模型。规范正文用电流源描述,实际电路里用电阻上拉到 VBUS 或 3.3V 也很常见。用电流源模型算最直观:
| DFP 广播能力 | Rp 电流源 | 等效电阻(上拉到 5V) | CC 稳态电压(Rd=5.1k) | DFP 判定窗口 |
|---|---|---|---|---|
| Default USB(500/900mA) | 80µA | 56kΩ | 约 0.41V | 0.25–0.61V |
| 1.5A @ 5V | 180µA | 22kΩ | 约 0.92V | 0.61–1.16V |
| 3A @ 5V | 330µA | 10kΩ | 约 1.68V | 1.16–2.45V |
| Ra(Emarker 线缆) | — | — | 约 0.2V | 0.15–0.25V |
窗口值按规范表格取,实际设计时两端各留 10%–20% 余量。Ra 那一档要特别注意:它比 Default 档还低,所以 DFP 的判定逻辑必须先判低再判高,顺序写反了就会把带 Emarker 的线缆误判成普通设备。
下面这段脚本可以直接拿去验证你手上的分压设计,输入实际选用的电阻和 VBUS,输出落在哪个窗口:
# cc_divider.py —— 校验 CC 分压是否落在规范判定窗口内 RD = 5.1e3 # UFP 侧下拉,规范 5.1k ±20% V_BUS = 5.0 # DFP 侧上拉参考电压 # (名称, Rp 等效电阻, 判定下限, 判定上限) WINDOWS = [ ("Ra (Emarker)", 1.0e3, 0.15, 0.25), ("Default USB", 56e3, 0.25, 0.61), ("1.5A @5V", 22e3, 0.61, 1.16), ("3A @5V", 10e3, 1.16, 2.45), ] def cc_voltage(rp, rd=RD, vbus=V_BUS): # 电阻分压:CC 点电压 = Vbus * Rd / (Rp + Rd) return vbus * rd / (rp + rd) for name, rp, lo, hi in WINDOWS: v = cc_voltage(rp) ok = lo <= v <= hi # 同时打印 ±20% 器件公差下的最坏情况,方便看边界 v_min = cc_voltage(rp * 0.8) v_max = cc_voltage(rp * 1.2) print(f"{name:14s} V={v:.3f}V 窗口[{lo},{hi}] {'OK' if ok else 'FAIL'} " f"公差范围 {v_min:.3f}~{v_max:.3f}V")这段代码做了三件事:按分压公式算出标称电压、和规范窗口比对、再把 Rp 的 ±20% 公差带算出来看有没有越界。第三个输出最有用——很多“大部分板子好、个别板子不识别”的问题,本质是标称值在窗口正中,但公差带压到了边界,换一批电阻就翻车。跑完如果发现公差范围跨过了窗口边界,别犹豫,换 1% 精度的电阻,或者把 Rp 的取值往窗口中心挪。
2.3 正反插识别与 MUX 翻转的落地配置
CC 电平一旦被正确读出,接下来就是数据通道的翻转。这一步的硬件通常是一颗四通道差分 MUX,串在 SSTX/SSRX 和连接器之间。逻辑很直白:正插时 CC1 有效、CC2 悬空,MUX 保持直通;反插时 CC2 有效,MUX 把 TX/RX 两组差分对交叉过去。
在带 TCPC(Type-C Port Controller)的平台上,这部分由芯片自动完成,软件只需要读状态;在没有 TCPC、用 GPIO 直驱 MUX 的方案上,翻转逻辑得自己写。下面是一段典型的轮询实现:
# cc_mux.py —— 无 TCPC 方案下的 CC 采样与 MUX 翻转 import time ADC_CH_CC1 = 0 ADC_CH_CC2 = 1 MUX_SEL_PIN = 17 # 高电平 = 反插,交叉 TX/RX V_RA_MAX = 0.25 # 低于此值视为 Ra,线缆带 Emarker V_OPEN_MIN = 2.45 # 高于此值视为悬空(无连接) def read_cc(ch): # 平台相关:读 ADC 原始值并换算成电压,这里返回毫伏 return adc_read_mv(ch) def detect_orientation(): v1 = read_cc(ADC_CH_CC1) v2 = read_cc(ADC_CH_CC2) # 两根都悬空 -> 没有插入 if v1 > V_OPEN_MIN * 1000 and v2 > V_OPEN_MIN * 1000: return "none", None # 哪根被拉低,哪根就是接上的那根 if v1 < v2: return "normal", v1 return "flipped", v2 def apply_mux(orient): if orient == "flipped": gpio_write(MUX_SEL_PIN, 1) elif orient == "normal": gpio_write(MUX_SEL_PIN, 0) # none 状态下保持上一次配置,避免插拔抖动时通道乱跳 while True: orient, voltage = detect_orientation() if orient != "none": apply_mux(orient) if voltage < V_RA_MAX * 1000: print("检测到 Ra:这是一条带 Emarker 的线缆,需要供 VCONN") time.sleep(0.05) # 50ms 轮询,兼顾响应和功耗参数上有几个点需要按你的实际器件改:V_OPEN_MIN对应你 DFP 侧 Rp 的最大分压值再留 10% 余量;MUX_SEL_PIN的电平极性看 MUX 手册,有的器件是高电平直通、低电平交叉,写反了表现就是“正插不识别、反插能识别”;time.sleep的周期别低于 20ms,太短会把插拔瞬间的抖动当成多次连接,反复翻转 MUX 反而导致链路训练失败。
还有一个容易被忽略的细节:MUX 翻转必须发生在高速链路训练之前。如果 SoC 的 USB3.0 控制器已经开始 LTSSM 训练,你这时候才翻 MUX,对端看到的就是一串无效码型,重试几次后直接降级到 USB2.0。常见做法是把 CC 检测放在上电自检阶段,MUX 状态稳定后再释放控制器的复位。
3. Rp、Rd、Ra 阻值与 PD 协商:把电流档位和 PPS 落到寄存器上
CC 电平只是把“对面接了什么”这件事说清楚了,接下来要谈的是“能给多少电”。这里的规则分两层:不支持 PD 的设备走纯硬件档位,靠 Rp 阻值广播三档电流;支持 PD 的设备走 CC 上的 BMC 编码通信,通过能力报文和请求报文逐级谈。两层混在一起是新手最容易搞乱的地方。
3.1 三档电流能力:没有 PD 时的供电天花板
不带 PD 协议的设备,供电能力完全由 DFP 侧 Rp 的阻值决定,UFP 只能被动接受。这个机制的设计意图是让最便宜的充电器也能安全供电:一个 5V/0.5A 的老充电头,用 56k 的 Rp,插上任何设备都只敢给 500mA,不会因为设备“贪心”而烧掉。
三档的对应关系在上一节的表里已经列了,这里补充几个工程上真正会踩的点:
- 第一档的电流值与接口速率挂钩。USB 2.0 只有 500mA,USB 3.0 因为多了两组高速差分对,允许到 900mA。同一个 Rp 值,实际能取多少电取决于枚举出来的速率,固件里要把这两个数分开存。
- UFP 侧不能只看 Rp 就无脑取满。规范要求 UFP 在取电前先确认 VBUS 稳定,并且要有一个软启动过程。直接拉满载会让某些容性负载大的适配器触发过流保护,表现为“插上瞬间掉电又恢复”。
- 双角色设备做 Source 时,Rp 的切换要和 VBUS 的开关同步。先切 Rp 再开 VBUS 会有几十毫秒的窗口,对端可能在这个窗口里完成了一次失败的检测;先开 VBUS 再切 Rp 则可能让对端按错误的档位启动。
3.2 PD 协商:能力报文的结构与 PDO 解析
进入 PD 之后,Source 会周期性发送 Source_Capabilities 报文,里面是一个报文头加最多 7 个 4 字节的 PDO。每个 PDO 的最高两位决定类型:00是固定电压,11是可调电压(APDO),APDO 里又细分 PPS 和其他类型。Sink 从里面挑一个,回 Request 报文,Source 回 Accept,再回 PS_RDY,VBUS 才真正切到目标电压。
解析这段报文是调试 PD 的基本功。下面这段代码按规范位域拆 PDO,输入是从 CC 抓到的原始 4 字节:
# pdo_parse.py —— 解析 Source_Capabilities 中的 PDO def parse_pdo(raw): ptype = (raw >> 30) & 0x3 if ptype == 0x0: # 固定电压 PDO voltage_mv = ((raw >> 10) & 0x3FF) * 50 # 单位 50mV current_ma = (raw & 0x3FF) * 10 # 单位 10mA dual_role = (raw >> 29) & 0x1 return {"type": "Fixed", "v_mv": voltage_mv, "i_ma": current_ma, "dual_role": dual_role} if ptype == 0x3: # APDO apdo_type = (raw >> 28) & 0x3 if apdo_type == 0x0: # PPS vmax = ((raw >> 17) & 0xFF) * 100 # 单位 100mV vmin = ((raw >> 8) & 0xFF) * 100 imax = (raw & 0x7F) * 50 # 单位 50mA return {"type": "PPS", "v_min_mv": vmin, "v_max_mv": vmax, "i_max_ma": imax} return {"type": "APDO-other", "raw": hex(raw)} return {"type": "Unknown", "raw": hex(raw)} # 示例:一个 5V/3A 固定档 + 一个 3.3~11V/3A 的 PPS 档 for raw in (0x0001912C, 0xC0DC213C): print(parse_pdo(raw))位域的含义需要对照规范记:固定 PDO 的电压在 bit19:10、电流在 bit9:0;PPS APDO 的最大电压在 bit24:17、最小电压在 bit16:9、最大电流在 bit6:0。这几个位置写错一位,解析出来的就是“20V/0.5A”这种明显不合理的组合——调试时如果看到这种值,先怀疑位域而不是怀疑设备。
PPS 的价值在步进精度:电压 20mV 一档、电流 50mA 一档,可以让 Sink 直接请求“比电池当前电压高 200mV”这种精细值,把充电效率拉上去、发热压下来。但要跑 PPS,Sink 侧必须支持在 PPS 模式下周期性发 Request 调整输出,且每次调整后要等到 PS_RDY 才能继续,这个握手节奏错了,Source 会直接退出 PPS 回到固定档。
功率上限方面,PD 2.0 和 3.0 都是 20V/5A 封顶 100W;PD 3.1 把电压扩展到 48V,同样 5A,合计 240W,并且向下兼容。做 3.1 的板子要注意,48V 对电容耐压、连接器爬电距离、MOS 耐压都是另一套要求,不能拿 20V 的方案直接改个分压电阻就上。
3.3 VCONN 与 Emarker:高速线缆的取电与识别
普通线缆里只有铜,带 Emarker 芯片的线缆里多了一颗小 MCU 和一段存储,用来告诉两端“我能跑多快、能过多大电流、我是谁”。这颗芯片的供电来自 VCONN,而 VCONN 就挂在 CC 上——具体做法是:DFP 检测到某根 CC 上的电压落在 Ra 窗口(0.15–0.25V),就判定这是一条带 Emarker 的线,然后把另一根CC 从 Rp 上拉切换成 VCONN 供电(典型 3.3V 或 5V,限流 100mA 左右)。
这里有两个非常容易出问题的地方:
第一,VCONN 的开关方向不能搞反。检测到 Ra 的是 CC1,供电的就得是 CC2;供错边的话,Emarker 芯片上电了,但 CC1 上的 Ra 被 VCONN 顶掉,DFP 会认为线缆被拔出了。
第二,VCONN 要在高速链路建立之前就位。Emarker 芯片上电后需要通过 CC 上的 BMC 通信把线缆能力报上来,如果主控在拿到这份能力之前就按“全功能线”去配置链路,遇到只有 2 对高速线的线缆就会训练失败。常见做法是给 VCONN 上电留 10–20ms 的稳定时间,读完线缆能力再放行链路。
线缆两头是镜像的,所以 VCONN Source 可以在任意一端,连接建立后 PD 协议还允许通过 VCONN_SWAP 把供电方换到另一端。做双口设备时这条命令一定要实现,否则两个口都插着带 Emarker 的线、又都抢着供 VCONN,会出现两边都供不上、线缆反复掉线的情况。
4. 避坑与排查:CC 链路上最容易翻车的五处
下面五条都是我在实际板子上反复遇到的,按“现象 → 原因 → 解决”的顺序写,遇到对得上的直接照着查。
第一条:同一批板子,八成能识别,两成死活不认。
现象是插上 UFP 后 CC 电压测出来 0.3V 左右,卡在 Ra 窗口和 Default 窗口的交界,DFP 固件先判 Ra,于是进了 VCONN 流程,永远等不到设备枚举。
原因基本是 Rd 用了 5% 精度甚至没标的便宜货,阻值偏到 6k 以上,分压点上移;或者 PCB 上 CC 走线离 VBUS 太近,耦合进了噪声。
解决办法是把 Rd 换成 1% 精度的 5.1k,CC 走线做包地处理,并且把固件的判定顺序改成“先判有效连接(电压 < 2.45V),再判 Ra,最后按窗口分档”,同时在窗口边界加 50mV 迟滞,避免抖动时来回跳档。
第二条:两个 C 口设备对接,谁都不供电。
现象是 C to C 线连接两台都是 DRP 的设备,插上后 VBUS 一直是 0V,两边都检测到对方存在但没有一方切到 Source。
原因是双方都把自己初始化成 Sink,或者都在做 Rp/Rd 翻转但周期不同步,翻转窗口永远错开。
解决方式是至少让一端在检测到对端 Rp 后主动切到 Source,翻转周期按规范取 tDRP(几十毫秒量级),并在固件里加一个超时:翻转 100ms 内没检测到对端变化就锁定当前角色,别无限翻下去。
第三条:VBUS 开了,但设备枚举失败或者反复重连。
现象是示波器上看 VBUS 已经到位,CC 电压也正常,但对端一直不枚举。
原因多半是 VBUS 上电时序和 CC 检测顺序反了:DFP 在 CC 还没稳定时就把 VBUS 的 MOS 打开,对端看到 VBUS 先到、CC 后稳,判定为异常插入;或者 VBUS 的上升沿太陡,浪涌触发对端 OVP。
解决方式是把 VBUS 使能放在 CC 判定确认之后,中间留 5–10ms,并且给 VBUS 加软启动(缓升 1–5ms),输出电容按负载容性核算,别一味加大。
第四条:带 Emarker 的线插上,4K 掉到 1080p,或者干脆不识别。
现象是换普通线正常,换长线或者全功能线就出问题。
原因通常是 VCONN 供电电流不够,Emarker 芯片上电复位不完整,能力没报上来;或者 VCONN 串的限流电阻偏大,压降让芯片工作在欠压边缘。
解决办法是把 VCONN 的限流放在 100mA 以上,串联电阻控制在 1Ω 以内,上电后用示波器量一下 VCONN 的稳态电压,低于 3.0V 就得查供电路径;同时确认固件在拿到线缆能力之前不要锁定链路速率。
第五条:DP Alt Mode 协商成功,但屏幕不亮。
现象是 PD 日志里能看到 Discover Identity 和 Enter Mode 都回了 Good CRC,MUX 也切了,显示器还是黑屏。
原因是 SBU1/SBU2 没接或者接反了。AUX 通道走的是 SBU,很多板子在做 USB3.0 的时候把 SBU 当保留脚空着,Alt Mode 一切换,DP 的 AUX 链路就断了,链路训练过不去。
解决办法是查原理图确认 SBU 走到了连接器并且有 ESD 保护,同时用示波器抓 SBU 上的 AUX 波形,没有波形就是硬件断路,波形畸变就是保护器件容性太大,换低容值的 TVS。
5. DP Alt Mode 的协商验证:用 VDM 报文和 MUX 状态把链路打通
DP Alt Mode 是 CC 这条链路上最复杂的一段,因为它要在 PD 的 VDM(Vendor Defined Message)通道里跑一套完整的发现和配置流程,然后再去切 MUX。整条路径是:PD 建立 → Discover Identity 拿到对端 ID → Discover SVIDs 确认支持 0xFF01 → Discover Modes 查具体引脚分配 → Enter Mode → DP 状态机启动 → MUX 切到 DP → AUX 通信 → 链路训练。
调试这段最有效的手段不是猜,是把 VDM 报文抓出来逐条对。我一般会准备一张对照表:
| 阶段 | 报文类型 | 关键字段 | 失败时的典型表现 |
|---|---|---|---|
| 发现身份 | Discover Identity | Vendor ID / Product Type | 回 NAK,说明对端不是 DFP 或 PD 没建立 |
| 查 SVID | Discover SVIDs | 是否含 0xFF01 | 不含就是设备不支持 DP Alt |
| 查模式 | Discover Modes | Pin Assignment C/D/E/F | 只支持某一种,MUX 配置要跟着改 |
| 进入模式 | Enter Mode | Object Position | 回 Good CRC 但 MUX 没切,是本地逻辑漏了 |
| 状态更新 | DP Status | 连接状态 / HPD | HPD 一直低,多半是 AUX 物理层问题 |
MUX 配置这块,两种模式的差别要记牢:USB3.0 + 2-Lane DP(Pin Assignment C/D)保留一对高速差分给 USB3.0,DP 只占两 lane,能跑 4K@30Hz(HBR2 源)或 4K@60Hz(HBR3 源);4-Lane DP(Pin Assignment E/F)把四对全给 DP,USB3.0 降级到 USB2.0,但 DP 带宽翻倍。如果你的产品宣称“4K60 + USB3.0 同时可用”,硬件上必须支持 C/D 分配,且 MUX 要能独立控制每一对通道。
验证环节我习惯从下往上倒推,比从上往下快得多:
- 先用万用表量 SBU1/SBU2 对地阻抗,确认没有短路和虚焊;
- 抓 AUX 波形,看有没有差分摆幅,AUX 完全没动静就先别怀疑协议;
- 读 DPCD 的接收器能力字段(从地址 0x00000 起),能读出来说明 AUX 通了、链路层活了;
- 再看 MUX 的配置寄存器回读值,和预期分配是否一致;
- 最后才去翻 PD 日志看 Enter Mode 有没有回 Good CRC。
这个顺序的道理是:协议层的问题会伪装成物理层问题,但物理层的问题不会伪装成协议层问题。先排除物理层,剩下的排查范围小一个数量级。
还有个血泪经验:MUX 切换和 DP 状态机启动之间必须留时间。我见过一块板子,Enter Mode 的 Good CRC 刚收到,固件立刻切 MUX,结果 DP 源端的状态机还停在配置阶段,切过去的信号被判为无效,链路直接锁死。后来在切换前加了 20ms 延时,并且等 DP Status 报文里的连接位置位再动手,问题消失。从那以后我每次做 Alt Mode 的板子,都强制走一遍“先抓 AUX、再读 DPCD、最后看 MUX 回读”的三段验证,不管多急都不跳步。希望帮到你。
本文还有配套的精品资源,点击获取