DEV Community

Hedy
Hedy

Posted on

How to wire a momentary switch to STM32?

Here’s a rock-solid, no-mystery way to wire a momentary pushbutton to an STM32, plus simple code that “just works.”

Recommended wiring (active-low with internal pull-up)

Most STM32 pins have an internal pull-up. Use it—then the switch only needs two wires.

Connections

STM32 GPIO pin ────────┐
                       ├─── momentary switch ─── GND
(Enable internal PULL-UP)
Enter fullscreen mode Exit fullscreen mode

Optional noise hardening

  • 100 Ω in series between GPIO and switch (limits ESD/spikes).
  • 0.1 µF from GPIO to GND (with 10–50 kΩ pull-up → ~1–5 ms debounce RC).

Why this way?
Input idles at logic 1 via pull-up. Pressing the button shorts to GND → 0 (active-low). Fewer parts, safer if a wire comes loose.

STM32 GPIO is 3.3 V logic. Don’t let the pin ever see >3.6 V. Tie the other side of the switch to GND, not 5 V.

Alternative (active-high with pull-down)

If you prefer reading 1 when pressed:

VDD (3.3 V) ──┬── momentary ── GPIO
              └─ 10 kΩ ─────── GND   (external pull-down)
Enter fullscreen mode Exit fullscreen mode

STM32s usually don’t have strong internal pull-downs; use an external 10 kΩ.

Minimal HAL code (polling + simple debounce)

#include "stm32f1xx_hal.h" // change series to match your MCU

#define BTN_GPIO_Port GPIOA
#define BTN_Pin       GPIO_PIN_0       // your chosen pin
#define LED_GPIO_Port GPIOC
#define LED_Pin       GPIO_PIN_13      // e.g., Nucleo LED

static uint32_t lastChange = 0;
static uint8_t  stable = 1, prev = 1;  // active-low input

int main(void) {
  HAL_Init();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();

  // Button as input with internal pull-up
  GPIO_InitTypeDef gi = {0};
  gi.Pin  = BTN_Pin;
  gi.Mode = GPIO_MODE_INPUT;
  gi.Pull = GPIO_PULLUP;       // <-- enables internal pull-up
  HAL_GPIO_Init(BTN_GPIO_Port, &gi);

  // LED output (for testing)
  gi.Pin   = LED_Pin;
  gi.Mode  = GPIO_MODE_OUTPUT_PP;
  gi.Pull  = GPIO_NOPULL;
  gi.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED_GPIO_Port, &gi);

  while (1) {
    uint8_t raw = HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin); // 1 = released, 0 = pressed
    uint32_t now = HAL_GetTick();

    // 10 ms debounce
    if (raw != prev) { prev = raw; lastChange = now; }
    if (now - lastChange > 10 && raw != stable) {
      stable = raw;
      if (stable == 0) { // on press
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Interrupt (EXTI) version (no polling)

Configure the pin as EXTI on falling edge (active-low press), then handle it in the callback.

// Enable clock as above…

// Configure EXTI on BTN pin (falling edge)
GPIO_InitTypeDef gi = {0};
gi.Pin  = BTN_Pin;
gi.Mode = GPIO_MODE_IT_FALLING; // interrupt on press (to GND)
gi.Pull = GPIO_PULLUP;
HAL_GPIO_Init(BTN_GPIO_Port, &gi);

// Enable NVIC for the correct EXTI line
HAL_NVIC_SetPriority(EXTI0_IRQn, 5, 0);  // if BTN_Pin is PA0
HAL_NVIC_EnableIRQ(EXTI0_IRQn);

// IRQ handler (provided by HAL templates)
void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(BTN_Pin); }

// HAL callback (debounce light with timestamp if needed)
void HAL_GPIO_EXTI_Callback(uint16_t pin) {
  static uint32_t last = 0;
  if (pin == BTN_Pin) {
    uint32_t now = HAL_GetTick();
    if (now - last > 10) { // 10 ms debounce
      HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
      last = now;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Pick the correct EXTI IRQ for your pin (e.g., EXTI0 for Px0, EXTI1 for Px1, …, EXTI15_10 for Px10–Px15). On many Nucleo boards the user button is PC13, which uses EXTI15_10.

Practical tips

  • Debounce: 5–20 ms is typical. Use either the RC shown above or software (or both for long cables).
  • Long leads / noisy environments: add the 100 Ω series resistor and 0.1 µF to GND at the MCU side; run GND and signal as a twisted pair.
  • Multiple buttons: give each its own pin; you can share the same pull-up and debounce separately in software.
  • Pin choice: any GPIO with EXTI works; avoid pins with special boot functions unless you know their state at reset.

Top comments (0)