CLI tool
cpq/bare-metal-programming-guide avatar
cpq/bare-metal-programming-guide

bare-metal-programming-guide: ARM Microcontrollers Without Any Framework

A bare metal programming guide (ARM microcontrollers)

4,912 stars450 forksCMIT

At a glance

What is it?
This guide walks developers through programming ARM microcontrollers using only a GCC compiler and a datasheet, with no Cube, Keil, Arduino, or other framework in the picture. Each chapter adds functionality incrementally, and complete buildable source code accompanies every step.
Who is it for?
This guide suits embedded developers who want to understand what their development environment is actually doing when it compiles and flashes firmware. It is not a shortcut to productivity: working through it requires a physical development board, patience with datasheets, and comfort at the command line.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 89 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The Gap This Guide Fills in Embedded Development

Most embedded tutorials start with a framework: install STM32CubeIDE, open a project wizard, configure a pin, and run the generated code. The framework works, but the developer has no model for why it works. When the generated initialization fails or a peripheral behaves unexpectedly, there is no conceptual foundation to debug from. This guide takes a different approach: it starts from a minimal C program, explains how the linker script maps code to flash and RAM, shows how to toggle a GPIO pin by writing to a memory-mapped register, and then builds upward from there. The goal stated in the README is to explain the fundamentals and help the reader understand how embedded frameworks work, not to replace them in production.

Board Coverage and Repository Structure

The guide uses the Nucleo-F429ZI as its primary teaching board, a Cortex-M4 development board from STMicroelectronics. It also provides template projects for the Nucleo-F303K8, Nucleo-L432KC, Nucleo-H563ZI (Cortex-M33), SAME54 Xplained (Cortex-M4), TI EK-TM4C1294XL (Cortex-M4F), RP2040 Pico-W5500 (Cortex-M0+), and ESP32-C3 (RISC-V). The steps/ directory holds the progressive examples, numbered step-0 through step-7. The templates/ directory holds four ready-to-use starting points: blinky (LED blink with debug output), cli (UART command line interface with hexdump support), lfs (filesystem using littlefs in upper flash, storing a boot count across resets), and webui (embedded web server with a device dashboard using the mongoose library).

Setting Up the Toolchain and Building the First Example

The guide requires ARM GCC for compiling and linking, GNU make for build automation, ST link for flashing, and Git. On macOS:

sh
$ brew install gcc-arm-embedded make stlink git

On Linux (Ubuntu):

sh
$ sudo apt -y install gcc-arm-none-eabi make stlink-tools git

After installing the tools, verify the setup by cloning the repository and building the minimal example:

sh
git clone https://github.com/cpq/bare-metal-programming-guide
cd bare-metal-programming-guide/steps/step-0-minimal
make

Windows setup requires downloading and manually placing several binaries and adding them to the PATH. The guide documents specific file names and versions for the GCC installer, stlink, and make executables, and requires enabling Developer Mode for symbolic link support. Each step directory has its own Makefile, so building is consistent throughout the guide. The .github/ directory contains a GitHub Actions workflow that builds the examples as part of CI, which verifies that the toolchain setup described in the README produces valid firmware on each commit.

Core Concepts Covered: From Registers to Networking

The guide explains the ARM 32-bit address space and how it is divided into regions: flash at a fixed address, RAM at another, and peripherals in a memory-mapped I/O region. Setting a GPIO pin high or low means writing a specific bit to the register at that peripheral's base address. The guide shows how to read a microcontroller datasheet to find those addresses and bit definitions. Later chapters cover UART for serial output, the NVIC (nested vector interrupt controller) for interrupt handling, SysTick for system timing, SPI for peripheral communication, and, in step-7, Ethernet for a working embedded web server. The curriculum ends with a professional-grade browser dashboard, which demonstrates that the same fundamentals carry through to non-trivial applications.

What the Guide Does Not Teach: Real Gaps to Consider

