Der Mikrocontroller und der Maschinencode 

Der Intel 4004 wurde 1971 eingeführt und war der erste kommerziell erhältliche Mikroprozessor. Er war ein 4-Bit-Prozessor, was bedeutet, dass er vier Bits an Daten gleichzeitig verarbeiten konnte. Mit einer Taktrate von 740 Kilohertz konnte der 4004 etwa 60.000 Anweisungen pro Sekunde ausführen.

Eine der ersten Anwendungen des 4004 war in einem Gerät namens Busicom 141-PF, einem Taschenrechner. Der 4004 war so revolutionär, weil er eine kleine, vielseitige und kostengünstige Lösung für die Steuerung von digitalen Geräten darstellte. Damals war die Idee eines Mikroprozessors - eines vollständigen Computers auf einem einzigen Chip - ziemlich revolutionär. 

Vergleich mit dem Arduino:

Der Arduino, der fast 35 Jahre nach dem 4004 eingeführt wurde, ist ein Mikrocontroller, kein Mikroprozessor. Der Hauptunterschied besteht darin, dass Mikrocontroller wie der Arduino in der Regel über eingebaute Peripheriegeräte wie GPIO-Pins, ADCs, DACs und Timer verfügen, während Mikroprozessoren wie der 4004 in der Regel keine eingebauten Peripheriegeräte haben.

Ein typischer Arduino (z.B. der Arduino Uno) hat einen 8-Bit-Mikrocontroller mit dem Namen ATmega328P, der eine Taktfrequenz von 16 MHz und eine Rechenleistung von etwa 16 MIPS (Million Instructions per Second). Dies bedeutet, dass er theoretisch bis zu 16 Millionen Anweisungen pro Sekunde ausführen kann. Das bedeutet, dass der Arduino im Vergleich zum 4004 mehr Bits gleichzeitig verarbeiten kann und eine viel höhere Taktrate hat.

Die Fähigkeiten des Arduino gehen jedoch weit über die des 4004 hinaus. Er kann eine viel größere Vielfalt an Anweisungen verarbeiten und hat eine viel größere Speicherkapazität. Darüber hinaus verfügt der Arduino über eine Vielzahl von eingebauten Funktionen und Anschlüssen, die es ermöglichen, eine Vielzahl von Sensoren, Aktuatoren und anderen Komponenten zu verbinden.

Es ist wichtig zu beachten, dass es nicht wirklich fair ist, den 4004 und den Arduino direkt zu vergleichen. Der 4004 war eine bahnbrechende Erfindung, die den Weg für die Entwicklung von Mikroprozessoren und Mikrocontrollern ebnete, während der Arduino das Ergebnis von Jahrzehnten technologischer Entwicklung und Innovation ist. Beide haben auf ihre eigene Art und Weise einen großen Beitrag zur Welt der Technologie geleistet.

Mikrocontroller: Der kompakte Computer für Embedded-Systeme

Mikrocontroller sind heute praktisch in jedem elektronischen Gerät vorhanden. Sie finden oft in Echtzeitanwendungen Verwendung und sind eine bevorzugte Wahl gegenüber Mikroprozessoren. Sie bilden das Herz des Arduino-Boards und ermöglichen das Ausführen von Programmen und die Interaktion mit anderen elektronischen Komponenten.

Historisch betrachtet begann die Entwicklung von Mikrocontrollern in den 1970er Jahren, parallel zur Miniaturisierung elektronischer Komponenten. Einer der ersten kommerziell erhältlichen Mikrocontroller war der Intel 8048. Im Laufe der Zeit haben sich Mikrocontroller weiterentwickelt und bieten heute erweiterte Funktionen, höhere Leistung und verbesserte Integration von Komponenten.

Ein Mikrocontroller besteht aus mehreren Komponenten, die in einem einzigen integrierten Schaltkreis vereint sind - daher auch die Bezeichnung "Ein-Chip-Computer". Im Vergleich zu PC-Prozessoren sind die Komponenten eines Mikrocontrollers in der Regel auf einem einzigen Chip integriert. Als kompakter Vollwert-Computer enthält er alle Peripheriegeräte, die auch ein Heimcomputer aufweist.

