The MCUXpresso Config Tool will happily generate a pin_mux.c for every pin on the board in about four clicks. On my Control Unit mainboard it did exactly that, and it set every USDHC1 pin — CMD, CLK, DATA0-3, card detect — to 100K Pull Down.

That is wrong in two separate ways at once. The SD specification requires CMD and DAT to read high when undriven during card identification, so a pull-down can break enumeration outright. And the card-detect net already had a 10 kΩ external pull-up on the schematic, which the internal pull-down was now fighting.

The tool is not broken. It just does not know about my external resistors or my protocol. Nobody had told it, and I had not checked.

In this article I show what each pad-control field does, how to derive its value from the schematic and the protocol spec, and a cheat sheet you can run through before you hit Generate.

flowchart TD
    A["For each routed pin"] --> B{"Driven by exactly<br>one fixed source?"}
    B -->|yes| C["Pull/Keeper = Disabled"]
    B -->|no| D{"External pull resistor<br>already on the net?"}
    D -->|yes| E["Pull/Keeper = Disabled<br>don't fight it"]
    D -->|no| F{"Protocol requires a<br>defined idle level?"}
    F -->|yes| G["Pull, PUS matches<br>the required level"]
    F -->|no| H{"Bidirectional bus,<br>always driven by someone?"}
    H -->|yes| I["Keeper"]
    H -->|no| C

The decision I should have walked for each pin before generating anything.

TL;DR

Pull actively biases a line toward a rail. Keeper only latches the last driven level. Disabled lets the pin float. Pick by asking whether the line is ever genuinely undriven — then check the protocol spec, because SD and I²C demand pull-ups even when the schematic shows nothing. Never let an internal pull fight an external resistor.

Step 1 — Know what each field does

Hysteresis (HYS)

Enables a Schmitt-trigger input buffer instead of a plain comparator.

  • What it is for: rejecting slow, noisy, or bouncy transitions on an input so the pin does not chatter or false-trigger.
  • Enable for: mechanical switches and buttons, open-drain shared lines, any input with a slow or noisy edge (long trace, RC-filtered line).
  • Leave disabled for: clean, fast, actively-driven digital signals. It buys nothing there.

Pull Up/Down Config (PUS)

Selects the value of the internal pull resistor — typically 100K Pull Down, 47K Pull Up, 100K Pull Up, 22K Pull Up on the RT1062.

Only matters if Pull/Keeper is set to Pull and enabled. It picks the rest state of a pin that can be undriven — tri-stated, open-drain, or a true input with no other bias.

Pull/Keeper Select + Enable (PUE / PKE)

This is the field people mix up most.

  • Pull enabled: the pad actively biases the line toward the configured rail through a real resistor. It will source or sink current to hold that level even with nothing else driving.
  • Keeper enabled: the pad only latches the last driven logic level with a very weak feedback loop. It does not pull toward a fixed rail — it prevents the line drifting between drive events, and it will not fight an active driver on either side.
  • Disabled: the pin floats completely when undriven. Fine for a pin that is always actively driven by exactly one side, such as a clock output.

Rule of thumb

Use Pull when a line has real periods of being genuinely undriven and needs a defined level — protocol requirement, tri-state bus idle, mechanical input. Use Keeper for a bidirectional bus line that is always driven by someone, just to stop it floating during the microsecond turnaround. Use Disabled for a pin always driven by exactly one fixed source (most outputs, most clocks).

Drive Strength (DSE)

Sets the pad’s output impedance, expressed as a fraction of a reference impedance — R0, about 260 Ω at 3.3 V nominal on RT1062 pads. A lower divisor (R0/7) means a stronger driver, lower output impedance, more current, sharper edges.

  • Stronger drive is needed for longer traces, higher capacitive load, or multiple parallel receivers, to keep edges clean at the far end.
  • Stronger drive on a short, lightly-loaded trace mostly adds ringing, overshoot and EMI — no timing benefit.
  • Match it to what is actually on the schematic: trace length, number of loads, whether there is series termination.

Slew Rate (SRE: Fast/Slow)

Controls how fast the output transitions between logic levels, independently of drive strength.

  • Fast: sharper edges, needed to meet setup/hold timing at higher clock and data rates.
  • Slow: softer edges, reduces overshoot, ringing and EMI. Prefer it whenever the timing budget allows.

Faster is not automatically better. It is a timing-margin-versus-EMI trade.

Open Drain Enable (ODE)

Forces the output stage to open-drain — only pulls low, never actively drives high — regardless of what the peripheral logic thinks it is doing.

  • Needed for genuine wired-AND buses (classic I²C SCL/SDA) or when the schematic shows an external pull-up doing the high side on purpose.
  • Do not enable it on push-pull peripheral signals (SPI, most USDHC lines in normal push-pull mode) — you will break the high level entirely.

Speed (pad frequency class)

