{"id":2142,"date":"2026-03-14T08:52:05","date_gmt":"2026-03-14T07:52:05","guid":{"rendered":"https:\/\/g7itchme.wordpress.com\/?p=2142"},"modified":"2026-03-14T08:52:05","modified_gmt":"2026-03-14T07:52:05","slug":"reihe-embedded-world-die-unsichtbaren-gehirne-verstehen-teil-8","status":"publish","type":"post","link":"https:\/\/technodidact.de\/en\/reihe-embedded-world-die-unsichtbaren-gehirne-verstehen-teil-8\/","title":{"rendered":"Reihe: Embedded World \u2013 Die unsichtbaren Gehirne verstehen (Teil 8)"},"content":{"rendered":"<h3 class=\"wp-block-heading\">Nacktes Metall \u2013 Programmieren ohne Betriebssystem (Bare-Metal)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Von DerSchneider<\/strong><\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Einleitung: Die Stille vor dem Betriebssystem<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In der Welt der Desktop-Computer und Smartphones ist ein Leben ohne Betriebssystem unvorstellbar. Windows, macOS, Linux, Android \u2013 sie alle sind die unsichtbaren Vermittler zwischen Anwendung und Hardware, verwalten Speicher, starten Programme, treiben Ger\u00e4te an. Ohne sie w\u00e4re ein moderner PC nur ein teures Briefbeschwerer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In der Embedded World ist das oft anders. Millionen von Mikrocontrollern laufen ihr ganzes Leben lang ohne jedes Betriebssystem. Ihr Programm sitzt direkt auf der Hardware, spricht unvermittelt mit Registern und Interrupts, kennt keinen Scheduler und keinen Speicherschutz. Diese Art der Programmierung nennt man &#8222;Bare-Metal&#8220; \u2013 nacktes Metall.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dieser Artikel taucht ein in diese urspr\u00fcngliche Form der Embedded-Entwicklung. Wir lernen, wie ein Bare-Metal-Programm aufgebaut ist, warum dieser Ansatz trotz seiner Einfachheit oft die beste Wahl ist \u2013 und wann man besser zu einem Betriebssystem greift.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Hauptteil<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>1. Was bedeutet &#8222;Bare-Metal&#8220;?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der Begriff &#8222;Bare-Metal&#8220; bedeutet w\u00f6rtlich &#8222;nacktes Metall&#8220; und meint: Das Programm l\u00e4uft direkt auf der Hardware, ohne Zwischenschicht. Es gibt kein Betriebssystem, das Ressourcen verwaltet, keine Treiber, die die Hardware abstrahieren, keinen Scheduler, der Tasks koordiniert.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Programm hat die vollst\u00e4ndige Kontrolle \u00fcber den Mikrocontroller \u2013 und die volle Verantwortung. Es muss selbst daf\u00fcr sorgen, dass alle Peripherie richtig initialisiert wird, dass Interrupts korrekt behandelt werden, dass der Stack nicht \u00fcberl\u00e4uft und dass Zeitbedingungen eingehalten werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In einem Bare-Metal-System verschmilzt die Anwendungssoftware mit der Systemsoftware. Es gibt keine Trennung zwischen &#8222;User Space&#8220; und &#8222;Kernel Space&#8220;. Alles l\u00e4uft in einem einzigen Adressraum, mit den h\u00f6chsten Privilegien.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>2. Die Urform: Die Super-Loop<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Das Herzst\u00fcck jedes Bare-Metal-Programms ist die Super-Loop \u2013 eine endlose Schleife, die immer wieder durchlaufen wird. Das Grundger\u00fcst sieht so aus:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">c<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">void main(void) {\n    <em>\/\/ Initialisierung: Hardware konfigurieren<\/em>\n    init_hardware();\n    init_peripherie();\n    init_interrupts();\n    \n    <em>\/\/ Super-Loop<\/em>\n    while(1) {\n        task1(); <em>\/\/ Aufgabe 1 erledigen<\/em>\n        task2(); <em>\/\/ Aufgabe 2 erledigen<\/em>\n        task3(); <em>\/\/ Aufgabe 3 erledigen<\/em>\n        <em>\/\/ ...<\/em>\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nach der einmaligen Initialisierung im&nbsp;<code>main<\/code>-Programm l\u00e4uft der Mikrocontroller f\u00fcr den Rest seines Lebens in dieser Schleife. Jeder Durchlauf erledigt der Reihe nach alle anstehenden Aufgaben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Dieses Muster ist einfach, \u00fcberschaubar und deterministisch. Solange alle Aufgaben zusammen in jedem Schleifendurchlauf Platz finden, funktioniert es hervorragend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>3. Die Herausforderung: Unterschiedliche Zeitanforderungen<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Super-Loop hat ein grundlegendes Problem: Alle Aufgaben werden in fester Reihenfolge abgearbeitet, und die Zeit f\u00fcr einen kompletten Durchlauf ist die Summe aller Aufgaben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn eine Aufgabe sehr lange dauert, verz\u00f6gern sich alle anderen. Wenn manche Aufgaben schnell reagieren m\u00fcssen (Echtzeit!), andere aber viel Zeit brauchen, ger\u00e4t das System schnell an seine Grenzen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stellen Sie sich vor:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ein Taster muss alle 10 ms abgefragt werden, um Prellen zu erkennen.<\/li>\n\n\n\n<li>Ein Display muss alle 50 ms aktualisiert werden.<\/li>\n\n\n\n<li>Ein Sensor muss alle 100 ms ausgelesen werden.<\/li>\n\n\n\n<li>Eine komplexe Berechnung dauert 200 ms.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">In einer einfachen Super-Loop w\u00fcrde die Berechnung alle anderen Aufgaben blockieren. Der Taster w\u00fcrde nur alle 200 ms abgefragt \u2013 viel zu langsam f\u00fcr eine zuverl\u00e4ssige Entprellung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>4. Die L\u00f6sung: Zeitgesteuerte Aufgaben<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die erste Verbesserung besteht darin, Aufgaben nicht einfach der Reihe nach abzuarbeiten, sondern zeitgesteuert. Mit einem Timer, der regelm\u00e4\u00dfig Interrupts ausl\u00f6st, kann man einen &#8222;Takt&#8220; erzeugen:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">c<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">volatile int tick = 0;\n\n<em>\/\/ Timer-Interrupt, alle 1 ms<\/em>\nISR(TIMER_ISR) {\n    tick++;\n}\n\nvoid main(void) {\n    init_timer(1); <em>\/\/ Timer alle 1 ms<\/em>\n    int letzteTaster = 0;\n    int letzteDisplay = 0;\n    \n    while(1) {\n        if (tick - letzteTaster &gt;= 10) { <em>\/\/ alle 10 ms<\/em>\n            taster_abfragen();\n            letzteTaster = tick;\n        }\n        \n        if (tick - letzteDisplay &gt;= 50) { <em>\/\/ alle 50 ms<\/em>\n            display_aktualisieren();\n            letzteDisplay = tick;\n        }\n        \n        <em>\/\/ Langsame Aufgabe, die immer l\u00e4uft<\/em>\n        langsame_berechnung();\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt wird der Taster alle 10 ms abgefragt, auch wenn die Berechnung l\u00e4uft. Die&nbsp;<code>langsame_berechnung()<\/code>&nbsp;muss nur darauf achten, regelm\u00e4\u00dfig zum Hauptprogramm zur\u00fcckzukehren (kooperatives Multitasking), damit die anderen Aufgaben drankommen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>5. Kooperatives vs. Pr\u00e4emptives Multitasking<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die obige L\u00f6sung ist ein Beispiel f\u00fcr&nbsp;<strong>kooperatives Multitasking<\/strong>: Jede Aufgabe muss von sich aus die Kontrolle abgeben, damit andere drankommen. Das ist einfach und sicher (keine gleichzeitigen Zugriffe), aber eine Aufgabe, die sich nicht kooperativ verh\u00e4lt (z.B. eine Endlosschleife), blockiert das ganze System.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die Alternative ist&nbsp;<strong>pr\u00e4emptives Multitasking<\/strong>, bei dem der Scheduler Aufgaben unterbrechen kann. Das erfordert aber ein Betriebssystem (RTOS) und ist deutlich komplexer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In Bare-Metal-Systemen ist kooperatives Multitasking die Regel. Die Kunst besteht darin, alle Aufgaben so zu schreiben, dass sie regelm\u00e4\u00dfig zur\u00fcckkehren \u2013 typischerweise in sogenannten &#8222;Zustandsmaschinen&#8220;, die nach jedem Schritt pausieren k\u00f6nnen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>6. Zustandsmaschinen als Retter<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Komplexere Abl\u00e4ufe lassen sich oft elegant als Zustandsmaschine (State Machine) modellieren. Statt einer langen, blockierenden Funktion hat man eine Funktion, die bei jedem Aufruf einen Schritt weitergeht.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Beispiel: Ein Ampelsystem<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">c<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">typedef enum {ROT, ROT_GELB, GRUEN, GELB} ampel_zustand_t;\nampel_zustand_t zustand = ROT;\nint letzterWechsel = 0;\n\nvoid ampel_schritt(int zeit) {\n    switch(zustand) {\n        case ROT:\n            if (zeit - letzterWechsel &gt;= 5000) { <em>\/\/ 5 Sekunden<\/em>\n                zustand = ROT_GELB;\n                letzterWechsel = zeit;\n                led_rot(ON);\n                led_gelb(ON);\n                led_gruen(OFF);\n            }\n            break;\n            \n        case ROT_GELB:\n            if (zeit - letzterWechsel &gt;= 1000) { <em>\/\/ 1 Sekunde<\/em>\n                zustand = GRUEN;\n                letzterWechsel = zeit;\n                led_rot(OFF);\n                led_gelb(OFF);\n                led_gruen(ON);\n            }\n            break;\n            \n        <em>\/\/ weitere Zust\u00e4nde...<\/em>\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Funktion wird in der Super-Loop regelm\u00e4\u00dfig aufgerufen. Sie blockiert nie, macht immer nur einen Schritt und kehrt sofort zur\u00fcck.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>7. Die Vorteile von Bare-Metal<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Warum programmiert man heute noch Bare-Metal, wo es doch RTOS-L\u00f6sungen f\u00fcr fast jeden Mikrocontroller gibt?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Einfachheit:<\/strong>&nbsp;Ein Bare-Metal-Programm ist oft \u00fcberschaubarer als ein RTOS-System. Keine komplexen Scheduler-Konfigurationen, keine Synchronisationsprobleme, kein Speicherschutz.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ressourceneffizienz:<\/strong>&nbsp;Ein RTOS braucht Speicher und Rechenzeit. Bei winzigen 8-Bit-Controllern mit 2 KB RAM ist das oft nicht vorhanden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Determinismus:<\/strong>&nbsp;In einem gut geschriebenen Bare-Metal-System ist genau vorhersagbar, wann welche Aufgabe l\u00e4uft. Bei RTOS kann es durch Priorit\u00e4ten und Interrupts zu komplexen, schwer vorhersagbaren Abl\u00e4ufen kommen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Kontrolle:<\/strong>&nbsp;Man hat die vollst\u00e4ndige Kontrolle \u00fcber alles. Keine Black Box, keine \u00dcberraschungen durch Betriebssystem-Verhalten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>8. Die Grenzen von Bare-Metal<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Aber Bare-Metal hat auch klare Grenzen:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Komplexit\u00e4t:<\/strong>&nbsp;Sobald viele Aufgaben mit unterschiedlichen Priorit\u00e4ten koordiniert werden m\u00fcssen, wird der Code schnell un\u00fcbersichtlich. Eine Zustandsmaschine mit Dutzenden Zust\u00e4nden ist schwer zu warten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Sicherheit:<\/strong>&nbsp;Es gibt keine Isolierung zwischen Aufgaben. Ein Fehler in einer Aufgabe kann das ganze System zum Absturz bringen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Wiederverwendbarkeit:<\/strong>&nbsp;Code ist oft eng mit der spezifischen Hardware verzahnt und schwer auf andere Plattformen zu portieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Kommunikation:<\/strong>&nbsp;Wenn Aufgaben Daten austauschen m\u00fcssen, muss man selbst Mechanismen daf\u00fcr bauen (globale Variablen, Queues, etc.).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>9. Ein Praxisbeispiel: Kleiner Datenlogger<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Stellen wir uns einen einfachen Datenlogger vor: Ein Temperatursensor wird alle 10 Sekunden ausgelesen, der Wert auf einem Display angezeigt und auf einer SD-Karte gespeichert. Ein Taster erlaubt das Umschalten der Anzeige.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Bare-Metal-Design k\u00f6nnte so aussehen:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">c<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">volatile int sekunden = 0;\n\n<em>\/\/ Timer-Interrupt alle 1 Sekunde<\/em>\nISR(TIMER_ISR) {\n    sekunden++;\n}\n\nvoid main(void) {\n    init_timer(1000); <em>\/\/ 1 Sekunde<\/em>\n    int letzteMessung = 0;\n    int letzteDisplay = 0;\n    int anzeigeModus = 0;\n    \n    while(1) {\n        <em>\/\/ Taster abfragen (entprellt)<\/em>\n        if (taster_gedrueckt()) {\n            anzeigeModus = !anzeigeModus;\n        }\n        \n        <em>\/\/ Alle 10 Sekunden messen<\/em>\n        if (sekunden - letzteMessung &gt;= 10) {\n            int temp = sensor_lesen();\n            sd_karte_schreiben(temp);\n            letzteMessung = sekunden;\n        }\n        \n        <em>\/\/ Display alle 500 ms aktualisieren<\/em>\n        if (sekunden - letzteDisplay &gt;= 0.5) { <em>\/\/ mit Timer-Aufl\u00f6sung<\/em>\n            display_aktualisieren(anzeigeModus);\n            letzteDisplay = sekunden;\n        }\n        \n        <em>\/\/ Schlafen, wenn nichts zu tun<\/em>\n        power_save();\n    }\n}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das System ist einfach, robust und stromsparend \u2013 perfekt f\u00fcr einen batteriebetriebenen Datenlogger.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>10. Der \u00dcbergang: Wann wird ein RTOS n\u00f6tig?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Irgendwann st\u00f6\u00dft Bare-Metal an seine Grenzen. Typische Anzeichen:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sie haben mehr als 5-10 Aufgaben mit unterschiedlichen Priorit\u00e4ten.<\/li>\n\n\n\n<li>Aufgaben haben komplexe zeitliche Abh\u00e4ngigkeiten.<\/li>\n\n\n\n<li>Sie brauchen zuverl\u00e4ssige Kommunikation zwischen Aufgaben (Queues, Mailboxen).<\/li>\n\n\n\n<li>Sie portieren Code zwischen verschiedenen Plattformen.<\/li>\n\n\n\n<li>Die Zustandsmaschinen werden un\u00fcbersichtlich und fehleranf\u00e4llig.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Dann ist es Zeit, \u00fcber ein Echtzeitbetriebssystem (RTOS) nachzudenken \u2013 das Thema unseres n\u00e4chsten Artikels.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\" \/>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Fazit und Ausblick<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bare-Metal-Programmierung ist die Urform der Embedded-Entwicklung. Sie ist einfach, effizient und gibt maximale Kontrolle. Mit Techniken wie zeitgesteuerten Aufgaben und Zustandsmaschinen lassen sich auch komplexere Anwendungen realisieren. F\u00fcr viele \u2013 vielleicht die meisten \u2013 Embedded-Projekte ist Bare-Metal v\u00f6llig ausreichend.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Doch wenn die Komplexit\u00e4t w\u00e4chst, wird ein Betriebssystem zur sinnvollen Abstraktion. Es \u00fcbernimmt das Scheduling, die Synchronisation und die Kommunikation \u2013 und erlaubt dem Entwickler, sich auf die eigentliche Anwendung zu konzentrieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mit diesen Echtzeitbetriebssystemen besch\u00e4ftigen wir uns im n\u00e4chsten Artikel.<\/p>","protected":false},"excerpt":{"rendered":"<p>Nacktes Metall \u2013 Programmieren ohne Betriebssystem (Bare-Metal) Von DerSchneider Einleitung: Die Stille vor dem Betriebssystem In der Welt der Desktop-Computer und Smartphones ist ein Leben ohne Betriebssystem unvorstellbar. Windows, macOS, Linux, Android \u2013 sie alle sind die unsichtbaren Vermittler zwischen Anwendung und Hardware, verwalten Speicher, starten Programme, treiben Ger\u00e4te an. Ohne sie w\u00e4re ein moderner [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[46,26],"tags":[2069],"class_list":["post-2142","post","type-post","status-publish","format-standard","hentry","category-industrie-4-0","category-mit-den-handen","tag-embedded-world"],"_links":{"self":[{"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/posts\/2142","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/comments?post=2142"}],"version-history":[{"count":0,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/posts\/2142\/revisions"}],"wp:attachment":[{"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/media?parent=2142"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/categories?post=2142"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/technodidact.de\/en\/wp-json\/wp\/v2\/tags?post=2142"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}