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.
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
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
| Bits | Name | Meaning |
|---|---|---|
| [1:0] | MODE | 00=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
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
#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
- Compute the CRL bits to set PA5 (in pins0–7) as push-pull output
- Blink using only BSRR/BRR instead of ODR toggle
- Blink PC13 and PA5 in opposite phase
All lecture materials and example code (with homework and answers) are openly available on GitHub.
View on GitHub ↗