Switches

5 min read Last updated Fri Jun 12 2026 01:59:21 GMT+0000 (Coordinated Universal Time)

Configurations

Switches are described by 2 parameters:

  • Poles
    Number of independent circuits controlled simultaneously.
  • Throws
    Number of output positions each pole can connect to.

SPST

Single Pole Single Throw. 1 input, 1 output. Either connected or disconnected.

Used for on/off control, power switches, simple button inputs.

SPDT

Single Pole Double Throw. Common terminal connects to either of 2 outputs.

In MCU circuits: 1 throw to VCCV_{CC}, 1 to GND, common to GPIO. Pin is always at a valid level. No pull resistor required.

DPST

Double Pole Single Throw. 2 independent poles, each with 1 throw, ganged on 1 actuator.

Used when 2 circuits must switch simultaneously (e.g. live and neutral in a mains circuit).

DPDT

Double Pole Double Throw. 2 independent poles, each with 2 throws, ganged on 1 actuator.

Used for DC motor direction reversal and switching 2 signal lines between 2 destinations.

Types

Push Button

A momentary contact switch. Circuit closes only while physically held.

Requires a pull-up or pull-down resistor. Without one, the pin floats when released.

Bistable

A latching switch. Each actuation toggles between 2 stable states.

  • MCU reads current state at any time. No interrupt needed.
  • Transition edges still bounce.

Examples: toggle switches, rocker switches.

Monostable

Produces a fixed-duration pulse per actuation regardless of hold duration.

A monostable multivibrator starts a timer on the trigger edge. Output goes HIGH for T=R×CT = R \times C, then returns LOW. Produces a clean, bounce-free pulse. No software debouncing needed.

Edge Detection

Level detection acts continuously while the pin is held. Edge detection acts once per transition.

2 transitions per press-release cycle:

  • Falling edge
    HIGH to LOW. On a pull-up circuit, moment of press.
  • Rising edge
    LOW to HIGH. On a pull-up circuit, moment of release.
uint8_t prev = HIGH;

void loop(void) {
    uint8_t curr = read_pin();
    if (prev == HIGH && curr == LOW)
        handle_press();
    prev = curr;
}

Requires debouncing. Without it, bounce produces multiple spurious transitions per physical press.

Bouncing

When 2 metal contacts meet or separate, elastic rebound causes multiple rapid transitions before settling. Duration: typically 1 ms1\ \text{ms}10 ms10\ \text{ms}.

Clean Bounce

Transitions are sharp. MCU sees valid logic transitions in quick succession.

Dirty bounce

Voltage sits near the forbidden band. Irregular amplitude and timing. Harder to filter.

Causes:

  • Contact oxidation
    Oxide layer causes erratic make resistance during early contact.
  • Contamination
    Dust, flux residue, or moisture increase resistance during partial contact.
  • Worn contacts
    Reduced contact pressure worsens bounce count and edge quality.

Debouncing

Delay and Confirm

After the first edge, block for 20 ms20\ \text{ms}50 ms50\ \text{ms}, then re-sample.

if (button_pressed()) {
    delay_ms(20);
    if (button_still_pressed())
        handle_press();
}

Blocks the CPU for the blanking period. Unsuitable in event-driven or RTOS firmware.

Counter Method

Timer ISR samples the pin periodically. Counter increments while pin holds its new state. State accepted when counter reaches threshold NN.

void debounce_tick(void) {  // 1 ms ISR
    if (read_pin() == NEW_STATE) {
        if (++stable_count >= THRESHOLD) {
            accepted_state = NEW_STATE;
            stable_count = 0;
        }
    } else {
        stable_count = 0;
    }
}

Any bounce resets the counter. Threshold of 10–20 at 1 ms rate gives 10 ms10\ \text{ms}20 ms20\ \text{ms} settle time. Non-blocking.

Shift Register Method

A fixed-width integer (8 or 16 bits) holds recent samples. Each tick, shift left and OR in the current pin state at LSB. State accepted only when all NN bits are identical.

void debounce_tick(void) {  // timer ISR
    shift_reg = (shift_reg << 1) | read_pin();
    if (shift_reg == 0xFF) accepted_state = HIGH;
    else if (shift_reg == 0x00) accepted_state = LOW;
}

Advantages over counter method:

  • No counter to reset. History is implicit in the register.
  • Detects instability over the full window, not just from the last edge.
  • Works correctly during dirty bounce.

Hardware RC

RC low-pass filter on the input line smooths bounce transitions. Schmitt trigger converts the rounded edge back to a clean transition. Time constant must exceed worst-case bounce duration.

Hardware RC + Diode

Diode in parallel with the series resistor creates asymmetric time constants.

  • On press
    Diode forward-biased. Capacitor discharges through diode. Time constant Rdiode×C\approx R_{diode} \times C, very short.
  • On bounce or release
    Diode reverse-biased. Capacitor charges through RR. Time constant =R×C= R \times C, long. Bounce cannot reach VIHV_{IH} before contact re-closes.

Choose R×C10 msR \times C \approx 10\ \text{ms}20 ms20\ \text{ms}.

SR Latch

SPDT switch wired to SR latch (S\overline{S}/R\overline{R} inputs). Moving to 1 throw sets the latch. Moving to the other resets it. Subsequent bounces cannot change the output. Zero software overhead.

Dedicated Debounce IC

RC filter + Schmitt trigger per channel in a single package. Some ICs latch the output until MCU reads it.

Advantages over discrete or software solutions:

  • No CPU cycles consumed.
  • Consistent timing across all channels.
  • Fewer discrete components.

Common ICs:

  • MAX6816 / MAX6817 / MAX6818 (Maxim)
    Single, dual, octal variants. 40 ms40\ \text{ms} debounce time. Internal pull-ups. Active-low output.
  • MC14490 (ON Semiconductor)
    6-channel. Debounce period set by CextC_{ext}: ~1 ms1\ \text{ms} to ~100 ms100\ \text{ms}.
  • SN74LV1T34 + external RC
    Schmitt trigger buffer with discrete RC. Lower cost, more components.
Was this helpful?