Das Herzstück des Mikrocontrollers ist die CPU (Central Processing Unit). Sie führt die Verarbeitung von Befehlen und Daten durch. Zusätzlich verfügt der Mikrocontroller über Speicher für den Programmcode und Daten, sowie über verschiedene Peripheriegeräte wie Analog-Digital-Wandler, Timer und serielle Kommunikationsschnittstellen.

Die Arbeitsweise eines Mikrocontrollers basiert auf einem Schleifenzyklus, in dem der Programmcode sequenziell abgerufen und ausgeführt wird. Der Mikrocontroller überwacht Eingangssignale, verarbeitet Daten (inklusive grundlegender Berechnungen wie Addition, Subtraktion, Division und Fließkommaberechnungen), generiert Ausgabesignale und steuert Peripheriegeräte gemäß den Anweisungen im Programmcode.

Mikrocontroller bieten eine Reihe von Vorteilen. Sie sind kompakt und ermöglichen die Integration vieler Funktionen auf einem einzigen Chip. Zudem sind sie stromsparend, kosteneffizient und vielseitig einsetzbar. Entwickler können Mikrocontroller programmieren und an die spezifischen Anforderungen ihrer Anwendungen anpassen.

Allerdings gibt es auch einige Nachteile von Mikrocontrollern. Sie haben begrenzte Ressourcen wie Speicherplatz und Rechenleistung im Vergleich zu größeren Computern oder Prozessoren. Darüber hinaus sind ihre Funktionen und Kapazitäten durch die begrenzte Hardware eingeschränkt.

Mikrocontroller finden heutzutage in vielen Bereichen Anwendung, darunter IoT-Geräte, Robotik, Hausautomatisierung, Sensornetzwerke, industrielle Steuerungssysteme und medizinische Geräte. Dank der Popularität von Arduino hat Mikrocontroller-Technologie ein breites Publikum erreicht und die Entwicklung interaktiver Elektronikprojekte erleichtert.

Zusammenfassend lässt sich sagen, dass Mikrocontroller eine essentielle Komponente in der Elektronik darstellen, die es erlaubt, diverse Funktionen in kompakten und kosteneffizienten Geräten umzusetzen. Sie haben die Art und Weise, wie wir Elektronikprojekte entwickeln und interaktive Anwendungen kreieren, maßgeblich beeinflusst.

Um den Mikrocontroller besser zu verstehen, kann man ein vereinfachtes Blockschaltbild heranziehen, das die grundlegenden Funktionen und Komponenten zeigt. Hier ist eine Beschreibung der wichtigsten Elemente:

Die Zentrale Recheneinheit

Die Central Processing Unit (CPU), das zentrale Herz und mathematische Gehirn eines Mikrocontrollers, hat die Hauptaufgabe, Befehle zu dekodieren und auszuführen. Die Rechenleistung der CPU hängt maßgeblich von ihrer maximalen Taktfrequenz ab, welche bestimmt, wie zügig Aufgaben abgearbeitet werden. Bei der Auswahl eines Mikrocontrollers solltest du die benötigte Rechenleistung entsprechend den Anforderungen deines Projekts berücksichtigen. Darüber hinaus hat die CPU auch die Aufgabe, den Speicher zu adressieren, Ein- und Ausgänge zu verwalten und auf Interrupts zu reagieren. 

Interrupt-Steuerung

Die Interrupt-Steuerung ist eine außerordentlich nützliche Funktion, die den laufenden Rechenzyklus eines Mikrocontrollers unterbricht, um auf spezifische Ereignisse zu reagieren. Ein Interrupt (Interrupt Request, kurz IRQ) tritt auf, wenn ein externes Ereignis oder ein spezieller Zustand eintritt, der eine sofortige Reaktion erfordert.

