If you are starting with embedded systems on ARM Cortex-M microcontrollers, you will often hear about bare-metal programming. This means working directly with the hardware without an operating system managing tasks. This post explains how your C code transforms into something that runs on a microcontroller by covering the compilation, linking, and flashing steps in the context of ARM Cortex-M devices.
Most development happens on x86-based computers like laptops or desktops, but microcontrollers such as STM32 use ARM architecture. To build code for these devices on your PC, you need cross compilation. This allows you to compile programs for a different CPU architecture than your development machine. The most widely used toolchain for ARM Cortex-M development is arm-none-eabi-gcc
, part of the GNU Arm Embedded Toolchain.
Here is an overview of how a C program gets built for an ARM microcontroller:
main.c
, producing main.i
.main.s
.main.o
.main.elf
.arm-none-eabi-gcc -c -mcpu=cortex-m4 -mthumb -S file.c -o file.o
This command instructs the compiler to generate assembly code optimized for a Cortex-M4 processor using the Thumb instruction set.
To simplify the build process, you can use a Makefile that compiles multiple source files and links them into one executable. Here is a basic example:
CC = arm-none-eabi-gcc
CFLAGS = -c -O0 -mcpu=cortex-m4 -mthumb -std=gnu11 -Wall
LDFLAGS = -nostdlib -T stm32_ls.ld -Wl,-Map=final.map
all: main.o led.o stm32_startup.o final.elf
main.o: main.c
$(CC) $(CFLAGS) main.c -o main.o
led.o: led.c
$(CC) $(CFLAGS) led.c -o led.o
stm32_startup.o: stm32_startup.c
$(CC) $(CFLAGS) stm32_startup.c -o stm32_startup.o
final.elf: main.o led.o stm32_startup.o
$(CC) $(LDFLAGS) -o $@ $^
clean:
rm -rf *.o *.elf
After compiling, you can use the objdump
tool to examine object files and their sections:
arm-none-eabi-objdump -h main.o
Common sections include:
On reset, the microcontroller does not directly jump to your main()
function. Instead, it jumps to an address defined in the vector table, where the startup code resides. The startup code sets up the environment by initializing memory sections and then calls your main()
function.
A simplified version of a reset handler in C looks like this:
void Reset_Handler(void)
{
uint32_t size = (uint32_t)&_edata - (uint32_t)&_sdata;
uint8_t *dest = (uint8_t*)&_sdata;
uint8_t *src = (uint8_t*)&_la_data;
for(uint32_t i = 0; i < size; i++) {
*dest++ = *src++;
}
size = (uint32_t)&_ebss - (uint32_t)&_sbss;
dest = (uint8_t*)&_sbss;
for(uint32_t i = 0; i < size; i++) {
*dest++ = 0;
}
main();
}
This code copies initialized variables from flash to RAM and clears uninitialized variables before running the main application.
A linker script controls where each piece of the program resides in memory. Here is a common simplified layout:
Starts at address 0x08000000 and holds the interrupt vector table, code (.text), constants (.rodata), and the load image of initialized variables.
Starts at 0x20000000 and contains initialized global variables (.data) during runtime, uninitialized globals (.bss), the heap, and the stack.
This script defines memory regions and sections:
ENTRY(Reset_Handler)
MEMORY
{
FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 1024K
SRAM(rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.text :
{
*(.isr_vector)
*(.text*)
*(.init)
*(.fini)
*(.rodata*)
. = ALIGN(4);
_etext = .;
} > FLASH
_la_data = LOADADDR(.data);
.data :
{
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} > SRAM AT > FLASH
.bss :
{
_sbss = .;
*(COMMON)
*(.bss*)
. = ALIGN(4);
_ebss = .;
} > SRAM
}
Once the program is compiled and linked, it must be uploaded to the microcontroller. A typical workflow includes:
target remote localhost:3333
.monitor reset halt
, then loading the program using load
.After this process, the microcontroller begins executing your code. This is bare-metal development in its purest form, where you control all hardware and software aspects.
For the complete source code and examples related to this project, visit my GitHub repository at https://github.com/sudoXpg/stm32f4-startup.