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

cpq/bare-metal-programming-guide: STM32 From a Datasheet and GCC

A bare metal programming guide (ARM microcontrollers)

5,010 stars462 forksCMIT

At a glance

What is it?
A step-by-step C tutorial that builds firmware for ARM microcontrollers without any vendor framework, ending with a UART CLI, littlefs and an embedded web server. It is teaching material first, a template collection second.
Who is it for?
Adopt this guide if you already write C and want to know what Cube or Keil generates on your behalf, or if you need a small starting point for a Nucleo, SAME54, TM4C1294 or RP2040 board. Do not adopt it if you need a vendor-supported HAL, a certified toolchain or long-term maintenance guarantees, because the repository is a tutorial whose templates are tied to specific boards.
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 135 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 November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • cpq/bare-metal-programming-guide5,010 ★ · +112
Star history
Repository2026-09-232026-11-14
cpq/bare-metal-programming-guide4.9k5k

112 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What the guide actually targets: developers who want the datasheet to be enough

The README states the audience directly: developers who wish to start programming microcontrollers using a GCC compiler and a datasheet, without using any framework. That framing matters. Most STM32 material starts from CubeMX, which generates initialization code and hides the register writes behind HAL calls. This guide goes the other way. It explains the fundamentals and, in its own words, helps to understand how embedded frameworks like Cube, Keil, Arduino, and others, work.

So the reader is someone who can already write C and wants to remove a layer. Not a beginner looking for the fastest path to a blinking LED, and not a product team looking for a supported SDK. The repository is organised as numbered steps under steps/, each a complete project that builds on the previous one, plus finished templates under templates/ and steps/step-7-webserver/ for eight board and architecture combinations: Cortex-M4, Cortex-M4F, Cortex-M33, Cortex-M0+ and RISCV. The primary language is C, the licence is MIT, and the last push was on 2026-07-03, so the repository is not archived.

How the steps build up: registers, startup code, linker script, then a web server

The mechanism is progressive disclosure through code. Each step is a self-contained directory with its own Makefile, so you can read the diff between two steps instead of guessing which line matters. The README describes the memory model that underpins everything: the 32-bit address space of the MCU is divided by regions, and firmware reaches hardware by writing to addresses in those regions. A pin goes high when a specific bit in a specific register is set.

From there the tutorial supplies the pieces a framework normally supplies for you: startup code that runs before main, a linker script that places code in flash and data in RAM, and the vector table that routes interrupts. The endpoint is visible in the template list. blinky blinks an LED and prints a debug message periodically. cli adds a UART command line interface with commands to set LED status and hexdump RAM. lfs implements file functions such as mkdir(), readdir() and fopen() on top of littlefs in the upper region of built-in flash, storing a boot count in a file. webui runs an embedded web server with a device dashboard using the mongoose library. That last step is where the guide stops being a toy: webui templates exist for the Nucleo-F429ZI, the EK-TM4C1294XL and the RP2040 Pico-W5500.

The trade-off is that every one of those layers is your responsibility afterwards. The guide shows how littlefs and mongoose are integrated; if you lift the webui template into a product, keeping them current is your job.

Installing the toolchain and building step-0-minimal

The README lists four required tools: ARM GCC for compiling and linking, GNU make for build automation, ST link for flashing, and Git. It gives per-platform install commands. On macOS it uses Homebrew; on Ubuntu it uses apt. Both install the same set, so pick the line for your system and run it in a terminal.

bash
sudo apt -y update
sudo apt -y install gcc-arm-none-eabi make stlink-tools git

On Windows the README does not use a package manager. It points to a specific installer, gcc-arm-none-eabi-10.3-2021.10-win32.exe, with the instruction to enable "Add path to environment variable" during installation, then asks you to create c:\tools and unpack st-flash.exe from the stlink release archive and make.exe from the ezwinports archive into it, add c:\tools to Path, enable Developer Mode for symbolic link support, and install Git with "Enable symlink" checked. That is a manual setup, and the README is explicit about each file rather than offering a script.

With the tools in place, the README's own verification sequence clones the repository and builds the first step. Run these three commands in order.

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

The expected result is a compiled firmware image in that directory, produced by the Makefile that ships with the step. The README stops there for this sequence; it does not show the flash command in the verification section. The tutorial proper uses the Nucleo-F429ZI, and the README tells you to download both the MCU datasheet and the board datasheet for it, which is the point of the exercise: the datasheet is the reference you keep open next to the code.

Where this guide is the wrong tool

