Minimales Bare-Metal-Firmware für STM32F103: Vom Linker-Skript zur blinkenden LED
Nach dem Reset lädt der Cortex-M3 den Stackzeiger von 0x08000000, springt zum Reset_Handler aus der Vektortabelle und führt den Startcode aus. In einem Bare-Metal-Projekt ohne HAL ist dieser Prozess vollständig transparent: manuelle Speichereinrichtung, Konfiguration der GPIO- und TIM2-Register. Dieses Projekt zeigt die Arbeit mit dem STM32F103C8T6 – Blinken einer LED an PC13 mit 1-Sekunden-Verzögerung über einen Hardware-Timer.
Die Projektstruktur ist geschichtet: inc/ für Schnittstellen, src/ für Implementierungen, startup/ für Initialisierung, myLinker.ld für Speicherkartierung.
Linker-Skript: Platzierung der Sektionen in FLASH und RAM
Das Linker-Skript definiert die Speicherbereiche des STM32F103C8T6 und verteilt die Sektionen:
- FLASH (0x08000000, 64 KB):
.isr_vector,.text,.rodata - RAM (0x20000000, 20 KB):
.data,.bss, Stack
Wichtige Symbole für den Start:
_estack = ORIGIN(RAM) + LENGTH(RAM);
MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x00010000
RAM (rwx): ORIGIN = 0x20000000, LENGTH = 0x00005000
}
SECTIONS {
.isr_vector : { KEEP(*(.isr_vector)) } > FLASH
.text : { *(.text*) } > FLASH
.rodata : { *(.rodata*) } > FLASH
.data : {
_sdata = .;
*(.data*)
_edata = .;
} > RAM AT > FLASH
_sidata = LOADADDR(.data);
.bss : {
_sbss = .;
*(.bss*)
_ebss = .;
} > RAM
}
.data wird aus FLASH in RAM kopiert mit den Adressen _sidata → _sdata.._edata. .bss wird von _sbss bis _ebss mit Nullen gefüllt. _estack legt den initialen Stackzeiger fest.
Startcode: Reset_Handler und Vektortabelle
Der Startcode läuft vor main(): Kopiert .data, löscht .bss, ruft main() auf. Vektortabelle in .isr_vector:
#include <stdint.h>
extern uint32_t _estack;
extern uint32_t _sidata, _sdata, _edata, _sbss, _ebss;
int main(void);
void Reset_Handler(void);
void Default_Handler(void) {
while (1);
}
void NMI_Handler(void) __attribute__((weak, alias("Default_Handler")));
void HardFault_Handler(void) __attribute__((weak, alias("Default_Handler")));
__attribute__((used, section(".isr_vector")))
const void* vector_table[] = {
&_estack,
Reset_Handler,
NMI_Handler,
HardFault_Handler
};
void Reset_Handler(void) {
uint32_t* src = &_sidata;
uint32_t* dst = &_sdata;
while (dst < &_edata) {
*dst++ = *src++;
}
dst = &_sbss;
while (dst < &_ebss) {
*dst++ = 0;
}
main();
while (1) {}
}
weak alias leitet nicht implementierte ISRs an Default_Handler weiter.
Registerzugriff: volatile und direkte Adressierung
Bare-Metal bedeutet Schreiben in Register per Adresse:
*(volatile uint32_t*)0x40021018 |= (1 << 4); // RCC_APB2ENR, GPIOC
volatile verhindert Optimierungen: Der Compiler erzeugt jeden Zugriff. Für TIM2 eine Struktur verwenden:
typedef struct {
volatile uint32_t CR1, CR2, SMCR, DIER, SR, EGR;
volatile uint32_t CCMR1, CCMR2, CCER, CNT;
volatile uint32_t PSC, ARR, RCR;
volatile uint32_t CCR1, CCR2, CCR3, CCR4;
volatile uint32_t BDTR, DCR, DMAR;
} TIM_TypeDef;
#define TIM2 ((TIM_TypeDef*)0x40000000)
Zugriff: TIM2->PSC = 7999;.
Peripherie-Initialisierung: GPIO und TIM2
GPIO
GPIOC-Takt aktivieren: RCC->APB2ENR |= (1 << 4);. PC13 als Ausgang konfigurieren:
GPIOC->CRH &= ~(0xF << 20);GPIOC->CRH |= (0x2 << 20);(2.0 MODE, 00 CNF)
LED: GPIOC->ODR |= (1 << 13); einschalten, &= ~(1 << 13); ausschalten.
TIM2
Für 1 Hz einrichten (72 MHz / 8000 / 9000 = 1 Hz):
RCC->APB1ENR |= (1 << 0);— TIM2-Takt aktivierenTIM2->PSC = 7999;(Vorteiler)TIM2->ARR = 8999;(Auto-Reload)TIM2->CR1 |= (1 << 0);— Timer starten
delayOneSecond(): Warten bis TIM2->CNT >= 9000, CNT zurücksetzen.
Ablauf in main()
GPIO_init()TIMERS_init()- Endlosschleife:
turnOnLED()→delayOneSecond()→turnOffLED()→delayOneSecond()
Wichtige Erkenntnisse
- Linker-Symbole (_sdata, _edata usw.) sind essenziell für korrekte Speicherinitialisierung.
- volatile ist entscheidend für Register: Ohne es cached der Compiler Werte.
- Vektortabelle in
.isr_vectormuss zuerst in FLASH stehen. - Register-Strukturen vereinfachen den Zugriff gegenüber Raw-Pointern.
- Bare-Metal enthüllt den echten Ablauf: Startup → main → Peripherie.
— Editorial Team
Noch keine Kommentare.