Series Context: This is Part 2 of the 17-part USB Development Mastery series. In Part 1 we built the conceptual model of USB as a system. Now we descend to the physical layer — the electrons, resistors, capacitors, and copper traces that make USB work in hardware.
1
USB Fundamentals
USB system architecture, transfer types, host/device model, protocol stack
2
Electrical & Hardware Layer
D+/D- signalling, pull-ups, connectors, USB-C, STM32 USB peripherals
You Are Here
3
Protocol & Enumeration
Enumeration sequence, USB packets, descriptors, endpoint concepts
4
USB Device Classes
HID, CDC, MSC, MIDI, Audio, composite devices, vendor class
5
TinyUSB Deep Dive
Stack architecture, execution model, STM32 integration, descriptor callbacks
6
CDC Virtual COM Port
CDC class, bulk transfers, printf over USB, baud rate handling
7
HID Devices
HID descriptors, report format, keyboard/mouse/gamepad implementation
8
USB Mass Storage
MSC class, SCSI commands, FATFS integration, RAM disk
9
Composite Devices
Multiple classes, IAD descriptor, CDC+HID, CDC+MSC
10
Debugging USB
Wireshark capture, protocol analyser, enumeration debugging, common failures
11
RTOS + USB Integration
FreeRTOS + TinyUSB, task priorities, thread-safe communication
12
Advanced USB Topics
Host mode, OTG, isochronous, USB audio, USB video
13
Performance & Optimisation
DMA, zero-copy buffers, throughput maximisation, latency tuning
14
Custom USB Class Drivers
Vendor class, writing descriptors, OS driver interaction
15
Bare-Metal USB
Direct register programming, writing USB stack from scratch, PHY timing
16
Security in USB
BadUSB attacks, device authentication, secure firmware, USB firewall
17
USB Hardware Design
PCB layout, differential pairs, impedance matching, EMI, USB-C PD
USB Electrical Basics
USB signaling uses differential signaling — the same fundamental technique used in RS-485, CAN bus, and Ethernet. Instead of measuring the voltage on a single wire relative to ground, the USB receiver measures the difference in voltage between two wires: D+ (Data Plus) and D- (Data Minus). This gives USB two critical properties that a single-ended interface like UART cannot offer at speed:
- Common-mode noise rejection: Any noise that couples equally onto both D+ and D- lines — from nearby switching power supplies, motor drives, or radio-frequency interference — is cancelled out by the differential receiver. The signal is only in the difference between the two lines.
- Reduced EMI emissions: Because D+ and D- carry complementary signals, their electromagnetic fields partially cancel each other. A properly routed differential pair emits far less radiated noise than two single-ended signals carrying the same data rate.
Signal States: SE0, J, K
USB defines its logical states in terms of the differential voltage between D+ and D-, plus a special "both lines low" state. Understanding these three states is essential before you can interpret oscilloscope traces or understand reset and speed detection:
| State |
D+ Voltage |
D- Voltage |
FS Meaning |
LS Meaning |
HS Meaning |
| SE0 (Single-Ended Zero) |
< 0.3 V |
< 0.3 V |
Reset / End-of-Packet |
Reset / End-of-Packet |
Chirp (HS negotiation) |
| J State |
> 2.8 V |
< 0.3 V |
Idle / Logic 1 |
Idle / Logic 0 (inverted) |
HS idle (0 differential) |
| K State |
< 0.3 V |
> 2.8 V |
Logic 0 / Start-of-Packet |
Logic 1 / Start-of-Packet |
Chirp K (HS capable device) |
| Differential 1 |
D+ > D- + 200 mV |
— |
Logic 1 |
— |
— |
| Differential 0 |
D- > D+ + 200 mV |
— |
Logic 0 |
— |
— |
Key Insight: Low Speed USB has J and K states inverted relative to Full Speed. This is not a typo — it is a deliberate design choice that lets the host hub distinguish LS devices from FS devices based on which data line has the pull-up resistor.
NRZI Encoding and Bit Stuffing
USB uses NRZI encoding (Non-Return-to-Zero Inverted). In NRZI, a logic 0 is represented by a transition of the signal (J to K or K to J), and a logic 1 is represented by no transition (the line stays in the same state). This is the opposite of what many developers intuitively expect coming from UART experience.
Because a long run of 1s produces no transitions, and the USB receiver uses clock recovery from transitions, USB mandates bit stuffing: after any six consecutive 1-bits in the encoded data stream, a 0-bit is forcibly inserted. The receiver knows to strip this extra bit. This ensures transitions occur frequently enough for the receiver's PLL to stay locked. When you see a 0-bit appearing at a position that doesn't match the expected data pattern in a protocol analyser trace, bit stuffing is the explanation.
Speed Detection via Pull-up Resistors
When a USB device is connected to a host port, no firmware has run and no enumeration has started. The host needs to determine the device's speed before it can even send a reset. It does this entirely in hardware, by detecting which data line has a pull-up resistor:
| USB Speed |
Pull-up Line |
Resistor Value |
Voltage Level Seen by Host |
| Low Speed (1.5 Mbit/s) |
D- |
1.5 kΩ to 3.3 V |
D- pulled high, D+ pulled low by hub |
| Full Speed (12 Mbit/s) |
D+ |
1.5 kΩ to 3.3 V |
D+ pulled high, D- pulled low by hub |
| High Speed (480 Mbit/s) |
D+ (starts as FS) |
1.5 kΩ to 3.3 V initially |
FS detected, then Chirp K negotiation |
The host port has 15 kΩ pull-down resistors on both D+ and D-. These create a voltage divider with the device's pull-up: a 1.5 kΩ pull-up on D+ into a 15 kΩ pull-down results in approximately 2.9 V on D+, which the host interprets as a Full Speed device attached. The pull-down resistors also ensure that D+ and D- are at a defined low voltage when no device is connected.
High Speed Chirp Negotiation
High Speed devices begin life as Full Speed — they assert the 1.5 kΩ pull-up on D+, just like any FS device. Once the host detects the FS device and issues a USB Reset (SE0 for at least 10 ms), the device signals that it supports High Speed by driving a Chirp K on the bus: it removes the pull-up resistor and drives D- high (K state) for at least 2.5 µs. The host responds with alternating Chirp K / Chirp J pulses. If the device detects four or more Chirp K–J pairs, both sides switch to the HS electrical signaling mode (400 mV differential, terminated with 45 Ω to ground on each line).
TinyUSB Pull-up Configuration
In TinyUSB with STM32 HAL, the USB pull-up is controlled by the USB peripheral hardware — not by a GPIO you manually set. The OTG_FS peripheral controls the pull-up through its internal register. However, some STM32 designs use a discrete external pull-up transistor controlled by a GPIO pin. Here is how both cases are handled:
/* ---------------------------------------------------------------
* Case 1: OTG_FS peripheral with internal pull-up (most STM32s)
* The pull-up is enabled automatically when USB_OTG_DCTL SDIS bit
* is cleared — TinyUSB / HAL does this for you inside tusb_init().
* ---------------------------------------------------------------*/
/* In tusb_config.h — no manual pull-up needed */
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED
#define CFG_TUSB_OS OPT_OS_NONE
/* ---------------------------------------------------------------
* Case 2: External pull-up transistor on a GPIO (some custom boards)
* e.g., PB12 = low → Q1 NPN pulls D+ high via 1.5 kΩ
* ---------------------------------------------------------------*/
void usb_pullup_enable(bool enable)
{
/* PA8 used as USB_DP_PULLUP on a custom board */
GPIO_InitTypeDef gpio = {0};
gpio.Pin = GPIO_PIN_8;
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &gpio);
/* Active-high: set PA8 HIGH to enable pull-up via transistor */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8,
enable ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/* Call before tusb_init() to ensure pull-up is driven correctly */
int main(void)
{
HAL_Init();
SystemClock_Config(); /* must reach 48 MHz USB clock first */
usb_pullup_enable(true);
tusb_init();
while (1) {
tud_task();
}
}
Common Mistake: Enabling the USB pull-up before the 48 MHz USB clock is running causes the device to appear at the wrong speed or fail enumeration entirely. Always configure the system clock and ensure the USB clock is running before enabling the pull-up.
VBUS Power Management
VBUS is the 5 V power supply that the USB host provides to bus-powered devices. It is supplied on pin 1 of every USB connector (Micro-B pin 1, Type-C VBUS pins A4/A9/B4/B9). The USB specification defines tight voltage and current limits that your hardware must meet to pass compliance testing and work reliably across all hosts.
VBUS Voltage Specification
The USB 2.0 specification defines VBUS at the device's cable plug as:
- Nominal: 5.0 V
- Minimum (at device receptacle): 4.75 V (accounting for cable drop)
- Maximum (at device receptacle): 5.25 V
- Absolute maximum (device must survive): 6.0 V (with ESD or surge)
The cable resistance plus connector contact resistance can drop up to 250 mV at the maximum 500 mA bus current, which is why the floor is 4.75 V rather than exactly 5.0 V. Your LDO or DCDC converter input range must accommodate the full 4.75–5.25 V range.
Power States and Current Limits
| State |
Device Type |
Max Current Draw |
Notes |
| Unconfigured |
Bus-powered |
100 mA |
Before SET_CONFIGURATION completes |
| Configured (low power) |
Bus-powered |
100 mA |
bMaxPower = 50 in descriptor (units of 2 mA) |
| Configured (high power) |
Bus-powered |
500 mA |
bMaxPower = 250 in descriptor (units of 2 mA) |
| USB Suspend |
Bus-powered |
2.5 mA |
Mandatory — host may cut power to non-compliant devices |
| Any state |
Self-powered |
0 mA (from VBUS) |
Device provides its own power; bmAttributes bit 6 set |
Inrush Current and Soft-Start
Large input capacitors on your 3.3 V rail charge through the cable's resistance when VBUS first appears. This inrush current spike can exceed the host's overcurrent limit and cause it to disconnect your device before enumeration begins. The USB specification limits the maximum inrush charge to 50 µC. For a 5 V to 3.3 V LDO with a 100 µF output capacitor, the inrush charge is approximately:
Q = C × ΔV = 100 µF × (5.0 − 0) = 500 µC — ten times the limit.
Solutions include: (1) use a smaller input capacitor (4.7–10 µF is acceptable if your regulator is stable), (2) add an NTC thermistor in series with VBUS for passive inrush limiting, or (3) use a dedicated soft-start circuit or an ideal diode / MOSFET controller with programmable slew rate.
VBUS Detection on STM32
/* ---------------------------------------------------------------
* VBUS detection using STM32 OTG_FS VBUS sensing
* Enabled via GPIOA pin 9 in analog mode (USB_OTG_FS VBUS pin)
* ---------------------------------------------------------------*/
/* In MX_USB_OTG_FS_PCD_Init() — generated by CubeMX */
static void MX_USB_OTG_FS_PCD_Init(void)
{
hpcd_USB_OTG_FS.Instance = USB_OTG_FS;
hpcd_USB_OTG_FS.Init.dev_endpoints = 4;
hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL;
hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED;
hpcd_USB_OTG_FS.Init.Sof_enable = DISABLE;
hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE;
hpcd_USB_OTG_FS.Init.lpm_enable = DISABLE;
hpcd_USB_OTG_FS.Init.vbus_sensing_enable = ENABLE; /* VBUS sensing ON */
hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE;
if (HAL_PCD_Init(&hpcd_USB_OTG_FS) != HAL_OK) {
Error_Handler();
}
}
/* PA9 must be configured as analog input for VBUS sensing */
static void USB_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
/* PA9 = USB_OTG_FS_VBUS — must be analog, NOT alternate function */
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* PA11 = USB_OTG_FS_DM, PA12 = USB_OTG_FS_DP */
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
/* TinyUSB callback: called when VBUS rises above threshold */
void tud_mount_cb(void)
{
/* Device has been enumerated and configured by host */
/* Safe to begin USB-dependent tasks */
led_set(LED_USB_ACTIVE);
}
/* TinyUSB callback: called when VBUS drops (cable unplugged) */
void tud_umount_cb(void)
{
/* Device disconnected — clean up USB-dependent state */
led_set(LED_USB_INACTIVE);
}
Design Tip: If your board disables VBUS sensing (vbus_sensing_enable = DISABLE), the OTG core assumes VBUS is always present. This works on boards where VBUS is always available (self-powered or dedicated USB power), but will cause enumeration failures on hosts that monitor VBUS current.
ESD Protection
USB connectors are directly handled by users and are exposed to electrostatic discharge events of up to ±15 kV (Human Body Model). Your MCU's USB pins typically tolerate ±2 kV at best. An unprotected USB port is therefore a reliability time bomb — it may survive 1,000 plug cycles in the lab but fail on the first plug in a static-charged user's hands in a dry winter environment.
TVS Diode Requirements for USB
A Transient Voltage Suppressor (TVS) diode clamps voltage spikes to a safe level by becoming a low-impedance path to ground when the voltage exceeds its breakdown voltage. For USB protection, you need a bi-directional TVS diode (to handle both positive and negative transients) or a TVS diode array designed specifically for USB lines. The key specification constraints are:
- Clamping voltage: Must clamp below the MCU's absolute maximum voltage on the USB pins (typically 3.6–4.0 V for 3.3 V GPIO). Choose a part with Vclamp ≤ 3.6 V at the rated transient current.
- Standoff voltage: Must be above 3.3 V so the TVS doesn't conduct during normal operation. 5.0 V or 5.5 V standoff is typical for VBUS; 3.3 V standoff for D+ and D- lines.
- Capacitance: This is the critical constraint for High Speed USB. Every picofarad of parasitic capacitance from the protection device degrades signal integrity. For FS (12 Mbit/s), <100 pF is acceptable. For HS (480 Mbit/s), each protection element must be <1 pF.
- Leakage current: Must be negligible at 3.3 V standby voltage — keep <1 µA to avoid pulling D+ or D- below the threshold that the host needs to see for speed detection.
| Vendor |
Part Number |
Lines Protected |
Capacitance |
Clamping Voltage |
USB Speed |
| Nexperia |
PRTR5V0U2X |
D+, D- |
0.45 pF per line |
6.0 V @ 10 mA |
FS and HS |
| STMicroelectronics |
USBLC6-2SC6 |
D+, D-, VBUS |
0.5 pF (D+/D-) |
6.5 V @ 5 mA |
FS and HS |
| Littelfuse |
SP0504BAHTG |
D+, D- |
0.35 pF per line |
7.0 V @ 1 A |
FS and HS |
| ON Semiconductor |
ESD7L5.0DT5G |
Single line |
0.5 pF |
8.5 V @ 1 A |
FS only |
| Vishay |
VESD05C2-02V-GS08 |
D+, D- |
0.8 pF per line |
7.0 V @ 1 A |
FS (marginal for HS) |
ESD Protection Placement Rules
Placement is as important as part selection. An ESD protection device placed far from the connector — even with the right electrical specifications — provides degraded protection because the trace from the connector to the protection device acts as an antenna that couples transients directly into the board. Follow these rules:
- Place the TVS diode array within 500 µm of the USB connector pins. The exact distance depends on trace width and layer stack, but "right next to the connector" is the correct mental model.
- Connect the TVS ground pin to the PCB ground plane directly below the device via a low-inductance via — use multiple small vias in parallel rather than a single via.
- Route the protected trace from the TVS device to the MCU — never route the unprotected trace from the connector past the TVS to the MCU. The trace before the TVS is the "dirty" side; the trace after is the "clean" side.
- Keep the D+ and D- trace lengths from the TVS to the MCU as short as possible — every millimeter of trace adds inductance and capacitance.
USB Cable & Connector Types
Choosing the right connector is not just a mechanical decision — connector type determines the pinout, the mechanical mating force, the expected mating cycles, and whether your device can support USB Power Delivery. Here is the complete picture:
| Connector |
Pins |
Mating Cycles |
Max Current |
Typical Use |
| Type-A Plug |
VBUS, D-, D+, GND |
1,500 |
500 mA (2.0) / 900 mA (3.0) |
Host side (computer, hub) |
| Type-B Plug |
VBUS, D-, D+, GND |
1,500 |
500 mA |
Printers, USB hubs (device side) |
| Mini-B (5-pin) |
VBUS, D-, D+, ID, GND |
5,000 |
500 mA |
Older cameras, GPS units |
| Micro-B (5-pin) |
VBUS, D-, D+, ID, GND |
10,000 |
1.8 A (USB BC 1.2) |
Phones, dev boards (Arduino, STM32) |
| USB-C (24-pin) |
VBUS ×4, D+/D- ×2, CC1/CC2, SBU1/SBU2, GND ×4, TX/RX ×4 |
10,000 |
3 A (5 V) / 5 A with E-marker |
Modern devices, laptops, dev boards |
USB-C CC Pin Orientation Detection
USB-C is rotationally symmetric — the connector can be inserted in either orientation. The CC1 and CC2 (Configuration Channel) pins are the mechanism by which the device detects cable orientation and negotiates power roles. In a basic USB 2.0 device (no Power Delivery):
- The device places a 5.1 kΩ resistor from each CC pin to GND (Rd pull-down).
- The host places a current source (80 µA for 500 mA, 180 µA for 1.5 A, 330 µA for 3 A) on both CC1 and CC2 pins.
- When the cable is inserted, only one CC pin connects to the host's CC pin (the other connects to VCONN, which powers active cables). The voltage on the connected CC pin tells the device how much current the host is advertising.
- The orientation is determined by which CC pin has the host's current source active — this tells the device (or mux IC) which orientation the cable was inserted in, so it can route the D+/D- lines correctly through a multiplexer if necessary.
For a simple USB 2.0 device, you only need the two 5.1 kΩ resistors on CC1 and CC2. No firmware is required for basic cable orientation — the USB 2.0 D+/D- signals are on both sides of the USB-C connector symmetrically (pins A6/B6 for D+, pins A7/B7 for D-).
Cable Impedance Requirements
USB cables must maintain 90 Ω ± 15% differential impedance. This is the same impedance your PCB traces must be designed for. At Full Speed, cable impedance mismatch causes signal reflections that increase the bit error rate. At High Speed (480 Mbit/s), impedance mismatch creates pre-shoot and overshoot that can cause the eye diagram to fail the USB 2.0 compliance mask.
PCB Layout Rules for USB
USB signal integrity is heavily dependent on PCB layout quality. A well-chosen MCU with excellent USB hardware will fail compliance tests if the PCB layout violates the fundamental rules. These rules are not guidelines — they are requirements from the USB specification's signal integrity sections.
Differential Pair Routing
- Length matching: D+ and D- traces must be matched in length to within ±5 mil (0.127 mm) of each other. Unequal lengths cause the common-mode component of the differential signal to increase, degrading noise immunity.
- Spacing: Keep the two traces of a differential pair as close together as practicable — the gap between D+ and D- should be approximately equal to the trace width (typically 4–6 mil for 90 Ω controlled impedance on a typical 4-layer FR-4 board). The closer the traces, the tighter the coupling and the lower the emissions.
- No sharp corners: Route differential pairs with 45° bends or smooth curves. Sharp 90° corners cause impedance discontinuities.
- Avoid vias if possible: Each via in a differential pair adds parasitic inductance and capacitance. Route on a single layer where possible. If vias are unavoidable, use matched pairs of vias placed symmetrically.
Impedance Control
The target differential impedance for USB 2.0 is 90 Ω ± 10% (i.e., 81–99 Ω). Your PCB manufacturer must be asked to control impedance, and your stackup must be designed appropriately. For a typical 4-layer 1.6 mm FR-4 board:
| Trace Width |
Trace Gap |
Layer |
Dielectric to Ground Plane |
Approx Zdiff |
| 8 mil (0.20 mm) |
8 mil (0.20 mm) |
Top (L1) |
100 µm to L2 GND plane |
~90 Ω |
| 6 mil (0.15 mm) |
6 mil (0.15 mm) |
Inner (L2 or L3) |
130 µm to adjacent GND plane |
~90 Ω |
Ground Plane and Keep-out Rules
- Maintain a continuous ground plane directly beneath the USB differential pair traces. Splits or cutouts in the ground plane below the traces create impedance discontinuities and increase loop area (and therefore EMI).
- Establish a keep-out zone of at least 20 mil on each side of the differential pair — no power planes, no unrelated signals, no copper fills in this zone.
- Route the USB traces away from clock signals, especially the 48 MHz USB clock, high-speed SPI clocks, or SDIO clocks. Crosstalk from adjacent clock traces can increase USB jitter above the compliance limit.
- Add a ferrite bead on VBUS between the connector and your onboard filtering capacitors. This reduces conducted EMI from your board back onto the host's power rail. A 600 Ω @ 100 MHz ferrite (e.g., Murata BLM18AG601SN1D) in series with VBUS is a good starting point.
Reference Designator Checklist
Every USB hardware design should include these components. Missing any of them is a common cause of EMC test failures or field reliability problems:
| Ref Des |
Component |
Value / Part |
Purpose |
| D1 |
TVS diode array |
PRTR5V0U2X or USBLC6-2 |
ESD protection on D+, D-, VBUS |
| FB1 |
Ferrite bead |
600 Ω @ 100 MHz |
VBUS EMI filtering |
| C1 |
Decoupling capacitor |
4.7 µF / 10 V ceramic |
VBUS bulk decoupling after ferrite |
| C2 |
Decoupling capacitor |
100 nF / 10 V ceramic |
VBUS high-frequency decoupling |
| R1 |
Pull-up resistor (if external) |
1.5 kΩ 1% |
D+ speed detection (FS) |
| R2, R3 |
CC pull-down resistors (USB-C) |
5.1 kΩ 1% |
USB-C device role indication |
STM32 USB Hardware Initialization
STM32 microcontrollers offer two USB peripheral variants with very different characteristics. Choosing incorrectly early in your design can result in a hardware redesign:
| Peripheral |
Max Speed |
PHY |
External Components |
Typical MCUs |
| USB_FS (non-OTG) |
Full Speed (12 Mbit/s) |
Internal |
None |
STM32F1, STM32F3, STM32L4 |
| USB_OTG_FS |
Full Speed (12 Mbit/s) |
Internal |
None (except pull-up on some variants) |
STM32F2, STM32F4, STM32F7, STM32H7 |
| USB_OTG_HS (internal PHY) |
Full Speed (12 Mbit/s) |
Internal FS PHY |
None |
STM32F4, STM32H7 |
| USB_OTG_HS (external ULPI) |
High Speed (480 Mbit/s) |
External ULPI (e.g., USB3300) |
ULPI PHY chip, 20–30 Ω series resistors |
STM32F4, STM32F7, STM32H7 |
48 MHz USB Clock Configuration
The USB peripheral requires an exactly 48 MHz clock. On STM32F4/F7, this is derived from the main PLL or a dedicated PLLSAI. Common configurations:
- HSE 8 MHz → PLL → 48 MHz: PLL_M=8, PLL_N=336, PLL_P=4 gives a 168 MHz system clock and PLL_Q=7 gives 48 MHz for USB. This is the most common STM32F407 configuration.
- HSI48 (STM32L4, STM32G4): Some STM32 variants have a dedicated 48 MHz RC oscillator that feeds USB directly, with CRS (Clock Recovery System) to trim it against USB SOF packets for <500 ppm accuracy.
/* ---------------------------------------------------------------
* USB GPIO initialization for STM32F407 OTG_FS
* PA11 = OTG_FS_DM (D-)
* PA12 = OTG_FS_DP (D+)
* PA9 = OTG_FS_VBUS (analog input for VBUS sensing)
* ---------------------------------------------------------------*/
void USB_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Enable GPIO clocks */
__HAL_RCC_GPIOA_CLK_ENABLE();
/* ---- PA9: VBUS sensing (analog, no pull) ---- */
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ---- PA11, PA12: D- and D+ (alternate function) ---- */
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Enable OTG_FS peripheral clock */
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
}
/* ---------------------------------------------------------------
* System clock configuration — STM32F407, HSE 8 MHz, 168 MHz AHB
* USB clock derived from PLL_Q = 48 MHz
* ---------------------------------------------------------------*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* Enable HSE oscillator */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8; /* HSE 8 MHz / 8 = 1 MHz VCO input */
RCC_OscInitStruct.PLL.PLLN = 336; /* 1 MHz × 336 = 336 MHz VCO output */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; /* 336 / 2 = 168 MHz system */
RCC_OscInitStruct.PLL.PLLQ = 7; /* 336 / 7 = 48 MHz USB clock */
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/* Set AHB, APB1, APB2 clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_PCLK1 |
RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; /* 168 MHz AHB */
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; /* 42 MHz APB1 */
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; /* 84 MHz APB2 */
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
}
Clock Accuracy Requirement: The USB specification requires the bit clock to be within ±500 ppm (0.05%) of 12 MHz (FS) or 480 MHz (HS). Internal RC oscillators typically have ±1–2% tolerance — they are NOT suitable as USB clocks without CRS hardware trimming. Always use an HSE crystal or the STM32's CRS-trimmed HSI48 for USB clocking.
Hardware Validation Checklist
Before committing a hardware design to production, run through this systematic hardware validation process. Each item requires specific equipment and produces a definitive pass/fail result.
Oscilloscope Eye Diagram Check
An eye diagram is the gold-standard test for USB signal integrity. To create one:
- Connect a differential probe to D+ and D- at the device connector pins (not at the MCU pads — the connector is the compliance test point).
- Trigger the oscilloscope on the USB bit clock (use the frame sync output from a protocol analyser if available).
- Enable infinite persistence mode and accumulate at least 1,000 transitions.
- Overlay the USB compliance eye mask template for your speed (available from the USB-IF compliance test specification documents).
- For Full Speed (12 Mbit/s), the mask requires minimum eye opening of ≥ 300 mV (differential) and the timing margin at the mask crossing point.
- For High Speed (480 Mbit/s), the eye mask is far tighter: minimum 200 mV amplitude, very tight timing margins.
| Validation Item |
Pass Criterion |
Tool Required |
| D+ and D- idle voltage (FS device) |
D+ ≥ 2.8 V, D- < 0.3 V when connected to host |
Digital multimeter or scope |
| Pull-up resistor value |
1.5 kΩ ± 5% on D+ |
LCR meter or resistance measurement |
| VBUS voltage at device connector |
4.75 V – 5.25 V under rated load |
Digital multimeter |
| VBUS inrush current |
Peak charge < 50 µC within first 10 ms |
Current probe + oscilloscope, integrate i(t) |
| Differential impedance of D+/D- traces |
90 Ω ± 10% (81–99 Ω) |
TDR (Time Domain Reflectometry) or impedance analyser |
| Eye diagram (Full Speed) |
All transitions within USB FS eye mask, no violations |
Differential oscilloscope probe + USB eye mask template |
| ESD protection clamping voltage |
V_clamp ≤ 3.6 V at rated transient current (8 × 20 µs pulse) |
ESD simulator + oscilloscope |
| Enumeration on Windows, Linux, macOS |
Device appears in device manager / lsusb / System Information with correct VID/PID |
PC with each OS + USB protocol analyser |
| USB suspend current |
< 2.5 mA total from VBUS during suspend |
Precision ammeter in series with VBUS |
| Cable hot-plug 100 times |
Device enumerates correctly on every plug cycle, no hangs |
Automated test rig or manual testing |
Practical Exercises
These exercises reinforce the electrical and hardware concepts from this article. Work through them in order — each builds on the previous.
Exercise 1
Beginner
Measure USB Idle Voltages
Take a Micro-B or USB-C development board (Arduino, STM32 Nucleo, or similar), connect it to a powered USB hub, and use a multimeter to measure the voltage on D+ and D- relative to GND while the device is idle (not sending or receiving data). Verify that D+ is approximately 2.9 V (pulled up through the 1.5 kΩ device pull-up against the 15 kΩ host pull-down), and D- is close to 0 V. Calculate the theoretical voltage using the voltage divider formula: V = 3.3 × (15 / (1.5 + 15)).
Measurement
Pull-up Resistors
Voltage Divider
Exercise 2
Intermediate
Capture USB Reset Waveform on an Oscilloscope
Connect an oscilloscope (any bandwidth ≥ 100 MHz is sufficient for FS USB) to D+ and D- on a USB device's connector pins. Set the trigger to trigger on both channels going below 300 mV simultaneously (the SE0 condition). Plug in a Full Speed USB device. Capture and measure the USB reset: (a) verify the SE0 duration is ≥ 10 ms, (b) identify the J state before and after the reset, (c) measure the D+ rise time and verify it is within the USB 2.0 specification of ≤ 18 ns for FS. If your scope has a differential mode, use it to view the differential signal directly.
Oscilloscope
USB Reset
SE0 State
Exercise 3
Advanced
Design a USB Connector Front-End for High Speed Operation
Design the complete PCB front-end for a High Speed USB device using a USB-C connector. Your design must include: (a) a USB-C receptacle with correct pinout assignment, (b) 5.1 kΩ pull-down resistors on CC1 and CC2 for device role detection, (c) an ESD protection array with <1 pF capacitance per D+/D- line, (d) a ferrite bead and dual-capacitor pi-filter on VBUS, (e) impedance-controlled routing plan (90 Ω differential) with trace widths and gaps for your chosen PCB stackup. Document each design choice with reference to the USB specification section that mandates or recommends it. Calculate whether your VBUS decoupling capacitance exceeds the 50 µC inrush charge limit.
PCB Design
USB-C
High Speed
ESD
Impedance Control
USB Hardware Design Checklist Generator
Use this tool to document your USB hardware design — target MCU, USB speed, connector type, VBUS protection scheme, ESD components, and PCB notes. Download as Word, Excel, PDF, or PowerPoint for design review or project documentation.
Conclusion & Next Steps
In this article we covered the complete USB electrical and hardware layer:
- Differential signaling with D+ and D- gives USB its noise immunity and low emissions. Understanding the J, K, and SE0 states is foundational to reading oscilloscope traces and protocol analyser output.
- Speed detection via pull-up resistor position is entirely passive and happens before any firmware runs. A 1.5 kΩ pull-up on D+ signals Full Speed; on D- signals Low Speed. High Speed devices start as FS and upgrade via the Chirp K negotiation sequence.
- VBUS management requires attention to voltage range (4.75–5.25 V), inrush current limits (50 µC), and power state transitions (100 mA unconfigured, 500 mA configured, 2.5 mA suspend).
- ESD protection is mandatory for production hardware. Choose a TVS array with <1 pF capacitance for HS USB, place it directly adjacent to the connector, and route the clean (protected) side to the MCU.
- PCB layout is not optional detail — 90 Ω ± 10% differential impedance, matched trace lengths to ±5 mil, continuous ground plane beneath the traces, and a ferrite-filtered VBUS supply are required for reliable hardware.
- The STM32 USB peripheral requires the 48 MHz clock to be running before the pull-up is enabled, and VBUS sensing to be correctly configured for the host to properly manage power.
Next in the Series
In Part 3: Protocol & Enumeration, we ascend from the hardware layer to the protocol layer. We'll examine USB packet structure (SYNC, PID, DATA, CRC fields), the four transaction types (IN, OUT, SETUP, SOF), and — most importantly — the complete nine-step USB enumeration sequence that every device must execute correctly from the first millisecond after attach. You'll see exactly what bytes the host sends and what your device must reply with, with annotated TinyUSB code for each step.
Related Articles in This Series
Part 1: USB Fundamentals & System Architecture
The USB system model, host-device architecture, transfer types, speed tiers, and where TinyUSB fits in the stack.
Read Article
Part 3: Protocol & Enumeration
USB packet structure, transaction types, the complete enumeration sequence, and all descriptor types with annotated TinyUSB examples.
Read Article
Part 17: USB Hardware Design
Advanced PCB design for USB: multi-layer stackup, EMC compliance, USB-C Power Delivery hardware, and production test strategies.
Read Article