Tutorial · Einsteiger · 30 Min.
8051-Architektur: Pins, Speicherkarte und Register
Was im 8051 wirklich steckt: Harvard-Kern, vier Ports mit eigenem Charakter, 256 Byte RAM und ein Maschinenzyklus, den man mitzählen kann.
Einführung
Die meisten Mikrocontroller-Kurse beginnen mit dem 8051, und die meisten Studierenden begegnen ihm als Liste auswendig zu lernender Fakten: vier Ports, 128 Byte RAM, zwei Timer, sechs Interrupts. So auswendig gelernt ist er schnell vergessen. Versteht man ihn dagegen als Bündel bewusster Kompromisse aus dem Jahr 1980 – als RAM richtig Geld kostete und ein Chip in 40 Pins passen musste –, erklärt er fast alles, wie kleine Mikrocontroller bis heute funktionieren.
Dies ist der Architektur-Rundgang: was im Gehäuse steckt, wie der Speicher aufgeteilt ist und warum die Timing-Rechnung ungewöhnlich einfach ist. Die beiden Fortsetzungen behandeln die Ansteuerung der Pins und die PWM-Erzeugung per Timer.
„8051“ ist eine Familie, kein Chip
Intel führte den MCS-51 im Jahr 1980 ein und lizenzierte den Kern breit, daher kauft heute niemand mehr einen Intel 8051. Was man kauft, ist ein kompatibler Baustein – ganz überwiegend Atmels (heute Microchips) AT89S52, der Flash-Speicher hinzufügt, den man über vier Pins neu programmieren kann, statt des UV-löschbaren ROMs des Originals.
Die Unterschiede, die man kennen sollte:
| 8051 (Original) | AT89S52 (dein Baustein) | |
|---|---|---|
| Programmspeicher | 4 KB ROM | 8 KB Flash |
| Internes RAM | 128 Byte | 256 Byte |
| Timer | 2 | 3 |
| Interruptquellen | 5 | 6 |
| Programmierung | Masken-ROM ab Werk | Im System, über SPI-Pins |
Alles andere – Befehlssatz, Ports, Registerlayout, Timing – ist identisch. Kursmaterial zum 8051 gilt unverändert für den Chip auf deinem Tisch.
Was im Gehäuse steckt
Ein 40-poliges DIP enthält einen kompletten Computer: eine 8-Bit-CPU, Programm-Flash, Daten-RAM, drei Timer/Zähler, eine Vollduplex-Serienschnittstelle, eine Interruptsteuerung und vier 8-Bit-I/O-Ports. Es gibt kein Betriebssystem und nichts läuft außer deinem Code.
Vier Pins sind überhaupt kein I/O, und alle vier müssen stimmen, bevor irgendetwas läuft: XTAL1 und XTAL2 für den Quarz, RST für das Reset-Netzwerk und EA zur Wahl von internem oder externem Programmspeicher. Ist EA falsch, tut der Chip nichts – lautlos.
Harvard: Code und Daten sind verschiedene Welten
Der 8051 hält Programmspeicher und Datenspeicher in getrennten Adressräumen. Eine Adresse 0x0030 bedeutet für einen Befehlszugriff etwas anderes als für einen Datenzugriff – es sind verschiedene Speicher, erreicht über verschiedene Befehle.
Darum zwingen C-Compiler für den 8051 zum Nachdenken über Speicherklassen, die andere Plattformen verbergen. Ein String-Literal, das du nie änderst, gehört in den code-Speicher, wo Kilobytes übrig sind. Lege es versehentlich in data ab, frisst es die 256 Byte, die du für alles andere brauchtest:
// In code (flash) — costs nothing from your 256 bytes of RAM.
const char code banner[] = "Line follower v1";
// In RAM — 17 of your 256 bytes, gone, for a string that never changes.
const char banner_bad[] = "Line follower v1";
Auf einem Arduino existiert genau dafür PROGMEM. Auf dem 8051 ist das keine Optimierung, sondern der Unterschied zwischen einem Programm, das passt, und einem, das nicht passt.
Die interne RAM-Karte
Die 256 Byte internes RAM sind kein flacher Notizzettel. Sie sind in Bereiche mit unterschiedlichen Fähigkeiten aufgeteilt, und zu wissen, welcher welcher ist, ist der größte Teil der Speicherkarte.
00h–1Fh – vier Registerbänke. R0 bis R7 sind keine festen Adressen, sondern ein Fenster auf eine von vier 8-Byte-Bänken, gewählt über zwei Bits (RS0 und RS1) im Programmstatuswort. Das Umschalten der Bank ist ein Zwei-Bit-Schreibzugriff und damit ein sehr billiger Weg, einem Interrupt eigene Register zu geben, statt acht davon zu sichern und zurückzuholen.
20h–2Fh – bitadressierbar. Diese 16 Byte sind zusätzlich als 128 einzelne Bits adressierbar, 00h bis 7Fh. Der 8051 hat echte Einzelbit-Befehle – SETB, CLR, JB, JNB –, sodass ein Flag hier ein Bit und einen Befehl kostet statt eines Bytes und einer Maske. Darum bieten 8051-C-Compiler einen bit-Typ, den es sonst nirgends gibt.
30h–7Fh – Arbeitsbereich und Stack. Achtzig Byte für deine Variablen und deinen Aufruf-Stack, und genau diese Ressource geht tatsächlich aus.
80h–FFh – zwei Speicher, ein Adressbereich. Direkte Adressierung erreicht hier die Spezialfunktionsregister; indirekte Adressierung (über R0 oder R1) erreicht die oberen 128 Byte RAM. Gleiche Nummern, verschiedene Speicher, gewählt über die Befehlsform. Beim ursprünglichen 128-Byte-8051 existiert das obere RAM schlicht nicht.
Spezialfunktionsregister sind nur Speicher
Jede Peripherie des 8051 wird gesteuert, indem man Bytes an Adressen im direkten Bereich 80h–FFh schreibt. Es gibt keine API – es gibt ein Register, und das Setzen eines Bits darin ändert, was das Silizium tut.
| Register | Adresse | Was es steuert |
|---|---|---|
P0 P1 P2 P3 |
80h, 90h, A0h, B0h | Die vier I/O-Ports |
TMOD |
89h | Timer-0- und -1-Betriebsart |
TCON |
88h | Timer-Startbits und Überlaufflags |
TH0 TL0 TH1 TL1 |
8Ch, 8Ah, 8Dh, 8Bh | Timer-Zählregister |
SCON SBUF |
98h, 99h | Serienschnittstellen-Steuerung und Daten |
IE IP |
A8h, B8h | Interruptfreigabe und -priorität |
PSW |
D0h | Flags und die Registerbank-Auswahlbits |
SP |
81h | Stackpointer |
P1 = 0x0F; in C zu schreiben ist ein Speicherzugriff auf Adresse 90h, und acht Pins ändern ihren Zustand. Diese Direktheit ist der ganze Reiz des Chips als Lehrbaustein.
Timing zum Mitzählen
Ein Maschinenzyklus sind zwölf Oszillatorperioden beim klassischen 8051-Kern, und die meisten Befehle brauchen einen oder zwei Maschinenzyklen. Dieses feste Verhältnis erlaubt, die Ausführungszeit exakt zu berechnen statt sie zu messen:
machine cycle = 12 / f_osc
at 11.0592 MHz : 12 / 11 059 200 = 1.085 µs
at 12 MHz : 12 / 12 000 000 = 1.000 µs
Eine Schleife aus drei Einzyklus-Befehlen bei 11,0592 MHz dauert jedes Mal 3,26 µs – ohne Cache, ohne Pipeline und ohne Interrupt-Jitter außer dem, den du selbst freigibst. Kaum ein moderner Prozessor erlaubt solches Schlussfolgern, und darum überlebt der 8051 in zeitkritischer Peripherie.
Warum der Quarz 11,0592 MHz hat
Diese Zahl wirkt willkürlich und ist es nicht. Die Serienschnittstelle leitet ihre Baudrate durch Teilung des Maschinenzyklustakts ab, und 11,0592 MHz lässt sich exakt auf Standard-Baudraten teilen:
baud = f_osc / (12 × 32 × (256 − TH1))
11.0592 MHz, TH1 = 253 : 11 059 200 / (12 × 32 × 3) = 9600.0 ✓ exact
12 MHz, TH1 = 253 : 12 000 000 / (12 × 32 × 3) = 10416.7 ✗ 8.5% off
Ein UART verträgt wenige Prozent Fehler; 8,5 % verstümmeln jeden Rahmen. Wenn dein 8051-Board Müll über die Serienschnittstelle druckt, schau erst auf den Quarz, dann auf deinen Code.
Was der 8051 nicht hat
Zwei Lücken prägen jedes 8051-Projekt, und keine ist aus einer Funktionsliste ersichtlich:
- Kein Analog-Digital-Wandler. Für ein Potenziometer, einen Thermistor oder einen analogen Liniensensor braucht es einen externen ADC-Baustein wie den ADC0804. Es gibt kein
analogRead(), nach dem man greifen könnte. - Keine PWM-Hardware. Nichts auf dem Chip erzeugt ein Tastverhältnis-Signal. Eine LED zu dimmen oder eine Motordrehzahl zu setzen heißt, PWM selbst aus einem Timer-Interrupt zu bauen – und dafür CPU-Zeit zu zahlen.
Ein Arduino Uno hat beides eingebaut. Das ist der ehrliche Vergleich: Der 8051 ist kein schlechterer Arduino, sondern die Schicht darunter – mit entfernten Annehmlichkeiten, damit man sieht, was sie geleistet haben.
Wenn es schiefgeht
- Das Board ist tot und nichts wird warm. Prüfe, ob EA (Pin 31) auf +5 V liegt. Schwebend oder auf Masse holt der Chip Code aus nicht vorhandenem externem Speicher und führt nichts aus – ganz ohne Symptom.
- Es startet ständig neu oder gar nicht. Reset beim 8051 ist Active-High. Die Einschalt-Schaltung ist ein 10-µF-Kondensator von +5 V nach RST und ein 10-kΩ-Widerstand von RST nach Masse. Wie ein Active-Low-Reset verdrahtet hält sie den Chip in dauerhaftem Reset.
- Serielle Ausgabe ist Müll. Fast immer der Quarz. Ein 12-MHz-Baustein erreicht keine Standard-Baudraten.
- Variablen überschreiben einander ohne Grund. Wahrscheinlich sind die 80 Byte bei 30h–7Fh aufgebraucht oder der Stack ist in deine Variablen hineingewachsen. Prüfe die Speicherkarte des Compilers.
- Register ändern sich unerwartet im Interrupt. Der Stackpointer startet bei 07h, also beginnt der Stack bei 08h – und das ist Registerbank 1. Verschiebe SP entweder im Startcode oder lasse Bank 1 in Ruhe.
Den Graphen erkunden
Teil dieser Bauprojekte
Projekte und Lernpfade, die dieses Tutorial enthalten.
Weiterführende Literatur