---
# System prepended metadata

title: How to Connect a Tactile Button to an STM32 Black Pill?
tags: [stm32]

---

The [STM32](https://www.ampheo.com/search/STM32) Black Pill (usually based on [STM32F401](https://www.ampheo.com/search/STM32F401) / F411) makes button input very straightforward, because GPIO pins support internal pull-up and pull-down [resistors](https://www.onzuu.com/category/resistors).
Below is the recommended, industry-standard approach.

![hq720 (55)](https://hackmd.io/_uploads/HyRr_mAHWl.jpg)

**1. Understand the Tactile Button (4-Pin Type)**

A common tactile push button has 4 pins:

```
[ A ] [ A ]
[ B ] [ B ]
```


* Pins on the same side are internally connected
* When pressed: A ↔ B are shorted

You only need one A pin and one B pin

**2. Recommended Wiring (Internal Pull-Up)**
**Why pull-up?**

* Fewer external components
* Stable default state
* Widely used in STM32 designs

**Wiring Diagram (Concept)**
```
STM32 GPIO  ──────┐
                  ├───[ Button ]─── GND
GND ──────────────┘
```

**Physical Connections**

| Button Pin     | Connect To            |
| -------------- | --------------------- |
| One side (A)   | STM32 GPIO (e.g. PA0) |
| Other side (B) | GND                   |


✔ No external resistor needed
✔ Uses STM32 internal pull-up

**3. GPIO Configuration (CubeMX / HAL)**
**GPIO Settings**

* Mode: GPIO Input
* Pull-up / Pull-down: Pull-Up
* Speed: Low (default)

When not pressed:

`GPIO = HIGH (1)`


When pressed:

`GPIO = LOW (0)`

**4. Example Code (HAL, STM32CubeIDE)**
**Read Button State**
```
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
{
    // Button pressed
}
```


Because:

* Pull-up → default HIGH
* Press → pin pulled to GND → LOW

**5. Full Example: Toggle LED with Button**
```
uint8_t last_state = 1;

while (1)
{
    uint8_t current_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

    if (last_state == 1 && current_state == 0)
    {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // LED
        HAL_Delay(50); // debounce
    }

    last_state = current_state;
}
```

**6. Button Debouncing (Very Important)**
**Why?**

Mechanical buttons bounce for 5–20 ms, causing multiple false triggers.

**Software Debounce (Simple)**
`HAL_Delay(20);`

**Better (Timer-Based)**

* Sample every 1 ms
* Require stable state for N samples

**Hardware Debounce (Optional)**
```
GPIO ──┬── Button ── GND
       |
      100nF
       |
      GND
```

**7. Alternative Wiring: Internal Pull-Down**

If you prefer active-high logic:

```
3.3V ─── Button ─── GPIO
GPIO internal pull-down
```

| State    | GPIO |
| -------- | ---- |
| Released | LOW  |
| Pressed  | HIGH |


Either method is fine—pull-up is more common.

**8. Using External Interrupt (EXTI) (Advanced & Clean)**

Instead of polling:

**GPIO Config**

* Mode: GPIO_EXTI
* Trigger: Falling Edge (pull-up case)

**Callback Example**
```
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_0)
    {
        HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    }
}
```


✔ Lower CPU usage
✔ Cleaner logic
✔ Ideal for low-power designs

**9. Electrical Rules You Must Follow**

**Important**

* STM32 GPIO is 3.3V only
* Do NOT connect button to 5V
* Always share common GND
* Never leave input floating (use pull-up/down)

**10. Common Mistakes (Very Common)**

❌ Using diagonal pins of the button incorrectly
❌ Forgetting pull-up / pull-down
❌ No debounce handling
❌ Mixing 5V logic
❌ Assuming button is NO/NC (it’s momentary)

**11. Quick Recommendation (Best Practice)**

**Button → GND + GPIO with internal pull-up + EXTI interrupt**

This is:

* Stable
* Minimal components
* Industry-standard for [STM32](https://www.ampheoelec.de/search/STM32)