Stell dir vor, dein Roboter stößt gegen ein Objekt und muss ein Ausweichmanöver durchführen. Während dieser Bewegung ist jedoch hinter dem Roboter ein Abgrund oder eine Treppe. Ein Sensor erkennt den Abgrund im letzten Moment. Genau hier greift die Interrupt-Steuerung ein, unterbricht das laufende Programm und startet ein Notfallprogramm, um den Roboter vor dem Herunterfallen zu schützen. Der Mikrocontroller reagiert also unmittelbar auf den Interrupt und unterbricht seine aktuelle Tätigkeit, um das Notfallprogramm auszuführen. Dies ermöglicht eine sofortige Reaktion auf wichtige Ereignisse, ohne den normalen Programmablauf zu beeinträchtigen. Durch eine effektive Nutzung von Interrupts kannst du die Reaktionszeit des Mikrocontrollers verbessern und zeitkritische Aufgaben effizient ausführen.

Der Datenbus

Der Datenbus ist eine Art virtuelle Transportstraße für Daten zwischen verschiedenen Komponenten. Wenn die CPU Daten aus dem Speicher benötigt, werden diese Daten auf Anforderung auf den Datenbus gelegt und zur CPU übertragen. Nachdem die CPU Berechnungen durchgeführt hat, legt sie die Ergebnisse oder Anweisungen wieder auf den Bus, die dann beispielsweise an einen Ausgangsport weitergeleitet werden können, um einen Motor zu steuern. Der Datenbus spielt eine zentrale Rolle bei der Kommunikation und Koordination innerhalb des Mikrocontrollers.

Der Oszillator

Wie jeder andere Prozessor benötigt auch das Arduino einen Taktgeber. Mithilfe der Taktung werden Berechnungsimpulse vorgegeben, um sicherzustellen, dass alle elektronischen Komponenten auf dem Board im Gleichschritt arbeiten. Das Arduino nutzt hierfür einen externen Quarzoszillator, um ein präzises Taktsignal zu erzeugen. Ein Arduino Uno hat beispielsweise eine Taktfrequenz von 16MHz, was bedeutet, dass die CPU 16 Millionen Befehle pro Sekunde ausführt! Die präzise Taktfrequenz ist für die korrekte Ausführung von Programmen und die Synchronisation der Komponenten auf dem Arduino-Board unerlässlich.

Speicherbereiche

Der Speicher eines Arduino kann in flüchtigen und nichtflüchtigen Speicher unterteilt werden. Ähnlich wie beim Computer werden nach dem Ausschalten alle Daten aus dem flüchtigen RAM-Speicher gelöscht. Das Arduino besitzt einen Programmspeicher und einen flüchtigen Datenspeicher. Der Programmspeicher, der als eine Art Festplatte agiert, enthält das hochgeladene Programm (Sketch), das von der CPU ausgeführt werden soll. Im Gegensatz dazu verliert der flüchtige Datenspeicher alle Daten, sobald das Board vom Strom getrennt wird. Hier werden während der Laufzeit des Programms Variablen und andere Daten temporär

Die Ein- und Ausgabeports

Ein- und Ausgabeports sind essenzielle Schnittstellen des Mikrocontrollers, die es ihm ermöglichen, mit der Außenwelt zu kommunizieren. Durch Eingangsports kann der Mikrocontroller Informationen von Sensoren oder anderen Quellen erfassen und verarbeiten. Die Ausgangsports ermöglichen das Steuern von Aktoren wie LEDs, Motoren oder Relais. Diese Ports werden über Pins realisiert, die physische Anschlüsse auf dem Mikrocontroller darstellen. Durch individuelle Programmierung kannst du die Funktionalität der Ports anpassen und steuern, was dem Mikrocontroller eine Vielzahl von Anwendungsmöglichkeiten eröffnet.

Was passiert hinter den Kulissen?

Wenn du dich mit der Programmierung von Arduino beschäftigst, findest du dich schnell in einem faszinierenden Prozess wieder, der hinter den Kulissen abläuft. Lass uns diesen genauer betrachten.

Zuerst setzt du dich an deinen Computer und öffnest die Arduino-Entwicklungsumgebung (IDE). Hier schreibst du deinen Sketch. Du nutzt dabei die Arduino-Syntax und verschiedene Funktionen, um die Abläufe und Funktionen deines Projekts zu definieren.

Hast du den Code fertiggestellt, beginnt die Übersetzungsphase. In dieser Phase nimmt die Arduino-IDE deinen Sketch und übersetzt ihn in C++-Code. Wieso C++? Die Arduino-Plattform setzt auf C/C++ als ihre Hauptprogrammiersprache, da sie relativ einfach zu lesen und zu verstehen ist.