The clearest limitation is hardware coverage. The board table is finite: Nucleo-F429ZI, Nucleo-F303K8, Nucleo-L432KC, Nucleo-H563ZI, SAME54 Xplained, TI EK-TM4C1294XL, RP2040 Pico-W5500 and ESP32-C3. If your product runs on a part outside that list, you are not merely adapting a pin number. You are writing startup code, a linker script and peripheral initialization for a memory map the guide has never described.

A second limitation is that the guide is not a library. There is no package to depend on, no versioning scheme, and no published releases. You copy code. When the tutorial's UART driver is good enough for a hexdump command but not for a DMA-driven high-throughput link, nothing in the repository tells you how to get from one to the other.

Third, the absence of a framework is not free. Cube and Keil exist partly because clock tree configuration, errata workarounds and vendor peripheral quirks are tedious and easy to get subtly wrong. A bare metal bring-up that works on a bench can fail across a temperature range or a silicon revision. The guide teaches you to read the datasheet, which is the right skill, but reading is not the same as having validated every register setting against every revision of the part. Treat the templates as a starting point to be verified, not as validated production code.

How it compares with an interrupt-driven HAL project

The natural alternative is a vendor HAL project, for example an STM32CubeIDE project generated from CubeMX with the HAL or LL drivers. The difference in approach is who owns the register writes. In a Cube project, a generated file performs clock and peripheral initialization, and your application calls functions such as HAL_GPIO_TogglePin; the vendor maintains the layer beneath you and ships updates when silicon errata change. In this guide, you write those initialization sequences yourself, guided by the datasheet, and there is no upstream to update.

That changes the debugging experience in both directions. With a HAL, a misconfigured peripheral usually traces back to a checkbox in the generator. With bare metal, it traces back to a bit you did not set, and you have the code in front of you to prove it. The cost is time on first bring-up and the risk of missing a documented erratum.

There is also a middle option worth naming: the guide's own webui template depends on mongoose, and its lfs template depends on littlefs. So the project is not arguing against all third-party code. It argues against a framework that hides the machine. Pulling in a networking library while writing your own GPIO and UART setup is a coherent position, and it is the one the final steps take.

Licence and the cost of keeping a copy current

The repository is MIT licensed, which is permissive and places few conditions on reuse; the LICENSE file is at the top level. That covers the guide's own code. It does not cover the dependencies it pulls in: littlefs and mongoose are separate projects with their own licences, and if you ship the lfs or webui template you need to read those terms yourself. Nothing here is legal advice.

Upgrade cost is the more practical concern. Because the repository has no published releases, there is no changelog to follow and no version to pin. The last push was on 2026-07-03, so the code is not stale, but a tutorial repository does not carry the compatibility promises a library does. If you fork a template, your maintenance burden is the whole firmware: startup code, linker script, drivers and any vendored dependency. The realistic workflow is to vendor what you need at a known commit and track littlefs and mongoose upstream separately, since those are the parts most likely to receive security-relevant fixes.

Editorial conclusion

Adopt this guide if you already write C and want to know what Cube or Keil generates on your behalf, or if you need a small starting point for a Nucleo, SAME54, TM4C1294 or RP2040 board. Do not adopt it if you need a vendor-supported HAL, a certified toolchain or long-term maintenance guarantees, because the repository is a tutorial whose templates are tied to specific boards. Before committing, clone it, run make inside steps/step-0-minimal, and confirm that st-flash detects your board; then check whether your target appears in the board table, since the templates are per-board and the repository has no published releases.

Frequently asked questions

What is bare metal programming in cpq/bare-metal-programming-guide?

The guide describes it as programming microcontrollers using a GCC compiler and a datasheet, without using any framework. In practice that means writing the startup code, linker script and register writes yourself instead of generating them with Cube, Keil or Arduino.

Is the code in cpq/bare-metal-programming-guide written in C or C++?

The repository's primary language is C, and the README frames the tutorial around a GCC compiler and a datasheet. The examples are C projects built with GNU make.

Which tools does cpq/bare-metal-programming-guide require?

ARM GCC for compiling and linking, GNU make for build automation, ST link for flashing, and Git for downloading source code and version control. The README gives setup instructions for Mac, Ubuntu and Windows.

Which boards does cpq/bare-metal-programming-guide cover?

The board table lists STM32 Nucleo-F429ZI, Nucleo-F303K8, Nucleo-L432KC and Nucleo-H563ZI, SAME54 Xplained, TI EK-TM4C1294XL, RP2040 Pico-W5500 and ESP32-C3. The tutorial itself uses the Nucleo-F429ZI.

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)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/cpq-bare-metal-programming-guide.svg)](https://hysenlabs.com/projects/cpq-bare-metal-programming-guide)

Was this useful?