The guide focuses on Cortex-M and RISC-V microcontrollers specifically. It does not cover real-time operating systems (RTOS), USB device stacks, Bluetooth, or Wi-Fi beyond what the mongoose library handles in the web server example. The README does not document a simulation or emulation path: all examples require physical hardware. The RP2040 and ESP32-C3 board templates exist but the narrative chapters use the Nucleo-F429ZI; developers targeting other architectures must transfer the concepts themselves. Debugging beyond serial output, such as JTAG-based source-level debugging, is not covered in the guide sections visible in the repository README. The images/ directory contains diagrams used in the written guide, referenced from the README text to illustrate how memory regions map to physical registers and how the linker places code sections.

Comparison with Framework-Based Development

STM32CubeIDE and the HAL (Hardware Abstraction Layer) library let a developer initialize UART in a few clicks and generate boilerplate code. The trade-off is opacity: the generated code is hundreds of lines, and the developer's mental model of what the microcontroller is doing is thin. The Arduino ecosystem trades even more detail for an even simpler entry point. This guide occupies the opposite end of the spectrum: the developer writes the linker script, the startup code, and the peripheral initialization by hand. The payoff is a deep, transferable understanding of embedded systems. For developers who want to modify or debug a HAL driver or port to a chip without an existing HAL, the knowledge built by working through this guide applies directly.

Repository License and Maintenance

The guide is licensed under the MIT License, which places no restrictions on commercial use or redistribution. The last push was on 2026-07-03. The repository has no GitHub releases; versioning is informal, tracked through commits. The README is available in English, Chinese (README_zh-CN.md), and Turkish (README_tr-TR.md), suggesting the guide has attracted readers across language communities. Continuous integration is configured through GitHub Actions, and the badge in the README indicates the CI status. The four template types (blinky, cli, lfs, webui) vary in which boards they support: the Nucleo-F429ZI is the most fully covered, with templates for blinky, cli, and webui, while some boards like the SAME54 Xplained only have a blinky template. Developers targeting a board that lacks a template for their desired starting point will need to adapt one from the most similar supported board. The guide's scope is intentionally narrow: it teaches the fundamentals, not production firmware engineering practices like unit testing embedded code, continuous integration for embedded targets, or bootloader design.

Editorial conclusion

This guide suits embedded developers who want to understand what their development environment is actually doing when it compiles and flashes firmware. It is not a shortcut to productivity: working through it requires a physical development board, patience with datasheets, and comfort at the command line. Engineers who need to ship a product quickly on a supported platform are better served by a framework. Those who need to port to an unsupported chip, debug a hardware abstraction layer, or understand why a peripheral initialization sequence fails silently will find the material directly applicable. The last push was on 2026-07-03. Start with step-0-minimal in the steps/ directory: building that example with make on your target board is the quickest check that your toolchain is configured correctly.

Frequently asked questions

What is bare metal programming?

Bare metal programming means writing firmware that runs directly on a microcontroller with no operating system or hardware abstraction framework in between. The developer configures peripherals by writing directly to the hardware registers documented in the chip's datasheet.

What is bare metal C programming?

Bare metal C programming means using the C language to write code that interacts directly with a microcontroller's memory-mapped registers, interrupt vectors, and linker script without any framework, RTOS, or HAL library mediating those interactions.

What does bare metal firmware mean?

Bare metal firmware is software that runs directly on hardware without an operating system. On an ARM microcontroller, the firmware is the only code running, and it handles all hardware initialization, peripheral configuration, and application logic directly.

Is STM32 programmed in C or C++?

The examples in this guide are written in C. STM32 microcontrollers can also be programmed in C++, though the guide does not cover C++ usage. The ARM GCC toolchain supports both languages.

Official sources

  1. cpq/bare-metal-programming-guide on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/cpq-bare-metal-programming-guide.svg)](https://hysenlabs.com/projects/cpq-bare-metal-programming-guide)