STM32 Code-Optimierung beginnt nicht erst beim Feintuning einzelner Funktionen, sondern bei einer sauberen Konfiguration des GCC-Compilers und des Linkers. Gerade auf STM32-Mikrocontrollern sind Flash- und RAM-Budgets begrenzt, und auch die verfügbare CPU-Zeit ist je nach Takt, Peripherie-Last und Energiemodus nicht „endlos“. Wer den GCC nur mit Standardparametern laufen lässt, verschenkt häufig messbar Leistung, erzeugt unnötig große Binärdateien oder erschwert sich Debugging und Fehlersuche. Gleichzeitig gilt: Optimierung ist kein Selbstzweck. In Embedded-Projekten zählt die Gesamtwirkung – also stabile Laufzeiten, reproduzierbare Builds, nachvollziehbare Änderungen und ein gutes Verhältnis aus Performance, Codegröße und Wartbarkeit. Dieser Leitfaden zeigt Ihnen praxisnah, wie Sie GCC für STM32 richtig konfigurieren: von den wichtigsten Flags (CPU/FPU, Optimierungsstufen, LTO, Sections) über Linker-Optionen und Newlib-Nano bis hin zu Mess- und Analysewerkzeugen. Damit erhalten Sie eine belastbare Grundlage, um Ihr Projekt zielgerichtet zu beschleunigen oder zu verkleinern – ohne dabei die Kontrolle über Debugbarkeit und Systemverhalten zu verlieren.
Optimierung auf STM32: Was genau wollen Sie verbessern?
Bevor Sie Compiler-Flags ändern, definieren Sie das Optimierungsziel. „Schneller“ und „kleiner“ sind nicht immer kompatibel. Außerdem beeinflussen manche Optionen die Debugbarkeit oder das Timing von Interrupts. Klare Ziele verhindern, dass Sie blind an Parametern drehen.
- Codegröße (Flash): Firmware passt nicht mehr in den Flash oder OTA-Update-Pakete werden zu groß.
- RAM-Verbrauch: Stack-Überläufe, Heap-Probleme, zu große Buffer, zu hoher statischer RAM.
- Performance: Echtzeitgrenzen werden knapp, ISR-Latenzen sind zu hoch, Datenpfade benötigen zu lange.
- Energieeffizienz: CPU-Last muss sinken, um längere Sleep-Phasen zu ermöglichen.
- Build- und Debug-Qualität: reproduzierbare Builds, aussagekräftige Warnungen, stabile Backtraces.
Die Grundlage: Richtige Target-Flags für Cortex-M
Viele „Optimierungsprobleme“ entstehen, weil das Target nicht exakt konfiguriert ist. GCC muss wissen, welche CPU er ansprechen soll, ob Thumb-Code genutzt wird und ob eine FPU vorhanden ist. Falsche Einstellungen führen zu ineffizientem Code oder im schlimmsten Fall zu Laufzeitfehlern.
CPU und Instruction Set: -mcpu und -mthumb
STM32-Controller basieren je nach Familie auf unterschiedlichen Cortex-M-Kernen (z. B. M0/M0+, M3, M4, M7, M33). Das korrekte -mcpu ist entscheidend, damit der Compiler passende Instruktionen und Scheduling-Entscheidungen trifft. -mthumb ist für Cortex-M praktisch immer Standard.
- -mcpu=cortex-m0 / cortex-m0plus: z. B. STM32F0, STM32G0 (je nach Modell)
- -mcpu=cortex-m3: z. B. STM32F1
- -mcpu=cortex-m4: z. B. STM32F3, viele STM32L4, STM32G4
- -mcpu=cortex-m7: z. B. STM32F7, STM32H7
- -mcpu=cortex-m33: z. B. STM32U5 (je nach Serie)
Eine verlässliche Referenz zu GCC-Optionen finden Sie in der offiziellen GCC-Dokumentation: GCC Online Documentation.
FPU und Floating-ABI: -mfpu und -mfloat-abi
Wenn Ihr STM32 eine Hardware-FPU besitzt (typisch Cortex-M4F/M7F), entscheidet die Kombination aus -mfpu und -mfloat-abi darüber, ob Floating-Point-Berechnungen tatsächlich hardwarebeschleunigt laufen. Ein häufiger Fehler ist, die FPU zwar im Chip zu haben, aber im Compiler nicht korrekt zu aktivieren.
- -mfloat-abi=soft: keine Hardware-FPU-Nutzung (kompatibel, aber langsam bei Float-lastigen Workloads)
- -mfloat-abi=hard: nutzt FPU und übergibt Floats in FPU-Registern (Performance, aber ABI muss im gesamten Projekt konsistent sein)
- -mfloat-abi=softfp: nutzt FPU, behält aber „soft“-Calling-Conventions (Kompromiss, je nach Toolchain)
Wichtig: Diese Einstellung muss zu Startup, Libraries und dem gesamten Build passen. Eine ABI-Mischung führt zu schwer nachvollziehbaren Problemen.
Optimierungsstufen: -O0, -Og, -O1, -O2, -O3, -Os
Die Wahl der Optimierungsstufe ist der größte Hebel – und gleichzeitig die häufigste Ursache für „Debugging ist kaputt“ oder „Timing hat sich geändert“. In Embedded-Projekten ist es üblich, zwischen Debug- und Release-Profilen zu unterscheiden.
- -O0: keine Optimierung, bestes Debugging, aber langsam und groß
- -Og: optimiert moderat, ohne Debugbarkeit stark zu zerstören (guter Debug-Standard)
- -O1 / -O2: praxisnahe Optimierung; -O2 ist oft der Release-Standard
- -O3: aggressive Optimierung; kann Code aufblasen und Worst-Case-Latenzen verschlechtern
- -Os: optimiert auf Codegröße; häufig ideal, wenn Flash knapp ist
Für viele STM32-Projekte ist ein sinnvolles Basisset: Debug: -Og -g3, Release: -O2 oder -Os (je nach Ziel). Details zu den Stufen sind ebenfalls in den GCC-Manuals beschrieben: GCC Optimize Options.
Linker-Optimierung: Der unterschätzte Schlüssel zu kleiner Firmware
Ein Großteil unnötiger Codegröße entsteht nicht im Compiler, sondern im Linker: Funktionen und Daten werden eingebunden, obwohl sie am Ende nie genutzt werden. Die klassische Embedded-Optimierung ist daher: Funktionen und Daten in eigene Sections legen und ungenutzte Sections verwerfen.
Funktionen und Daten in Sections: -ffunction-sections und -fdata-sections
- -ffunction-sections: jede Funktion in eine eigene Section
- -fdata-sections: globale/konstante Daten in eigene Sections
Unbenutztes entfernen: -Wl,–gc-sections
Der Linker kann dann unbenutzte Sections entfernen. In der Praxis ist das ein massiver Gewinn für Codegröße, besonders wenn Sie HAL/Middleware verwenden oder Bibliotheken mit vielen Features einbinden.
- -Wl,–gc-sections: entfernt nicht referenzierte Sections
- -Wl,-Map=firmware.map: erzeugt eine Map-Datei für Analyse
Die Map-Datei ist eines der wichtigsten Werkzeuge, um „Warum ist mein Binary so groß?“ objektiv zu beantworten.
LTO: Link-Time Optimization als Turbo für Größe und Performance
Link-Time Optimization (LTO) ermöglicht Optimierungen über Dateigrenzen hinweg. Das kann Funktionen besser inline’n, toten Code entfernen und manchmal sogar Performance verbessern – oft bei gleichzeitiger Reduktion der Codegröße. LTO ist ein typischer „2026-Standard“ in vielen Embedded-Projekten, weil Toolchains und CI-Prozesse reifer geworden sind.
- -flto: aktiviert LTO (muss bei Compile und Link gesetzt sein)
- -fuse-linker-plugin: häufig automatisch, kann aber je nach Setup relevant sein
Hinweis aus der Praxis: LTO kann Debugging erschweren, weil Funktionen stärker zusammengeführt werden. Für Release-Profile ist LTO häufig sinnvoll; für Debug-Profile eher optional.
Debug vs. Release: Konfiguration, die Ihnen Zeit spart
Professionelle STM32-Projekte trennen Debug- und Release-Builds sauber. Das Ziel ist: Debug bleibt gut debugbar, Release ist performant und klein. Wichtig ist, dass beide Builds funktional möglichst ähnlich sind, damit Sie Fehler reproduzieren können.
Empfohlenes Debug-Profil
- -Og statt -O0 (oft deutlich näher am Release-Verhalten)
- -g3 für umfangreiche Debug-Information
- -fno-omit-frame-pointer (optional) für bessere Backtraces
- -fstack-usage zur Stackanalyse pro Funktion
Empfohlenes Release-Profil
- -O2 für Performance oder -Os für kleine Firmware
- -flto (wenn Debug nicht im Vordergrund steht)
- -ffunction-sections -fdata-sections plus -Wl,–gc-sections
Newlib Nano und printf: Kleine Standardbibliothek, große Wirkung
Ein klassischer Flash-Fresser in STM32-Projekten ist die C-Standardbibliothek, insbesondere wenn printf mit Float-Unterstützung und umfangreichen Formatierungsfunktionen eingebunden wird. Viele GCC-basierte STM32-Setups nutzen daher newlib-nano, eine schlankere Variante der Standardbibliothek.
- -specs=nano.specs: bindet newlib-nano (wenn verfügbar) ein
- printf ohne Float: deutlich kleiner; Float-printf nur aktivieren, wenn wirklich benötigt
- gezielt aktivieren: je nach Toolchain über Linker-Optionen (z. B. Symbol-Enable) statt „immer“
Wenn Sie verstehen möchten, welche Flags GCC und Linker unterstützen, ist das „Using the GNU Compiler Collection“-Manual eine gute Ausgangsbasis: GCC Manual.
Warnungen und statische Qualität: Optimierung beginnt bei sauberen Builds
Code-Optimierung ist nicht nur „Speed“. Ein stabiler Build mit guten Warnungen verhindert, dass Optimierungen undefiniertes Verhalten sichtbarer machen. Gerade auf Cortex-M kann „zufälliges“ Verhalten bei höherer Optimierung plötzlich reproduzierbar werden – weil der Compiler aggressiver umsortiert.
- -Wall -Wextra: solide Basis
- -Wshadow: verhindert häufige Logikfehler
- -Wdouble-promotion: relevant, wenn Float/Double bewusst genutzt wird
- -Wconversion (mit Bedacht): kann helfen, erzeugt aber oft viele Meldungen
- -fno-common: verhindert Mehrfachdefinitionen (robuster Link)
Für sicherheits- oder qualitätskritische Projekte lohnt es sich, zusätzlich Coding-Guidelines (z. B. MISRA) und entsprechende Analysewerkzeuge einzusetzen. Das ist zwar nicht „GCC-Flag“, wirkt aber direkt auf die Optimierbarkeit und Stabilität Ihrer Firmware.
„Schneller“ ist nicht immer „besser“: Timing, Interrupts und Worst-Case-Latenzen
Auf STM32 bedeutet Performance häufig: Deadline einhalten. Eine durchschnittliche Beschleunigung hilft wenig, wenn Worst-Case-Latenzen steigen oder ISR-Timing instabil wird. Einige Optimierungen können Code aufblasen oder unerwartete Pfade erzeugen, die das Timing verschlechtern.
- -O3: kann Schleifen stärker unrollen und Codegröße erhöhen
- Inlining: kann Performance erhöhen, aber Flash und I-Cache belasten (relevant bei M7/H7)
- LTO: kann Pfade zusammenziehen; gut messen, nicht raten
Bei hochzeitkritischen Bereichen ist oft eine gezielte Optimierung einzelner Dateien oder Funktionen sinnvoll, statt „global alles aggressiv“. GCC erlaubt fileweise oder sogar funktionsweise Attribute (z. B. optimize-Attribute), allerdings sollten Sie das sparsam einsetzen, um Wartbarkeit zu behalten.
Messung und Analyse: Ohne Zahlen keine echte Optimierung
Der wichtigste Profi-Schritt ist: Messen. STM32 Code-Optimierung sollte auf objektiven Daten basieren, nicht auf Bauchgefühl. Dazu gehören Größenanalysen (Flash/RAM), Laufzeitmessungen (Cycle Counter, Timer), sowie Linker-Map-Auswertungen.
Flash/RAM-Verbrauch sichtbar machen
- arm-none-eabi-size: schnelle Übersicht über text/data/bss
- Map-Datei: zeigt, welche Objekte und Symbole Platz belegen
- objdump/nm: detaillierte Symbolanalyse
Laufzeit messen: DWT Cycle Counter
Viele Cortex-M3/M4/M7 bieten einen Cycle Counter (DWT), mit dem Sie Codeabschnitte sehr genau messen können. Damit lassen sich Optimierungen verifizieren, ohne auf grobe GPIO-Toggles angewiesen zu sein. Ergänzend sind Debugger-Features, Trace oder SWO hilfreich, wenn Ihr Board das unterstützt.
Empfohlenes Basisset an GCC/Linker-Flags für STM32-Projekte
Die folgenden Optionen sind als Ausgangspunkt in vielen STM32-GCC-Projekten etabliert. Sie ersetzen keine Messung, bilden aber eine solide Grundlage, auf der Sie gezielt weiter optimieren können.
- Target: -mcpu=<core> -mthumb (plus -mfpu/-mfloat-abi bei FPU-Kernen)
- Release-Optimierung: -O2 oder -Os
- Dead-Code-Elimination: -ffunction-sections -fdata-sections und Linker: -Wl,–gc-sections
- LTO: -flto (optional, aber häufig sehr effektiv)
- Diagnose: -Wall -Wextra (und projektabhängig weitere Warnungen)
- Analyse: -Wl,-Map=firmware.map
STM32CubeIDE und GCC: Wo Sie die Einstellungen typischerweise finden
In STM32CubeIDE werden Compiler- und Linker-Optionen üblicherweise in den Projekt-Eigenschaften (C/C++ Build Settings) gepflegt. Für Teams ist es oft sinnvoll, diese Einstellungen nicht nur in der IDE zu klicken, sondern sie im Buildsystem (Make/CMake oder IDE-Projektdateien) nachvollziehbar zu versionieren. Die offizielle Produktseite bietet einen Einstieg in die Tooling-Welt und Dokumentationslinks: STM32CubeIDE (ST).
Typische Optimierungsfallen und wie Sie sie vermeiden
Viele Optimierungsprobleme wirken wie „GCC macht komische Dinge“, sind aber häufig Folge von undefiniertem Verhalten oder inkonsistenten Build-Parametern. Eine kurze Checkliste hilft, typische Fehlerquellen früh auszuschließen.
- ABI-Mix bei FPU: -mfloat-abi muss mit Libraries und allen Objekten zusammenpassen.
- Uninitialisierte Variablen: bei höherer Optimierung treten Effekte häufiger sichtbar auf.
- Fehlendes volatile: bei Memory-Mapped-IO oder ISR-geteilten Variablen kann der Compiler Zugriffe wegoptimieren.
- Überoptimierter Debug: -O2 in Debug kann das Debugging erheblich erschweren; -Og ist oft besser.
- Keine Verifikation: Optimierungen müssen gemessen werden (Größe, Timing, Energie).
Weiterführende, verlässliche Quellen
- GCC Online Documentation
- GCC Optimize Options (Details zu -O und verwandten Flags)
- GCC Link Options (Übergabe an den Linker)
- STM32CubeIDE (GCC/GDB-Tooling im STM32-Workflow)
- Arm Developer Documentation (Cortex-M Grundlagen, Debug/Trace, Performance)
IoT-PCB-Design, Mikrocontroller-Programmierung & Firmware-Entwicklung
PCB Design • Arduino • Embedded Systems • Firmware
Ich biete professionelle Entwicklung von IoT-Hardware, einschließlich PCB-Design, Arduino- und Mikrocontroller-Programmierung sowie Firmware-Entwicklung. Die Lösungen werden zuverlässig, effizient und anwendungsorientiert umgesetzt – von der Konzeptphase bis zum funktionsfähigen Prototyp.
Diese Dienstleistung richtet sich an Unternehmen, Start-ups, Entwickler und Produktteams, die maßgeschneiderte Embedded- und IoT-Lösungen benötigen. Finden Sie mich auf Fiverr.
Leistungsumfang:
-
IoT-PCB-Design & Schaltplanerstellung
-
Leiterplattenlayout (mehrlagig, produktionstauglich)
-
Arduino- & Mikrocontroller-Programmierung (z. B. ESP32, STM32, ATmega)
-
Firmware-Entwicklung für Embedded Systems
-
Sensor- & Aktor-Integration
-
Kommunikation: Wi-Fi, Bluetooth, MQTT, I²C, SPI, UART
-
Optimierung für Leistung, Stabilität & Energieeffizienz
Lieferumfang:
-
Schaltpläne & PCB-Layouts
-
Gerber- & Produktionsdaten
-
Quellcode & Firmware
-
Dokumentation & Support zur Integration
Arbeitsweise:Strukturiert • Zuverlässig • Hardware-nah • Produktorientiert
CTA:
Planen Sie ein IoT- oder Embedded-System-Projekt?
Kontaktieren Sie mich gerne für eine technische Abstimmung oder ein unverbindliches Angebot. Finden Sie mich auf Fiverr.