A coarser frequency-class setting some RT1062 pads expose alongside SRE, e.g. fast(150MHz). Pick the class that comfortably covers your actual signal frequency, not the fastest available by default — the fastest class trades more EMI and overshoot for margin you may not need.

Step 2 — Derive the values from the schematic

For every pin, walk this sequence before touching the config tool:

  1. Read the direction and role off the schematic. Input, output, or bidirectional? Actively driven by exactly one side, or can it be undriven or tri-stated?
  2. Look for an external resistor already on the net. If the schematic already has a discrete pull to a rail, that resistor wins electrically — the internal pad setting should be disabled, or at most a consistent, weaker bias in the same direction. Never fight an external resistor with an opposing internal pull.
  3. Check the protocol spec for the peripheral, not just the schematic. Some interfaces have required pull directions during specific phases — SD command/identification mode requires pull-ups — even if nothing on the schematic looks like it needs one.
  4. Estimate trace length and loading to pick drive strength. Short on-board trace to one load → moderate. Longer trace, connector, multiple loads → stronger.
  5. Match slew rate and speed class to the frequency you will actually run, not the peripheral’s theoretical maximum. Leave margin by picking slow unless timing demands fast.
  6. Set hysteresis only where the input can be slow-edged or noisy.

Step 3 — Worked example: the microSD interface

The facts from the SD-card sheet:

  • SD-A_CMD, SD-A_CLK and SD-A_DATA[0:3] go directly from the RT1062 USDHC1 pads to the microSD socket — short, single-load, on-board traces.
  • SD-A_nCD (card detect) has an external 10 kΩ pull-up to 3V3MEM already on the schematic; the card shorts it to ground when inserted.
  • SD-A_nRST drives a transistor that gates the SD card’s power switch. It is a GPIO push-pull output, not a bus signal.
PinDirectionPull/KeeperDriveSlewWhy
CLKOutput (host-driven)DisabledR0/6Slow (Fast only for SD High-Speed 50 MHz)Always actively driven by the host — a pull just adds load; a short trace does not need max drive
CMD, DATA0-3Bidirectional (open-drain during identification, push-pull after)Pull, PUS = 47K/100K Pull-UpR0/6Slow (Fast for High-Speed)The SD spec requires these to read high when undriven during card identification; a pull-down here can break enumeration
CD (card detect)InputDisabled — the external 10 k already owns the linen/an/aAn internal pull-down directly opposes the external pull-up; let the schematic’s resistor do its job
RESETOutput (drives the power-switch transistor)Pull-Down (idle-safe: card held powered-off before GPIO init)Low/defaultSlowPush-pull GPIO output — the pull setting only matters before the GPIO driver takes over at boot

The bug this article exists for

The initial config-tool export for this board set every USDHC1 pin — including CMD, DATA and CD — to 100K Pull Down. That contradicts the SD spec, which wants CMD and DAT high when idle, and it fights the external 10 kΩ pull-up on card detect. Always eyeball the bulk-generated defaults against the schematic: the config tool does not know about your external resistors or your protocol requirements.

Step 4 — Run the cheat sheet before generating

QuestionAnswer
Pin is always driven by exactly one fixed source (e.g. a clock output)?Pull/Keeper → Disabled
Pin can float or be undriven, and the protocol or circuit needs a defined idle level?Pull/Keeper → Pull, PUS direction matching the required idle level
Bidirectional bus line, always driven by someone, just needs to not float during turnaround?Pull/Keeper → Keeper
Schematic already has a discrete pull resistor on this net?Internal pull → Disabled (do not fight it)
Protocol spec requires a specific pull direction (SD CMD/DAT, I²C)?Follow the spec even if the schematic shows nothing — pull-up for SD/I²C-style lines
Signal is a true wired-AND bus (I²C, shared open-drain line)?Open Drain = ON, plus external pull-up
Signal is a push-pull peripheral output (SPI, USDHC normal mode)?Open Drain = OFF
Long trace, multiple loads, or a connector in the path?Higher drive strength (lower R0/N divisor)
Short on-board trace, single load?Moderate drive strength (R0/6) — do not over-drive
High-frequency or tight-timing signal?Fast slew rate, matched speed class
Lower-speed or noise-sensitive signal with timing margin available?Slow slew rate — free reduction in ringing and EMI
Input from a mechanical switch or slow/noisy source?Hysteresis = Enabled
Clean, fast, actively-driven digital signal?Hysteresis = Disabled (no benefit)

Summary

The pad-control fields are not style choices — three of them (pull direction, open-drain, and to a lesser extent drive strength) can stop a peripheral working at all. The config tool generates defaults from the pin function, not from your board, so its output is a starting point rather than an answer.

What works now: the SD card enumerates reliably with pull-ups on CMD/DAT and the internal pull disabled on card detect. What I would do differently: walk the decision tree at the top of this article before the first pin_mux.c generation, not after the card failed to mount.


💬 Questions, corrections, or your own war story with IOMUXC defaults? Leave a comment below — I read and answer all of them.