ENGINEERING DOCUMENTATION / PROJECT 01
STM32F103 Bare-Metal Hello World
ARM Cortex-M3 · libopencm3 · Renode 1.17.0
A reproducible C firmware baseline for the STM32F103C8T6: compile, link, load, and run a program that transmits Hello, World! over simulated USART1. No target operating system or physical board was required.
01 / Objective and verified outcome
Develop a bare-metal C application for the STM32F103C8T6, cross-compile it for ARM Cortex-M3, and execute the ARM ELF firmware in Renode. The application sends a single message through USART1 and then remains in an infinite loop.
Hello, World!Result: Successful firmware execution in simulation. Physical STM32 Blue Pill validation remains a separate future step.
02 / Development environment
| Component | Configuration |
|---|---|
| Host OS | Ubuntu Linux |
| Microcontroller | STM32F103C8T6 |
| CPU / architecture | ARM Cortex-M3 / ARMv7-M |
| Cross-compiler | arm-none-eabi-gcc 14.2.1 |
| Peripheral library | libopencm3 (previously built) |
| Simulator | Renode 1.17.0 portable |
| Serial | USART1, 115200 baud, 8N1 |
| Firmware | ARM ELF |
Prerequisites: the ARM cross-toolchain, compiled libopencm3 library, and portable Renode installation.
03 / Project files and paths
The paths below record the development machine used for the successful run. Adjust them when reproducing the project elsewhere.
Application:
/home/mannysoto/Documents/2026/Oct/week5/Sat/embedded/stm32_hello_ms
libopencm3:
/home/mannysoto/GitHub/embedded/libopencm3
Renode:
~/GitHub/embedded/renode/renode_1.17.0-portablestm32_hello_ms/
├── main.c
├── main.o
├── stm32f103.ld
└── hello.elfmain.o and hello.elf are generated build artifacts; main.c and stm32f103.ld are the source files to preserve.
04 / Configure Renode
Start Renode in one Ubuntu terminal:
cd ~/GitHub/embedded/renode/renode_1.17.0-portable
./renodeIn the Renode monitor, create the machine, load the STM32F103 platform, and inspect the CPU and serial peripheral:
mach create
machine LoadPlatformDescription @platforms/cpus/stm32f103.repl
cpu Model
sysbus.usart1
showAnalyzer sysbus.usart1cpu Model reported cortex-m3. The USART1 analyzer opened as a separate, initially blank window. Keep Renode open while building the firmware.
05 / C firmware source
Create the application directory and edit main.c in another terminal:
mkdir -p /home/mannysoto/Documents/2026/Oct/week5/Sat/embedded/stm32_hello_ms
cd /home/mannysoto/Documents/2026/Oct/week5/Sat/embedded/stm32_hello_ms
nano main.c#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
int main(void)
{
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_USART1);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ,
GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
usart_set_baudrate(USART1, 115200);
usart_set_databits(USART1, 8);
usart_set_stopbits(USART1, USART_STOPBITS_1);
usart_set_parity(USART1, USART_PARITY_NONE);
usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
usart_set_mode(USART1, USART_MODE_TX);
usart_enable(USART1);
const char *message = "Hello, World!\r\n";
while (*message) {
usart_send_blocking(USART1, *message++);
}
while (1) {
// Keep firmware running.
}
}The program enables GPIOA and USART1 clocks, configures PA9 as the USART1 transmit pin, sets 115200 baud with 8 data bits, no parity and one stop bit, sends each character, then loops indefinitely. \r\n advances the serial terminal to the next line.
06 / Linker script and memory map
Create stm32f103.ld:
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 64K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
INCLUDE cortex-m-generic.ldThis specifies the target Flash and SRAM layout and includes the generic Cortex-M linker script from libopencm3.
| Region | Start address | Capacity |
|---|---|---|
| Flash | 0x08000000 | 64 KB |
| SRAM | 0x20000000 | 20 KB |
07 / Compile, inspect, and link
Compile main.c to an ARM object file:
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -O0 -g \
-DSTM32F1 \
-I ~/GitHub/embedded/libopencm3/include \
-c main.c -o main.o-DSTM32F1 selects the STM32 family for libopencm3. Inspect the object:
arm-none-eabi-objdump -f main.oExpected identifying fields:
file format elf32-littlearm
architecture: armv7
HAS_RELOC, HAS_SYMSLink the object, library, and target memory layout:
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -g \
-nostartfiles \
-T stm32f103.ld \
-L ~/GitHub/embedded/libopencm3/lib \
-Wl,-L,$HOME/GitHub/embedded/libopencm3/lib \
main.o -lopencm3_stm32f1 \
-o hello.elfThe output is hello.elf, an ARM executable containing the firmware and required library code.
08 / Firmware size
arm-none-eabi-size hello.elfRecorded successful build output:
text data bss dec hex filename
4264 12 0 4276 10b4 hello.elfThe recorded image size fits within the nominal 64 KB Flash and 20 KB SRAM target capacities.
09 / Load and execute in Renode
Return to the configured Renode monitor and load the ELF:
sysbus LoadELF @/home/mannysoto/Documents/2026/Oct/week5/Sat/embedded/stm32_hello_ms/hello.elf
startRenode reported Starting emulation..., and the USART1 analyzer displayed:
Hello, World!The output was observed in simulation, confirming the end-to-end firmware build, load, execution, and serial transmit path.
10 / Quick reproduction reference
After creating main.c and stm32f103.ld, the essential Ubuntu build commands are:
cd /home/mannysoto/Documents/2026/Oct/week5/Sat/embedded/stm32_hello_ms
# Compile
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -O0 -g \
-DSTM32F1 \
-I ~/GitHub/embedded/libopencm3/include \
-c main.c -o main.o
# Link
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -g \
-nostartfiles \
-T stm32f103.ld \
-L ~/GitHub/embedded/libopencm3/lib \
-Wl,-L,$HOME/GitHub/embedded/libopencm3/lib \
main.o -lopencm3_stm32f1 \
-o hello.elf
# Verify
arm-none-eabi-size hello.elfIn an existing configured Renode session:
showAnalyzer sysbus.usart1
sysbus LoadELF @/home/mannysoto/Documents/2026/Oct/week5/Sat/embedded/stm32_hello_ms/hello.elf
startFor a fresh Renode session, first repeat the machine creation and platform-loading steps in Section 04.
11 / Engineering capabilities demonstrated
This completed simulation exercise establishes a reference for ARM cross-compilation, bare-metal C firmware, STM32 peripheral clocks, GPIO alternate functions, USART transmission, linker scripts, embedded memory layout, ELF generation, and Renode execution.
Project status: Completed in Renode simulation. Physical STM32 Blue Pill hardware testing is a separate future milestone.