A from-scratch bare-metal firmware project for the STM32F103RB (ARM Cortex-M3) Nucleo board.
This repo shows how to build embedded systems without vendor IDEs or auto-generated HAL code : using only Makefiles, linker scripts, and OpenOCD.
Most STM32 tutorials start with CubeIDE or HAL libraries. Here, everything is done manually:
- Custom startup code (vector table + Reset_Handler).
- Hand-written linker script.
- Minimal Makefile toolchain.
- Direct register access (no HAL, no CMSIS).
- Flash + debug using OpenOCD + GDB.
This repo proves end-to-end understanding of ARM Cortex-M architecture and the firmware build pipeline.
- Target MCU: STM32F103RB (Cortex-M3, ARMv7-M)
- Board: Nucleo-F103RB
- Peripherals Used: GPIO (LED blink on PA5)
- Workflow: GCC ARM toolchain + Make + OpenOCD
- Vector table at
0x08000000 - Reset handler:
.datacopy,.bsszero, callmain() - Custom linker script with defined memory regions
- GPIOA clock enable + PA5 LED toggle
- Makefile build system
- Flash/debug with OpenOCD & GDB
- src/ – Application C sources (main.c)
- startup/ – Startup assembly (startup.s)
- include/ – Optional headers
- linker.ld – Custom linker script
- Makefile – Build system
- build/ – Generated build artifacts (.o, .elf, .bin)
- docs/ – In-depth technical explanations
- README.md
- How Cortex-M boots after reset.
- Why startup code matters (stack pointer, Reset_Handler).
- How
.text,.data,.bss, and stack are placed in memory. - How to control peripherals via RCC and GPIO registers.
- How OpenOCD + GDB interact with the MCU via SWD.
arm-none-eabi-gcc→ compile & linkarm-none-eabi-objcopy→ ELF → BIN/HEXOpenOCD→ flash binary & debug bridgeGDB→ live debugging
Pipeline:
source.c ---> gcc ---> .o ---> ld ---> firmware.elf
---> objcopy ---> firmware.bin ---> OpenOCD ---> MCU
Make sure you have the following tools installed before building or flashing this project:
Used to compile and link the firmware for Cortex-M microcontrollers.
sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi gdb-multiarch -yUsed to flash and debug the firmware on STM32 boards.
sudo apt install openocd -yTest detection:
openocd -f interface/stlink.cfg -f target/stm32f1x.cfgFor Makefile-based builds:
sudo apt install build-essential make git -y1. Build project
makeGenerates build/firmware.elf → ELF executable build/firmware.bin → raw binary for flashing
- Flash to board (via ST-Link)
make flash- Run + inspect binary size
make run- Debug with GDB over OpenOCD
bash
make gdb
Opens OpenOCD GDB server.
Launches arm-none-eabi-gdb connected to :3333.
This repo doubles as a learning resource.
- Startup & Reset Flow
- Linker Script Deep Dive
- GPIO & Peripheral Registers
- Toolchain Explained
- ARM Cortex-M3 Architecture Notes
- 🔹 Beginners who want to learn Cortex-M bare metal.
- 🔹 Engineers tired of opaque IDEs/HALs.
- 🔹 Recruiters who want proof of deep embedded expertise.
- Add SysTick timer for delay (instead of busy loops).
- Implement UART driver (printf over USART2).
- Explore interrupts & NVIC.
- Extend docs into a mini Cortex-M bare-metal handbook.
Built with curiosity and first-principles thinking by Ronit.
Always learning, always building.