A bare-metal STM32 project that demonstrates LED blink delay control using two push buttons, implemented using direct register access (no HAL GPIO APIs for logic control).
This project focuses on real-world embedded behavior: timing control, input handling, and boundary protection — all at the register level.
The goal of this project is to dynamically control LED blink speed using two buttons:
- LED on Pin PA5 (Arduino D13 / Pin 13)
- Button 1 on Pin PA6 (Arduino D12 / Pin 12) → Increase delay
- Button 2 on Pin PA7 (Arduino D11 / Pin 11) → Decrease delay
| Action | Result |
|---|---|
| Button-1 pressed | Delay increases by 100 ms |
| Button-2 pressed | Delay decreases by 100 ms |
| Delay range | 0 ms to 1000 ms |
| LED behavior | Blinks continuously using current delay |
This mimics a human-controlled timing system, a pattern widely used in industrial HMI logic.
This project intentionally avoids HAL GPIO logic to help you:
- Understand how GPIO really works
- Gain full control over timing & pin state
- Eliminate abstraction overhead
- Debug hardware with absolute clarity
Everything is driven by:
RCC->AHB1ENRGPIOx->MODERGPIOx->IDRGPIOx->ODR- SysTick timing
No magic. No shortcuts.
| Component | STM32 Pin | Arduino Label |
|---|---|---|
| LED | PA5 | D13 |
| Button – Increment | PA6 | D12 |
| Button – Decrement | PA7 | D11 |
Buttons should use pull-down or pull-up logic (external or internal).
STM32-LED-BUTTON-CTRL-Register-Coding-Method/
├── Core/
│ ├── Src/
│ │ └── main.c # Register-level LED & button logic
│ └── Inc/
│ └── main.h
├── Drivers/
│ └── CMSIS/ # Device headers & startup code
├── startup_stm32f446xx.s
├── STM32F446RETX_FLASH.ld
├── .project
├── .cproject
└── README.md
All logic lives inside main.c.
Enable GPIOA and SysTick clocks using RCC.
- PA5 → Output (LED)
- PA6, PA7 → Input (Buttons)
Configured manually via MODER.
- Delay variable starts at 500 ms (default)
- Button presses update delay dynamically
- Delay is clamped between
0–1000 ms
if (button_inc_pressed) {
delay += 100;
}
if (button_dec_pressed) {
delay -= 100;
}Boundary protection ensures:
if (delay > 1000) delay = 1000;
if (delay < 0) delay = 0;LED toggles continuously using the current delay value:
GPIOA->ODR ^= (1 << 5);
delay_ms(current_delay);This creates real-time visual feedback based on button input.
git clone https://github.com/DanielRajChristeen/STM32-LED-BUTTON-CTRL-Register-Coding-Method.gitCubeIDE is used only as compiler and debugger
- Import existing project
- Build once
- Connect board via ST-LINK
- Run / Debug
- Press buttons and watch LED speed change live
By completing this project, you’ll understand:
- GPIO input & output at register level
- Button-driven control systems
- Timing control using SysTick
- Delay clamping & boundary conditions
- Real-time firmware behavior
This is core embedded thinking, not just coding.
- Beginner-friendly embedded timing demo
- Register-level GPIO practice
- Interview preparation project
- PCB button/LED validation
- Transition step from HAL → Bare-metal
MIT License
Copyright (c) 2025 Daniel Raj Christeen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.