From button to beatSTM32F411RE · a bare-metal rhythm-game learning project
START HERE · THE DESIGN DECISION

A rhythm game is a clock
with physical inputs.

Learn the path from a switch contact to a measured timing error—one small experiment at a time. STM32F411RE / NUCLEO-F411RE, bare-metal C and CMSIS.

RECOMMENDED LEARNING PATH

Poll first. Then EXTI + a free-running TIM2.

Polling makes the electrical behavior visible. A 1 MHz TIM2 counter gives one consistent timebase. EXTI then teaches interrupt routing and captures a timestamp promptly without making main-loop speed the normal measurement limit. Keep debounce and judgment in main. Only move to input capture after measuring the jitter you want to remove.

ApproachWhat you actually measureRhythm-game trade-offPlace in this course
A · Main-loop pollingFirst read that notices the changed GPIO level.Simple and easy to debug. Delay ranges from nearly zero to the maximum time between reads. Display/audio/blocking work makes that interval variable. A tightly bounded fast loop can be entirely adequate for tens-of-ms windows.Stage 1 baseline; TIM2 still supplies timestamps.
B · EXTI + TIM2CNT read after the CPU enters the handler.Flexible GPIO routing, good learning value and usually ample performance for ±25/100 ms windows. Jitter remains from interrupt service, masking and electrical bounce. EXTI cannot recover the original edge time after long masking.Core project architecture.
C · Timer input captureHardware-latched count when an input edge is recognized.Most deterministic edge timestamp here; ISR delay need not change the latched value. Limited channels and AF pins; filters add delay; overcapture loses data; still needs debounce. Great for measuring, not automatically necessary for human taps.Stage 13 controlled comparison.
D · SysTickCommonly a software millisecond count updated by a core exception.Easy coarse scheduler, but 1 ms ticks quantize timestamps. Delayed or coalesced interrupts can invalidate naive “one ISR means exactly one elapsed tick” logic. Sub-tick VAL reads are possible but rollover races complicate them.Understand it; avoid a second game clock initially.
E · DWT cycle counterCore cycles at the point software reads CYCCNT.Fine resolution for short profiling, but an ISR read still includes interrupt latency. Frequency changes, wrap and sleep/debug behavior complicate song time. No GPIO capture latch.Measure code cost after the basics.

SysTick is a 24-bit core down-counter with CTRL/LOAD/VAL and no RCC peripheral gate; a 1 kHz interrupt does not make GPIO edges interrupt-driven. DWT is also a core facility. TIM2 runs independently of ordinary CPU instructions, but its rate still depends on the RCC clock tree. PM0214 · p. 246 · SysTick.

Accuracy has several different meanings

Resolution

1 µs timer steps determine the smallest count difference. They do not eliminate input or audio delay.

Latency & jitter

How late, and how variably, the event is observed. Fixed latency can be calibrated; variable delay cannot be removed with one offset.

Clock accuracy

How closely the time ruler follows real seconds and the audio clock. A fine-resolution oscillator can still drift.

The route you will build

Click a stage to follow the electrical and CPU responsibilities.

How to use the course

Read Hardware checks before connecting the module. Complete each numbered stage in order, test its expected result, and perform the experiment before continuing. Code blocks are incremental exercises, not one finished game to paste into main.c. Stage 1 supplies a tiny standalone main; later stages replace loop fragments or add helpers. A comment naming a later integration step is a deliberate exercise, not a hidden library.

Use a CMSIS device project with the STM32F411xE startup/vector table, linker script and system_stm32f4xx.c. Include stm32f411xe.h. Build for Cortex-M4/Thumb with the project’s consistent floating-point ABI. Keep one main and one definition per ISR; no HAL initialization or Arduino calls are required.