Nach der Übersetzung kommt die Kompilierung. In dieser Phase wandelt der Compiler deinen nun in C++ vorliegenden Code in Maschinencode um. Dieser Code besteht aus einer Reihe von binären Anweisungen, die der Mikrocontroller direkt ausführen kann.

Ist die Kompilierung abgeschlossen, wird der Maschinencode in ein hexadezimales Format überführt. Dieses Format enthält sowohl die Anweisungen als auch die Daten deines Programms, die bereit sind, zum Mikrocontroller gesendet zu werden.

Im nächsten Schritt wird dein Sketch auf den Arduino hochgeladen. Dabei wird der hexadezimale Code über eine serielle Verbindung, meistens über USB, an den Mikrocontroller übermittelt. Der Mikrocontroller speichert diesen Code in seinem internen Speicher, um ihn bei Bedarf abzurufen und auszuführen.

Sobald der Sketch auf den Mikrocontroller geladen ist, kann er ausgeführt werden. Der Mikrocontroller liest die Anweisungen aus dem Speicher und führt sie entsprechend deinem Programmcode aus. Das führt zur Aktivierung der von dir programmierten Aktionen und Funktionen, wie das Steuern von Ein- und Ausgängen, die Verarbeitung von Sensorwerten oder die Darstellung von Informationen auf einem Display.

Dieser gesamte Prozess bildet das Herzstück der Arbeit mit Arduino – von der ersten Codezeile bis hin zur Ausführung auf dem Mikrocontroller. Es ist ein Prozess, der Technik, Kreativität und Wissen vereint und zu faszinierenden Projekten führt.

Einblick in Maschinen- und Hochsprachen

Maschinencode ist eine primäre Programmiersprache, die auf einer sehr niedrigen Abstraktionsebene arbeitet und direkt von der Hardware eines Computers oder Mikrocontrollers interpretiert und ausgeführt wird. Sie besteht aus einer Sequenz von binären Anweisungen – jeder Befehl repräsentiert spezifische Operationen und Befehle.

Bezogen auf das Arduino-System wird der vom Compiler generierte Maschinencode direkt auf den Mikrocontroller geladen. Der Mikrocontroller kann dann diese Anweisungen lesen und ausführen, um die gewünschten Funktionen zu erfüllen.

Es ist wichtig zu verstehen, dass Maschinencode spezifisch für den jeweiligen Mikrocontroller und seine Architektur ist. Jeder Mikrocontroller hat seine einzigartigen Befehlssätze und Operationen, die im Maschinencode dargestellt werden. Daher muss der Code entsprechend für den bestimmten Mikrocontroller kompiliert werden, um den korrekten Maschinencode zu generieren. Maschinencode ist allerdings für Menschen schwierig zu lesen und zu interpretieren, da er aus binären Zahlen besteht.

“Ein in Maschinensprache geschriebenes Programm zu betrachten, ist ungefähr vergleichbar mit der Betrachtung eines DNA-Moleküls Atom für Atom.”
— Douglas Hofstadter

Dieses Zitat von Douglas Hofstadter wirft ein interessantes Licht auf die Komplexität und die Feinheiten, die in einem in Maschinensprache geschriebenen Programm stecken. Es unterstreicht die Tatsache, dass Maschinencode, obwohl er auf den ersten Blick für den menschlichen Betrachter schwer zu verstehen ist, äußerst präzise und spezifisch ist. Jeder Befehl, jede Operation, die in Maschinencode geschrieben ist, spielt eine genaue Rolle, ähnlich wie jedes Atom in einem DNA-Molekül.

In der Biologie bildet die DNA die Blaupause für alle lebenden Organismen. Jedes Atom in einem DNA-Molekül trägt zur Gesamtfunktion und -struktur des Organismus bei. Ähnlich wie die Atome in der DNA, bilden die einzelnen Anweisungen in Maschinencode das Rückgrat eines Programms oder einer Anwendung. Sie sind präzise, spezifisch und unverzichtbar für die Gesamtfunktion des Programms.

