$ show cpq/bare-metal-programming-guide
This repository is a comprehensive bare-metal microcontroller programming guide, providing step-by-step examples from minimal firmware to a full embedded web server with a device dashboard. The code targets ARM Cortex-M and RISC-V microcontrollers (STM32, TI TM4C, RP2040, ESP32-C3, nRF52840, etc.) and uses only GCC, a datasheet, and the Mongoose networking library. The architecture is bare-metal, single-threaded with an event loop in the webserver step; it demonstrates direct register manipulation, interrupt handling, UART, SysTick, clock configuration, printf retargeting via newlib syscalls, CMSIS integration, and Ethernet with built-in TCP/IP (MIP). The repository also includes reusable project templates for blinky, CLI, and LittleFS filesystem. Despite being educational, the code is production-quality and follows embedded best practices such as non-blocking timers, hardware abstraction layers (hal.h), and modular startup code.
The LLM identified 3 files worth highlighting for reuse:
Provides complete, clean syscall stubs (_write, _sbrk, _read, etc.) for Newlib printf retargeting over UART or other physical media. The implementation is minimal, properly handles heap management via _sbrk using a defined heap region, and can be reused verbatim in any ARM Cortex-M project using GCC and Newlib. It demonstrates a well-understood embedded pattern with clear separation of concerns.
Implements the reset handler, vector table, and weak interrupt aliases for ARM Cortex-M microcontrollers. It initialises the .bss and .data sections, calls SystemInit (if available), and jumps to main. Weak aliases for all standard interrupt handlers allow users to override only needed handlers. This file is self-contained, uses only standard CMSIS macros, and can be extracted as a generic startup template for any Cortex-M project.
Demonstrates the timer_expired() pattern (defined in hal.h) using SysTick to create a non-blocking periodic timer. The main loop polls the timer to toggle an LED at a defined interval without busy-waiting. The timer_expired() macro is a reusable, straightforward pattern for scheduling multiple periodic tasks in bare-metal systems, and the example code showcases it clearly.