USB-C PD Confusion: A Field Guide to Getting Negotiation Wrong
USB Power Delivery negotiation looks simple in the spec and feels impossible on real hardware. Three boards, two oscilloscopes, and one blown FUSB302.

USB Power Delivery negotiation looks simple in the spec and feels impossible on real hardware. Three boards, two oscilloscopes, and one blown FUSB302 later — a field guide to getting negotiation wrong less often.
Problem statement
Design: USB-C sensor puck, STM32G4 + FUSB302B (I2C PD PHY), 5 V / 9 V / 15 V request for heater load, battery charge path via BQ25798.
Symptom: device booted on some chargers (Apple 20 W), failed on Anker 65 W (no contract), smoked FUSB302 on one lab supply "PD" port that lied about capabilities.
Spec vs. bench
USB PD 3.1 spec is 800+ pages. What mattered on the bench:
- CC line termination — Rd for UFP sink, correct value (5.1 kΩ ±10%), on both CC pins if receptacle (only one active CC — detect which).
- Message timing — GoodCRC within tReceiverResponse 15–30 ms; we missed deadlines when I2C bus blocked by display driver.
- Caps mismatch — Request 15 V when source only offers 9 V → hard reset loop.
- VBUS hot-plug — inrush without ideal diode control tripped OCP on marginal sources.
Oscilloscope on CC (split termination test points) + VBUS — use trigger cheat sheet pulse mode for BMC edges after hard reset.
Failure taxonomy (our three boards)
Board A: wrong Rd
Used 4.7 kΩ "because we had reels." Some sources detected UFP; others didn't. Fix: 5.1 kΩ 1% on CC1/CC2. Immediate.
Board B: FUSB302 interrupt storm
INT_N shared with barometer on same I2C ISR — GoodCRC TX delayed. PD stack entered ErrorRecovery every 400 ms.
Fix: dedicated EXTI line for FUSB302, priority above sensor poll; PD stack on dedicated workqueue thread (Zephyr) / highest bare-metal priority.
Lesson overlaps I2C glitch debug — bus contention is PD timing failure.
Board C: blown FUSB302
Lab PSU marketed "PD" — emitted 9 V on VBUS before Accept message. FUSB302 VBUS pin abs max 28 V but internal front-end not hot-switch rated for abuse.
Fix: high-side load switch with OVP clamp; software never enables load until PSSourceOn state; power sequencing — 3.3 V MCU rail before FUSB302 I2C init.
Negotiation flow we implemented (sink)
Attach Rd → detect CC → wait Capabilities → Send Request → Accept → PS_RDY → enable load
Logged state transitions to RTT (deft). Most bugs visible as oscillation between Capability and HardReset — Saleae USB PD decoder (beta) helped on Keysight; on Rigol we counted BMC bit times manually — painful but educational.
PDO selection policy
Don't greedily max voltage — heater needs 12 W; requesting 15 V @ 3 A angered sources that renegotiated when actual draw 0.5 A. Request fixed PDO matching power need with headroom, not max headline number.
// Pseudocode — pick lowest sufficient fixed PDO
for (i = 0; i < cap_count; i++) {
if (pdo_type_fixed && voltage_mv >= 9000 && max_ma >= 1500)
select(i);
}
Tools and parts
| Item | Role |
|---|---|
| FUSB302B + eval board | golden compare |
| USB-C PD trigger tester (ZY12PDN class) | source spoof |
| Apple 20 W, Anker 735, Dell 130 W | compatibility matrix |
| Nordic Power Profiler Kit II | inrush capture |
Keep a charger wall in lab — label negotiated voltage/current per device.
Compliance vs. bring-up
Formal USB-IF PD compliance — separate budget ($ + time). Pre-compliance: eye diagram on CC BMC optional for sink-only low power; do verify inrush per USB BC 1.2 / PD load rules.
EMI note: PD messaging noise on CC couples to nearby sensitive analog — see EMI bench testing; ferrite on CC optional, layout separation better.
Integration with OTA
Fleet OTA over USB-C while charging — policy: only negotiate 5 V during flash write unless heater off and PMIC confirms margin. Brownout during OTA tied to one brick in OTA lessons.
What I'd do next
Replace bit-banged PD policy with vendor stack on STM32G4 UCPD peripheral for rev D — FUSB302 fine for gen 1, not where I'd start greenfield in 2026.
Build automated charger matrix CI — robot isn't needed; intern + spreadsheet + nightly script logging contract voltage.
Document per-charger quirks in support wiki — customers blame our puck when Anker firmware update changed PDO order.
USB-C PD is a protocol stack and an analog problem. Debug both simultaneously or lose a PHY.
FUSB302 register debug sequence
When negotiation fails silently, read in order:
STATUS0/STATUS1— attach, BC levelINTERRUPT/INTERRUPTa— clear pendingSWITCHES0— pull-downs enabled on correct CCCONTROL0— ensure not in hard reset loop
Log over RTT at state transition — not in hot path I2C read during GoodCRC window.
Charger compatibility matrix (partial)
| Charger | Contract | Notes |
|---|---|---|
| Apple 20 W | 9 V 2.2 A | baseline gold |
| Anker 735 | 9 V only after re-plug | PD 3.0 PPS noise |
| Dell 130 W | 15 V offered | we request 9 V fixed |
| Fake PD lab PSU | 9 V before Accept | blew PHY once |
What I'd do next
Manish Bookreader
Electronics enthusiast, Embedded Systems Expert, Linux/Networking programmer, and Software Engineer passionate about AI, electronics, books, and cooking.