Gleichzeitig zeigt das Zitat auf, dass das Verstehen von Maschinencode, ähnlich wie das Verstehen der DNA auf atomarer Ebene, eine gewisse Expertise und Kenntnis der zugrundeliegenden Prinzipien erfordert. Für die meisten Menschen, die nicht in die Details von Maschinencode oder Genetik eingetaucht sind, mag die Betrachtung dieser Strukturen verwirrend und überwältigend erscheinen. Aber für diejenigen, die die Sprache beherrschen, offenbaren sie eine Welt voller Möglichkeiten und Präzision.

Deswegen bevorzugen wir für gewöhnlich höhere Programmiersprachen wie C/C++, die eine abstraktere und menschenfreundlichere Darstellung des Codes ermöglichen.

Auf der anderen Seite steht die Hochsprache – eine Programmiersprache, die entwickelt wurde, um für Menschen verständlich und lesbar zu sein. Im Gegensatz zur Maschinensprache, die von der Hardware direkt interpretiert wird, ist eine Hochsprache dazu ausgelegt, die Programmierung intuitiver und zugänglicher zu gestalten.

In Hochsprachen, wie C, C++, Java oder Python, kannst du komplexe Anweisungen und Algorithmen mit einer Vielzahl von verständlichen Symbolen, Schlüsselwörtern und Funktionen formulieren. Ein weiterer bedeutender Vorteil von Hochsprachen besteht darin, dass sie plattformunabhängig sind. Ein in einer Hochsprache geschriebenes Programm kann auf verschiedenen Systemen und Mikrocontrollern ausgeführt werden, vorausgesetzt, es gibt eine kompatible Implementierung der Sprache für die jeweilige Plattform.

Hochsprachen vereinfachen den Programmierprozess erheblich, indem sie verschiedene Abstraktionsebenen bereitstellen und die Komplexität der Hardware maskieren. Dadurch können Entwickler sich auf das Lösen von Problemen und die Implementierung von Funktionen konzentrieren, ohne sich zu sehr mit den technischen Details der Hardware auseinandersetzen zu müssen.

Schließlich, obwohl Maschinen- und Hochsprachen ihre spezifischen Rollen und Vorteile haben, hängt die Wahl der Programmiersprache letztendlich von den Anforderungen des Projekts, den vorhandenen Fähigkeiten und dem gewünschten Endziel ab.

 

Der Kompiler

Ein entscheidendes Bindeglied zwischen der Hochsprache, die von Entwicklern verwendet wird, und dem Maschinencode, den die CPU versteht, ist der Compiler. Er fungiert als Übersetzer, der den Code, den du schreibst, in eine Form umwandelt, die von der Hardware verstanden und ausgeführt werden kann.

Wenn du in der Arduino-Integrierten Entwicklungsumgebung (IDE) auf "Upload" oder "Verify" klickst, startet der Compiler seine Arbeit.

Der Compiler ist ein leistungsfähiges Werkzeug. Er überprüft die Syntax deines Codes, übersetzt ihn in effizienten Maschinencode, organisiert die Laufzeitumgebung deines Programms und formt den Code in eine kompakte und ausführbare Form. Darüber hinaus gibt der Compiler klare und verständliche Fehler- oder Warnmeldungen aus, wenn er auf Probleme im Code stößt.

Nehmen wir zum Beispiel an, du gibst "ich bin kein C-Code" in die Arduino-IDE ein und klickst auf "Verify". Der Compiler meldet sich sofort und zeigt die Fehlermeldung "ich bin kein C-Code" does not name a type. Dieser Text stellt keinen gültigen C-Code dar und kann daher nicht in Maschinencode übersetzt werden.

Als weiteres Beispiel, stelle dir vor, du lässt die Arduino-IDE leer, ohne die Funktionen void setup() und void loop(), und klickst auf "Verify". In diesem Fall wirst du eine umfangreichere Fehlermeldung erhalten, die unter anderem die Zeilen 'undefined reference to setup' und 'undefined reference to loop' enthält. Dies bedeutet, dass das Fehlen der Funktionen void setup() und void loop() nicht akzeptiert wird. Beide Funktionen sind unverzichtbare Elemente eines Arduino-Sketches und bilden die grundlegenden Bausteine für die Ausführung deines Programms.