15. GPIO Input · Pull-up/Pull-down
Lesson 14 sent signals out; now we read them in. A classic trap: a floating input — an unconnected pin is neither 0 nor 1 and wobbles with noise. Pull-up/pull-down resistors fix this, and STM32 has them built in. We set PA0 as input + internal pull-up, read the button via IDR, and control the PC13 LED. The F1 quirk — pull-up/pull-down is selected by ODR — is the key.
What you'll learn
- 1Set GPIO to input mode and read pin state via IDR
- 2Explain floating-input danger and the need for pull-up/pull-down
- 3Select the internal pull-up/pull-down via the ODR bit on F1
- 4Implement active-low/high button logic
- 5Turn the LED on/off from button input
Introduction
Buttons·switches are embedded basics. A floating input drifts between 0 and 1 with noise, so the button may read pressed when it isn't. Pull-up (idle 1) or pull-down (idle 0) fixes the default level.
Key concepts
1) Input-mode 4 bits and pull selection
| CNF | Input type | 4 bits |
|---|---|---|
| 00 | analog | 0x0 |
| 01 | floating (reset default) | 0x4 |
| 10 | pull-up/pull-down | 0x8 |
GPIOA->CRL |= (0x8u << 0); /* PA0 = pull-up/down input */
GPIOA->ODR |= (1u << 0); /* ODR=1 → pull-up, ODR=0 → pull-down */F1 quirk: with CNF=10, the ODR bit picks pull-up (1) or pull-down (0). Skip the ODR setting and the pull-up won't engage.
2) Reading (IDR) and active logic
if ((GPIOA->IDR & (1u << 0)) == 0u) { /* PA0 = 0 (pressed) */ }
/* pull-up + button to GND → idle=1, pressed=0 (active-low) */IDR (Input Data Register) is read-only. Output is via ODR/BSRR/BRR.
Core example
#include "stm32f10x.h"
#define BTN 0u /* PA0 */
#define LED 13u /* PC13 */
int main(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN;
GPIOA->CRL &= ~(0xFu << (BTN*4u));
GPIOA->CRL |= (0x8u << (BTN*4u)); /* input pull-up/down */
GPIOA->ODR |= (1u << BTN); /* pull-up */
GPIOC->CRH &= ~(0xFu << ((LED-8u)*4u));
GPIOC->CRH |= (0x2u << ((LED-8u)*4u));
while (1) {
if ((GPIOA->IDR & (1u << BTN)) == 0u) GPIOC->BRR = (1u << LED); /* pressed ON */
else GPIOC->BSRR = (1u << LED); /* OFF */
}
}Logic check: IDR=0x0001→released(OFF), IDR=0x0000→PRESSED(ON). In the simulator, mimic with `PORTA &= ~0x0001`(pressed)/`PORTA |= 0x0001`(released).
Common mistakes
Q. The value wobbles when nothing is pressed.
A. Pull-up/down isn't enabled — it's floating. Use CNF=10 (0x8) and pick pull-up(1)/pull-down(0) via ODR.
Q. I set CNF=10 but no pull-up engages.
A. On F1 the ODR bit selects pull-up/down. In input mode ODR=1 is pull-up, 0 is pull-down. Don't skip the ODR setting.
Q. Does writing IDR change the pin?
A. IDR is read-only. Output via ODR/BSRR/BRR. IDR only reads the current input state.
Summary
- Input is MODE=00 with CNF picking the type (0x8 = pull-up/down input)
- Enable pull-up/down to avoid floating; F1 selects it via ODR
- Read pin state via read-only IDR
- Pull-up + button to GND = pressed is 0 (active-low)
- Bounce handling is in debouncing (lesson 21)
Exercises
- Switch PA0 to pull-down (ODR=0) with the button to VCC and write the logic
- Compare mirroring (LED on while held) vs edge-detect toggle
- Explain the precedence trap in masking ((IDR & m) == 0)
All lecture materials and example code (with homework and answers) are openly available on GitHub.
View on GitHub ↗