← Back to Embedded C series
🔌
STM32
STM32 · Prereq: lesson 13 · STM32 · Prereq: lesson 14

14–15. RCC · GPIO Registers for LED · GPIO Input · Pull-up/Pull-down

We put lesson 13's foundations (CMSIS·RCC·clock) to work for the first time: STM32's "Hello, World" — blinking an LED with registers only, no HAL. The Blue Pill's onboard LED is on PC13, active-low. The key is STM32F1's distinctive GPIO config — 4 bits per pin (MODE 2 + CNF 2) written to CRL(pins0–7)·CRH(pins8–15). Understand this 4-bit combo and every F1 GPIO setup follows the same pattern. 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.

GPIOCRL/CRHODRBSRRPC13push-pullGPIO inputIDRpull-uppull-downfloatingactive-low
Duration
⏱ ~1.5 hours + ~1.5 hours
Level
📊 Intermediate
Prerequisite
🎯 Lesson 13
OUTCOME
Enable the GPIO clock via RCC, set a pin to push-pull output via CRL/CRH 4 bits (MODE+CNF), and toggle the PC13 LED with ODR/BSRR/BRR. / Set input mode (CNF=10, 0x8) with the internal pull-up/pull-down chosen via ODR, read the button via read-only IDR, and control the LED with active-low logic.

What you'll learn

  • 1Enable the GPIO clock via RCC and set pin mode via F1's CRL/CRH
  • 2Make push-pull output 2MHz (0x2) from the MODE/CNF 4-bit combo
  • 3Toggle with ODR and do atomic set/reset with BSRR/BRR
  • 4Understand the active-low LED (PC13) ON/OFF logic
  • 5Preserve other pins with the clear-then-set pattern
  • 6Set GPIO to input mode and read pin state via IDR
  • 7Explain floating-input danger and the need for pull-up/pull-down
  • 8Select the internal pull-up/pull-down via the ODR bit on F1
  • 9Implement active-low/high button logic
  • 10Turn the LED on/off from button input

14. RCC · GPIO Registers for LED

We put lesson 13's foundations (CMSIS·RCC·clock) to work for the first time: STM32's "Hello, World" — blinking an LED with registers only, no HAL. The Blue Pill's onboard LED is on PC13, active-low. The key is STM32F1's distinctive GPIO config — 4 bits per pin (MODE 2 + CNF 2) written to CRL(pins0–7)·CRH(pins8–15). Understand this 4-bit combo and every F1 GPIO setup follows the same pattern.

Introduction

Unlike F4/L4's MODER, F1 writes 4 bits per pin into CRL/CRH. Pin n's 4 bits sit at (n%8)*4 (pins0–7 → CRL, pins8–15 → CRH).

Key concepts

1) F1 GPIO 4 bits = MODE + CNF

BitsNameMeaning
[1:0]MODE00=input, 10=output 2MHz, 11=output 50MHz
[3:2]CNF(output)00=push-pull, 01=open-drain, 10=AF push-pull

LEDs need no speed, so MODE=10(2MHz)+CNF=00(push-pull) → 0b0010 = 0x2. PC13 is at CRH bit (13-8)*4=20, four bits.

2) Clear-then-set + output registers

c
GPIOC->CRH &= ~(0xFu << 20);   /* clear PC13's 4 bits */
GPIOC->CRH |=  (0x2u << 20);   /* set 0010 */

GPIOC->ODR ^= (1u << 13);       /* toggle (read-modify-write) */
GPIOC->BSRR = (1u << 13);       /* atomic set (PC13=1) */
GPIOC->BRR  = (1u << 13);       /* atomic reset (PC13=0) */
ℹ️

BSRR/BRR set/reset only a pin atomically without reading — safer than ODR read-modify-write in interrupt contexts. PC13 LED is active-low, so pin=0 is ON.

Core example

c
#include "stm32f10x.h"
#define LED_PIN 13u
static void delay(volatile uint32_t c){ while(c--){} }
int main(void) {
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;                 /* GPIOC clock */
    GPIOC->CRH &= ~(0xFu << ((LED_PIN-8u)*4u));
    GPIOC->CRH |=  (0x2u << ((LED_PIN-8u)*4u));         /* push-pull output 2MHz */
    while (1) {
        GPIOC->ODR ^= (1u << LED_PIN);                  /* toggle PC13 */
        delay(200000u);
    }
}

CRH bit check: reset 0x44444444 → PC13 to 0x2 → 0x44244444. Add PORTC.13 to the Logic Analyzer to see a periodic square wave.

Common mistakes

Q. I configured it but the pin won't move.

A. Confirm the GPIO clock (RCC->APB2ENR |= RCC_APB2ENR_IOPCEN) is on first. Without it, writes to CRH/ODR have no effect.

Q. Neighboring pins change too.

A. Overwriting with = wipes other pins. Always clear that field with &= ~(0xF<<shift) and OR in the new value.

Q. Why is PC13 at (13-8)*4 in CRH?

A. CRL holds pins0–7, CRH pins8–15. In CRH, pin8 is index 0, so pin13 is index 5 → bit 20.

Summary

  • F1 GPIO writes 4 bits per pin (MODE+CNF) to CRL/CRH (push-pull output 2MHz=0x2)
  • Use clear-then-set to preserve other pins
  • Output via ODR (toggle) and BSRR/BRR (atomic set/reset)
  • PC13 LED is active-low — pin=0 is ON
  • Every action presupposes RCC clock enable

Exercises

  1. Compute the CRL bits to set PA5 (in pins0–7) as push-pull output
  2. Blink using only BSRR/BRR instead of ODR toggle
  3. Blink PC13 and PA5 in opposite phase

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.

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

CNFInput type4 bits
00analog0x0
01floating (reset default)0x4
10pull-up/pull-down0x8
c
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

c
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

c
#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

  1. Switch PA0 to pull-down (ODR=0) with the button to VCC and write the logic
  2. Compare mirroring (LED on while held) vs edge-detect toggle
  3. Explain the precedence trap in masking ((IDR & m) == 0)
Example code / lecture materials

All lecture materials and example code (with homework and answers) are openly available on GitHub.

View on GitHub ↗

Comments 0

No comments yet. Be the first.

Only members can comment.

Comments with ads, abuse, personal information or illegal content are hidden or removed. Comments are shown with your nickname. Privacy Policy