home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Best of German Only 1
/
romside_best_of_german_only_1.iso
/
anwender
/
sim
/
sim51_04.arj
/
MCS51.DOC
< prev
next >
Wrap
Text File
|
1993-02-01
|
204KB
|
4,656 lines
zum Drucken diese Zeilen durch ESC-Seq. ersetzen (Druck mit 12 CPI und Rand):
z.B. IBM-Proprinter <ESC>@ <ESC>I<3> <ESC>: <ESC>X<12><90>
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
█ █
█ █
█ █
█ ▄███▄ ▄███▄ ███████ ▄██ █
█ ▐█ █▌ ▐█ █▌ ██ ▄█ ██ █
█ ▐█ █▌ ██ ██ ██ ██ █
█ ▐█ █▌ ██ ██ ██ ▄▄ ██ █
█ █████ ██ ██ ██▀▀▀█▄ ██ █
█ ▐█ █▌ ██ ██ █▌ ██ █
█ ▐█ █▌ ██ ██ █▌ ██ █
█ ▐█ █▌ ▐█ █▌ ▄ █▌ ██ █
█ ▀███▀ ▀███▀ ▀███▀ ██ █
█ █
█ █
█ █
█ █
█ █
█ █
█ H H A RRRRr DDDDd W W A RRRRr EEEEEE █
█ H H A A R R D D W W A A R R E █
█ H H A A R R D D W W A A R R E █
█ HHHHHH AAAAA RRRR D D W W W AAAAA RRRR EEEEE █
█ H H A A R R D D W W W W A A R R E █
█ H H A A R R D D W W W W A A R R E █
█ H H A A R R DDDD W W A A R R EEEEEE █
█ █
█ █
█ █
█ █
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
Hardware-Beschreibung des 8051 Mikrocontrollers
Esslingen im August 1991
Vorwort:
--------
Diese Dokumentation wendet sich an Anfänger, die sich mit dem Aufbau und
der Funktion von Controllern der MSC-51 Familie beschäftigen wollen. Sie
ist dennoch sehr ausführlich gehalten, denn ein unvollständiges Wissen über
die Hardware verschleiert eher das Verständnis der Materie.
Die Dokumentation wurde so geschrieben, daß sie wie ein Lehrbuch durchgele-
sen werden kann. Es wurde versucht, nichts vorrauszusetzen, was erst in
späteren Kapiteln behandelt wird. Dies geht natürlich nicht immer, da die
einzelnen Komponenten voneinander abhängen.
In den einzelnen Kapiteln wurde jedoch versucht ein möglichst umfassendes
Wissen zu dem Stichwort zu geben, damit auch gezielt zu einem Problem nach-
gelesen werden kann.
Wenn möglich, sind in den einzelnen Kapiteln kleine Beispiele gegeben. Da-
neben wird in einem extra Kapitel als Beispiel der Aufbau und die Program-
mierung einer Rechnerkarte mit zwei verschiedenen PC-Koppel-Schnittstellen
beschrieben.
Der grundlegende Typ der MSC-51 Familie ist der 8051 Controller von Intel.
Die anderen von Intel und dann auch von anderen Firmen entwickelten Con-
troller dieser Familie besitzen dieselben Funktionen wie der 8051 plus ei-
nige zusätzliche. So besitzt der 8052 einen zusätzlichen Timer und ein grö-
ßeres internes RAM.
Die Typen 8031, 8751 sind identisch mit dem 8051, jedoch besitzen sie kein
internes ROM bzw. ein intenes EPROM.
Zunächst wird nur der 8051 betrachtet, um den Aufbau zu erläutern. Einige
Funktionen der weiteren CPU's aus der MSC-51 Familie werden anschließend
angegeben.
Ich kann keine Garantie für Vollständigkeit und absoluter Richtigkeit der
Dokumentation übernehmen. Für Ergänzungen, Hinweiße und Fehlerkorrekturen
bin ich immer dankbar.
(c)1991,1993 Werner Hennig-Roleff
Sulzgrieser Str. 101
73733 Esslingen
0711/376718
Inhaltsverzeichnis:
-------------------
1. 8051 Eigenschaften ................................... 1
2. Daten und Adressen ................................... 2
3 Rechnerkern des 8051 ................................. 4
3.1 8051-Blockschaltbild 4
3.2 Takt / Zyklus 7
3.3 Befehlsausführung 8
3.4 Spezial Funktion Register 10
3.4.1 ACC 11
3.4.2 B 11
3.4.3 PSW 12
3.4.4 SP 13
3.4.5 DPTR = DPH:DPL 14
3.4.6 PCON 14
3.5 Ports 16
3.6 externer Programm-Speicher Zugriff 20
3.7 externer Daten-Speicher Zugriff 23
3.8 Anschaltungen für den externen Bus 25
3.9 internes RAM 28
4. Befehlssatz .......................................... 33
4.1 Daten-Transfer 35
4.2 Rechenoperationen 38
4.3 logische Verknüpfungen 41
4.4 Bit Manipulationen 43
4.5 unbedingte Sprünge und Call's 44
4.6 bedingte Sprünge 47
5. Assembler ............................................ 49
5.1 der ASM51 49
5.2 Segmentierung unter ASM51 51
5.2.1 feste Adresszuordnung 52
5.2.2 relocatable Adresszuordnung 54
5.3 ASM51 Aufruf 54
5.4 Programmbeispiel 55
5.5 ASM51 Bug's 57
6. weitere Komponenten des 8051 ......................... 58
6.1 Interrupt Logik 58
6.2 Timer0 und Timer1 61
6.3 serielle Schnittstelle 62
6.3.1 asynchron, synchron Mode 63
6.3.2 seriell Mode Einstellungen 63
6.3.3 Interrupt Mode, Polled Mode 67
6.3.4 Baud Rate Tabelle 70
7. weitere Controller der 8051 Familie 71
7.1 der 8052 71
7.1.1 zusätzliche SFR des 8052 71
7.1.2 Timer2 71
7.1.3 serielle Schnittstelle mit Timer2 73
Anhang:
-------
A.1 ASCII-Tabelle ........................................ 75
A.2 Befehlssatz in Hexadezimaler Reihenfolge ............. 77
1. 8051 Eigenschaften Seite 1
───────────────────────────────────────────────────────────────────────────
1. 8051 Eigenschaften:
-----------------------
Der 8051 wird oft in Regelschaltungen eingesetzt, deßhalb hat sich der Name
Mikrocontroller für den 8051 eingebürgert.
Der 8051 arbeitet mit einem 8-Bit Datenbus. Extern kann er getrennt je
64 kByte Daten und Code adressieren. Er verfügt zusätzlich über ein inter-
nes RAM (128 Byte) und über ein internes ROM (4 kByte) als Programmspei-
cher. Aus dem internen Programmspeicher werden die Befehle der Adressen
0000 bis 0FFFh abgearbeitet. Befehle höherer Adressen werden immer aus dem
externen Programmspeicher gelesen. Über einen Eingangspin kann der interne
Programmspeicher abgeschaltet werden.
Zur Abarbeitung der meißten Befehle wird nur ein Zyklus (1 µs) benötigt.
Zur Datenmanipulation stehen folgende Befehle zur Verfügung:
MOV (Daten kopieren),
XCH (Datenaustausch),
INC, DEC (Increment +1, Decrement -1),
ANL, ORL, XOL (logische Verknüpfung),
ADD, ADDC, SUBB (Addition, Subtraktion),
MUL, DIV (Multiplikation, Division).
Eine Besonderheit des 8051 ist, daß auf fast alle seiner elektrischen An-
schlüsse (Pin's) per Software zugegriffen werden kann. Auch verfügt er über
zwei integrierte Timer, eine serielle Schnittstelle (UART) sowie zwei In-
terrupteingänge.
Dazu gibt es die Möglichkeit, über MOV, SETB, CLR und CPL einzelne BIT's
zu manipulieren.
Der 8051 verfügt über bedingte und unbedingte Sprungbefehle. Es können Un-
terprogramme aufgerufen werden. Die Returnadresse wird von der CPU auf den
Stack gelegt. Der Stack wird im internen RAM angelegt und wächst von von
unten nach oben.
Bei Divisionen kann nur byteweise (8 BIT) dividiert werden. Für größere Da-
ten sind aufwendige Proceduren nötig. Die Größe des internen RAM's ist auch
oft begrenzend.
Gerade als preisgünstiger Controller erfreut sich der 8051 jedoch großer
Beliebtheit.
2. Daten und Adressen Seite 2
───────────────────────────────────────────────────────────────────────────
2. Daten und Adressen:
----------------------
Auf einem Rechner können nur Zahlenwerte verarbeitet werden: z.B. Meßgrö-
ßen, Kalkulationswerte, usw. Zur Bearbeitung von Texten auf Rechnern wird
jedem Zeichen ein Zahlenwert zugeordnet.
In der Digitaltechnik wird mit zwei logischen Zuständen (1=high oder 0=low)
gearbeitet. Damit können mit einer Leitung 2 Zahlenwerte selektiert werden.
Mit 2 Leitungen können schon 4 verschiedene Zahlenwerte dargestellt werden:
0 = 00b, 1 = 01b, 2 = 10b und 3 = 11b (das "b" steht als Kennzeichen für
eine Binärzahl).
Das Alphabeth beinhaltet 26 Zeichen. Unterscheidet man zwischen groß und
klein und nimmt noch einige Sonderzeichen (1 2 3..0 , + - ? ! : ; / ( ö ä ü
usw.) hinzu, so kommt man auf ca. 80 Zeichen. Um die Zeichen eindeutig zu
unterscheiden wären mindestens 7 Leitungen nötig.
Die Zuordnung von Zeichen zu Zahlenwerten wurde natürlich längst genormt.
In der ASCII-Norm (American Standard Code for Information Interchange) wer-
den 8 Leitungen zur Darstellung eines Zahlenwertes verwendet. Ein solcher
Zahlenwert zwischen 0 und 255 wird als BYTE bezeichnet. Eine einzelne Lei-
tung daraus als BIT.
Die Wahl von 8 BIT je BYTE kommt auch der Darstellung von Zahlenwerten bei
Berechnungen zugute, denn in Hexadezimaler Schreibweise kann damit gerade
eine zweistellige Hex-Zahl dargestellt werden. Größere Meßwerte (mit mehre-
ren Stellen) belegen mehrere Bytes.
Dem Rechnung tragend, wurden Controller und Prozessoren mit 8 BIT breiten
Datenleitungen ausgestattet. Neuere, schnellere CPU's erhielten später 16
oder 32 BIT breite Datenleitungen. Bei allen wird zur Codierung von Texten
weiterhin die ASCII-Tabelle (siehe Anhang) verwendet. Folgende Bezeichnun-
gen sind üblich:
BIT 1 Datenleitung
NIBBLE 4 Datenleitungen = ½ BYTE einstellige Hexzahl 0..0F
BYTE 8 Datenleitungen zweistellige Hexzahl 0..0FF
WORD 16 Datenleitungen vierstellige Hexzahl 0..0FFFF
DWORD 32 Datenleitungen achtstellige Hexzahl 0..0FFFFFFFF
MSB LSB
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
│ Bit7 │ Bit6 │ Bit5 │ Bit4 │ Bit3 │ Bit2 │ Bit1 │ Bit0 │ BYTE
└──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘
| High NIBBLE | Low NIBBLE |
MSB = most significant Bit
LSB = least significant Bit
2. Daten und Adressen Seite 3
───────────────────────────────────────────────────────────────────────────
Der 8051 ist ein 8 Bit-Rechner, das heißt: Zahlenwerte , Zeichen aber auch
der Code werden als Bytes abgelegt. Als Datenbus besitzt er 8 parallele Da-
tenleitungen.
On Chip wird der Datenaustausch über einen INTERNEN DATENBUS abgewickelt.
Wird eine Rechnerkarte aufgebaut und ein externes RAM oder/und EPROM ver-
wendet, so müssen für die Daten 8 Verbindungsleitungen zwischen dem 8051
und den externen Speichern gezogen werden. Dies ist der EXTERNER DATENBUS.
Der Übergang zwischen dem internen und dem externen Datenbus erfolgt über
spezielle Treiber (siehe Ports).
Um eine Zahl von der Größe eines BYTE zu speichern werden 8 Flip-Flop's be-
nötigt. Um einen ganzen Text zu speichern, werden eine Vielzahl solcher
achtstelligen Flip-Flop-Speicherstellen benötigt. Dabei muß jede Speicher-
stelle einzeln angesprochen werden können. Für jede Speicherstelle wird al-
so eine Auswahl-Leitung benötigt.
Speicher-Chips werden heute hochintegriert hergestellt (ein RAM 61256 be-
sitzt beispielsweise 32768 Speicherstellen: es können 32 kByte gespeichert
werden). Von außen werden nicht 32768 Auswahl-Leitungen zugeführt, um auf
eine bestimmte Speicherstelle zuzugreifen. Stattdessen weden codierte
Adressen verwendet, die erst auf dem RAM-Chip decodiert werden. So können
mit den 16 Adressleitungen, die der 8051 bereitstellt, 65536 Speicherstel-
len adressiert werden.
Die Auswahl, auf welche Speicherstelle zugegriffen wird, hängt beim 8051
nicht nur von der Adresse ab, sondern auch vom Datentyp. Unterschieden wer-
den: SFR, internes RAM, externes RAM, internes ROM, externes EPROM
3.1 Blockschaltbild Seite 4
───────────────────────────────────────────────────────────────────────────
3.1 8051 Blockschaltbild:
-------------------------
Der 8051 verwendet einen 8 Bit breiten Datenbus. Er besitzt einige interne
Register, die ebenfalls 8 Bit breit sind. Über Treiber kann er auf externe
Bausteine (wie Speicher) zugreifen. Er besitzt ein internes RAM, 2 Timer,
einen seriellen Ein-/Ausgang und einen internen Programmspeicher.
┌┐
┌─┤││├─┐
│ └┘ │
┌─┴──────┴─┐ ┌──────────────────────────┐
ALE │ │ │ │
_PSEN │ Takt ├────────────┤ Recheneinheit ├─┐
│ │ ╔═══>│ │ │
└────┬─────┘ ║ └──────────────────────────┘ │
│ ║ | <Code-Fetch> | │
│ ║ | | │ _EA
Timer: │ ║i | | │ ┌───────────┐
┌───────────┐ ║n | | └─>│ Programm- │
│ TMOD │<═════╣t | ╔═════════════│ Counter, │
├───────────┤ ║e | ║i | │ Adress- │
│ TCON │<═════╣r | ║n | ╔══│ Register │
├───────────┤ ║n ┌──────────┐ ║t | ║e └───────────┘
│ TH0:TL0 │<═════╣ │ internes │ ║. | ║x
├───────────┤ ║D │ │ ║A | ║t
│ TH1:TL1 │<═════╣A │ PROM │<══╝d | ║.
└───────────┘ ║T │ │ r | ║A
║A └──────────┘ | ║d
║ | ║r
║ | ║
┌───────────┐ ║ Port-Latches: | ║ Treiber:
│ ACC │<═════╣ ┌────────┐ | ╠══════>│
├───────────┤ ╠══════════>│ P0 │<═════╧════════════>│ Port 0
│ B │<═════╣ └────────┘ ║
├───────────┤ ║ ┌────────┐ ╚══════>│
│ PSW │<═════╬══════════>│ P2 │<══════════════════>│ Port 2
├───────────┤ ║ ├────────┤
│ PCON │<═════╬══════════>│ P1 │<══════════════════>│ Port 1
├───────────┤ ║ ├────────┤
│ DPH:DPL │<═════╬══════════>│ P3 │<══════════════════>│ Port 3
├───────────┤ ║ └────────┘
│ SP │<═════╣
└───────────┘ ║
║
║ ┌──────────┐
serielle Schnittstelle: ║ │ internes │
┌───────────┐ ║══════>│ │
│ SCON │<═════╣ │ RAM │
├───────────┤ ║ │ │
│ SBUF │<═════╝ └──────────┘
└───────────┘
3.1 Blockschaltbild Seite 5
───────────────────────────────────────────────────────────────────────────
Das Blockschaltbild gibt schematisch den inneren Hardwareaufbau an. Es ist
natürlich nicht in allen Einzelheiten genau. Es wurde mehr von der Struk-
tur, wie sie der Programmierer sieht, ausgegangen als vom tatsächlichen
elektrischen Aufbau
Wie im Blockschaltbild angedeutet kann über den internen Datenbus der CPU
auf die SFR (siehe Kapitel 3.4) und das interne RAM zugegriffen werden. Für
externe Speicherzugriffe (XDATA) werden die Ports P0 und P2 verwendet. An
P2 wird dabei das High-Adress-Byte gelegt. An P0 wird zuerst das Low-
Adress-Byte gelegt. Nach der negativen Flanke in ALE (Adress-Latch-Enable)
werden dann Daten erwartet bzw. ausgegeben.
Die Befehle (CODE), die der 8051 ausführen soll, werden wahlweise aus dem
internen ROM oder aus einem externen Programmspeicher gelesen. Das Einlesen
von CODE findet in jedem Befehls-Zyklus 2 mal statt. Die Befehle sind eben-
falls als Zahlenwerte von der Größe eines BYTES abgelegt. Somit sind maxi-
mal 256 verschiedene Befehle möglich. Jeder Zahl ist ein bestimmter Befehl
zugeordnet: so bedeutet z.B. der CODE 0A4h --> multiplizier das Register
ACC mit dem Register B (für den vollständigen Befehlssatz siehe Kapitel 4).
Das folgende Bild zeigt ein Beispiel für eine externe Beschaltung des 8051
mit einem externen RAM und einem EPROM. Da Port 0 während der ersten Hälfte
eines Zugriffs zuerst die Low-Adressen führt und dann für die zweite Hälfte
umschaltet (multiplexer) auf Daten müssen die Low-Adressen in einem Latch
gespeichert werden (es gibt allerdings auch RAM's, die Multiplexed Eingänge
besitzen wie das DPR SAE 81C80 von Siemens, hier wäre kein Latch nötig).
8051 RAM
┌─────────┐ ┌────────┐
│ _WR├──────────────────────────────────>│_WR │
│ _RD├──────────────────────────────────>│_RD │
│ │ high Adr. │ │
│ P2╞═══════════════════════════╦══════>│ │<══════╗
│ │ ║A │ │ ║
│ │ LATCH ║D │ │ D║
│ │ ┌─────┐ ║R └────────┘ A║
│ ALE├─────────>│74 LS│ ║E EPROM T║
│ │ │ 373 │low Adr. ║S ┌────────┐ E║
│ P0│<═══╦════>│ ╞══════════╣S │ │ N║
│ │ ║ │ │ ║E │ │ ║
│ │ ║ └─────┘ ║N │ │<══════╣
│ │ ║ ╚══════>│ │ ║
│ │ ║ _PSEN │ │ ║
│ _PSEN├────║─────────────────────────────>│_RD │ ║
└─────────┘ ║ └────────┘ ║
║ externe DATEN ║
╚═══════════════════════════════════════════════╝
3.1 Blockschaltbild Seite 6
───────────────────────────────────────────────────────────────────────────
Mit zum Verständnis der Hard- ┌────────────────┐
ware und den folgenden Erläu- P1.0 │ 1 40│ + 5V
terungen zu den einzelnen P1.1 │ 2 39│ P0.0 (AD0)
Komponenten des 8051 gehört P1.2 │ 3 38│ P0.1 (AD1)
auch nebenstehendes Anschluß- P1.3 │ 4 37│ P0.2 (AD2)
bild des 8051. P1.4 │ 5 36│ P0.3 (AD3)
P1.5 │ 6 35│ P0.4 (AD4)
P1.6 │ 7 34│ P0.5 (AD5)
P1.7 │ 8 33│ P0.6 (AD6)
reset │ 9 32│ P0.7 (AD7)
(RxD) P3.0 │10 31│ _EA
(TxD) P3.1 │11 30│ ALE
(_INT0) P3.2 │12 29│ _PSEN
(_INT1) P3.3 │13 28│ P2.7 (A15)
(T0) P3.4 │14 28│ P2.6 (A14)
(T1) P3.5 │15 28│ P2.5 (A13)
(_WR) P3.6 │16 28│ P2.4 (A12)
(_RD) P3.7 │17 28│ P2.3 (A11)
XTAL2 │18 28│ P2.2 (A10)
XTAL1 │19 28│ P2.1 (A9)
GND │20 28│ P2.0 (A8)
└────────────────┘
Der 8055 wird mit +5V betrieben. Die meißten Anschlüssen sind in Ports zu-
sammengefaßt. Auf diese Port-Pins kann von der Software direkt zugegriffen
werden, daneben haben sie noch zusätzliche Hardwarefunktionen (oben in
Klammern angedeutet). Nur der RESET-Pin, die extern/intern Code-Fetch Um-
schaltung (_EA), den Quarzeingang, den Taktausgang für das Adress-Latch so-
wie das externe Programmspeicher-Lesesignal kann nicht über die Software
zugegriffen werden. Kurzbeschreibung:
Port0: XTAL1, XTAL2:
8-Bit Ein-/Ausgang. Alternativ Anschlüsse für einen Quarz oder
dient er zur Ausgabe der Low- einen externen Takt.
Adressen und zum Schreiben/Le-
sen beim Zugriff auf externe ALE:
Speicher. Adress Latch Enable Signal zum
Speichern der Low-Adresse von
Port1: Port0 beim Zugriff auf externe
8-Bit Ein-/Ausgang. Speicher.
Port2: _PSEN:
8-Bit Ein-/Ausgang. Alternativ Programm Store Enable (Lesesi-
dient er zur Ausgabe der High- gnal) beim Zugriff auf externen
Adressen beim Zugriff auf ex- Code.
terne Speicher.
reset:
Port3: muß high sein für mindestens 2
8-Bit Ein-/Ausgang. Alternativ Zykluse, dann Reset.
dienen P3.0 und P3.1 als Ein-
/Ausgang der seriellen Schnitt- _EA:
stelle, P3.2 und P3.3 als In- Extern Access: wenn low, so
terrupteingänge, P3.4 und P3.5 wird aller Code extern gelesen.
als Timer-Gate, P3.6 und P3.7 Wenn high, so wird Code 0000...
als Schreib- / Lesesignal beim 0FFFh intern gelesen, höhere
Zugriff auf externe Daten. Adressen extern.
3.2 Takt Seite 7
───────────────────────────────────────────────────────────────────────────
3.2 Takt / Zyklus:
-------------------
Die Logik im 8051 arbeitet getaktet. Der Takt wird aus einem Quarz abgelei-
tet. Die maximal verwendtbare Frequenz ist 12 MHz (es gibt auch schon neue-
re CPU's mit 16 MHz). Der Quarz muß zum Schwingen angeregt werden. Dazu
wird er in die Rückkoppelleitung eines invertierenden Verstärkers geschal-
tet. Dieser Treiber ist im 8051 integriert. Damit der Quarz nicht auf einer
Oberwelle schwingt, wird er mit zwei kleinen Kondensatoren beschaltet.
Quarz, max. 12 MHz
30pF ┌┐ 30pF
┌───┤├───┬───┤││├───┬───┤├───┐
GND│ │ └┘ │ │GND
─┴─ │ │ ─┴─
│ │
│ │
------o XTAL1----o XTAL2------
│ │
│ │
│ │ \ │
└───┤ \──┴─────> zu internen
│ / Taktgewinnung
Beschaltung mit Quarz bei 8051 HMOS + CMOS
! Achtung: 8051 Rechner gibt's in HMOS- und CMOS-Technologie. Die CMOS-
Ausführung beinhaltet einen etwas anderst beschalteten Quarztreiber.
An der externen Beschaltung mit einem Quarz (wie oben gezeichnet) än-
dert sich dabei nichts. Wird jedoch ein externer Taktoszillator ver-
wendet und dieser Takt (TTL-Pegel) eingespeist, so ist eine unter-
schiedliche Beschaltung nötig:
Bei der HMOS-Ausführung wird der Bei der CMOS-Ausführung wird der
externe Takt bei XTAL2 eingespeist. externe Takt bei XTAL1 eingespeist.
Zur Abschaltung des internen Quarz- Der Ausgang XTAL2 des internen
treibers wird XTAL1 auf GND gelegt. Quarztreibers bleibt offen.
┌──────┐ ┌──────┐
│ ┌┐ │ externe Takt │ ┌┐ │ externe Takt
│ ┤││├ ├──────────────┐ │ ┤││├ ├────┐
│ └┘ │ │ │ └┘ │ │
└──────┘ │ └──────┘ │
─┬─ GND │ │
│ │ ----o XTAL1----o XTAL2---
--------o XTAL1----o XTAL2--- │ │
│ │ │ │ \ │
HMOS │ │ CMOS ├───┤ \──┘
│ │ \ │ │ │ /
└───┤ \──┴───> interne │
│ / Takt └──────> interne Takt
3.3 Befehlsausführung Seite 8
───────────────────────────────────────────────────────────────────────────
Der von den Eingängen XTAL2 bzw. XTAL1 oder dem Quarztreiber kommende Takt
wird durch 2 geteilt. Danach steht ein symmetrischer Takt von maximal 6 MHz
zur Verfügung. Dieser Takt wird vom 8051 als Systemtakt verwendet, das
heißt alle logischen Zustandsänderungen innerhalb des 8051 werden mit die-
sem Takt gesetzt. Für die Abarbeitung eines Befehls werden mindestens 6 Sy-
stemtakte (State 1 bis 6 = 1 Zyklus) benötigt.
Quarz: 12 MHz maximal (einige spezielle CPU auch 16 MHz)
Systemtakt: 6 MHz
Zyklus: 1 MHz --> 1 µs
┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌┐ ┌─┐┌──┐ ┌─┐ ┌─
XTAL ─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └──┘└─┘ └┘ └─┘ └─┘
──┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───
SysClk └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘
State | 1 | 2 | 3 | 4 | 5 | 6 | 1
^ ^ ^
1.Code-Fetch 2.Code-Fetch
Selbst bei unsymetrischen Quarztakt ergibt sich so ein symetrischer System-
takt (angedeutet für State 5 und 6 in obigen Diagramm).
3.3 Befehlsausführung:
-----------------------
Die CPU ließt je Zyklus immer 2 Byte aus den Programm-Speicher (Code-
Fetch). Dies geschieht automatisch in State 1 und State 4 (siehe auch Ti-
ming für externen Code-Zugriff):
Es gibt Befehle ohne und mit 1 bis 2 Operanten. Es werden je Zyklus gene-
rell 2 Code-Bytes gelesen, zuviel gelesene werden wieder verworfen. Bei Be-
fehlen, deren Abarbeitung mehr als 1 Zyklus benötigt, wird der nächste Be-
fehl wiederholt gelesen, bis er zur Ausführung kommt.
--- Beispiele:
Befehl: Opcode: Zyklus: Fetch:
--------------------------------------------------------------------------
NOP 00 1 00 05 1 Byte zuviel, verworfen
INC SP 05 81 1 05 81
INC DPTR A3 2 A3 E2 E2 83 im 2.Zyklus nächster Fetch
MOVX A,@R0 E2 2 E2 83 __ __ im 2.Zyklus XDATA nicht Fetch
MOVC A,@A+PC 83 2 83 02 __ 02 im 2.Zyklus MOVC-Zugriff
LJMP 3344 02 33 44 2 02 33 44 ??
3.3 Befehlsausführung Seite 9
───────────────────────────────────────────────────────────────────────────
Beim NOP wurde ein Code-Byte zuviel gelesen. Es wird nicht ausgewertet,
sondern im nächsten Zyklus erneut gelesen. Bei INC DPTR wurde im ersten Zy-
klus 1 Code-Byte zuviel gelesen. Im 2. Zyklus werden sogar nocheinmal die
Code-Bytes des nächsten Befehls gelesen aber auch noch nicht ausgewertet.
Eine Besonderheit gibt es bei einem externen Datenspeicherzugriff (MOVX).
Hier erfolgt im 2. Zyklus kein Code-Fetch, sondern es wird auf das externe
RAM zugegriffen.
Beim Zugriff auf eine Tabelle im Programmspeicher über MOVC wurde im 1. Zy-
klus ein Byte des nächsten Befehls schon mitgelesen (zuviel). Im 2. Zyklus
erfolgt zunächst der Zugriff auf die Tabelle und dann noch ein Code-Fetch,
der aber such verworfen wird.
Ein LJMP-Befehl benötigt 3 Code-Bytes und 2 Zykluse. Im 1. Zyklus wird der
Opcode und das High-Byte der Zieladresse (1. Operand) gelesen. Im 2. Zyklus
wird das Low-Byte der Zieladresse (2. Operand) und (zuviel) der Opcode des
folgenden Code-Bytes gelesen. Nach Ausführung des Sprungs steht der
Programm-Counter auf der neuen Adresse. Der nächste Code-Fetch erfolgt
dort. Hieraus ist auch ersichtlich, warum der im vorigen Zyklus zuviel ge-
lesene Befehl immer wieder verworfen wird.
Die Ausführung des Befehls erfolgt zumeißt erst im State 5. Dort wird auch
schon wieder die Code-Adresse für den Code-Fetch des nächsten Zyklus ange-
legt.
3.4 SFR Seite 10
───────────────────────────────────────────────────────────────────────────
3.4 SFR (Spezial Funktion Register):
-------------------------------------
Im Unterschied zu einigen Processoren werden zur Steuerung von Timer und
serieller Schnittstelle keine speziellen Befehle verwendet. Dazu dienen
spezielle Register, die im Datenbereich des 8051 angeordnet sind. Jedem SFR
(spezial Funktion Register) ist dazu eine DATA-Adresse zugeordnet, der Zu-
griff erfolgt über den internen Datenbus.
Bei gegenüber dem 8051 erweiterten Controllern wie den 8052, 80515, 80552
und anderen kommen bei zusätzlichen Funktionen einfach zusätzliche SFR hin-
zu. Sie besitzen jedoch denselben Befehlssatz!
Anstelle der DATA-Adresse kann beim ASM51-Assembler eine vordefinierte Ab-
kürzung (Mnemonic) verwendet werden. Die folgende Tabelle enthält alle SFR
des 8051 mit Mnemonic und DATA-Adresse:
Ports: Timer:
P0 DATA 80h TCON DATA 88h
P1 DATA 90h TMOD DATA 89h
P2 DATA 0A0h TL0 DATA 8Ah
P3 DATA 0B0h TL1 DATA 8Bh
TH0 DATA 8Ch
Rechenregister und CPU-Control: TH1 DATA 8Dh
PSW DATA 0D0h
ACC DATA 0E0h Interrupt-Control:
B DATA 0F0h IE DATA 0A8h
PCON DATA 87h IP DATA 0B8h
Pointer: serielle Schnittstelle:
SP DATA 81h SCON DATA 98h
DPL DATA 82h SBUF DATA 99h
DPH DATA 83h
Einige SFR können bitweise adressiert werden (immer solche deren DDATA-
Adresse mit 0 oder 8 endet). Beim 8051 sind dies:
PSW - Bit's: IE - Bit's IP - Bit's
CY BIT 0D7h EA BIT 0AFh PS BIT 0BCh
AC BIT 0D6h ES BIT 0ACh PT1 BIT 0BBh
F0 BIT 0D5h ET1 BIT 0ABh PX1 BIT 0BAh
RS1 BIT 0D4h EX1 BIT 0AAh PT0 BIT 0B9h
RS0 BIT 0D3h ET0 BIT 0A9h PX0 BIT 0B8h
OV BIT 0D2h EX0 BIT 0A8h
P BIT 0D0h SCON - Bit's
P3 - Bit's SM0 BIT 9Fh
TCON - Bit's RD BIT 0B7h SM1 BIT 9Eh
TF1 BIT 8Fh WR BIT 0B6h SM2 BIT 9Dh
TR1 BIT 8Eh T1 BIT 0B5h REN BIT 9Ch
TF0 BIT 8Dh T0 BIT 0B4h TB8 BIT 9Bh
TR0 BIT 8Ch INT1 BIT 0B3h RB8 BIT 9Ah
IE1 BIT 8Bh INT0 BIT 0B2h TI BIT 99h
IT1 BIT 8Ah TXD BIT 0B1h RI BIT 98h
IE0 BIT 89h RXD BIT 0B0h
IT0 BIT 88h
3.4 SFR Seite 11
───────────────────────────────────────────────────────────────────────────
Der Zugriff auf die SFR erfolgt direkt. Das heißt im Code steht die HEX-
Adresse des SFR als Operand. Beispiele für einen Zugriff auf ein SFR:
MOV TMOD, #20h ; einen Wert in TMOD schreiben
MOV 89h, #20h ; dasselbe mit DATA-Adr
ORL PSW, #18h ; Wert in PSW ändern
MOV A, SBUF ; Wert aus SBUF in Akkumulator laden
In diesem Kapitel werden noch die allgemeinen CPU-Register erklärt. Die be-
sonderen Komponenten (wie Ports, Timer, Interrupt, serielle Schnittstelle)
zugeordenten Register weden im Zusammenhang mit diesen Funktionen erklärt.
3.4.1 ACC (Akkumulator):
-------------------------
Der Akkumulator (DATA E0h) dient als Hauptrechenregister. Arithmetische
Operationen können nur in Verbindung mit dem ACC ausgeführt werden. Außer-
dem wird der ACC bei Zugriffen auf externe Speicher und indirekten Sprüngen
verwendet (siehe Befehle ADD, ADDC, SUBB, MUL, DIV, MOVX, MOVC, JMP, JZ,
SWAP, CLR,...). Der ACC kann bitweise addressiert werden:
ACC.0 BIT 0E0h ACC.4 BIT 0E4h
ACC.1 BIT 0E1h ACC.5 BIT 0E5h
ACC.2 BIT 0E2h ACC.6 BIT 0E6h
ACC.3 BIT 0E3h ACC.7 BIT 0E7h
Zum Zugriff auf den ACC stehen besondere (kürzere und schnellere) Befehle
zur Verfügung. Zur Unterscheidung wird bei den ASM51-Mnemonic-Abkürzungen,
"A" anstelle von "ACC" geschrieben. Für den 8051 werden verschiedene Opco-
des generiert.
Beispiel:
MOV ACC, #44h Hex-Code: 75 E0 44 2 Zykluse
oder MOV A, #44h Hex-Code: 74 44 1 Zyklus
3.4.2 B-Register:
------------------
Das B-Register (DATA F0h) dient als universelles Register. Es kann zum Zwi-
schenspeichern von Werten dienen. Bei der Multiplikation und der Division
wird es als Hilfsrechenregister verwendet. Es kann bitweise angesprochen
werden:
B.0 BIT 0F0h B.4 BIT 0F4h
B.1 BIT 0F1h B.5 BIT 0F5h
B.2 BIT 0F2h B.6 BIT 0F6h
B.3 BIT 0F3h B.7 BIT 0F7h
3.4 SFR Seite 12
───────────────────────────────────────────────────────────────────────────
3.4.3 PSW (Programm Status Word):
----------------------------------
Das PSW (DATA D0h) enthält die wichtigsten Flags der CPU sowie zwei Bits
zur Register-Bank Auswahl (siehe 3.9 internes RAM). Die Flags sind wichtig
bei Rechenoperationen und Vergleichen:
P BIT 0D0h RS1 BIT 0D4h
PSW.1 BIT 0D1h F0 BIT 0D5h
OV BIT 0D2h AC BIT 0D6h
RS0 BIT 0D3h CY BIT 0D7h
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ CY │ AC │ F0 │ RS1 │ RS0 │ OV │PSW.1│ P │ PSW (D0h)
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
┌───────┐ ┌────┐
│ PSW.1 │ und │ F0 │ haben keine Hardware-Funktion. Sie können vom Anwender
└───────┘ └────┘ zu beliebigen Zwecken verwendet weden. PSW.5 ist le-
diglich beim ASM51 als F0 (Flag 0) vordefiniert. PSW.1
und PSW.5 können zum Zwischenspeichern beliebiger Bit-Werte verwendet wer-
den.
┌───┐
│ P │ ist das Parity-Flag. Es spiegelt immer den Zustand des ACC wieder.
└───┘ Ist die Summe über alle Bits des ACC gerade (even Parity), dann ist
P = 0. Ist die Summe ungerade (odd Parity), dann ist P = 1. Das
Parity-Flag kann nicht von der Software beschrieben werden (nur lesen).
Beispiel: ACC = 11h = 00010001b --> Σ = 2, gerade --> P = 0
ACC = E3h = 11100011b --> Σ = 5, ungerade --> P = 1
┌────┐
│ OV │ ist das Overflow-Flag (Überlauf-Flag). Es wird gesetzt, nach einer
└────┘ Multiplikation, wenn das Ergebnis größer als ein Byte ist (dann
paßt es nicht mehr in den ACC allein). Es wird auch gesetzt bei ei-
ner Division durch 0. Ferner wird es gesetzt bei der Addition und Subtrak-
tion bei einem Vorzeichenfehler.
Beim Rechnen mit vorzeichenbehafteten Zahlen repräsentiert das höchste Bit
das Vorzeichen (Beispiel: 31 = 1Fh, -31 = 9Fh). Es lassen sich so nur Zah-
len von -128 bis +127 darstellen. Wird nun eine positive Zahl mit einer po-
sitiven Zahl addiert, so müßte das Ergebnis wieder positiv sein. Eine nega-
tive Zahl minus eine negative Zahl müsste dagegen wieder negativ sein. Bei
einem Überlauf ist dies nicht der Fall:
Beispiel: 42h + 47h = 89h = -9 --> Fehler! richtig wäre +137
┌────┐
│ AC │ ist das Auxiliary-Carry-Flag (Hilfs-Übertrag-Flag): es wird gesetzt
└────┘ bei einem Übertrag aus Bit 3 bei der Addition und Subtraktion.
3.4 SFR Seite 13
───────────────────────────────────────────────────────────────────────────
┌────┐
│ CY │ ist das Carry-Flag (Übertrag-Flag): es wird gesetzt bei einem Über-
└────┘ trag aus Bit 7 bei der Addition, Subtraktion, beim Dezimal Adjust
und beim Rotate-Left-durch-Carry. Außerdem wird es gesetzt bei ei-
nem Übertrag aus Bit 0 beim Rotate-Right-durch-Carry und wenn der linke
Operand kleiner ist als der rechte bei "Compare and Jump if Not Equal".
Wie beim ACC gibt es auch für den Zugriff auf das Carry-Flag besondere Be-
fehle. Zur Unterscheidung wird bei den Mnemonics "C" anstelle von "CY" ver-
wendet.
Beispiel: SETB CY Hex-Code: D2 D7 1 Zyklus
SETB C Hex-Code: D3 1 Zyklus
┌─────┐ ┌─────┐
│ RS1 │ und │ RS0 │ dienen zur Register-Bank Auswahl. Diese Bits werden
└─────┘ └─────┘ von der Software gesetzt (durch CPU nur lesen).
RS1:RS0 = 00 --> Reg.Bnk 0 RS1:RS0 = 10 --> Reg.Bnk 2
RS1:RS0 = 01 --> Reg.Bnk 1 RS1:RS0 = 11 --> Reg.Bnk 3
3.4.4 SP (Stack Pointer):
--------------------------
Die CPU richtet im internen RAM einen Stapelspeicher ein. Auf diesen Stapel
wird von der CPU bei jedem Unterprogrammaufruf (LCALL oder ACALL) die Re-
turnadresse abgelegt. Dasselbe erfolgt auch automatisch durch die CPU beim
Eintritt in Interrupt-Routinen. Bei einem RET bzw. RETI werden die Return-
adressen wieder vom Stack geholt. Ein Anwenderprogramm kann mit den Befeh-
len PUSH und POP auf den Stapelspeicher zugreifen.
Der SP (DATA 81h) zeigt immer auf den zuletzt gepuschten Wert. Zum Ablegen
eines Bytes wird der SP zuerst incrementiert und dann der Wert in die in-
tern RAM Adresse geschrieben, auf die dann der SP zeigt. Return-Adressen
sind vom Typ WORD, bei ihnen legt die CPU zuerst das Low-Byte und dann das
High-Byte auf den Stack.
Der Stack wächst von unten nach oben. Nach einem Reset zeigt der SP auf die
intern RAM-Adresse 07h. Das erste gepushte Byte würde also in 08h geschrie-
ben (damit wird Reg-Bnk 0 nicht vom Stack überschrieben). Ein Anwenderpro-
gramm sollte gleich nach einem Reset den SP mit der letzen von dem Programm
verwendeten intern RAM-Adresse laden, um den Stack oberhalb der Programm-
Daten einzurichten.
Achtung! Beim 8051 endet das interne RAM bei Adresse 7Fh. Zeigt der SP auf
höhere Adress-Werte, so wird er er bei einem PUSH weiterhin incremen-
tiert, bei einem POP kommt nur Schrott zurück!
3.4 SFR Seite 14
───────────────────────────────────────────────────────────────────────────
3.4.5 DPL, DPH (Datenpointer):
-------------------------------
Der Datenpointer (DPTR) ist das einzige 16-Bit Register des 8051, das auch
mit einem 16-Bit Zugriff geladen werden kann. Er setzt sich zusammen aus
den Spezial Funktion Registern DPH = Datenpointer High (DATA 83h) und
DPL = Datenpointer Low (DATA 82h). Der Datenpointer dient zum Zugriff auf
das externe RAM (siehe MOVX), auf Tabellen im Codespeicher (siehe MOVC) und
für indirekte Sprünge (siehe JMP).
3.4.6 PCON (Power-Control):
----------------------------
Das PCON-Register (DATA 87h) enthält weitere CPU-Flags. Die Power-Control
Flags sind nur in der CMOS Versionen (80C51) vorhanden. Das höchstwertigste
Bit (SMOD) gehört zur Einstellung der Baud Rate bei der seriellen Schnitt-
stelle (siehe serielle Schnittstelle). PCON ist nicht bitweise adressier-
bar.
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│SMOD │ - │ - │ - │ GF1 │ GF0 │ PD │ IDL │ PCON (87h)
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
┌──────┐
│ SMOD │ bestimmt den Vorteil-Faktor für den Schiebetakt der seriellen
└──────┘ Schnittstelle. Bei seriell Mode 1, 2 oder 3 wird die Baud-Rate
(Shift-Takt) durch 2 geteilt, wenn SMOD = 0 ist.
┌─────┐ ┌─────┐
│ GF1 │ und │ GF0 │ haben keine Hardwarefunktion. Sie können nicht mit
└─────┘ └─────┘ BIT-Adressen angesprochen werden (Setzen und Löschen
nur über ORL - bzw. ANL PCON). Sie können vom Programm
zu beliebigen Zwecken verwendet werden. Vorgeschlagen wird die Verwendung
als Kennzeichen, ob Idle-Mode aktiviert wurde.
┌─────┐
│ IDL │ schaltet die CPU in den Idle-Mode (nur CMOS!). Wird IDL = 1 ge-
└─────┘ setzt (über OR PCON, #1), so wird der folgende Befehl nicht mehr
ausgeführt. Der 8051 geht in den IDLE-Mode. Dies bedeutet:
* die Befehlsausführung stoppt.
* die Port-Ausgangs-Pins behalten ihren letzten elektrischen Wert
* die Timer laufen weiter
* die serielle Schnittstelle arbeitet weiter
* alle anderen Register behalten ihren Wert
* der Versorgungsstrom reduziert sich auf ca. 23% (bei 5V und 12 MHz)
Der Idle-Mode kann verlassen werden, durch Aktivierung eines Interrupt und
durch einen Reset. Der Interrupt muß dazu enabled sein. Tritt ein Interrupt
auf, so wird mit Eintritt in die Interrupt-Routine IDL gelöscht. Die Inter-
ruptroutine wird abgearbeitet. Nach dem RETI geht die Befehlsabarbeitung
3.4 SFR Seite 15
───────────────────────────────────────────────────────────────────────────
weiter bei dem Befehl, der der Idle-Mode Aktivierung folgt. Als Kennung für
die Interrupt-Routine, das Idle-Mode gesetzt war, kann falls nötig GF1 oder
GF0 dienen (IDL wurde ja durch die Hardware gelöscht).
Tritt während des Idle-Modes ein Reset auf, so wird sofort IDL in PCON ge-
löscht. Die Befehlsabarbeitung geht mit den folgenden Befehlen weiter -->
bis zwei Zykluse später die Reset-Logik anläuft und alle SFR sowie den
Programm-Counter zurücksetzt. Die Abarbeitung startet wieder bei der
Reset-Adresse 0000.
Es werden aber zunächst die der Idle-Mode Aktivierung folgenden zwei oder
drei Befehle ausgeführt. Die korrekte Ausführung der Befehle für diesen Zu-
stand ist nicht garantiert. Zugriffe auf Ports oder den externen Datenspei-
cher sollten deshalb hinter Idle-Mode Aktivierungs Befehlen vermieden wer-
den (eventuel 3 NOP's einfügen). Zugriffe auf das interne RAM sind unpro-
blematisch, denn die Hardware verhindert in dieser Zeit einen Zugriff auf
das interne RAM.
Nach Verlassen des Idle-Mode per Reset sind alle SFR zurückgesetzt. Auch
GF1 und GF0 sind gelöscht. Jedoch das interne und das externe RAM sind un-
verändert.
┌────┐
│ PD │ schaltet die CPU in den Power-Down Mode (nur CMOS!). Wird PD = 1
└────┘ gesetzt (über OR PCON, #2), so wird der folgende Befehl nicht mehr
ausgeführt. Der 8051 geht in den POWER DOWN-Mode. Dies bedeutet:
* die Befehlsausführung stoppt.
* die Port-Ausgangs-Pins behalten ihren letzten elektrischen Wert
* ALE und _PSEN werden low
* die Timer stoppen
* die serielle Schnittstelle stoppt
* der Versorgungsstrom reduziert sich auf ca.0,3% (bei 5V und 12 MHz)
Der Power Down-Mode kann nur durch einen Reset verlassen werden. Dieser Re-
set wird dann alle SFR zurücksetzen (auch Ports auf 0FFh), nicht aber das
interne und externe RAM.
Während des Power-Down Zustandes kann die Versorgungsspannung auf 2 V her-
runtergesetzt werden ohne das der Inhalt des internen RAM's verloren geht.
Dies darf aber erst erfolgen, nachdem PD aktiviert wurde, und die Spannung
muß wieder auf 5 V sein, bevor der Reset ausgelöst wird.
3.5 Ports Seite 16
───────────────────────────────────────────────────────────────────────────
3.5 Ports:
-----------
Je 8 elektrische Anschlüsse des 8051 sind zu einem Port zusammengefaßt. Es
kann direkt von der Software auf die Port-Pins zugegriffen werden. Die An-
schlüsse werden aber auch noch für andere Funktionen verwendet, die nur von
der Hardware gesteuert werden, wie zum Beispiel den Code-Fetch aus einem
externen Programmspeicher.
Damit die Ports als Eingänge und Ausgänge verwendet werden können besitzen
sie spezielle Treiber:
║ /│Pin lesen
║ ┌────< ├───────────────────────────────────┐
║ │ \│ │
i║ │ │
n║ │ /│Latch lesen +5V o │
t║\───<──┴────< ├────┐ │ │
e║ \│ │ │└┘ │
r║ │ ┌─────┤┌┐ │
n║ │ │ │ │ Pin0.x
║ ┌─────┐ │ │ ├──────┴─────o
║\────>────┤D Q├──┘ Umschalter │ │
D║ │ │ ┌─────┐ │ │└┘
A║ │ /Q├──────┤ _\ ├───────────┤┌┐
T║ └─────┘ └──┬──┘ │ │
A║ Latch │ │ ┴
║ │ │
║ Adress/Data Ausgang
Das obige Bild zeigt vereinfacht den Aufbau eines Pins von Port 0. Port 2
besitzt prinzipiell denselben Aufbau, nur ist dort ein zusätzlicher Pull-Up
Widerstand an Ausgang vorhanden.
alternative Funktion bei P0, P2:
--------------------------------
Port 0 und Port 2 dienen alternativ zum Lesen vom externen Programmspeicher
(Code-Fetch) und zum Lesen und Schreiben von/in den externen Datenspeicher.
Über Port 0 werden die Low-Byte Adressen ausgegeben und die Daten gelesen
bzw. geschrieben. Port 2 dient zur Ausgabe der High-Byte Adressen.
Wird Code nur aus dem internen ROM abgearbeitet und kein externer Daten-
speicher verwendet (kein MOVX-Befehl), so werden die alternativen Funktio-
nen von Port 0 und Port 2 nie angesprochen. Port 0 und Port 2 können dann
beliebigen Funktionen (Ein-/Ausgabepins) gewidmet werden. An Port 0 sind
dann allerdings Pull-Up Widerstände anzubringen.
Werden Port 0 und Port 2 zum Code-Fetch verwendet, so schließt dies meißt
andere Anwendungen aus. Denn die Hardware löscht bei jedem externen Daten-
zugriff automatisch den Inhalt des Port 0 - Latches.
Port 2 wird bei MOVX @Ri - Befehlen nicht von der Hardware mit High-
Adressen angesteuert.
3.5 Ports Seite 17
───────────────────────────────────────────────────────────────────────────
Schreiben in Port P0 und P2:
----------------------------
In der "Normalen" Funktion wird ein über den internen Datenbus auf die Por-
tadresse (P0 DATA 80h, P2 DATA A0h) geschriebenes Byte in den Latches ge-
speichert. Ein Umschalter wechselt zwischen der "Normalen" Funktion und der
Alternativen Funktion, wann immer die Alternative Funktion Werte auszugeben
hat. Der Inhalt des Latch bleibt davon unverändert (Ausnahme: Lesen über P0
bei Code-Fetch, MOVX und MOVC).
Der obere Transistor wird nur angesteuert, wenn für die Alternative Funkti-
on eine logische 1 auf den Pin gelegt werden soll. Steht eine 1 im Latch,
und wird für die Alternative Funktion kein Wert ausgegeben, so ist der Aus-
gang bei Port 0 hochohmig (floating). Bei Port 2 wirkt der integrierte
Pull-Up. Bei beiden kann der Pin dann als Eingang verwendet werden. Stünde
eine 0 im Latch, würde der untere Transistor angesteuert, und der Pin wäre
immer low (nicht als Eingang verwendtbar).
Lesen von Port P0 und P2:
-------------------------
Bei Port 2 erfolgt kein Einlesen von Daten für die Alternative Funktion des
Speicherzugriffs (nur High-Adresse ausgeben). Bei Port 0 werden Daten über
einen tristate Treiber auf den internen Datenbus eingelesen. Vor jedem Le-
sezugriff auf externe Speicher schreibt die Hardware automatisch #0FFh in
die Port P0 - Latches (nur bei alternativen Funktionen). Bei "normalen" Le-
sezugriffen hat der Anwender selbst dafür Sorge zu tragen, daß die Latch-
Ausgänge high sind, denn nur dann kann über einen Port-Pin richtig gelesen
werden (nach einem Reset ist dies erfüllt).
Wird über einen Software-Befehl "normal" von der SFR-Adresse eines Ports
gelesen, so erfolgt dies je nach Befehl über den Treiber direkt von den
Pins oder über einen weiteren tristate Treiber vom Latch-Ausgang. Bei allen
Read-Modify-Write Zugriffen wird vom Latch gelesen und sonst vom Pin.
Ein Beispiel:
CPL P2.1 ist ein Read-Modify-Write Befehl: Der Wert im Latch von Pin
P2.1 soll geändert werden. Stand eine 0 drinn, soll eine 1 hineinge-
schrieben werden und umgekeht.
Würde vom Pin gelesen kann bei o | <--- Last
einer Anschaltung wie in nebenste- │ | ┌─────o
henden Beispiel ein Fehlern auftre- ┌┴┐ | │
ten. Die Basis des NPN-Transistors │ │ | │
wird direkt aus dem Pin gespeist, └┬┘ P2.1 /
was wegen dem internen Pull-Up zu- ├────o──────┤< NPN
lässig ist. Bei einem gesetzten │ | \E
Latch beträgt die Spannung am Pin │└┘ | │
nur ca. 0,6 V. Vom Pin würde ein ──┤┌┐ | │
Low-Pegel zurückgelesen, obwohl im │ | ─┴─
Latch eine 1 steht. Der CPL-Befehl ─┴─ |
würde falsch ausgeführt.
Read-Modify-Write Befehle sind:
JBC Springe wenn Bit (in Latch) gesetzt und lösche Latch
DJNZ decrement Port (SFR des Ports) und springe wenn nicht 0
CPL ändere (Port-) Bit
ANL, ORL, XRL, INC, DEC ändere (Port-) Byte
3.5 Ports Seite 18
───────────────────────────────────────────────────────────────────────────
zur Unterscheidung:
JB (springe, wenn Bit gesetzt) ist kein Read-Modify-Write Befehl, da
der Inhalt des Latch hier nicht verändert wird. Hier würde vom Pin ge-
lesen.
Problematisch ist folgende Befehlsfolge:
PUSH P2 denn PUSH P2 ist kein Read-Modify-Write Befehl, deß-
MOV P2, #80h halb wird der Wert von den Port-Pin's auf den Stack
MOV R0, #0 gelegt. Dies kann gleich dem Latch-Inhalt sein, muß
MOVX A, @R0 es aber nicht (siehe Schaltung auf voriger Seite).
POP P2 Der Inhalt des Latch darf nur dann auf den Stack ge-
sichert werden, wenn kein Port-Pin als Eingang ge-
schaltet ist, und die Belastung der Ausgangspins so ist, daß der elektri-
sche Pegel nicht verfälscht wird.
Unterschiede von Port P1 und P3 zu P0:
--------------------------------------
Im Unterschied zu Port P0 besitzen P1 und P3 keine Multiplexer. Soweit be-
legt, wird der Ausgang für die Alternative Funktion mit dem Latch-Ausgang
über ein NAND verknüpft. Eingelesen wird nicht auf den internen Datenbus,
sondern direkt zu den Baugruppen der alternativen Funktionen.
║ /│Pin lesen
║ ┌────< ├─────────────────────────────┐
║ │ \│ │
i║ │ │
n║ │ /│Latch lesen +5V o │
t║\───<──┴────< ├────┐ │ │
e║ \│ │ ┌┴┐ │
r║ │ │ │ │
n║ ┌─────┐ │ └┬┘ │ Pin 3.x
║\────>────┤D Q├──┤ NAND ├──────┼─────o
D║ │ │ │ ┌────┐ │└┘ │ Pin 1.x
A║ │ /Q│ └────┤ & ├o────┤┌┐ │
T║ └─────┘ ┌───┤ │ │ │
A║ Latch │ └────┘ ┴ │
║ │ │
║ │ /│ │
║ │ ┌────< ├────┘
│ │ \│
alternativ: │
Ausgang Eingang
In Bezug auf einen "Normalen" Zugriff auf eine Port-Adresse (SFR) durch die
Software gibt es keinen Unterschied zu Port P0 (siehe Bemerkungen bei P0).
P1 und P3 besitzen integrierte Pull-Up Widerstände. Der bei dem Bild für P0
eingezeichnete obere Transistor ist nicht vorhanden. Jedoch gibt es eine
Besonderheit: Auf dem Chip des 8051 sind die Pull-Up Widerstände nicht
durch feste Widerstandsbahnen realisiert, sondern über Feldeffekttransisto-
ren. Wird ein Pin von 0 auf 1 geschaltet, so wird bei Port P1 und P3 für
die Dauer von 2 Quarztakten (= 1/6 Zyklus) dieser als Widerstand geschalte-
3.5 Ports Seite 19
───────────────────────────────────────────────────────────────────────────
te FET niederohmiger gesteuert (bei Port 2 für die ganze Zeit der alterna-
tiv Funktion der High-Adress Ausgabe). Dadurch werden schnellere Ansiegs-
zeiten des Ausgangssignals eines Pin's erreicht, und dennoch ist der Pull-
Up hochohmig genug, daß der Pin als Eingang verwendet werden kann.
Erfolgt bei Port P3 keine alternative Ausgabe, so ist der Eingang des NAND
high. Für diesen Fall ist der logische Zustand des Pins vom Latch abhängig.
Soll der Pin als Eingang verwendet werden, so ist wie bei P0 #0FFh in die
Port-Latches von P1 bzw. P3 zu schreiben. Dies muß aber auch erfolgen,
falls alternative Ausgabe-Funktionen verwendet werden sollen, denn entgegen
Port P0 und P2 sind bei P1 und P3 keine Umschalter vorhanden.
3.6 externer Programmspeicher Seite 20
───────────────────────────────────────────────────────────────────────────
Alternative Funktionen beim 8051:
---------------------------------
Port 0: Adresse (low Byte) und Daten für externen Speicherzugriff.
Port 2: Adresse (high Byte) für externen Speicherzugriff.
P3.7 = _RD: Lesesignal für externen Datenspeicherzugriff
P3.6 = _WR: Schreibsignal für externen Datenspeicherzugriff
P3.5 = T1: Timer 1 Gate
P3.4 = T0: Timer 0 Gate
P3.3 = _INT1 externer Interrupt-Eingang 1
P3.2 = _INT0 externer Interrupt-Eingang 0
P3.1 = TxD Sende-Ausgang der seriellen Schnittstelle
P3.0 = RxD Empfangs-Eingang der seriellen Schnittstelle
elektrische Kenndaten der Ports:
--------------------------------
Isink = 2,4 mA Port 0 (UoL < 0,45 V garantiert)
Isink = 1,6 mA Port 1,2,3 (UoL < 0,45 V garantiert)
Pull-Up 10...30 kΩ Port 1,2,3
Idrive = 0,4 mA Port 0 (UoH > 2,4 V garantiert)
Wichtig: bei allen Portpins muß im Latch eine 1 stehen, wenn der Pin als
Eingang verwendet werden soll!
3.6 Zugriff auf den externen Programm-Speicher:
------------------------------------------------
Der Zugriff auf den externen Programmspeicher erfolgt automatisch 2 mal je
Zyklus durch die Hardware (siehe 3.3 Befehlsausführung). Der 8051 besitzt
ein internes ROM und einen Eingangspin _EA (Extern Access).
_EA = 1 : Die Befehle der Code-Adressen 0000-0FFFh werden intern gelesen.
Die Befehle der Code-Adressen 1000-FFFFh werden extern gelesen.
(8052 besitzt 8 kByte internes ROM: Adressen 0000-1FFFh)
_EA = 0 : Die Befehle aller Code-Adressen werden extern gelesen.
Der _EA-Pin dient zur Abschaltung des internen ROM's. _EA kann nicht wie
die Port-Pins von der Software gelesen werden.
Der 8031 besitzt kein internes ROM. Hier werden alle Befehle extern gele-
sen. Es ist jedoch zu empfehlen den _EA-Pin (der wegen der gleichen Gehäus-
ebauform beim 8031 auch vorhanden ist) auf GND zu legen. Beim 8344 (die
ROM-lose Version des 8044) konnte ich schon beobachten, daß er mit _EA = 1
nicht korrekt funktionierte. Es schien, als ob er doch ein internes ROM be-
saß. Vielleicht ist es für den Hersteller günstiger, nur eine Maske zu fer-
tigen und die Chips verschieden zu bedrucken.
3.6 externer Programmspeicher Seite 21
───────────────────────────────────────────────────────────────────────────
_PSEN (Programm Store Enable dient als Lesesignal für den externen Pro-
grammspeicher. Auf _PSEN kann ebenfalls nicht per Software zugegriffen wer-
den.
externen Programmspeicher lesen:
--------------------------------
(vergleiche dazu auch 3.3 Befehlsausführung und 3.5 Ports):
-- voriger -------->|<--------------- dieser Zyklus --------------->|
| | | | | |
┌───┐ ┌────┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
SysClk ─┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └
| | | | | | |
State 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 |
| ^1.Fetch | | ^2.Fetch | |
─┐ | ┌───────┐ | | ┌───────┐ | |
ALE └────────────────┘ | └───────────────┘ | └────────────
─────┐ | ┌───────────┐ | ┌───────────┐ |
_PSEN └────────────┘ | └───────────┘ | └────────
| | | | | | |
P2 CCCCCCCCCCCCCCCCCCCCC-CCCCCCCCCCCCCCCCCCCCCCC-CCCCCCCCCCCCCCCCC
| | | | | | |
P0cpuOut AAAAA---------?????---AAAAAAAA--------?????---AAAAAAAA---------
P0epromOut -----DDDDDDDDDDDDD------------DDDDDDDDDDDD-------------------D
CCC = Code-Adresse
DDD = Daten out
? = Daten lesen
--- = hochohmig
Der Code-Fetch Zugriff beginnt schon in State 5 des vorigen Zyklus mit dem
Anlegen der Adresse. In der Mitte von State 5 wird ALE low. ALE dient bei
einem externen Zugriff als Signal für das Adress-Latch, die Low-Adressen
von P0 zu übernehmen und zu speichern (siehe 3.1 ff).
Mit dem Ende von State 5 wird _PSEN low und gleichzeitig P0 hochohmig (als
Eingang: angedeutet durch das ---). Ein angeschlossenes EPROM legt nun Da-
ten auf den externen Datenbus (solange _PSEN = 0).
Ab dem Anfang von State 1 erwartet der 8051 an P0 Daten (angedeutet durch
das ???, weiterhin hochohmig). Diese liegen schon am Ausgang des EPROM an.
In der Mitte von State 1 werden die Daten über P0 auf den internen Datenbus
übernommen und _PSEN und ALE werden beide wieder high.
Zum Ende von State 1 wird die (incrementierte) Adresse für den nächsten
Code-Fetch angelegt.
Das obige Timing-Diagramm zeigt ideal den Ablauf der beiden CODE-Fetch Zu-
griffe eines Zyklus. Real sind alle Übergänge mit Schaltzeiten behaftet.
Für eine einwandtfreie Funktion wird vom 8051 die Einhaltung folgender Zei-
ten gefordert:
- spätestens tPLIV nachdem _PSEN low wurde, muß der Programmspeicher gül-
tige Daten auf den Bus legen.
3.6 externer Programmspeicher Seite 22
───────────────────────────────────────────────────────────────────────────
- spätestens tAVIV nachdem vom 8051 gültige Adressen ausgegeben wurden,
sind vom Programmspeicher gültige Daten auf den Bus zu legen.
Die Zeitwerte hängen vom 8051-Typ ab (Fabrikat...). Exakte Werte sind den
Datenblättern zu entnehmen. Allgemein gilt etwa ( mit T = 1/fOszi):
tPLIV = (3 * T) - 105 ns .... (3 * T) - 150 ns > 100 ns (bei 12 MHz)
tAVIV = (5 * T) - 105 ns .... (5 * T) - 150 ns > 267 ns (bei 12 MHz)
Als Programmspeicher werden oft EPROM's, PROM's verwendet. Die übliche An-
gabe EPROM 27256-250 besagt das EPROM besitzt 256 kBIT (= 32 kByte) an
Speicherzellen mit einer Adress-Access Zeit von 250 ns.
Angegeben wird die Adress-Access-Time: die Zeit vom Anlegen der Adresse bis
gültige Daten am Ausgang liegen. Die Chip-Enable-Access-Time ist etwa
gleich groß (Zeit von Anliegen eines aktiven Chip-Select-Signal bis gültige
Daten). Die Output-Enable-Access-Time (Zeit von Anliegen eines aktiven
_PSEN bis gültige Daten) ist wesentlich kleiner (Größenordnung 20 ns bis 60
ns). Die bei der Anwendung als Programmspeicher des 8051 kritischte Zeit
ist die Chip-Access-Time (= Adress-Access-Time). Wird diese Bedingung er-
füllt, sind auch die anderen Zeiten ok.
Der eingesetzte Speicher muß eine Adress-Access-Time von 267 ns besitzen
(bei 12 MHz CPU-Takt). Wird das Chip-Selekt mit Hilfe eines Gatters aus den
Adressen gebildet, so muß der Programmspeicher um die Gatterlaufzeit
schneller sein. Ein GAL 16V8-25 hat eine Laufzeit von 25 ns. Es wäre also
ein Programmspeicher mit einer Zugriffszeit kleiner 242 ns zu verwenden.
Maßgeblich ist immer die für den eingesetzten CPU-Typ und den verwendeten
CPU-Takt in den Datenblättern angegebene Zeit tAVIV (Adress-Valid-
Instruktion-Valid) zuzüglich der Gatterlaufzeit der Chip-Select-Logik.
Bei einem CPU-Takt von 12 MHz wird man mit EPROM's mit 200 ns immer sicher
liegen. Bei vielen Anwendungen werden auch 250 ns genügen.
Bei einem MOVC-Befehl erfolgt der Lesezugriff auf den Programmspeicher
durch die Software im zweiten Zyklus anstelle des 1. Code-Fetch.
3.7 externer Datenspeicher Seite 23
───────────────────────────────────────────────────────────────────────────
3.7 Zugriff auf den externen Daten-Speicher:
---------------------------------------------
Der Zugriff auf den externen Datenspeicher erfolgt im zweiten Zyklus bei
der Abarbeitung eines MOVX-Befehls (siehe 3.3 Befehlsausführung). In diesem
Zyklus erfolgt dafür kein Code-Fetch (einziger Fall, wo ALE und _PSEN aus-
bleiben). Ein XDATA-Zugriff dauert 6 System-Takte (doppelt so lange wie ein
CODE-Zugriff).
_RD (Port P3.7 Pin) dient als Lesesignal für den externen Datenspeicher.
_WR (Port P3.6 Pin) dient als Schreibsignal.
Werden MOVX-Befehle verwendet, sind diese Bits im Port 3 - Latch auf 1
zu setzen (dies ist nach einem Reset erfüllt).
XDATA lesen:
------------
-- voriger -------->|<--------------- dieser Zyklus --------------->|
| | | | | |
┌───┐ ┌────┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
SysClk ─┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └
| | | | | | |
State 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 |
─┐ | | | | ┌───────┐ | |
ALE └────────────────────────────────────────┘ | └────────────
| | | | | | |
──────────────┐ | | ┌────────────────────────
_RD └───────────────────────┘ | | |
| | | | | | |
P2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_CCCCCCCCCCCCCCCCC
| | | | | | |
P0cpuOut XXXXXXXXXX--------------------?????-----------CCCCCCCCC--------
P0ramOut ---------------DDDDDDDDDDDDDDDDDDDDDDDD------------------------
XXX = XDATA-Adresse
(vergleiche dazu auch 3.6 Programmspeicher-Zugriff)
Bereits in State 5 des 1. Zyklus des MOVX - Befehls werden die XDATA-
Adressen angelegt. Bei MOVX A, @DPTR: an P2 die High Adresse (DPH) und an
P0 die Low Adresse (DPL). Bei MOVX A, @Ri: bleibt an P2 der Wert aus dem
Latch (SFR von P2) und an P0 wird die Adresse (aus Ri) angelegt.
In der Mitte von State 5 wird ALE low. Damit können die Adressen von P0 in
ein (externes) Adress-Latch (siehe 3.1 ff) übernommen werden.
Gegen Ende von State 6 wird P0 hochohmig (Adresse weggenommen), anschlie-
ßend wird _RD low. Ein angeschlossenes RAM legt nun Daten auf den externen
Datenbus (_RD = 0).
Ab dem Anfang von State 3 erwartet der 8051 an P0 Daten (angedeutet durch
das ???). In der Mitte von State 3 werden die Daten auf den internen Daten-
bus übernommen anschließend werden _RD und ALE wieder high.
Zum Ende von State 4 wird die Adresse für den Code-Fetch des nächsten Zy-
klus angelegt falls der Code-Fetch extern erfolgt (wenn intern, bleiben P0
und P2 hochohmig - siehe 3.6 und 3.3).
3.7 externer Datenspeicher Seite 24
───────────────────────────────────────────────────────────────────────────
Ein Zugriff auf den externen Datenspeicher dauert länger als ein Code-
Fetch. Das ALE wird einmal nicht ausgegeben. Für den Zugriff stehen längere
Zeiten zur Verfügung:
- spätestens tRLDV nachdem _RD low wurde, muß der externe Datenspeicher
gültige Daten auf den Bus legen.
- spätestens tAVDV nachdem vom 8051 gültige Adressen ausgegeben wurden,
sind vom externen Datenspeicher gültige Daten auf den Bus zu legen.
Allgemein gilt etwa ( mit T = 1/fOszi):
tRLDV = (5 * T) - 105 ns .... (5 * T) - 165 ns > 251 ns (bei 12 MHz)
tAVDV = (9 * T) - 105 ns .... (9 * T) - 165 ns > 585 ns (bei 12 MHz)
XDATA schreiben:
----------------
-- voriger -------->|<--------------- dieser Zyklus --------------->|
| | | | | |
┌───┐ ┌────┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐
SysClk ─┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ └
| | | | | | |
State 5 | 6 | 1 | 2 | 3 | 4 | 5 | 6 |
─┐ | | | | ┌───────┐ | |
ALE └────────────────────────────────────────┘ | └────────────
| | | | | | |
──────────────┐ | | ┌────────────────────────
_WR └───────────────────────┘ | | |
| | | | | | |
P2 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX_CCCCCCCCCCCCCCCCC
| | | | | | |
P0cpuOut XXXXXXXXX-DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD----CCCCCCCCC--------
P0ramOut --------------------------------------?------------------------
XXX = XDATA-Adresse
(vergleiche dazu auch 3.6 Programmspeicher-Zugriff)
Bereits in State 5 des 1. Zyklus des MOVX - Befehls werden die XDATA-
Adressen angelegt. Bei MOVX A, @DPTR: an P2 die High Adresse (DPH) und an
P0 die Low Adresse (DPL). Bei MOVX A, @Ri: an P2 der Wert aus dem Latch
(SFR von P2) und an P0 die Adresse (Ri).
In der Mitte von State 5 wird ALE low. Damit werden die Adressen von P0 ins
(externe) Adress-Latch übernommen.
Gegen Ende von State 6 wird an P0 die Adresse weggenommen und Daten ange-
legt, anschließend wird _WR low.
Zum Ende von State 3 wird _WR wieder high. Damit übernimmt der externe Da-
tenspeicher die Daten von P0. Anschließend wird P0 wieder hochohmig und ALE
wieder high.
Zum Ende von State 4 wird die Adresse für den Code-Fetch des nächsten Zy-
klus angelegt falls der Code-Fetch extern erfolgt (wenn intern, bleiben P0
und P2 hochohmig - siehe 3.6 und 3.3).
3.8 externe Anschaltungen, Beispiele Seite 25
───────────────────────────────────────────────────────────────────────────
- Der 8051 garantiert, daß mindestens tQVWH bevor _WR high wird, gültige
Daten auf den Bus liegen.
- der 8051 garantiert, daß mindestens tWLWH + tAVWL bevor _WR high wird,
gültige Adressen ausgegeben wurden.
Allgemein gilt etwa ( mit T = 1/fOszi):
tQVWH = (7 * T) - 105 ns .... (7 * T) - 150 ns > 433 ns (bei 12 MHz)
tWLWH = (6 * T) - 100 ns = 400 ns (bei 12 MHz)
tAVWL = (4 * T) - 70 ns ..... (4 * T) - 130 ns > 96 ns (bei 12 MHz)
Für den Datenspeicher besteht somit die Forderung einer Adress-Access-Time
von 585 ns zum Lesen und 496 ns zum Schreiben (bei 12 MHz CPU-Takt). Die
meißten Typen der Reihe 6164 ... 61256 (statische RAM's) besitzen Adress-
Access-Zeiten unter 150 ns, so daß es hier keine Probleme geben dürfte.
3.8 Anschaltungen für den externen Datenbus (Beispiele):
---------------------------------------------------------
Der Adressraum für CODE und XDATA ist getrennt. Die elektrische Unterschei-
dung wird durch getrennten Lesesignale _PSEN (für CODE) und _RD (für XDATA)
erreicht. Eine typische Beschaltung mit EPROM und RAM wurde schon in 3.1
vorgestellt. Hier nun eine Beschaltung des gesamten Adressraums mit
64 kByte RAM und 64 kByte EPROM:
┌───────┐
┌─────────────_WR──>│ │ ┌───┐
│ ┌───────────_RD──>│ RAM │<────o│INV│─┐
│ │ │ │ └───┘ │
│ │ ╔════════>│ 32kx8 │<═══╗ │
8051 CPU │ │ ║ └───────┘ ║ │
┌────────┐ │ │ ║ ┌───────┐ ║ │
│ _WR├─────────┴─────────║───_WR──>│ │ ║ │
│ _RD├───────────┴───────║───_RD──>│ RAM │<───║───────┴── A15
│ │ ║ │ │ ║
│ P2╞═══════ADRESSBUS═══╬════════>│ 32kx8 │<═══╣
│ │ ┌─────┐ ║ └───────┘ ║
│ ALE├─────────>│LATCH│ ║ ┌───────┐ ║
│ P0│<═══╦════>│ ╞══╣ │ │ ║
│ │ ║ └─────┘ ╚════════>│ EPROM │<═══╣
│ │ ║ │ │ ║
│ _PSEN├────║────────────────PSEN───>│ 64kx8 │<─┐ ║
└────────┘ ║ └───────┘ ┴ ║
╚═══DATENBUS══════════════════════════╝
Da statische RAM's nur bis 32kx8 erhältlich sind, werden zwei RAM-Chips be-
nötigt. Das 1. RAM erhält als Chip-Selekt die Adresse A15 und das zweite
RAM den invertierten Wert von A15. Als CODE-Speicher wird ein EPROM 64kx8
verwendet. Sein Chip-Enable Pin wird auf GND gelegt (immer selektiert).
3.8 externe Anschaltungen, Beispiele Seite 26
───────────────────────────────────────────────────────────────────────────
Das 1. RAM wird bei allen Adressen aktiv, bei denen A15 = 0 ist, also bei
0000...7FFFh. Das 2. RAM wird aktiv bei 8000...FFFFh (A15 = 1).
In kaum einer Schaltung wird ein derart großer externer Datenspeicher benö-
tigt. Deshalb wird meißt nur mit einem RAM bestückt. Oft wird sogar noch
ein kleineres RAM verwendet (billiger). Zu empfehlen sind die Typen 6164
(8kx8), 61128 (16kx8) und 61256 (32kx8). Kleinere Typen als 8kx8 könnten
zwar eingesetzt werden, jedoch sind sie wieder teurer.
Wird nur ein RAM im XDATA-Raum eingesetzt, so kann dessen Chip-Enable Pin
auf GND gelegt werden (immer selektiert). Auf ein und dieselbe Speicherzel-
le des RAM's kann dann über verschiedene XDATA-Adressen zugegriffen werden
(mehrfach sleektiert). Das stört allerdings nicht wenn die Software ok ist.
--- IO-Bereich -----------------------------------------------------------
Der XDATA-Bereich kann neben der Verwendung als externer Datenspeicher für
andere Zwecke dienen. Einige Beispiele:
- zum Einlesen von Jumper-Stellungen oder anderen Daten über einen tristate
Treiber.
- als universeller Port (8-Bit Ein/Ausgabe) über bidirektionale tristate
Treiber (74 LS 245).
- als Anschluß spezieller Bausteine wie externen Analog/Digital Wandlern
einem SCC (Serial Communication Controller) für eine zweite serielle
Schnittstelle, einem Display-Baustein und anderen.
- als Schnittstelle zu anderen Rechnern über 8 Bit-Register (realisiert
bei der Intel 8044-BITBUS-Rechnerkarte) oder über DPR (Dual Port RAM).
(-- siehe Beispiel im Anhang)
Werden IO-Funktionen installiert, sind dafür eine oder mehrere XDATA-
Adressen zu reservieren. Im allgemeinen ist dann ein Adress-Decoder nötig.
Oft wird das RAM in den Bereich von 0000...7FFFh gelegt (A15 als Chip Se-
lect) und der IO-Bereich ab 8000h.
--- ladbarer Code --------------------------------------------------------
Wird die aufgebaute Rechnerkarte mit einer Schnittstelle z.B. zu einem PC
versehen, dann kann es sinnvoll sein, für einen Teil des CODE-Bereichs ein
RAM zu verwenden um CODE über die Schnittstelle zu laden. Ein EPROM ist im-
mer notwendig, zumindest für die Laderroutinen der Schnittstelle. Der Code
bei Adresse 0 (Reset-Adresse) ist aus dem EPROM zu lesen.
Unter Verwendung eines EPROMS 16kx8 und eines RAM's 32kx8 kann beispiels-
weise folgende Aufteilung gewählt werden:
XDATA 0000...7FFFh --> Zugriff auf RAM 0000...7FFFh
CODE 0000...3FFFh --> Zugriff auf EPROM 0000...3FFFh
CODE 4000...7FFFh --> Zugriff auf RAM 4000...7FFFh
3.8 externe Anschaltungen, Beispiele Seite 27
───────────────────────────────────────────────────────────────────────────
│ │ │
_PSEN ├─ _PSEN ────────────┬────────────────────────────┤_OE │
│ │ │ EPROM │
P2.6 ├─ A14 ──┬────────────────────────────────────────┤_CE 16kx8│
│ │ │ ┌─────┐ └───────────┘
│ │ └──┤ │ ┌─────┐
│ │ ┌─────┐ │ NOR ├─────┤ │ ┌───────────┐
8051 │ └──┤ INV ├─────┤ │ │ NOR ├───────┤_OE │
CPU │ └─────┘ └─────┘ ┌──┤ │ │ │
│ ┌─────┐ │ └─────┘ │ │
P3.7 ├─ _RD ─────────────────┤ INV ├──┘ ┌──┤_CE │
│ └─────┘ ┴ │ RAM │
│ │ 32kx8│
P3.6 ├─ _WR ───────────────────────────────────────────┤_WR │
│ │ │
Das Bild zeigt eine mögliche Verknüpfung von _PSEN und _RD zur Erzeug-
ung des RAM-Lesesignals für das obige Beispiel. Damit wird CODE bis
Adresse 3FFFh aus dem EPROM und CODE ab 4000h aus dem RAM abgearbeitet.
Über MOVX kann CODE ins RAM geladen werden. Die Interrupt-Einsprünge
liegen im EPROM. Dort können per LJMP 4003h, LJMP 400Bh ... die Ein-
sprünge in den Lader-Bereich umgelenkt werden, soweit sie nicht von der
Laderroutine verwendet werden.
Eine besonders einfache Realisierung der Code-Abarbeitung aus dem externen
RAM (ladbarer Code) kann erreicht werden, wenn die Laderoutinen ins interne
EPROM gebrannt werden und _EA = 1 gesetzt wird. Nun brauchen _PSEN und _RD
nur noch über ein AND verknüpft zu werden. Es ist kein externes EPROM mehr
nötig.
│ o +5V
_EA ├───┘ │ │
│ ┌─────┐ ┌──┤_CE │
_PSEN ├─ _PSEN ─────────┤ │ ┴ │ │
│ │ AND ├──────────┤_OE │
P3.7 ├─ _RD ───────────┤ │ │ RAM │
│ └─────┘ │ 32kx8│
P3.6 ├─ _WR ────────────────────────────┤_WR │
│ │ │
3.9 internes RAM Seite 28
───────────────────────────────────────────────────────────────────────────
3.9 internes RAM:
------------------
Eine Besonderheit des 8051 ist sein internes RAM von 128 Byte. Der Zugriff
ist schneller als auf das externe RAM und es gibt verschiedene Zugriffsar-
ten. Bei einfachen Programmen kann der 8051 (in Verbindung mit dem internen
EPROM so als Stand-Allone Rechner dienen).
Zugriffsarten auf das interne RAM:
----------------------------------
direkt: Im Code ist die intern RAM-Adresse mit angegeben.
register: spezielle Befehle für den Zugriff auf Register R0..R7,
die im internen RAM auf Adresse 00..1Fh liegen.
indirekt: über Pointer. Als Pointer kann dienen R0, R1, SP
bit: die intern RAM-Adressen 20h..2Fh können zusätzlich bit-
weise angesprochen werden.
┌ ─ ─ ─ ─ ─ ─ ┐ FF Nebenstehend ist die Aufteilung des inter-
| | nen RAM's nach den Adressiermöglichkeiten
| 80h .. FFh | skizziert.
| |
80 ┌─────────────┐ Der 8051 besitzt ein internes RAM von 128
│ │ 7F Byte. Der gesamte Bereich ist direkt und
| | indirekt adressierbar.
| | Der 8052 und andere besitzen ein internes
│ │ RAM bis 256 Byte. Nur 00...7Fh sind direkt
│ │ adressierbar. Die direkt DATA-Adressen ab
│ │ 80h sind für die SFR reserviert (siehe 3.4)
30 ├─────────────┤
28 │ Bit │ 2F Die Adressen 20...2Fh können bitweise
20 ├─────────────┤ adressiert werden. Zugeordnet sind die BIT-
18 │ Reg.-Bank 3 │ 1F Adessen 00..7Fh. Die BIT-Adressen ab 80h
10 │ Reg.-Bank 2 │ sind für SFR reserviert.
08 │ Reg.-Bank 1 │
00 │ Reg.-Bank 0 │ Von 00 bis 1Fh weden die Register abgelegt
└─────────────┘ (siehe auch 3.4.3 PSW).
Register:
---------
Beim 8051 wurden 8 universell verwendbare Register definiert. Sie werden
nicht zu den SFR (spezial Funktion Register) gezählt, da sie keine speziel-
len Hardwarefunktion wie TCON, SCON und andere SFR besitzen.
Die Register sind dadurch ausgezeichnet, daß es für den Zugriff darauf spe-
zielle (einfache und schnelle) Befehle gibt. Sie werden nicht wie die SFR
in extra Speicherzellen abgelegt, sondern ihr Wert wird im internen RAM ge-
speichert. Dies erfolgt in 8 aufeinanderfolgenden intern RAM-Adresen = ei-
ner Register-Bank. Über zwei BIT's im Programm Status Word (siehe 3.4.3
PSW) kann die aktive Register-Bank selektiert werden.
Ist Register-Bank 0 selektiert (dies ist der Fall nach einem Reset, oder
nach ANL PSW, #11100111b oder CLR RS0 und CLR RS1), wird R0 im internen RAM
auf Adresse 00 abgelegt, R1 auf 01 usw. Ist Register-Bank 3 selektiert,
wird R0 auf Adresse 18h, R1 auf 19h usw. abgelegt.
3.9 internes RAM Seite 29
───────────────────────────────────────────────────────────────────────────
RS1:RS0 Reg.Bnk DATA-Adresse
------------------------------------
00 0 00..07
01 1 08..0Fh
10 2 10..17h
11 3 18..1Fh
Die verschiedenen Register-Banks können vorteilhaft bei Interrupts verwen-
det werden:
Werden in einer Interrupt-Routine Register verwendet, müßte ihr In-
halt beim Eintritt in die Interrupt-Routine gerettet werden (PUSH auf
Stack). Vor dem RETI müßten die Register wieder hergestellt werden
(POP von Stack). Es kann einer Interrupt-Routine eine Register-Bank
zugeordnet werden. Nun muß nur die Register-Bank umgeschaltet werden
(schneller!).
Obwohl beim 8051 fünf Interruptquellen vorhanden sind, kommt man mit 3
Reg.Bnk's aus, denn es gibt nur 2 Interrupt-Prioritätsstufen. Ein In-
terrupt kann nie von einem anderen Interrupt gleicher oder niederer
Priorität unterbrochen werden. Interrupts derselben Priorität kann al-
so dieselbe Registerbank zugeordnet werden, sofern nach dem RETI keine
Werte in den Registern gehalten werden sollen bis zum nächsten Inter-
rupt.
Da bei einem Reset nur einige SFR, jedoch nicht das interne RAM gelöscht
wird, bleibt der Inhalt der Register bei einem Reset bestehen sofern
Reg.Bnk 0 gewählt war. Bei einem Reset wird die Register-Bank umgeschaltet
auf Reg.Bnk 0.
R0 und R1 haben über die Möglichkeit einer universellen Verwendung durch
die Software noch die Funktion von Indexregistern bei der indirekten Adres-
sierung im internen RAM und beim pageweisen Zugriff auf das externe RAM
(siehe Befehlssatz).
indirekte Adressierung:
-----------------------
Auf das interne RAM kann zusätzlich indirekt über Pointer zugegriffen wer-
den. Als Pointer können die Register R0 und R1 oder der Stack Pointer (SP)
dienen. Bei RAM-Adressen ab 80h (nur 8052...) ist die indirekte Adressie-
rung die einzige Möglichkeit auf das interne RAM zuzugreifen.
Ein Zugriff indirekt über den SP auf das interne RAM erfolgt automatisch
bei jedem Aufruf eines Unterprogramms, aber auch beim Eintritt in eine In-
terruptroutine. Die CPU legt dabei die Return-Adresse auf den Stack ab (low
Byte zuerst). Für den Anwender besteht mit den Befehlen PUSH die Möglich-
keit auch Werte auf den Stack abzulegen bzw. mit POP wieder zurückzulesen
(siehe auch 3.4.4 Stack Pointer).
Um über R0 oder R1 auf das interne RAM zuzugreifen muß zuerst die Adresse
der Speicherstelle in R0 bzw. R1 geladen werden. Mit einem weiteren Befehl
erfolgt dann der Zugriff:
3.9 internes RAM Seite 30
───────────────────────────────────────────────────────────────────────────
Beispiel:
MOV R0, #30h ; intern RAM-Adresse 30h laden
MOV @R0, #0Dh ; #0Dh in intern RAM 30h schreiben
Ein indirekter Zugriff mutet so recht umständlich an, jedoch gibt es Anwen-
dungen, wo eine indirekte Adressierung unerlässlich ist (z.B. bei der Bear-
beitung von Textstrings, bei Empfangs- und Sende-Buffern ...).
direkte Adressierung:
---------------------
Eine direkte Adressierung erlaubt einen schnellen Zugriff auf Daten. Bei
der Programmerstellung werden an bestimmten festen Adressen im internen RAM
Speicherplätze zum Ablegen von Werten reserviert. Der Assembler erlaubt die
Definition von Variablennamen.
Im internen RAM des 8051 (und auch der erweiterten CPU's 8052, 80535,...)
können nur 128 Byte direkt adressiert werden (Adessen 00...7Fh). Beim Lin-
ken eines Programms wird die Adresse, mit in den Code aufgenommen.
Beispiel:
Mnemonic: Opcode:
MOV A, 20h ; Inhalt von int.RAM 20h in ACC --> E5 20
MOV A, #20h ; zum Unterschied: hier Wert in ACC --> 74 20
direkt BIT-Adressierung:
------------------------
Die intern-RAM Adressen 20h...2Fh können bitweise angesprochen werden. Die-
se Speicherplätze können so als Flag-Bank (8 Bit's je Byte) benutzt werden.
Der Zugriff erfolgt über spezielle Bit-Befehle.
MSB LSB
┌────┬────┬────┬────┬────┬────┬────┬────┐
│ 7F │ 7E │ 7D │ 7C │ 7B │ 7A │ 79 │ 78 │ DATA 2Fh
│ 77 │ 76 │ 75 │ 74 │ 73 │ 72 │ 71 │ 70 │ DATA 2Eh
│ 6F │ 6E │ 6D │ 6C │ 6B │ 6A │ 69 │ 68 │ DATA 2Dh
│ 67 │ 66 │ 65 │ 64 │ 63 │ 62 │ 61 │ 60 │ DATA 2Ch
│ 5F │ 5E │ 5D │ 5C │ 5B │ 5A │ 59 │ 58 │ DATA 2Bh
│ 57 │ 56 │ 55 │ 54 │ 53 │ 52 │ 51 │ 50 │ DATA 2Ah
│ 4F │ 4E │ 4D │ 4C │ 4B │ 4A │ 49 │ 48 │ DATA 29h
│ 47 │ 46 │ 45 │ 44 │ 43 │ 42 │ 41 │ 40 │ DATA 28h
│ 3F │ 3E │ 3D │ 3C │ 3B │ 3A │ 39 │ 38 │ DATA 27h
│ 37 │ 36 │ 35 │ 34 │ 33 │ 32 │ 31 │ 30 │ DATA 26h
│ 2F │ 2E │ 2D │ 2C │ 2B │ 2A │ 29 │ 28 │ DATA 25h
│ 27 │ 26 │ 25 │ 24 │ 23 │ 22 │ 21 │ 20 │ DATA 24h
│ 1F │ 1E │ 1D │ 1C │ 1B │ 1A │ 19 │ 18 │ DATA 23h
│ 17 │ 16 │ 15 │ 14 │ 13 │ 12 │ 11 │ 10 │ DATA 22h
│ 0F │ 0E │ 0D │ 0C │ 0B │ 0A │ 09 │ 08 │ DATA 21h
│ 07 │ 06 │ 05 │ 04 │ 03 │ 02 │ 01 │ 00 │ DATA 20h
└────┴────┴────┴────┴────┴────┴────┴────┘
Das LSB von DATA 20h hat die BIT-Adresse 00. Das nächste Bit aus DATA 20h
die BIT-Adresse 01 usw.:
3.9 internes RAM Seite 31
───────────────────────────────────────────────────────────────────────────
Die BIT-Adressen 80h...FFh sind für Spezial-Funktion Register reserviert,
auch wenn beim 8051 nicht alle SFR-Adressen belegt sind. Immer die SFR sind
bitweise adressierbar, bei deren das low Nibble der Adresse 0 oder 8 ist:
┌────┬────┬────┬────┬────┬────┬────┬────┐ 8051-SFB
B │ F7 │ F6 │ F5 │ F4 │ F3 │ F2 │ F1 │ F0 │ DATA F0h --------
├────┼────┼────┼────┼────┼────┼────┼────┤
ACC │ E7 │ E6 │ E5 │ E4 │ E3 │ E2 │ E1 │ E0 │ DATA E0h
├────┼────┼────┼────┼────┼────┼────┼────┤
PSW │ D7 │ D6 │ D5 │ D4 │ D3 │ D2 │ D1 │ D0 │ DATA D0h
├────┼────┼────┼────┼────┼────┼────┼────┤
IP │ BF │ BE │ BD │ BC │ BB │ BA │ B9 │ B8 │ DATA B8h
P3 │ B7 │ B6 │ B5 │ B4 │ B3 │ B2 │ B1 │ B0 │ DATA B0h
├────┼────┼────┼────┼────┼────┼────┼────┤
IE │ AF │ AE │ AD │ AC │ AB │ AA │ A9 │ A8 │ DATA A8h
P2 │ A7 │ A6 │ A5 │ A4 │ A3 │ A2 │ A1 │ A0 │ DATA A0h
├────┼────┼────┼────┼────┼────┼────┼────┤
SCON │ 9F │ 9E │ 9D │ 9C │ 9B │ 9A │ 99 │ 98 │ DATA 98h
P1 │ 97 │ 96 │ 95 │ 94 │ 93 │ 92 │ 91 │ 90 │ DATA 90h
├────┼────┼────┼────┼────┼────┼────┼────┤
TCON │ 8F │ 8E │ 8D │ 8C │ 8B │ 8A │ 89 │ 88 │ DATA 88h
P0 │ 87 │ 86 │ 85 │ 84 │ 83 │ 82 │ 81 │ 80 │ DATA 80h
└────┴────┴────┴────┴────┴────┴────┴────┘
Code für intern RAM-Zugriff:
----------------------------
Für jede Adressierungsart gibt es spezielle Befehle. --> siehe auch Be-
fehlssatz.
Bei der Erstellung eines Programms muß darauf geachtet werden, das sich
verschieden adressierte Speicherstellen nicht überlappen. Bei großen Pro-
grammen wird es schnell unübersichtlich. Bei korrekter Anwendung bieten je-
doch komfortablere Assembler und Linker eine Fehlererkennung. Die Speicher-
stellen werden je nach Datentyp eingeteilt.
Folgende Regeln sollten beachtet werden:
- Daten möglichst am Anfang des Source-Files mit Namen definieren. Nicht im
Programm Befehle mit absoluten Adressen verwenden, da man sonst schnell
die Übersicht verliert.
- beginnend mit der untersten Adresse das interne RAM "füllen". Das heißt:
*** zuerst die Register berücksichtigen: (beim ASM51 und A51 mit USING 0,
USING 1, usw.) Platz reservieren, je nach Anzahl der verwendeten
Reg.Bnk's.
*** dann Flags definieren (denn diese können nur in DATA 20h...2Fh unter-
gebracht werden)
3.9 internes RAM Seite 32
───────────────────────────────────────────────────────────────────────────
*** dann direkt DATA Variablen definieren (wenn Platz ist, können einzel-
ne noch vor den Flags angeordnet werden.
*** dann Platz für verwendete indirekt zu adressierende Buffer (IDATA)
vergeben
*** zuletzt Platz für Stack vergeben.
Beispiel für ASM51:
USING 1 ; Platz für Register-Bank 1 reservieren
USING 0 ; Platz für Register-Bank 0 reservieren
FLag1 BIT 0 ; dies liegt in DATA 20h
Variable1 DATA 10h ; Platz ist noch frei (Reg.Bnk 2 unbenutzt)
BufPtr DATA 11h ; bis 1Fh können noch direkt DATA definiert
BufCnt DATA 12h ; werden, dann aufpassen wegen BIT!
buffer IDATA 21h ; der Buffer oberhalb DATA und BIT
bufferLength EQU 20h
stack IDATA 41h ; Stack beginnt oberhalb von buffer - Platz
; bis 7Fh! Nach Reset ist der SP zu initiali-
; sieren mit: MOV SP, #(stack - 1)
die Zuordnung der Adressen wurde hier "von Hand" vorgenommen. Der ASM51
bietet auch die Möglichkeit dies durch den Linker vornehmen zu lassen
(siehe Assembler).
4. Befehlssatz Seite 33
───────────────────────────────────────────────────────────────────────────
4. 8051 Befehlssatz:
---------------------
Alle Controller der 8051-Familie verwenden denselben Befehlssatz. Bei den
neueren (weiterentwickelten) Typen sind zur Streuerung der zusätzliche
Hardwarefunktionen lediglich einige SFR hinzugekommen.
Ein Programm besteht aus einer Folge von Befehlen. Die Befehle werden in
Form von codierten Zahlenwerten im Programmspeicher (=Festwertspeicher z.B.
EPROM) abgelegt. Der 8051 besitzt intern eine Übersetzungstabelle, in der
jedem Zahlenwert eine bestimmte Operation zugeordnet ist (siehe Anhang).
Damit der 8051 eine gewollte Folge von Befehlen (Programm) ausführt, müßte
der Programmierer eine Folge von Zahlenwerten in das EPROM schreiben
(Maschinencode oder Hexcode). Für jeden Befehl müßte er in einer Tabelle
den Zahlenwert nachschlagen. Dies ist sehr unübersichtlich und umständlich.
Ein Werkzeug, daß einem diese Arbeit abnimmt ist der Assembler: er über-
setzt ein in einer abkürzenden Symbolsprache geschriebenes Programm in eine
Folge von Zahlenwerten. Die Abkürzungen der einzelnen Befehle werden als
Mnemonic bezeichnet. Das so niedergeschriebene Programm selbst als
Assembler-Sourcecode oder als Maschinensprache. Damit wird ausgedrückt, das
die Programmiersprache Assembler Befehle auf der Maschinenebene verwendet.
Jeder Befehl, den der 8051 später ausführen soll muß, nämlich als Mnemonic
angeschrieben werden.
Ein weiteres beliebtes Werkzeug zur Erzeugung von 8051 Maschinencode ist
ein C-Compiler. C ist eine Hochsprache, sie bietet fertige standartisierte
Funktionen, die dann gemäß den Funktionsaufrufen im C-Sourcefile zum ferti-
gen Programm zusammengelinkt werden. Die Funktionen wurden als Unterpro-
gramme in Assembler geschrieben, compiliert und in einer Libary abgelegt.
Der Vorteil von einer Hochsprache wie C ist, das Programme erstellt werden
können, die dann gelinkt mit einer anderen Libary auf verschiedenen Rech-
nern laufen können. Bei Programmen für den 8051 ist das nicht so gewichtig,
da die C-Funktionen für den 8051 doch sehr speziell sind. Allerdings bietet
C einige Funktionen wie Floating-Point Berechnungen, die den Einsatz des
C-Compilers für den 8051 interessant machen. Viele sind auch schon vertraut
mit der Sprache C, was neben einer besseren Übersichtlichkeit des Source-
Files auch oft ein Entscheidungspunkt zugunsten von C ist.
Im folgenden wird der vollständige Befehlssatz des 8051 erläutert. Angege-
ben werden die Mnemonics, wie sie beim Assembler verwendet werden und der
zugeordnete Hexcode. Bei einigen Befehlen sind hinter dem OpCode (codierter
Zahlenwert, der den Befehl kennzeichnet), noch ein bis zwei Operanten ange-
hängt wie eine Adresse oder ein Festwert.
In den Mnemonics werden Abkürzungen wie MOV (übertragen), ADD (Addiere)
usw. verwendet. Anstelle von absoluten Adressen sind Variablen-Namen zuläs-
sig. Die SFR-Namen sind bei einigen Assemblern schon default bekannt bzw.
können geladen werden (SFR des 8051 siehe 3.4).
4. Befehlssatz Seite 34
───────────────────────────────────────────────────────────────────────────
Zusätzlich gibt es einige spezielle Abkürzungen:
-----------------------------------------------
# konstanter Wert folgt
@ indirekte Adressierung: Wert aus der Speicherstelle, auf die das
folgende Register zeigt
A Akkumulator (= ACC, jedoch wird ein spezieller Opcode erzeugt, wenn
A anstelle von ACC verwendet wird)
C Carry-Flag (= CY, jedoch wird ein spezieller Opcode erzeugt, wenn
C anstelle von CY verwendet wird)
DPTR Datenpointer ( für Zugriff auf externe Daten) - setzt sich zusammen
aus DPH und DPL.
PC Programm-Counter (hält Code-Adresse)
Ri (mit i = 0...1) --> verwendet, um anzudeuten, daß R0 oder R1 für
diesen Befehl zulässig sind
Rn (mit n = 0...7) --> verwendet, um anzudeuten, daß R0 ... R7 für
diesen Befehl zulässig sind.
Im folgenden wird jeder 8051-Befehl kurz erläutert. Links steht der Code in
ASM51-Memomics und in den Kästchen in der Mitte der Opcode (Hexwert). Jedes
Kästchen entspricht ein Byte im Programm-Code. Ganz rechts sind die Anzahl
der benötigten Zyklen und die Flags, soweit sie sich ändern, angeführt.
Mnemonic: Hex-Code: Zyklus: Flags:
---------------------------------------------------------------------------
Zur Ausführung vieler Befehle wird nur ein CPU-Zyclus benötigt. Einige,
insbesondes solche mit Zugriff auf externe Speicher benötigen 2 Zycluse.
Ein Zyclus dauert 6 CPU-System-Takte (bei 12 MHz Quarztakte dauert ein Zy-
clus 1 µs). In jedem Zyclus werden die Timer falls sie laufen jeweils um 1
incrementiert.
4.1 Daten-Transfer Seite 35
───────────────────────────────────────────────────────────────────────────
4.1 Daten-Transfer:
--------------------
Mit dem MOV-Befehl können Daten von einem internen Register oder vom inter-
nen RAM in eine andere interne Speicherstelle kopiert werden.
┌───────────┐
MOV A, Rn │ 1110 1nnn │ 1 P
├───────────┤
MOV Rn, A │ 1111 1nnn │ 1 -
├───────────┼───────────┐
MOV A, direct │ E5h │ dataAdr │ 1 P
├───────────┼───────────┤
MOV direct, A │ F5h │ dataAdr │ 1 -
├───────────┼───────────┘
MOV A, @Ri │ 1110 011i │ 1 P
├───────────┤
MOV @Ri, A │ 1111 011i │ 1 -
├───────────┼───────────┐
MOV A, #data │ 74h │ data │ 1 P
├───────────┼───────────┤
MOV Rn, #data │ 0111 1nnn │ data │ 1 -
├───────────┼───────────┤
MOV @Ri, #data │ 0111 011i │ data │ 1 -
├───────────┼───────────┤
MOV Rn, direct │ 1010 1nnn │ dataAdr │ 2 -
├───────────┼───────────┤
MOV direct, Rn │ 1000 1nnn │ dataAdr │ 2 -
├───────────┼───────────┤
MOV @Ri, direct │ 1010 011i │ dataAdr │ 2 -
└───────────┴───────────┘
┌───────────┬───────────┐
MOV direct, @Ri │ 1000 011i │ dataAdr │ 2 -
├───────────┼───────────┼───────────┐
MOV direct, #data │ 75h │ dataAdr │ data │ 2 -
├───────────┼───────────┼───────────┤
MOV direct1, direct2 │ 85h │ dataAdr2 │ dataAdr1 │ 2 -
└───────────┴───────────┴───────────┘
Jeweils der in der Mnemonic rechts stehende Operand wird in den Linken
geschrieben.
Bemerkenswert ist, daß im Hex Code für "MOV direct1, direct2" die
Reihenfolge im Hexcode gegenüber der im Memomic vertauscht ist.
Beispiele: MOV A, R5 ; ED ->schreibe Inhalt von R5 in ACC
MOV PSW, A ; F5 D0 ->schreibe Inhalt von A in PSW
MOV A, @R1 ; E7 ->schreibe Inhalt aus int.RAM-
; Adresse die in R1 steht, in ACC
MOV R2, #22h ; 7A 22 ->schreibe 22h in R2
MOV R7, 22h ; AF 22 ->schreibe Inhalt aus int.RAM-
; Adresse 22h in R7
MOV 30h, SP ; 85 81 30 ->schreibe SP in int.RAM-Adr. 30h
4.1 Daten-Transfer Seite 36
───────────────────────────────────────────────────────────────────────────
Mit dem XCH-Befehl werden Daten zwischen internen Register oder dem inter-
nen RAM und dem Akkumulator ausgetauscht.
┌───────────┐
XCH A, Rn │ 1100 1nnn │ 1 P
├───────────┼───────────┐
XCH A, direct │ C5h │ dataAdr │ 1 P
├───────────┼───────────┘
XCH A, @Ri │ 1100 011i │ 1 P
└───────────┘
Beispiel: es sei Reg.Bnk 1 aktiv, in DATA 9 stehe #16h im ACC #9Fh
XCH A, R1 ; C9 -> in ACC #16h und in DATA 9 #9Fh
XCHD vertauscht das Low-Nibble des adressierten Bytes im internen RAM mit
dem Low_Nibble aus dem ACC.
┌───────────┐
XCHD A, @Ri │ 1101 011i │ 1 P
└───────────┘
Beispiel: in R0 stehe #33h, in DATA 33h stehe #16h im ACC #9Fh
XCHD A, @R0 ; D6 -> in ACC #96h und in DATA 33h #1Fh
Lade Datenpointer mit Festwert:
┌───────┬──────────────┬─────────────┐
MOV DPTR, #data16 │ 90h │ data16(high) │ data16(low) │ 2 -
└───────┴──────────────┴─────────────┘
dies ist die einzige 16-Bit Operation beim 8051. Sie hat dieselbe
Funktion wie:
MOV DPH, #HIGH(data16)
und MOV DPL, #LOW(data16)
4.1 Daten-Transfer Seite 37
───────────────────────────────────────────────────────────────────────────
externer Daten-Speicher-Zugriff:
┌───────────┐
MOVX A, @DPTR │ E0h │ 2 P
├───────────┤
MOVX @DPTR, A │ F0h │ 2 -
├───────────┤
MOVX A, @Ri │ 1110 001i │ 2 P
├───────────┤
MOVX @Ri, A │ 1111 001i │ 2 -
└───────────┘
Beim Zugriff auf den externen Datenspeicher wird _RD als Lesesignal
und _WR als Schreibsignal verwendet. Der Zugriff erfolgt über die al-
ternativen Funktionen von Port 0 und Port 2 (siehe 3.5). Der Wert im
Latch von Port 2 bleibt erhalten. Der Wert im Latch von Port 0 wird
von der Hardware beim Lesen mit 0FFh überschrieben.
WICHTIG!
Beim Zugriff indirekt über Ri wird nur die Low-Adresse multiplexed
über Port 0 ausgegeben. Der Inhalt des Latches von P2 bleibt während
des Zugriffs am Portausgang anliegen. Der Anwender hat bei Verwendung
eines externen RAMs vor dem Zugriff selbst das High-Byte der Adresse
in P2 zu schreiben.
Beispiel:
MOV DPTR, #0FF00h ; Datenpointer laden
MOVX A, @DPTR ; Zugriff (lesen) auf externes RAM
MOV R0, #0 ; diese Routine löscht das externe
MOV P2, #1 ; RAM von Adresse 100h bis 1FFh. In
CLR A ; P2 wird das High-Byte der Adresse
clearPage1: ; (=Page) geladen, in R0 das LOW-Byte
MOVX @R0, A ; R0 wird je Durchlauf decrementiert.
DJNZ R0, clearPage1
Byte aus dem Programmspeicher lesen:
┌───────────┐
MOVC A, @A+DPTR │ 93h │ 2 P
├───────────┤
MOVC A, @A+PC │ 83h │ 2 P
└───────────┘
Ein Byte aus dem Programmspeicher (extern oder intern: abhängig von
der Adresse und /EA Pin) wird in den ACC gelesen. Die Adresse wird aus
der Summe der Inhalte vom ACC und dem DPTR bzw. dem (zuvor um 1 incre-
mentierten) Programm-Counter gebildet (siehe auch 3.3, 3.5 und 3.6).
Der Inhalt des Latches von P2 ändert sich dabei nicht, während in P0
von der Hardware vor dem Zugriff 0FFh geschrieben wird.
4.2 Rechen-Operationen Seite 38
───────────────────────────────────────────────────────────────────────────
Byte auf/vom Stack:
┌───────────┬───────────┐
PUSH direct │ C0h │ dataAdr │ 2 -
├───────────┼───────────┤
POP direct │ D0h │ dataAdr │ 2 -
└───────────┴───────────┘
Beim PUSH wird der Stack Pointer um 1 incrementiert und dann der Wert
in diese Adresse geschrieben. Beim POP wird zuerst gelesen und dann
der SP decrementiert. Der Stackpointer zeigt also immer auf das zu-
letzt gepushte Byte.
Auch bei Unterprogrammaufrufen und Interrupts werden Werte auf den
Stack geschoben (siehe dazu ACALL, LCALL, RET, RETI). Ist der Stack
voll (SP zeigt auf Adressen oberhalb 7Fh, so wird trotzdem bei einem
PUSH der SP weiterhin incrementiert, die Daten gehen aber verloren.
Der 8044 besitzt ein internes RAM mit 192 Byte (bis Adresse 0BFh). Der
8052 besitzt ein internes RAM mit 256 Byte. Bei einem SP-Überlauf über
0FFh wird beim 8052 der Registerbereich des internen RAM's überschrie-
ben.
Nach einem Reset steht im SP die Adresse 07.
4.2 Rechenoperationen:
-----------------------
Addiere Wert, Register, intern RAM-Adr. zu Akku:
┌───────────┬───────────┐
ADD A, #data │ 24h │ data │ 1 C AC P OV
├───────────┼───────────┘
ADD A, @Ri │ 0010 011i │ 1 C AC P OV
├───────────┤
ADD A, Rn │ 0010 1nnn │ 1 C AC P OV
├───────────┼───────────┐
ADD A, direct │ 25h │ dataAdr │ 1 C AC P OV
└───────────┴───────────┘
Nach der Ausführung hält der ACC das Ergebnis, das C-Flag hält den
Übertrag aus Bit 7 und AC hält den Übertrag aus Bit 3. Das OV ist ge-
setzt, wenn ein Vorzeichenfehler auftrat (siehe 3.4.3 PSW).
Beispiel: vor ADD: nach ADD:
ADD A, #44h ; A = 33h --> A = 77h, C = 0, AC = 0, OV = 0
ADD A, #44h ; A = 43h --> A = 87h, C = 0, AC = 0, OV = 1
ADD A, #4Ah ; A = 2Ah --> A = 74h, C = 0, AC = 1, OV = 0
ADD A, #93h ; A = 0B1h --> A = 44h, C = 1, AC = 0, OV = 1
4.2 Rechen-Operationen Seite 39
───────────────────────────────────────────────────────────────────────────
Addiere Wert, Register, intern RAM-Adr. zu Akku mit Carry:
┌───────────┬───────────┐
ADDC A, #data │ 34h │ data │ 1 C AC P OV
├───────────┼───────────┘
ADDC A, @Ri │ 0011 011i │ 1 C AC P OV
├───────────┤
ADDC A, Rn │ 0011 1nnn │ 1 C AC P OV
├───────────┼───────────┐
ADDC A, direct │ 35h │ dataAdr │ 1 C AC P OV
└───────────┴───────────┘
Wie Addition ohne Carry. Jedoch wird zusätzlich der Wert des Carry-
Flags hinzuaddiert.
Beispiel: vor ADD: nach ADD:
ADDC A, #44h ; A = 33h, C = 1 --> A = 78h, C = 0, AC = 0, OV = 0
ADDC A, #44h ; A = 33h, C = 0 --> A = 77h, C = 0, AC = 0, OV = 0
Subtrahiere Wert, Register, intern RAM-Adr. von Akku mit Carry:
┌───────────┬───────────┐
SUBB A, #data │ 94h │ data │ 1 C AC P OV
├───────────┼───────────┘
SUBB A, @Ri │ 1001 011i │ 1 C AC P OV
├───────────┤
SUBB A, Rn │ 1001 1nnn │ 1 C AC P OV
├───────────┼───────────┐
SUBB A, direct │ 95h │ dataAdr │ 1 C AC P OV
└───────────┴───────────┘
Subtrahiere immediate Data, indirekt Byte, Inhalt von Rn bzw. direkt
Byte plus das Carry-Flag von ACC. Flags siehe ADD.
Increment = der Inhalt der angegebenen Register um 1 erhöht.
┌───────────┐
INC A │ 04h │ 1 P
├───────────┤
INC Rn │ 0000 1nnn │ 1 no Flags
├───────────┤
INC @Ri │ 0000 011i │ 1 no Flags
├───────────┼───────────┐
INC direct │ 05h │ dataAdr │ 1 no Flags
├───────────┼───────────┘
INC DPTR │ A3h │ 2 no Flags
└───────────┘
4.2 Rechen-Operationen Seite 40
───────────────────────────────────────────────────────────────────────────
Decrement = der Inhalt der angegebenen Register wird um 1 erniedrigt.
┌───────────┐
DEC A │ 14h │ 1 P
├───────────┤
DEC Rn │ 0001 1nnn │ 1 no Flags
├───────────┤
DEC @Ri │ 0001 011i │ 1 no Flags
├───────────┼───────────┐
DEC direct │ 15h │ dataAdr │ 1 no Flags
└───────────┴───────────┘
Multipiziere Akku mit B-Register:
┌───────────┐
MUL AB │ A4h │ 4 C P OV
└───────────┘
Das Ergebnis steht in ACC. Wenn das Ergebnis ein Word ist, steht das
low Byte in ACC, das High Byte in B und das OV-Flag ist gesetzt. Das
C-Flag wird immer gelöscht (C = 0).
Dividieren Akku durch B-Register:
┌───────────┐
DIV AB │ 84h │ 4 C P OV
└───────────┘
Das Ergebnis steht in ACC und der Rest in B. Bei einem Fehler (Divisi-
on durch 0) ist das OV - Flag gesetzt. Das Carry Flag wird immer ge-
löscht.
Decimal Adjust von ACC:
┌───────────┐
DA A │ D4h │ 1 C P
└───────────┘
Wurden zwei BCD Zahlen miteinander addiert, so muß danach das Ergebnis
korrigiert werden, soll es auch im BCD-Format erscheinen.
Ist das AC-Flag gesetzt, oder das low Nibble > 9, dann wird 6 addiert.
Ist das C-Flag gesetzt, oder das high Nibble > 9, dann wird 60h ad-
diert. Wenn beim Ergebnis ein Übertrag von Bit 7 des ACC erfolgt, wird
das Carry-Flag (C=1) gesetzt.
4.3 logische Verknüpfungen Seite 41
───────────────────────────────────────────────────────────────────────────
Beispiel: bcd_59 = 0101 1001
bcd_68 = 0110 1000
+ ────────────
nach ADD (ohne Korrektur): bcd_C1 = 1100 0001 (Flags: AC=1, C=0)
wegen (AC=1): 0000 0110
wegen (high Nibble > 9): 0110 0000
+ ────────────
Ergebnis (mit Korrektur): bcd_27 = 0010 0111 (Flags: C=1)
4.3 logische Verknüpfungen:
----------------------------
┌───────────┬───────────┐
ANL A, #data │ 54h │ data │ 1 P
├───────────┼───────────┘
ANL A, @Ri │ 0101 011i │ 1 P
├───────────┤
ANL A, Rn │ 0101 1nnn │ 1 P
├───────────┼───────────┐
ANL A, direct │ 55h │ dataAdr │ 1 P
├───────────┼───────────┤
ANL direct, A │ 52h │ dataAdr │ 1 no Flags
├───────────┼───────────┼───────────┐
ANL direct, #data │ 53h │ dataAdr │ data │ 2 no Flags
└───────────┴───────────┴───────────┘
bitweise logisch AND Verknüpfung:
wenn beide Bit 1, dann 1
┌───────────┬───────────┐
ORL A, #data │ 44h │ data │ 1 P
├───────────┼───────────┘
ORL A, @Ri │ 0100 011i │ 1 P
├───────────┤
ORL A, Rn │ 0100 1nnn │ 1 P
├───────────┼───────────┐
ORL A, direct │ 45h │ dataAdr │ 1 P
├───────────┼───────────┤
ORL direct, A │ 42h │ dataAdr │ 1 no Flags
├───────────┼───────────┼───────────┐
ORL direct, #data │ 43h │ dataAdr │ data │ 2 no Flags
└───────────┴───────────┴───────────┘
bitweise logisch OR Verknüpfung:
wenn eines der beiden Bits 1, dann 1
4.3 logische Verknüpfungen Seite 42
───────────────────────────────────────────────────────────────────────────
┌───────────┬───────────┐
XRL A, #data │ 64h │ data │ 1 P
├───────────┼───────────┘
XRL A, @Ri │ 0110 011i │ 1 P
├───────────┤
XRL A, Rn │ 0110 1nnn │ 1 P
├───────────┼───────────┐
XRL A, direct │ 65h │ dataAdr │ 1 P
├───────────┼───────────┤
XRL direct, A │ 62h │ dataAdr │ 1 no Flags
├───────────┼───────────┼───────────┐
XRL direct, #data │ 63h │ dataAdr │ data │ 2 no Flags
└───────────┴───────────┴───────────┘
bitweise logisch XOR Verknüpfung:
wenn beide Bit verschieden, dann 1
┌───────────┐
CLR A │ E4h │ 1 P
└───────────┘
lösche ACC --> ACC = 0
┌───────────┐
CPL A │ F4h │ 1 P
└───────────┘
Komplement zu ACC --> ACC wird bitweise invertiert
┌───────────┐
RL A │ 23h │ 1 no Flags
└───────────┘
rotate ACC bitweise nach links:
schiebt Inhalt von ACC bitweise nach links. Das höchste Bit wird dabei
in das niedrigste geschrieben. Das Parity-Flag ändert sich nicht, da
die Summe über alle Bit's gleich bleibt.
┌───────────┐
RLC A │ 33h │ 1 P C
└───────────┘
rotate ACC bitweise nach links durch Carry :
schiebt Inhalt von ACC bitweise nach links. Das höchste Bit wird dabei
in C und C in das niedrigste geschrieben.
4.4 Bit-Manipulationen Seite 43
───────────────────────────────────────────────────────────────────────────
┌───────────┐
RR A │ 03h │ 1 no Flags
└───────────┘
rotate ACC bitweise nach rechts:
schiebt Inhalt von ACC bitweise nach rechts. Das niederste Bit wird
dabei in das höchste geschrieben. Das Parity-Flag ändert sich nicht,
da die Summe über alle Bit's gleich bleibt.
┌───────────┐
RRC C │ 13h │ 1 P C
└───────────┘
rotate ACC bitweise nach rechts durch Carry:
schiebt Inhalt von ACC bitweise nach rechts. Das niederste Bit wird dabei
in C und C in das höchste geschrieben.
┌───────────┐
SWAP A │ C4h │ 1 no Flags
└───────────┘
vertausche in ACC low Nibble mit high Nibble
4.4 Bit-Manipulationen:
------------------------
┌───────────┬───────────┐
MOV C, bit │ A2h │ bitAdr │ 1 C
├───────────┼───────────┤
MOV bit, C │ 92h │ bitAdr │ 2 no Flags
└───────────┴───────────┘
kopiere Inhalt aus Bit-Adresse in C bzw. C in Bit-Adresse.
┌───────────┬───────────┐
ANL C, bit │ 82h │ bitAdr │ 2 C
├───────────┼───────────┤
ANL C, /bit │ B0h │ bitAdr │ 2 C
└───────────┴───────────┘
logisch AND Bit bzw. invertierter Wert von Bit zu C
4.5 unbedingte Sprünge und CALL's Seite 44
───────────────────────────────────────────────────────────────────────────
┌───────────┬───────────┐
ORL C, bit │ 72h │ bitAdr │ 2 C
├───────────┼───────────┤
ORL C, /bit │ A0h │ bitAdr │ 2 C
└───────────┴───────────┘
logisch OR Bit bzw invertierter Wert des Bits zu C
┌───────────┐
CLR C │ C3h │ 1 C
├───────────┼───────────┐
CLR bit │ C2h │ bitAdr │ 1 no Flags
└───────────┴───────────┘
lösche Bit bzw. Carry-Flag --> Bit = 0
┌───────────┐
CPL C │ B3h │ 1 C
├───────────┼───────────┐
CPL bit │ B2h │ bitAdr │ 1 no Flags
└───────────┴───────────┘
invertiere Bit bzw. Carry-Flag
┌───────────┐
SETB C │ D3h │ 1 C
├───────────┼───────────┐
SETB bit │ D2h │ bitAdr │ 1 no Flags
└───────────┴───────────┘
setze Bit bzw. Carry-Flag --> Bit = 1
4.5 unbedingte Sprünge und CALLs:
----------------------------------
CALL codeAdress
Dies ist kein Maschinen-Befehl des 8051. Der Assembler legt hier
selbst fest, ob ein ACALL oder LCALL verwendet wird. Bei "Forward Re-
ferenzen", also Adressen, die dem Assembler hier noch nicht bekannt
sind, wird immer ein LCALL eingesetzt, auch wenn ein ACALL genügte.
4.5 unbedingte Sprünge und CALL's Seite 45
───────────────────────────────────────────────────────────────────────────
JMP codeAdress
Dies ist kein Maschinen-Befehl des 8051. Der Assembler legt hier
selbst fest, ob ein SJMP, AJMP oder LJMP verwendet wird. Bei "Forward
Referenzen" wird immer ein LJMP eingesetzt, auch wenn er nicht effi-
zient ist.
┌──────────────────┬─────────────────────────┐
ACALL codeAdr │ A10 A9 A8 1 0001 │ A7 A6 A5 A4 A3 A2 A1 A0 │ 2 no Flags
└──────────────────┴─────────────────────────┘
Absolute CALL (innerhalb 2 KByte Page):
Der PC (Programm-Counter) wird um 2 incrementiert und die Return-
Adresse auf den Stack gelegt (low Byte zuerst). Dann wird die folgende
Adresse in den PC geladen:
A11 bis A15 im PC (Programm-Counter) bleiben unverändert,
die unteren 11 Bit werden aus dem Opcode geladen.
┌─────────┬────────────┬───────────┐
LCALL codeAdr │ 12h │ CodAdrHigh │ CodAdrLow │ 2 no Flags
└─────────┴────────────┴───────────┘
Long CALL:
Der PC (Programm Counter) wird um 3 incrementiert und die Return-
Adresse auf den Stack gelegt (low Byte zuerst). Dann wird mit Wert aus
dem Opcode geladen.
┌───────────┐
RET │ 22h │ 2 no Flags
└───────────┘
Return von Unterprogramm (CALL):
Die Return-Adresse wird vom Stack geholt (high Byte zuerst) und ange-
sprungen.
┌───────────┐
RETI │ 32h │ 2 no Flags
└───────────┘
Return von Interrupt:
In einem internen Register hat sich die CPU gemerkt, welche Interrupts
gerade in Bearbeitung sind. Dies können auch zwei Interrupts sein, da
es zwei Prioritätsstufen gibt, und eine Routine niederer Stufe von ei-
nem Interrupt höherer Stufe unterbrochen werden kann. Ist ein Inter-
rupt in Bearbeitung sind alle anderen Interrupts gleicher oder niede-
rer Prioritätsstufe gesperrt. Mit RETI wird der Höchstwertigste Inter-
rupt ausgetragen und die Interrupts sind wieder frei.
4.5 unbedingte Sprünge und CALL's Seite 46
───────────────────────────────────────────────────────────────────────────
Die Return-Adresse wird vom Stack geholt (high Byte zuerst) und ange-
sprungen.
Achtung: soll innerhalb einer MOV A, #0
Interrupt-Routine durch einen PUSH ACC
Sprung auf Adresse 0000 ein PUSH ACC
Software-Reset erfolgen, so MOV A, #LOW(retiReset)
hat der Sprung über ein zwei- PUSH ACC
maliges RETI zu erfolgen, da MOV A, #HIGH(retiReset)
sonst entgegen einen Hardware- PUSH ACC
Reset das internen Enable-Re- retiReset:
gister nicht gelöscht würde. RETI
┌───────────┬───────────┐
SJMP codeAdr │ 80h │ relOffset │ 2 no Flags
└───────────┴───────────┘
Short JUMP (-128 +127):
Der PC wird um 2 incrementiert und dazu wird der relOffset addiert.
Das höchste Bit der Wertes relOffset dient dabei als Vorzeichen.
Zur Berechnung: der relOffset (BYTE) wird erst in ein WORD umgewandelt
(auf einer 8088 kompatiblen CPU durch CBW). Ist der relOffset negativ
(höchstes Bit gesetzt), so wird dabei 0FF00h addiert, sonst 0. Dieses
WORD wird zum incrementierten PC addiert.
Beispiel: relOffset = 47h --> 0047h + PC
relOffset = 87h --> FF87h + PC
┌──────────────────┬─────────────────────────┐
AJMP codeAdr │ A10 A9 A8 0 0001 │ A7 A6 A5 A4 A3 A2 A1 A0 │ 2 no Flags
└──────────────────┴─────────────────────────┘
Absolute JUMP (innerhalb 2 KByte Page):
Der PC wird um 2 incrementiert, und die Adresse wird dann folgenderma-
ßen gebildet: A11 bis A15 im PC (Programm-Counter) bleiben unverän-
dert, die unteren 11 Bit werden aus dem Opcode geladen.
┌─────────┬────────────┬───────────┐
LJMP codeAdr │ 02h │ CodAdrHigh │ CodAdrLow │ 2 no Flags
└─────────┴────────────┴───────────┘
Long JUMP:
Sprung zur im Opcode angegebenen Adresse.
4.6 bedingte Sprünge Seite 47
───────────────────────────────────────────────────────────────────────────
┌───────────┐
JMP @A+DPTR │ 73h │ 2 no Flags
└───────────┘
Jump indirekt zu Accumulator + DataPointer:
Inhalt von ACC und DPTR addieren, Sprung zu dieser Adresse.
4.6 bedingte Sprünge:
----------------------
Sprung um relativen Offset, wenn die Bedingung erfüllt ist. Zur Berechnung
der Zieladresse siehe SJMP (4.5 unbedingte Sprünge).
┌───────────┬───────────┐
JZ codeAdress │ 60h │ relOffset │ 2 no Flags
├───────────┼───────────┤
JNZ codeAdress │ 70h │ relOffset │ 2 no Flags
└───────────┴───────────┘
springe, wenn ACC = 0 bzw. wenn ACC nicht = 0
┌───────────┬───────────┐
JC codeAdress │ 40h │ relOffset │ 2 no Flags
├───────────┼───────────┤
JNC codeAdress │ 50h │ relOffset │ 2 no Flags
└───────────┴───────────┘
springe, wenn C = 1 bzw. C = 0
┌─────────┬───────────┬───────────┐
JB bit, codeAdress │ 20h │ bitAdr │ relOffset │ 2 no Flags
├─────────┼───────────┼───────────┤
JNB bit, codeAdress │ 30h │ bitAdr │ relOffset │ 2 no Flags
├─────────┼───────────┼───────────┤
JBC bit, codeAdress │ 10h │ bitAdr │ relOffset │ 2 no Flags
└─────────┴───────────┴───────────┘
springe, wenn Bit = 1, bzw. Bit = 0:
Bei JBC: Sprung, wenn Bit = 1 und lösche Bit.
4.6 bedingte Sprünge Seite 48
───────────────────────────────────────────────────────────────────────────
┌───────────┬───────────┬───────────┐
CJNE A, direct,codeAdr │ B5h │ dataAdr │ relOffset │ 2 C
├───────────┼───────────┼───────────┤
CJNE A, #data, codeAdr │ B4h │ data │ relOffset │ 2 C
├───────────┼───────────┼───────────┤
CJNE Rn, #data,codeAdr │ 1011 1nnn │ data │ relOffset │ 2 C
├───────────┼───────────┼───────────┤
CJNE @Ri,#data,codeAdr │ 1011 011i │ data │ relOffset │ 2 C
└───────────┴───────────┴───────────┘
vergleiche und springe, wenn nicht gleich:
Ist der linke Operand kleiner als der Rechte, so ist anschließend das
Carry Flag gesetzt (C = 1).
┌───────────┬───────────┐
DJNZ Rn, codeAdr │ 1101 1nnn │ relOffset │ 2 no Flags
├───────────┼───────────┼───────────┐
DJNZ direct, codeAdr│ D5h │ dataAdr │ relOffset │ 2 no Flags
└───────────┴───────────┴───────────┘
Decrement und springe, wenn Inhalt danach ungleich 0
┌───────────┐
NOP │ 00h │ 1 no Flags
└───────────┘
keine Aktion. NOP ist oft dienlich, wenn eine Wartezeit überbrückt wer-
den soll. Bei einem Takt von 12 MHz dauert ein NOP 1 µs.
┌───────────┐
reserved │ A5h │ ? ?
└───────────┘
unerlaubter Opcode. Diesem HEX-Code ist kein Befehl zugeordnet.
5. Assembler Seite 49
───────────────────────────────────────────────────────────────────────────
5. Assembler:
--------------
Ein Assembler wird zur Übersetzung eines mit Mnemonic-Symbolen geschriebe-
nen Programms in ausführbaren HEX-Code verwendet. Im Source-Code wird jeder
Befehl einzeln angeschrieben. Manche Assembler bieten zwar die Möglichkeit
MACRO's zu definieren, aber auch dort ist letztlich jeder einzelne Befehl
im Source-File definiert, MARCOS dienen nur zur Abkürzung sich oft wieder-
holender Befehlsfolgen.
Der Assembler übersetzt jeden einzelnen Befehl in den entsprechenden HEX-
Code (siehe 4. Befehlssatz). Der Assembler berechnet aber auch die Ziel-
adressen bei einem JMP oder CALL.
Für den 8051 gibt es einige kommerzielle Assembler wie den ASM51 von Intel
oder den A51 von Keil. Es gibt aber auch einige Shareware-Produkte wie den
TASM, die sich ebenfalls eignen. Von der Zeitschrift elektor wird zur Zeit
(Start: 09.1991) ein Assemblerkurs durchgeführt, dazu gibt es ebenfalls ei-
nen preisgünstigen Assembler EASM51. Allerdings generiert der EASM51 einen
eigendefinierten HEX-Code. Die meißten anderen Assembler generieren einen
HEX-Code im Intel-HEX-Format.
5.1 Der ASM51:
---------------
Der ASM51 und der A51 sind weitgehend kompatibel. Der Assembler selbst ge-
neriert einen Objekt-File. Dieser File kann nicht in ein EPROM oder auf ei-
nen 8051 Rechner geladen werden. Der Objekt-File enthält noch keine absolu-
ten Adressen. Im Source-Code als relocatabel definierte Segmente (Codebe-
reiche) können noch innerhalb des 64 kByte Adressraums verschoben werden.
Die Adresszuweisung übernimmt der Linker (RL51.EXE). Der Linker generiert
einen Absolut-File. Das Programm ist nun fest auf Code-Adressem zugeordnet.
Dieses Verfahren hat seine Vorteile bei umfangreicheren Programmen. Vom
Linker können mehrere Objekt-Files zu einem Absolut-File zusammengelinkt
werden. Ein Programm kann so in mehreren kleinen Modulen erstellt werden.
Es bleibt übersichtlicher, außerdem eignet sich der ASM51 damit zur Anwen-
dung in Firmen, wo oft mehrere Leute an einem Programm basteln.
Es besteht zusätzlich die Möglichkeit, allgemeinem Routinen, die oft ver-
wendet werden in einen Libary-File abzulegen (LIB51.EXE). Libary-Routinen
können vom Linker in den Absolut-File eingebunden werden.
Der Absolut-File kann aber auf vielen Eprommern auch noch nicht geladen
werden. Es wird noch ein Konvertierprogramm von Absolut- in HEX-Format be-
nötigt (OH.EXE).
Assembler: ASM51 --> Objekt-File
Linker: RL51 --> Absolut-File
Konvertierung: OH --> HEX-File
5. Assembler Seite 50
───────────────────────────────────────────────────────────────────────────
Anmerkung: Der Absolut- und der Objekt-File besitzen denselben Aufbau: Die
Files bestehen aus einer Aneinanderreihung von RECORD's. Jeder Record
beginnt mit einem Byte, das den Record-Typ definiert, danach folgt ein
Word mit der Anzahl der im Record-Body folgenden Bytes. Am Schluß
folgt noch ein Byte mit einer Checksumme. Je nach Record-Typ sind die
Record-Body's von verschiedener Struktur. Absolut- und Objekt-Files
enthalten Code in binärer Form. Zusätzlich können noch die im Source-
File definierten Variablen-Namen enthalten sein.
Auch wenn ein Objekt-File Code in binärer Form enthält, darf er nicht
mit einem Binär-File verwechselt werden. Binär-Files werden von einfa-
chen Assemblern und Eprommern verwendet. In Binär-Files ist nur der
Code enthalten. Der Offset innerhalb des Files ist gleich der Adresse.
Binär-Files sind so bei kurzen Programmen, die bei Offset 0000 begin-
nen sehr kompakt. Bei Objekt-Files steht einleitend noch der Record-
Typ und die Record-Länge. Bei Programmen, die Code bei hohen Adressen
enthalten wird der Binär-File schnell umfangreicher. Nicht verwendete
Adressen, die davor liegen müssen mit 0FFh aufgefüllt werden.
HEX-Files beinhalten nur Code. Jedes Byte ist als ASCII-HEX-Zahl dar-
gestellt. Sie können mit einem normalen Editor betrachtet werden.
HEX-Files sind Standart beim Laden auf vielen Eprommern.
In dieser Beschreibung soll nicht auf die verschiedenen Finessen eines As-
semblers wie den ASM51 eingegangen werden. Es wird nur kurz die zur Erstel-
lung eines kleinen Programms wesentliche Syntax erläutert.
Der ASM kennt alle Mnemonics, wie sie in Kapitel 4 definiert sind. Default
kennt der ASM51 auch die SFR- und SFB-Definitionen des 8051. Es muß also
z.B. nicht erst die Abkürzung PSW DATA 0D0h definiert werden, PSW kann
direkt verwendet werden.
Für Bit-Adressen kann ebenfalls der vordefinierte SFB-Name eingesetzt wer-
den. Es kann aber auch der SFR-Namen des (DATA-)Bytes, in dem sich dieses
Bit befindet, verwendet werden und hinter einem . die Nummer des Bit's an-
gegeben werden.
Beispiel:
Das Auxillary-Carry-Flag (BIT 0D6h) liegt SETB 0D6h
im Programm-Status-Word (DATA 0D0h). Ne- SETB AC
benstehende Befehle führen zu demselben SETB PSW.6
Ergebnis.
Soll Code für eine andere CPU generiert werden, so müssten die zusätzlichen
SFR und SFB definiert werden. Hierzu wird praktisch ein Include-File ver-
wendet. Include-Files mit den SFR-Definitionen für andere CPU's werden beim
ASM51 auf Disk mitgeliefert. In den Include-Files sind alle SFR-
Definitionen für die CPU enthalten, auch die der ASM51 schon default (vom
8051) kennt. Der ASM51 bringt in einem solchen Fall eine Fehlermeldung,
deßhalb werden mit dem Befehl $NOMOD51 die default bekannten SFR-
Definitionen abgeschaltet.
5. Assembler Seite 51
───────────────────────────────────────────────────────────────────────────
Zur Steuerung des Assemblers können im Source-File Befehle (Direktriven)
mitangegeben werden. Es werden hier nur die wichtigsten erläutert. Die
meißten anderen betreffen ohnehin nur das Format des List-Files.
$noMOD51 die sonst default bekannten SFR für den 8051 werden abgeschal-
tet. $NOMOD51 muß am Anfang des Source-Files stehen.
$INCLUDE(fileName) die in dem angegebenen File enthaltenen Mnemonics
werden an jener Stelle in den Assembler-Source-File übernommen, wo
$INCLUDE steht. Der Include-File muß gültigen Source-Code enthalten.
$DEBUG die Variablen-Definitionen werden als Debug-Informationen in den
Objekt-File übernommen. $DEBUG am Anfang des Source-Files stehen.
$DEBUG und $NOMOD51 können auch in der Aufrufzeile mitangegeben werden
(dann ohne $).
Mit USING n (n = 0..3) kann eine Registerbank reserviert werden. Der
Linker reserviert die entsprechenden Adressen im internen RAM. Default ist
USING 0 eingestellt.
Der ASM51 kennt default folgende Code-Adressen. Diese Definitionen werden
auch mit dem Befehl $noMOD51 nicht abgeschaltet.
RESET CODE 00h Anmerkung: für den 8052 sollte
ExtI0 CODE 03h noch die Definition
Timer0 CODE 0Bh Timer2 CODE 2Bh
ExtI1 CODE 13h in den Include-File
Timer1 CODE 1Bh mit den SFR-Definition-
SINT CODE 23h en eingefügt werden.
5.2 Segmentierung unter ASM51:
-------------------------------
Ein Programm kann bereits im Sourcecode mit festen Adresszuordnungen verse-
hen, oder relocateable geschrieben sein. Diese Optionen können auch ge-
mischt verwendet werden.
Den Variablen der verschiedenen Speicherbereiche bzw. dem Code ist entspre-
chend der Adressierungsart der Typ DATA, IDATA, XDATA, BIT oder CODE zu-
geordnet. Jeder Variablen-Definition oder Code-Procedure ist eine Segmen-
tangabe richtigen Typs vorrauszustellen.
5. Assembler Seite 52
───────────────────────────────────────────────────────────────────────────
5.2.1 feste Adresszuordnung:
-----------------------------
Eine feste Adresszuordnung ist die einfachste Art unter ASM51 ein Programm
zu erstellen. Hier stehen die Adressenb schon im Objekt-File fest. Der Lin-
ker kann die übersetzten Code-Bereiche nicht mehr verschieben.
Bei professioneller Programmerstellung werden feste Segmente nur verwendet,
wo die Hardware feste Adresen erfordert. Zum Beispiel für die Einsprunga-
dressen für Reset und die Interrupts. Aber auch für bestimmte feste Adres-
sen im XDATA-Bereich (falls dort unter einer ganz bestimmten Adresse eine
Hardware-Erweiterung aufgebaut wurde).
Eine feste Adresszuordnung wird erreicht durch Voranstellen eines der fol-
genden Schlüsselwörter:
CSEG, XSEG, DSEG, ISEG, BSEG
Obigen Schlüsselwörtern kann noch der Zusatz "AT adr" folgen. Damit wird
der Beginn des Segmentes auf eine bestimmte feste Adresse gesetzt. Default
beginnt der ASM51 ansonsten in jedem Segment bei 0000.
Ein Segment bleibt solange geöffnet, bis wieder ein neues Schlüsselwort
(aus obigen) angegeben wird. Am Beginn des Source-Files wird vom Assembler
default CSEG AT 0000 angenommen.
Code kann selbstverständlich nur im CSEG eingegeben werden. Dazu werden die
in Kapitel 4. erklärten Mnemonics verwendet. Zusätzlich gibt es die folgen-
den Pseudo-Befehle, um Texte, Tabellen oder Festwerte in den Code einzufü-
gen:
DB byte ; reserviere 1 Byte (? bedeutet Wert = beliebig)
DW word ; reserviere 1 Word
Beispiele:
DB 0A5h ; Hexwert A5h
DB 'Dies ist ein Text.',0 ; Text in Hochkomma gefolgt von Hexwert 0
DW label1 ; Hexwert der Adresse von Label1. Das
; High Byte wird zuerst gespeichert.
In anderen Segmenten (DSEG, ISEG, XSEG oder BSEG) können keine Code-
Mnemonics stehen. Hierin werden nur Variablen-Namen definiert. Der Platz
für jede definierte Variable wird mit DS definiert. In jedem Segment-Typ
werden Variable durch ein Label festgelegt. Jedem Label ist ein : nachzu-
stellen.
DS n reserviere n Bytes
DBIT n reserviere n Bit
5. Assembler Seite 53
───────────────────────────────────────────────────────────────────────────
Beispiel:
ISEG AT 30h
buffer: DS 20 ; 20 Bytes für buffer reservieren
stack: DS 12
CSEG ; nun weiter mit Code = ISEG geschlossen
.......
Für ein ganz einfaches Programm kann man auch auf die Verwendung von Seg-
menten verzichten. Dann werden die Variablen-Namen mit folgenden Direktri-
ven definiert (Beispiele):
dAdr1 DATA 23h
iAdr1 IDATA 8Fh
xAdr1 XDATA 1234h
bAdr1 BIT 0
cAdr1 CODE 0FFF0h
wert1 EQU 1 ; Definition eines festen Wertes
Diese Form wird auch bei der Definition der SFR für andere CPUs als den
8051 verwendet, um sie mit INCLUDE einzubinden.
Einfache Assembler kennen oft nur die EQU-Zuordnung. Dort werden auch Va-
riablen vom Typ DATA, XDATA, IDATA und BIT mit EQU definiert. Dies kann
beim ASM51 auch gemacht werden. Allerdings wird dann auf eine Fehlermeldung
verzichtet, die ASM51 generiert, wenn in einem Befehl eine Variable mit
falschen Typ verwendet wird.
Einfache Assembler verwenden auch nicht die Direktrive CSEG AT Adr, um die
Assemblierung bei einer bestimmten Code-Adresse zu beginnen, sondern
ORG Adr.
Zur Erstellung eines Programms werden nur noch folgende Direktriven oft be-
nötigt:
END ; Dieses Schlüsselwort muß am Ende des Assembler-Source
; Files stehen.
$ ; anstelle dieses Symbols setzt der ASM51 immer die aktu-
; elle Adresse des PC ein.
5. Assembler Seite 54
───────────────────────────────────────────────────────────────────────────
5.2.2 relocatable Segmentierung:
---------------------------------
Bei einer relocatablen Segmentangabe sind zuerst Segmentnamen in folgender
Form zu definieren. Es können auch mehrere Segmente gleichen Typs definiert
werden.
segNameC SEGMENT CODE
segNameX SEGMENT XDATA
segNameD SEGMENT DATA
segNameI SEGMENT IDATA
segNameB SEGMENT BIT
Den einzelnen Bereichen sind dann folgende Schlüsselwörter vorranzustellen:
RSEG segNameC
RSEG segNameX
RSEG segNameD
RSEG segNameI
RSEG segNameB.
Die Eingabe von Code-Mnemonics und Variablendefinitionen erfolgt wie bei
festen Segmenten.
Im Zusammenhang mit relocatabel geschriebenen Programm-Modulen sind folgen-
de Direktriven wichtig:
PUBLIC varName1, varName2,... ; Variablen-Namen werden dem Linker
; bekanntgegeben
EXTRN CODE (cAdr1,cAdr2,...) ; Variablen-Namen werden extern dekla-
EXTRN DATA (dAdr1,dAdr1,...) ; riert. Der Assembler weiß damit, das
EXTRN IDATA (iAdr1,....) ; sie in einem anderen Segment defi-
EXTRN XDATA (xAdr1,....) ; niert sind.
EXTRN BIT (bAdr1,bAdr2....)
EXTRN NUMBER (wert1,wert2...)
5.3 ASM51 Aufruf:
------------------
Der Aufruf von Assembler, Linker und Konvertierprogramm erfolgt in unten-
stehender Form. Die Angaben in <> sind optional. Default erhält sonst der
ObjekFile die Extension .obj, der Listfile .lst und der AbsolutFile erhält
keine Extension. Die default eingestellt Größe des internen RAM beim Linker
ist 128 Byte.
ASM51 sourceName <OJ(objektName)> <PR(listName)>
RL51 objektName <TO absolutName> <RAMSIZE(256)>
OH absolutName TO hexName
5. Assembler Seite 55
───────────────────────────────────────────────────────────────────────────
5.4 Programmbeispiel:
----------------------
Folgendes Programmierbeispiel zeigt wie Variablennamen absolut definiert
werden. Gezeigt wird eine seriell-Interrupt Routine, die ein empfangenes
Zeichen als Echo wieder zurücksendet.
Es wird nur mit CSEG gearbeitet. Der Code beginnt mit der Default-
Einstellung CSEG AT Reset bei Adresse 0. Die Interrupt-Routine wird fest
auf die Adresse 23h gelegt.
┌──────────────────────────────────────────────────────────────────────┐
│TxFlag BIT 00 ; Bit0 in DATA 20h als Flag │
│stack IDATA 21h ; Adresse in int.RAM für Stack │
│ │
│;CSEG AT reset ; Code beginnt bei default Label Reset │
│ AJMP init ; Sprung zur Initialisierung │
│ │
│CSEG AT SINT ; an Adresse 23h ist Einsprung für SINT │
│ JNB RI, sintChkTx ; wurde etwas empfangen? │
│ PUSH ACC │
│ MOV A, SBUF ; einlesen │
│ CLR RI ; Empfangsbuffer wieder bereit │
│ JB TxFlag, echoChar │
│ JNB TI, $ ; warte bis Sendebuffer frei ist │
│echoChar: │
│ MOV SBUF, A ; als Echo wieder zurücksenden │
│ CLR TxFlag │
│ CLR TI ; Sendebuffer voll markieren │
│ POP ACC │
│sintChkTx: │
│ JNB TI, sintRet ; wenn SendeBuffer-leer Interrupt, dann │
│ SETB TxFlag ; Flag setzen, TI danach löschen, damit │
│ CLR TI ; serieller Interrupt wieder frei wird. │
│sintRet: │
│ RETI │
│ │
│init: │
│ MOV SP, #(stack - 1) ; Stack umlegen │
│ MOV IE, #90h ; SINT enablen │
│ MOV SCON, #50h ; serielle Schnittstelle und Timer 1 │
│ MOV TH1, #0FDh ; initialisieren │
│ MOV TMOD, #20h │
│ SETB TR1 │
│ JMP $ ; Warteschleife. Empfang über Interrupt │
│ │
│END │
└──────────────────────────────────────────────────────────────────────┘
In obigen Programm steht die spätere Lage des Code in Programmspeicher
schon während der Assemblierung fest.
Auf der folgenden Seite wird noch ein Programmbeispiel mit relocatablen Co-
de vorgestellt.:
5. Assembler Seite 56
───────────────────────────────────────────────────────────────────────────
┌──────────────────────────────────────────────────────────────────────┐
│idataSeg SEGMENT IDATA ; Namen für relocatable Segmente │
│dataSeg SEGMENT DATA ; werden definiert. │
│codeSeg SEGMENT CODE │
│ │
│ PUBLIC initRingBuf │
│ PUBLIC putCharToBuf, getCharFromBuf │
│ │
│ USING 0 ; Register-Bank 0 reservieren │
│ │
│RSEG dataSeg ; │
│ rdBufPtr: DS 1 ; Lesezeiger auf Ringbuffer │
│ wrBufPtr: DS 1 ; Schreibzeiger auf Ringbuffer │
│ │
│RSEG idataSeg ; einlesen │
│ ringBuf: DS 30 ; 30 Bytes für Ringbuffer │
│ │
│RSEG codeSeg │
│ initRingBuf: ; Initialisierungs-Routine │
│ MOV rdBufPtr, #ringBuf ; Lesezeiger auf Bufferanfang │
│ MOV wrBufPtr, #ringBuf ; Schreibzeiger auf Bufferanfang │
│ RET │
│ │
│ putCharToBuf: ; wrBufPtr zeigt auf den │
│ XCH A, wrBufPtr ; Platz im Buffer, auf │
│ INC A ; dem beim nächstenmal │
│ CJNE A, #(ringBuf+30), noRingBufTop ; geschrieben wird. │
│ MOV A, #ringBuf │
│ noRingBufTop: ; Geschrieben wird ohne │
│ CJNE A, rdBufPtr, leaveRdBufPtr ; Rücksicht, ob schon │
│ INC rdBufPtr │
│ CJNE rdBufPtr, #(ringBuf+30), leaveRdBufPtr │
│ MOV rdBufPtr, #ringBuf ; ausgelesen wurde. │
│ leaveRdBufPtr: ; Wird ein Zeichen nicht │
│ XCH A, wrBufPtr ; rechtzeitig ausgelesen, │
│ MOV @R0, A ; so geht es verloren. │
│ RET │
│ │
│ getCharFromBuf: │
│ MOV A, rdBufPtr ; wenn der Lesezeiger gleich │
│ CJNE A, wrBufPtr, getCharDo ; dem Schreibzeiger ist, ist │
│ SETB C ; der Buffer leer. Dann RET │
│ RET ; mit Carry. │
│ getCharDo: │
│ MOV R0, A │
│ MOV A, @R0 ; Nach dem Auslesen rückt der │
│ INC R0 ; Lesezeiger weiter. │
│ CJNE R0, #(ringBuf+30), leaveRdBufPtrR0 │
│ MOV R0, #ringBuf │
│ leaveRdBufPtrR0: ; Der Lesezeiger zeigt auf das │
│ MOV rdBufPtr, R0 ; nächste auszulesende Byte. │
│ CLR C │
│ RET │
│END │
└──────────────────────────────────────────────────────────────────────┘
5. Assembler Seite 57
───────────────────────────────────────────────────────────────────────────
Obiges Programm ist vollkommen relocateabel. Es kann vom Linker zu einem
anderen Programm dazugelinkt werden. Das Programm kann die hier bereitge-
stellten Routinen zur Verwaltung eines Ringbuffers über CALL's aufrufen. Zu
beachten ist nur, das R0 zerstört wird, das das Zeichen im Akku übergeben
wird und das bein Lesen das Carry gesetzt ist, falls der Buffer leer ist,
also kein Zeichen gelesen werden konnte.
Praktisch werden solche Routinen oft angewandt in Verbindung mit einer
Interrupt-Routine. Zum Beispiel beim seriellen Empfang. In einer
Interrupt-Routine müssen jedoch auf jeden Fall erst alle verwendeten Regi-
ster und die Flags gerettet werden und dann wird das empfangene Zeichen in
einen Ringbuffer eingetragen. Die Procedure putCharToBuf ist entsprechend
zu ergänzen. Das Auslesen des Ringbuffers erfolgt dann durch das Hauptpro-
gramm.
5.5 ASM51 - Bug's:
-------------------
Der ASM51 hat einige Besonderheiten, hier sei davor gewarnt:
* Der ASM51 kennt PCON DATA 87h nicht, obwohl dies zu den SFR des 8051
gehört.
* Einige Editoren setzen asc26 als End-Of-File Kennung an das Ende des
Source-Codes. ASM51 mäkelt dies als Fehler an, arbeitet aber korrekt wei-
ter. Die Fehlermeldung 2 ERRORS FOUND kann bei solchen Files ignoriert
werden.
* Beim Einlesen von Zeichen aus einem File löscht der ASM51 bei jedem Byte
das höchstwertigste Bit. asc8Dh ist also gleich asc0Dh usw. Zeichen grös-
ser asc7Fh können deßhalb nicht in Strings angegeben werden (auch nicht
ö, ä, ü, ß). asc8Dh = 'ì' (auch innerhalb eines Kommentars wird mit End-
Of-Line verwechselt.
* Das Zeichen asc25h = '%' ist beim ASM51 reserviert für die Kennzeichnung
von MACRO's. ES darf nicht in Strings verwendet werden. Der ASM51 macht
aber sogar einen Fehler, wenn ein '%' innerhalb eines Kommentars steht.
Es darf auch nicht das Zeichen ascA5h = 'Ñ' verwendet werden.
* Von Variablen-Namen werden nur die ersten 31 Zeichen gewertet, mehr Zei-
chen werden ignoriert. Dies ist kein Bug, der Programmierer sollte es nur
beachten.
6.1 Interrupt-Logik Seite 58
───────────────────────────────────────────────────────────────────────────
6. weitere Hardwarekomponenten des 8051:
-----------------------------------------
6.1 Interrupt-Logik:
---------------------
Der 8051 besitzt 5 Interrupt-Quellen. Zur Interrupt-Logik gehören die Regi-
ster IE (Interrupt Enable), IP (Interrupt Priority) und einige Flags aus
TCON (Timer-Controll).
Wird eine Interrupt-Bedingung erfüllt und ist der Interrupt enabled, dann
unterbricht die CPU das gerade laufende Programm, schiebt die Return-
Adresse auf den Stack und springt die Interrupt-Routine an. Die Flags wer-
den jedoch nicht automatisch gesichert (wie beim 8088). Jeder Interrupt-
Quelle ist eine feste Einsprungadresse zugeordnet.
EXTI0 CODE 0003h ; externer Interrupt-Eingang _INT0 (P3.2)
Timer0 CODE 000Bh ; Timer 0 Überlauf
EXTI1 CODE 0013h ; externer Interrupt-Eingang _INT1 (P3.3)
Timer1 CODE 001Bh ; Timer 1 Überlauf
SInt CODE 0023h ; serieller Interrupt: Receive (RI)
; oder Transmit (TI)
Die Interrupts können per Software gesperrt werden. Das heißt, sie werden
nur generiert, wenn das entsprechende Bit im Interrupt Enable Register ge-
setzt ist. Zusätzlich muß auch EA = 1 sein. Die Flags im IE-Register können
bitweise adressiert werden. Mit CLR EA können also alle Interrupts gesperrt
werden. Mit SETB EA werden die gesetzten wieder enabled. Nach einem Reset
sind alle Interrupts disabled (IE = 0xx00000b).
┌────┬───┬───┬────┬─────┬─────┬─────┬─────┐
│ EA │ _ │ _ │ ES │ ET1 │ EX1 │ ET0 │ EX0 │ IE DATA 0A8h
└────┴───┴───┴────┴─────┴─────┴─────┴─────┘
EX0 BIT 0A8h ; enabled extern INT0
ET0 BIT 0A9h ; enabled Timer0 Interrupt
EX1 BIT 0AAh ; enabled extern INT1
ET1 BIT 0ABh ; enabled Timer1 Interrupt
ES BIT 0ACh ; enabled seriellen Interrupt
EA BIT 0AFh ; freigeben/sperren aller gesetzten Interrupts
Der 8051 arbeitet mit 2 Interrupt-Prioritätsstufen. Über das Interrupt-
Priority Register (IP) kann jeder Interruptquelle eine Prioritätsstufe zu-
gewiesen werden (1 = high Priority).
┌───┬───┬───┬────┬─────┬─────┬─────┬─────┐
│ _ │ _ │ _ │ PS │ PT1 │ PX1 │ PT0 │ PX0 │ IP DATA 0B8h
└───┴───┴───┴────┴─────┴─────┴─────┴─────┘
PX0 BIT 0B8h ; extern INT0 Priorität
PT0 BIT 0B9h ; Timer0 Interrupt Priorität
PX1 BIT 0BAh ; extern INT1 Priorität
PT1 BIT 0BBh ; Timer1 Interrupt Priorität
PS BIT 0BCh ; seriellen Interrupt Priorität
6.1 Interrupt-Logik Seite 59
───────────────────────────────────────────────────────────────────────────
Ein sich gerade in Bearbeitung befindlicher Interrupt kann nicht durch ei-
nen anderen Interrupt gleicher oder niederer Prioritätsstufe unterbrochen
werden. Wohl aber kann ein Interrupt mit höherer Priorität einen solchen
mit niederer unterbrechen. Der 8051 merkt sich dazu in einem internen Regi-
ster, welche Interrupts in Bearbeitung sind. Dies können maximal 2 gleich-
zeitig sein, da es nur 2 Prioritätsstufen gibt. Bei einem RETI wird der
höchste Interrupt in Service ausgetragen.
Treten zwei Interrupts gleicher Priorität gleichzeitig auf, so geniest EX-
TI0 den Vorzug vor Timer0, vor EXTI1, vor Timer1, vor SINT (diese Reihen-
folge innerhalb einer Prioritätsstufe).
In State 5 eines jeden Zyklus (siehe 3.2 Takt und 3.3 Befehlsausführung)
werden die Interruptbedingungen in die Flags (siehe unten) gelatched. Im
folgenden Zyklus werden diese Bedingungen überprüft (poll). Ist dies der
letzte Zyklus eines Befehls (es gibt Befehle mit 2 Zyklus Dauer) so erfolgt
ein LCALL in die Interrupt-Routine. Wenn nicht, so wird ein Zyklus später
nocheinmal gepolled.
Ein LCALL wird jedoch nicht generiert, wenn der Interrupt disabled ist,
wenn sich ein anderer Interrupt in Bearbeitung (Priorität?) befindet oder
wenn der letzte Befehl ein RETI oder ein Zugriff auf IE oder IP war. Dies
hat mitunter zur Folge, das wenn ein Interrupt in Sevice ist, und ein ande-
rer ansteht, nach dem RETI erst noch mindestens ein Befehl der Hauptroutine
abgearbeitet wird, bevor ein Einsprung in den anstehenden Interrupt er-
folgt.
Vor jedem Poll werden in State 5 des vorigen Zyklus die Interruptbedingun-
gen neu übernommen. Lag ein Interrupt an, war aber gesperrt, und ist nun
wieder weg, so wird er von der CPU nicht angesprungen.
Als Interrupt-Anforderungen dienen folgende Flags:
IE0 (in TCON) extern INT0 Flag
TF0 (in TCON) Timer 0 Überlauf Flag
IE1 (in TCON) extern INT1 Flag
TF1 (in TCON) Timer 1 Überlauf Flag
RI (in SCON) seriell Receive full
TI (in SCON) seriell Transmit empty
Eine Interrupt-Anforderung liegt dann an, wenn das entsprechende Flag ge-
setzt ist. Beim seriellen Interrupt genügt eines der beiden: RI oder TI.
Dafür muß die Software in der Interrupt-Routine prüfen, welches (RI oder TI
oder beide) den Interrupt ausgelöst hat.
RI wird von der Hardware gesetzt, sobald über die serielle Schnittstelle
ein Zeichen empfangen wurde und in SBUF bereitsteht. TI wird von der Hard-
ware gesetzt, sobald beim Senden SBUF wieder leer ist. RI und TI müssen von
der Software zurückgesetzt werden.
TF0 und TF1 werden von der Hardware gesetzt, wenn ein Timer-Überlauf nach 0
erfolgt. Beim Einsprung in die Interrupt-Routine werden TF0 und TF1 automa-
tisch von der Hardware gelöscht.
6.1 Interrupt-Logik Seite 60
───────────────────────────────────────────────────────────────────────────
Bei den externen Interrupts gibt es eine Besonderheit: mit den Flags IT0
bzw. IT1 (in TCON) können sie auf "flankengetriggert" umgeschaltet werden.
Bei Level-Triggerung (IT0 = 0, bzw. IT1 = 0) wird im State 5 eines jeden
Zyklus der invertierte Wert von den Port-Pin's _INT0 = P3.2 bzw.
_INT1 = P3.3 in IE0 bzw. IE1 übernommen. Bei Level-Triggerung hat es somit
keinen Wert, die Flags IE0 und IE1 per Software zu setzen oder zu Löschen.
Die Flags spiegeln immer den Zustand der Eingänge wieder.
Bei Flanken-Triggerung werden die Flags IE0 bzw. IE1 erst dann durch die
Hardware gesetzt, wenn an _INT0 bzw. _INT1 eine negative Flanke auftrat.
Beim Einsprung in die Interrupt-Routine werden IE0 bzw, IE1 automatisch von
der Hardware gelöscht. Sie können aber auch von der Software geändert wer-
den.
TCON beinhaltet verschiedene Flags zur Steuerung der Timer und für die In-
terruptlogik. Die Flags können bitweise adressiert werden:
┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐
│ TF1 │ TR1 │ TF0 │ TR0 │ IE1 │ IT1 │ IE0 │ IT0 │ TCON DATA 88h
└─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘
IT0 BIT 88h ; extern INT0 auf Flanken/_Level Triggerung
IE0 BIT 89h ; extern INT0 Flag
IT1 BIT 8Ah ; extern INT1 auf Flanken/_Level Triggerung
IE1 BIT 8Bh ; extern INT1 Flag
TF0 BIT 8Dh ; Timer 0 Überlauf Flag
TF1 BIT 8Fh ; Timer 1 Überlauf Flag
TR0 BIT 8Ch ; Timer 0 run
TR1 BIT 8Eh ; Timer 1 run
6.2 8051 Timer Seite 61
───────────────────────────────────────────────────────────────────────────
6.2 8051 Timer:
----------------
Der 8051 besitzt zwei Timer/Counter: Timer 0 und Timer 1. Jeder Timer be-
steht aus zwei 8 Bit Registern. Sie können je in 4 verschiedenen Betriebs-
arten geschaltet werden.
Die Timer werden (wenn running) je Zyklus um 1 incrementiert. Sie können
aber auch als Counter verwendet werden, dann werden sie abhängig vom
Count-Eingang T0 bzw. T1 incrementiert.
Die Betriebsart wird mit dem TMOD-Register ausgewählt. TMOD ist nicht bit-
weise adressierbar. Das high Nibble von TMOD wählt die Betriebsart für
Timer 1 und das low Nibble für Timer 0:
┌──────┬─────┬────┬────┬──────┬─────┬────┬────┐
│ GATE │ C/T │ M1 │ M0 │ GATE │ C/T │ M1 │ M0 │ TMOD DATA 89h
└──────┴─────┴────┴────┴──────┴─────┴────┴────┘
C/T = 0 --> Timer Mode (increment um 1 je Zyklus)
C/T = 1 --> Counter Mode (increment um 1 je negativer Flanke am
Port-Pin T0 = P3.4 bzw. T1 = P3.5)
GATE = 0 --> Timer/Counter läuft immer, wenn TRx = 1 (in TCON)
GATE = 1 --> Timer/Counter läuft nur, wenn TRx = 1 und _INTx = 1
der Interrupt-Eingang kann als Gate für den Timer/
Counter dienen.
M1:M0 = 00 --> Mode 0: 13 Bit Timer/Counter
Als Zähler dienen die 8 Bits aus THx und die lowest
5 Bits aus TLx.
M1:M0 = 01 --> Mode 1: 16 Bit Timer/Counter
M1:M0 = 10 --> Mode 2: 8 Bit Timer/Counter mit Auto-Reload
TLx dient als Zähler. Bei jedem Überlauf wird der Wert
aus THx in TLx geladen.
M1:M0 = 11 --> Mode 3: TL0 dient als 8 Bit Timer/Counter. TL0 ver-
wendet die üblichen Controll-Bits von Timer 0.
TH0 dient als weiterer 8 Bit Timer/Counter.
TH0 verwendet die Controll-Bits von Timer 1 (_INT1 als
Gate, T1 als Counteingang, TR1, TF1).
Timer 1 steht, wenn er sich in Mode 3 befindet.
Sobald sich Timer 0 in Mode 3 befindet, kann Timer 1 nicht mehr mit TR1,
T1, _INT1 kontrollieren werden. Ein Timer 1 Überlauf setzt nicht mehr TF1
und kann keinen Interrupt mehr generieren. Timer 1 kann angehalten werden,
indem er ebenfalls in Mode 3 geschaltet wird, sonst läuft er. Obgleich
Timer 1 keine Controll-Bits mehr zugeordnet sind, kann er während Timer 0
im Mode 3 läuft, gut zur Schiebetakt-Erzeugung für die serielle Schnitt-
stelle dienen.
6.3 serielle Schnittstelle Seite 62
───────────────────────────────────────────────────────────────────────────
6.3 serielle Schnittstelle:
----------------------------
Über die serielle Schnittstelle des 8051 kann entweder synchron oder asyn-
chron gesendet und empfangen werden. Im Asynchron Mode kann sogar gleich-
zeitig gesendet und empfangen werden.
Der 8051 besitzt zum Senden und zum Empfang getrennte Register. Die Inhalte
sind voneinander getrennt, jedoch wird für beide dieselbe DATA-Adresse
(SBUF DATA 99h) verwendet.
Das folgende Bild zeigt vereinfacht die Funktion der seriellen Schnitt-
stelle im Asynchron Mode.
| ┌───────────────┐
S |──────────────────────>│ SBUF Tx-Shift ├────────────> TxD
o | └──────┬────────┘
f | ┌─────┴─────┐
t | SysTakt ───>││ ÷ 16 │
w | Timer 1 ───>│├───────────┤
a | Timer 2 ───>││ DPLL ÷ 16 ├───────┐
r | └─────┬─────┘ │
e | ┌─────────┐ ┌──────┴────────┐ │
|<─────│ SBUF Rx ├──────┤ Rx-Shift Reg. ├────┴───────< RxD
| └─────────┘ └───────────────┘
Wird in SBUF (transmit) ein Wert geschrieben, beginnt die Schnittstelle da-
mit, die Daten in TxD zu schieben. Wenn alle Bits gesendet sind, setzt die
Logik das TI Flag (BIT 99h in SCON). Der Schiebetakt kommt von einem inter-
nen 16 Bit- Zähler.
Empfangene Bits werden erst in das Receive Shift Register geschoben. Wenn
alle Bits empfangen wurden, wird das Zeichen in SBUF (receive) übernommen
und RI gesetzt. Der Schiebetakt kommt von einem DPLL, das mit den Empfangs-
daten synchronisiert wird.
Das folgende Bild zeigt vereinfacht die Funktion der seriellen Schnitt-
stelle im Synchron Mode.
| ┌───────────────┐
S |──────────────────────>│ SBUF Tx-Shift ├────┐
o | ┌─────┐ └──────┬────────┘ │
f | SysTakt ──>│ ÷ 6 ├───────────┤ senden │
t | └─────┘ o──────────│──────<> TxD
w | / empfang │
a | │ │
r | ┌─────────┐ ┌──────┴────────┐ │
e |<─────│ SBUF Rx ├──────┤ Rx-Shift Reg. ├────┴──────<> RxD
| └─────────┘ └───────────────┘
Senden-und Empfangs-Shift funktioniert wie beim Asynchron Mode. Jedoch wer-
den die Daten an RxD gesendet und empfangen. An TxD wird der Schiebetakt
gesendet bzw. synchron empfangen.
6.3 serielle Schnittstelle Seite 63
───────────────────────────────────────────────────────────────────────────
6.3.1 Asynchron Mode, Synchron Mode:
-------------------------------------
Beim Synchron Mode wird der Schiebetakt synchron mit den Daten übertragen.
Der Empfänger verwendet also den vom Sender kommenden Schiebetakt. Die
Übertragungslänge ist begrenzt, da bei größeren Entfernungen die Phasenlage
zwischen Daten und Takt abweichen könnte.
Beim Asynchron Mode wird kein Takt übertragen. Der Takt muß im Empfänger
aus den Flanken auf der Datenleitung zurückgewonnen werden. Dazu dient ein
16-stelliges Digital Phase Locked Loop (DPLL). Dies ist ein 16 Bit Zähler,
der mit den Flanken der empfangenen Daten synchronisiert wird:
Bei einer negativen Flanke an RxD wird der Ausgang des DPLL auf 1 ge-
setzt. Dort bleibt er 8 DPLL-Takte, dann geht er für 8 DPLL-Takte auf
0 usw. Jede negative Flanke erzwingt die Synchronisation.
Über eine Universal Asynchron Receive Transmit Schnittstelle (UART), wie
sie beim 8051, beim PC oder einem Terminal definiert ist, werden Daten nur
zeichenweise übertragen. Jedes Zeichen wird eingeleitet durch ein Start-
bit (=0) und abgeschlossen durch 1, 1½ oder 2 Stopbits (=1). Das Startbit
in Verbindung mit dem Stopbit garantiert, das beim Empfang der Beginn eines
Zeichens erkannt und das DPLL synchronisiert wird.
Das LSB (low Bit) wird immer zuerst übertragen. Sender und Empfänger müssen
im allgemeinen nicht in der Anzahl der Stopbits übereinstimmen. Die Baudra-
te sollte so gut übereinstimmen, daß während der Übertragungsdauer eines
Zeichens das DPLL nicht zu weit "wegläuft".
Beispiel für die Übertragung des Zeichens 'A' = asc41h im Asynchron Mode:
───────┐ ┌─────┐ ┌─────┐ ┌─────────
└─────┘ └─────────────────────────────┘ └─────┘
|Start| LSB | | | | | | | MSB |Stop |
6.3.2 8051 seriell Mode Einstellung:
-------------------------------------
Der 8051 kann im Synchron Mode mit maximal 1 MBit/s betrieben werden. Im
Asynchron Mode mit maximal 375 kBit/s
Die serielle Schnittstelle kann in 4 vershiedenen Betriebsarten betrieben
werden. Die Betriebsart wird eingestellt im SCON (Seriell Controll-
Register). SCON ist bitweise addressierbar.
┌─────┬─────┬─────┬─────┬─────┬─────┬────┬────┐
│ SM0 │ SM1 │ SM2 │ REN │ TB8 │ RB8 │ TI │ RI │ SCON DATA 98h
└─────┴─────┴─────┴─────┴─────┴─────┴────┴────┘
6.3 serielle Schnittstelle Seite 64
───────────────────────────────────────────────────────────────────────────
SM0 (BIT 9Fh) und SM1 (BIT 9Eh) dienen zur Modeeinstellung.
SM2 (BIT 9Dh) enabled spezielle Empfangsbedingungen.
REN (BIT 9Ch) ist das Receiver Enable Flag.
TB8 (BIT 9Bh) wird als 9th Bit gesendet.
In RB8 (BIT 9Ah) wird das 9th Bit empfangen.
TI (BIT 99h) ist das Transmit Interrupt Flag.
RI (BIT 98h) ist das Receive Interrupt Flag.
Zur Bedeutung und Anwendung dieser Flags siehe unten.
Mode 0: Synchron-Mode mit 1/12 Oszillatorfrequenz (max. 1 MBit/s):
-------------------------------------------------------------------
SM0:SM1 = 00
Es werden 8 Datenbits (LSB zuerst) über RxD gesendet und empfangen. Der
Schiebetakt wird über TxD gesendet bzw. empfangen. Gleichzeitiger Empfang
und Senden ist nicht möglich.
Der Schiebetakt wird beim sendenden Teilnehmer vom Systemtakt abgeleitet.
Der Schiebetakt entspricht der Zyklusfrequenz (= 1 MHz bei 12 MHz Oszilla-
tor). Wenn SBUF (transmit) wieder leer ist, wird TI gesetzt.
Damit Zeichen empfangen werden können muß der Empfänger enabled sein. Dazu
hat die Software REN = 1 und RI = 0 zu setzen. Das Zeichen wird in einem
internen Schiftregister empfangen. Erst wenn dies voll ist, wird der Wert
in SBUF übertragen und RI gesetzt.
In Mode 0 ist SM2 = 0 zu wählen, TB8 und RB8 sind ohne Bedeutung.
Einzelheiten Mode 0:
--------------------
Der Schiebetakt TxD ist low während State 3, 4 und 5 des System-Taktes
(siehe Kapitel 3.2 und 3.3). Mit der positiven Flanke im Schiebetakt wech-
selt der Wert an RxD, das nächste Bit wird rausgeschoben.
Nachdem ein neues Zeichen zum Senden in SBUF geschrieben wurde, verstreicht
erst ein voller Zyklus, bevor mit dem Shift begonnen wird. Nachdem das
letzte Bit (MSB) des Zeichens hinausgeschoben wurde, wird noch eine 1 hin-
terhergeschoben (damit RxD wieder in Ruhe auf 1 liegt) erst dann wird TI
gesetzt. Seit dem Schreibvorgang in SBUF durch die Software sind also 10
Zyklen verstrichen.
Beim Empfang werden mit der positiven Flanke an TxD die Daten ins Empfangs-
Shiftregister geschoben. In SBUF können zu diesem Zeitpunkt noch alte Daten
stehen. Sie werden erst von der Hardware mit dem neuen Zeichen überschrie-
ben, wenn alle Bits empfangen wurden. Danach wird RI = 0 gesetzt. Ist
REN = 0 (receiver nicht enabled) oder RI = 1 (SBUF noch voll), so werden
ankommende Daten nicht empfangen, sie sind verloren.
6.3 serielle Schnittstelle Seite 65
───────────────────────────────────────────────────────────────────────────
Mode 1: Asynchron-Mode mit variabler Baud Rate als 8 Bit UART:
---------------------------------------------------------------
SM0:SM1 = 01
Es wird kein Takt übertragen, sondern nur die Daten. Gesendet wird über TxD
mit 1 Startbit, den 8 Datenbits (LSB zuerst) und 1 Stopbit. Wenn SBUF
(transmit) wieder leer ist, wird TI gesetzt.
Empfangen wird über RxD. Der Takt wird im Empfänger aus den Flanken der Da-
ten zurückgewonnen. Damit Zeichen empfangen werden können muß REN = 1 und
RI = 0 sein (Receive enable). Ist SM2 = 1, so muß zusätzlich das empfangene
Stopbit = 1, also gültig sein. Das Zeichen wird in einem internen Schiftre-
gister empfangen. Erst wenn dies voll ist, wird der Wert in SBUF übertragen
und RI gesetzt. Das Stopbit wird in RB8 eingetragen.
Einzelheiten Mode 1:
--------------------
Das Senden beginnt mit der ersten Überlauf des Taktgenerators nachdem ein
Zeichen in SBUf geschrieben wurde. Zuerst wird als Startbit ein 0 gesendet,
dann folgen die 8 Bits des Zeichens (LSB zuerst). Als letztes wird eine 1
als Stopbit gesendet. Erst dann wird TI = 1.
Der Empfang beginnt mit der ersten negativen Flanke an RxD. Das Startbit
wird im Zustand 7, 8 und 9 des Schiebetakt-Zählers abgetastet. Es muß immer
0 sein (zur Datensicherheit, um Störungen auszublenden). Ist dies nicht der
Fall, oder ist nicht REN = 1, so wird der Empfang abgebrochen und die Logik
geht wieder in den Grundzustand (warten auf negative Flanke).
Nach Erkennen des Startbits und REN = =1, werden die ankommenden Bits ins
Receive-Shift Register geschoben. Zu diesem Zeitpunkt können in SBUF noch
alte Daten stehen, RI braucht noch nicht 0 zu sein. Der Empfang ist beendet
mit Empfang des Stopbits. Nur wenn jetzt folgende Bedingungen erfüllt sind,
wird das Zeichen in SBUF übernommen, RI = 1 gesetzt und das Stopbit in RB8
geschrieben:
RI = 0 und SM2 = 0
oder RI = 0 und SM2 = 1 und Stopbit = 1.
Mode 1 Schiebetakt:
-------------------
Der Schiebetakt wird zum Senden aus einem 16 stelligen Zähler abgeleitet,
der vom Timer1-Überlauf getriggert wird. Der Schiebetakt für den Empfang
wird aus einem DPLL abgeleitet, das ebenfalls vom Timer1-Überlauf getrig-
gert wird. Wird das Bit SMOD in PCON gelöscht (SMOD = 0), so wird der
Timer1-Übertrag zuvor zusätzlich durch 2 geteilt. Der Schiebetakt ergibt
sich nach folgender Formel :
Oszi 1 SMOD + 1 1 Oszi * (SMOD + 1)
BaudRate = ──── * ────────── * ──────── * ──── = ──────────────────
12 100h - TH1 2 16 384 * (100h - TH1)
│ │ │ └─── 16 Bit-Zähler / DPLL
│ │ └─────────── Vorteiler durch 2
│ └─────────────────────── Zählwert Timer 1
└──────────────────────────────── Trigger für Timer je Zyklus
6.3 serielle Schnittstelle Seite 66
───────────────────────────────────────────────────────────────────────────
Beim 8052 kann alternativ der Timer2-Überlauf gewählt werden um den Schie-
betaktzähler und/oder das DPLL zu triggern. Soll zum Senden Timer2 verwen-
det werden ist Bit TCLK in T2CON zu setzen (siehe 8052 Timer). Soll zum
Empfang Timer2 verwendet werden, ist Bit RCLK in T2CON zu setzen. Sender
und Empfänger können so mit verschiedene Baudraten laufen (einer mit Timer1
und der andere mit Timer2), sie können aber auch gleich sein: beide aus Ti-
mer1 oder beide aus Timer2.
Mode 2: Asynchron-Mode mit fester Baud Rate als 9 Bit UART:
------------------------------------------------------------
SM0:SM1 = 10
Es wird kein Takt übertragen, sondern nur die Daten. Gesendet wird über TxD
mit 1 Startbit, den 8 Datenbits (LSB zuerst) das Bit aus TB8 und 1 Stopbit.
Wenn SBUF (transmit) wieder leer ist, wird TI gesetzt.
Empfangen wird über RxD. Der Takt wird im Empfänger aus den Flanken der Da-
ten zurückgewonnen. Damit Zeichen empfangen werden können muß REN = 1 und
RI = 0 sein (Receive enable). Ist SM2 = 1, so muß zusätzlich das empfangene
9th Bit = 1 sein. Das Zeichen wird in einem internen Schiftregister empfan-
gen. Erst wenn dies voll ist, wird der Wert in SBUF übertragen und RI ge-
setzt. Das Stopbit wird in RB8 eingetragen.
Einzelheiten Mode 2:
--------------------
Das Senden beginnt mit der ersten Überlauf des Taktgenerators nachdem ein
Zeichen in SBUf geschrieben wurde. Zuerst wird als Startbit ein 0 gesendet,
dann folgen die 8 Bits des Zeichens (LSB zuerst). Danach wird das Bit aus
TB8 gesendet und schließlich eine 1 als Stopbit. Erst dann wird TI = 1.
Der Empfang beginnt mit der ersten negativen Flanke an RxD. Das Startbit
wird im Zustand 7, 8 und 9 des Schiebetakt-Zählers abgetastet. Es muß immer
0 sein (zur Datensicherheit, um Störungen auszublenden). Ist dies nicht der
Fall, oder ist nicht REN = 1, so wird der Empfang abgebrochen und die Logik
geht wieder in den Grundzustand (warten auf negative Flanke).
Nach Erkennen des Startbits und REN = =1, werden die ankommenden Bits ins
Receive-Shift Register geschoben. Zu diesem Zeitpunkt können in SBUF noch
alte Daten stehen, RI braucht noch nicht 0 zu sein. Der Empfang ist beendet
mit Empfang des Stopbits. Nur wenn jetzt folgende Bedingungen erfüllt sind,
wird das Zeichen in SBUF übernommen, RI = 1 gesetzt und das 9th Bit in RB8
geschrieben:
RI = 0 und SM2 = 0
oder RI = 0 und SM2 = 1 und 9th Bit = 1.
6.3 serielle Schnittstelle Seite 67
───────────────────────────────────────────────────────────────────────────
Mode 2 Schiebetakt:
-------------------
Der Schiebetakt wird zum Senden/Empfang aus einem 16 stelligen Zähler / dem
DPLL abgeleitet, der/das vom Systemtakt (= ½ Oszillatortakt, max 6 MHz bei
12 MHz Quarz) getriggert wird. Wird das Bit SMOD in PCON gelöscht
(SMOD = 0), so wird der Timer1-Übertrag zuvor zusätzlich durch 2 geteilt.
Der Schiebetakt ergibt sich nach folgender Formel:
Oszi SMOD + 1 1 Oszi * (SMOD + 1)
BaudRate = ──── * ──────── * ──── = ──────────────────
2 2 16 64
│ │ └─── 16 Bit-Zähler / DPLL
│ └──────────── Vorteiler durch 2
└───────────────────── System-Takt
Mode 3: Asynchron-Mode mit variabler Baud Rate als 9 Bit UART:
---------------------------------------------------------------
SM0:SM1 = 11
Die Übertragung erfolgt wie in Mode 2, jedoch wird der Schiebetakt gebildet
wie in Mode 1.
In Mode 2 und 3 kann mit Hilfe von RB8, TB8 und SM2 ein Feldbussystem auf-
gebaut werden. Es können mehrere 8051-Rechner an einer Busleitung betrieben
werden. Ein geeignetes Protokoll (z.B. Master-Slave) ist erforderlich. Ein
Teilnehmer, des sein SM2 = 0 gesetzt hat, empfängt alle über den Bus gesen-
deten Zeichen. Ein Teilnehmer, der sein SM2 = 1 gesetzt hat, empfängt nur
solche Zeichen die mit TB8 = 1 gesendet wurden. So können zwei Teilnehmer
miteinander kommunizieren, ohne die anderen zu stören.
6.3.3 Interrupt Mode oder Polled Mode:
---------------------------------------
Beim Senden über die serielle Schnittstelle darf erst dann das nächste Zei-
chen in SBUF geschrieben werden, wenn das alte Zeichen draußen ist. Sobald
SBUF (transmit) leer ist, setzt dazu die Hardware das TI-Flag. Der Anwender
darf erst dann das nächste Zeichen in SBUF schreibt, wenn TI = 1 ist.
Sobald über die serielle Schnittstelle ein Zeichen empfangen wurde, setzt
die Hardware das RI-Flag. Nur wenn RI = 1 ist, steht ein gültiges Zeichen
in SBUF (receive).
Die Hardware setzt nur RI und TI, löscht sie aber nicht. Nach einem Reset
sind jedoch RI, TI wie auch REN gelöscht (SCON = 0).
6.3 serielle Schnittstelle Seite 68
───────────────────────────────────────────────────────────────────────────
Polled Mode:
------------
Damit die serielle Schnittstelle korrekt funktioniert, ist von der Software
folgendes zu beachten:
- nur in SBUF schreiben, wenn TI = 1 ist.
- nachdem in SBUF geschrieben wurde TI löschen.
- vor dem Lesen von SBUF RI prüfen.
- nach dem Auslesen von SBUF RI löschen.
Wird so vorgegangen, stellt sich folgendes Problem: Nach einem Reset ist
TI = 0 obwohl SBUF (transmit) leer ist. Es wird von der Hardware nie ge-
setzt, da nicht gesendet wird, als nie der Zustand Senden-Ende auftritt.
Das erste zu sendende Zeichen darf also in SBUF geschrieben werden, obwohl
TI = 0 ist. Eleganter wird gleich nach einem Reset TI = 1 gesetzt.
Beispiel für serielle Übertragung im Polled Mode:
-------------------------------------------------
iniSCON:
CLR ES ; seriellen Interrupt löschen
CLR ET1 ; Timer 1 Interrupt löschen
MOV TH1, #-3 ; Vorladewert für Timer 1 = -3
MOV TL1, TH1
SETB TR1 ; Timer 1 starten
ANL PCON, #7Fh ; SMOD = 0
MOV SCON, #52h ; Mode 1, SM2=0,TB8=0,RB8=0, TI=1, REN=1,RI=0
RET
sendeChar:
JNB TI, sendeChar ; warte bis SBUF (transmit) leer ist
MOV SBUF, A
CLR TI
RET
empfangeChar:
JNB RI, empfangeChar
MOV A, SBUF
CLR RI
RET
nach einem Reset wird die serielle Schnittstelle wie oben initialisiert für
8 Bit UART mit 9600 Baud (mit 11,059 MHz Quarz). Der Receiver wird enabled
und TI=1 gesetzt.
Beim Senden wird jedesmal gewartet, bis SBUF leer ist. Dann wird das beim
Unterprogrammaufruf im Akku übergebene Zeichen in SBUF geschrieben. TI muß
von der Software gelöscht werden.
Beim Empfang wird hier auch gewartet, bis ein Zeichen empfangen wurde. Hat
die CPU nichts weiteres zu tun, ist das ok. Denn das Programm bleibt hier
hängen, wenn keine Zeichen empfangen werden.
6.3 serielle Schnittstelle Seite 69
───────────────────────────────────────────────────────────────────────────
Interrupt Mode:
---------------
Bei umfangreicheren Programmen, kann oft nur zu bestimmten Zeiten der se-
rielle Empfänger gepolled werden, falls von der CPU noch andere Routinen
bearbeitet werden müssen. Wird nicht genügend oft gepolled, so können Daten
verloren gehen. Manchmal ist es auch nicht zulässig, zu warten, bis ein
Zeichen gesendet werden kann. Als Lösung kann die serielle Schnittstelle im
Interrupt-Mode betrieben werden.
Von der Hardware wird bei gesetzten RI- oder TI-Flag ein Interrupt gene-
riert. Dazu muß der Interrupt enabled sein (Bit ES = 1). Beim Einsprung in
die Interrupt-Routine (bei CODE 0023h) werden RI und TI nicht von der Hard-
ware gelöscht. Sie sind innerhalb der Interrupt-Routine von der Software zu
löschen, sonst wird nach dem RETI die Interrupt-Routine gleich wieder ange-
sprungen.
Im Normalzustand ist der Sendebuffer leer. Wird mit Interrupts gearbeitet,
darf jedoch TI nicht stehen gelassen werden. Als Kennung für Sende-Buffer
leer muß ein extra Flag definiert werden.
Beispiel für serielle Übertragung im Interrupt Mode:
----------------------------------------------------
TxEmpty BIT 0 ; als Beispiel wird die Schnittstelle
; in asynchron Mode (Mode 2) betrieben.
iniSCON: ; Der Empfang erfolgt über Interrupt.
SETB TxEmpty ; Senden im Polled Mode. (TxEmpty-Flag)
ORL PCON, #80h ; SMOD = 1, damit 375 kBit/s bei 12 MHz Quarz
MOV SCON, #98h ; SM2=0, REN=1 und RI=0 enablen für Receive
SETB ES ; TI wird gelöscht, dafür wird das Flag
SETB EA ; TxEmpty gesetzt.
RET ; Die Interrupt-Routine liest das Zeichen
; aus SBUF und trägt es in einen Ringbuffer
CSEG AT 0023h ; ein, von wo es vom Hauptprogramm abgeholt
JNB RI, chkTxInt ; wird. Es könnte an dieser Stelle auch eine
PUSH PSW ; Befehlsdispatcher-Routine oder ähnliches
PUSH ACC ; angesprungen werden. Ein Beispiel für eine
MOV A, R0 ; Zeicheneintrags-Routine (Ringbuffer) steht
PUSH ACC ; in Kapitel 5.4.
MOV A, SBUF ; Die Interrupt-Routine muß auf jeden Fall
CLR RI ; die verwendeten Register und die Flags
CALL putCharToBuf ; sichern (hier ACC, R0 und PSW).
POP ACC ; Ist TI gesetzt, wird TxEmpty gesetzt. TI
MOV R0, A ; wird dafür gelöscht. Die Sende-Routine
POP ACC ; polled TxEmpty anstelle TI.
POP PSW ; Achtung: Vorsicht ist geboten, wenn die
chkTxInt: ; Sende-Routine Teil einer Interrupt-Routine
JNB TI, sintRet ; ist. Dann sind beide TI und TxEmpty zu
SETB TxEmpty ; pollen, da kein Interrupt durchkommt und
CLR TI ; so TxEmpty nicht gesetzt würde.
sintRet:
RETI
6.3 serielle Schnittstelle Seite 70
───────────────────────────────────────────────────────────────────────────
6.3.4 Baud-Rate Tabelle:
-------------------------
Diese Tabellen enthalten die Einstellwerte zur Erzielung einer bestimmten
Baud-Rate. Gleichungen siehe Kapitel 6.3.2
Quarz: SM0:SM1 SMOD TH1 BaudRate
------------------------------------- Mit einem 12 MHz Quarz lassen
12 MHz 00 X X 1 MBit/s sich die meißten der üblichen
12 MHz 10 1 X 375 kBit/s Baud-Raten nicht exakt ein-
12 MHz 10 0 X 187.5 kBit/s stellen.
12 MHz 01 1 -1 62 500 Bit/s
12 MHz 01 1 -3 20 800 Bit/s Da das DPLL jedoch maximal
12 MHz 01 1 -7 8 929 Bit/s über 10 Bitzellen die Phasen-
12 MHz 01 1 -14 4 808 Bit/s lage halten muß reicht die
12 MHz 01 1 -26 2 404 Bit/s Genauigkeit für Baudraten
12 MHz 01 1 -52 1 202 Bit/s bis 4800 Baud sicher aus.
12 MHz 01 1 -104 601 Bit/s
12 MHz 01 1 -208 300 Bit/s
12 MHz 01 0 -208 150 Bit/s BR in Mode 01 und 11 gleich
Quarz: SM0:SM1 SMOD TH1 BaudRate
----------------------------------------- Mit einem speziellen Quarz
11.0592 MHz 01 1 -1 57 600 Bit/s lassen sich BaudRaten bis
11.0592 MHz 01 1 -3 19 200 Bit/s 19200 Baud exakt einstellen.
11.0592 MHz 01 1 -6 9 600 Bit/s Jedoch arbeitet die CPU nun
11.0592 MHz 01 1 -12 4 800 Bit/s nicht mit der maximalen
11.0592 MHz 01 1 -24 2 400 Bit/s Frequenz und die Zeitbasis
11.0592 MHz 01 1 -48 1 200 Bit/s von sonst 1 µs für einen
11.0592 MHz 01 1 -96 600 Bit/s Zyklus geht verloren.
11.0592 MHz 01 1 -192 300 Bit/s
11.0592 MHz 01 0 -192 150 Bit/s
Niederere BaudRaten als 150 Bit/s lassen sich so (mit diesen Quarzen) nicht
erzielen. Es bleibt die Möglichkeit an T1 einen Takt einzuspeisen und
Timer 1 als Counter zu betreiben. Dabei ergibt sich folgende BaudRate:
Count * (SMOD + 1)
BaudRate = ───────────────────
32 * (100h - TH1)
Eine weitere Möglichkeit zur Erzielung niederer BaudRaten besteht darin,
Timer 1 als 16 Bit Timer zu betreiben. Es wird dazu eine Timer 1
Interrupt-Routine installiert, die einen 16 Bit Nachladewert in TH1:TL1
schreibt.
Beim 8052 lassen sich Baudraten exakter mit Timer 2 einstellen (siehe Kapi-
tel 7.1).
7.1 der 8052 Seite 71
───────────────────────────────────────────────────────────────────────────
7. weitere Controller der 8051-Familie:
----------------------------------------
7.1 der 8052:
--------------
Gegenüber dem 8051 besitzt der 8052 ein größeres internes RAM, ein größeres
internes ROM und einen weiteren Timer, sonst ist er identisch mit dem 8051.
internes RAM 8051: 128 Byte 00....7Fh
internes RAM 8052: 256 Byte 00...0FFh
internes ROM 8051: 4 kByte 0000...0FFFh ; 8031 und 8032 besitzen
internes ROM 8052: 8 kByte 0000...1FFFh ; kein internes ROM
; 8751 und 8752 besitzen
; ein internes EPROM
internes RAM:
-------------
Bei einem Stacküberlauf (SP über 0FFh), Wird der SP weiterhin incremen-
tiert. Dabei zeigt er auf die intern RAM-Adressen ab 00. Der Stack über-
schriebe somit den Registerbereich. Es ist also Sorge dafür zu tragen, daß
kein Stacküberlauf stattfindet.
Die intern RAM-Adressen 80h...FFh können nicht direkt adressiert werden.
Auf diesen Bereich kann nur indirekt zugegriffen werden (MOV @Ri, ..).
(vergleiche auch Kapitel 3.9)
7.1.2 zusätzliche SFR des 8052:
--------------------------------
Zur Steuerung des Timer2 sind beim 8052 einige zusätzliche SFR vorhanden:
T2CON DATA 0C8h
RCAP2L DATA 0CAh
RCAP2H DATA 0CBh
TL2 DATA 0CCh
TH2 DATA 0CDh
7.1.3 Timer 2:
---------------
Timer2 ist ein 16 stelliger Zähler. Sein Zählregister wird gebildet aus
TH2:TL2 (Timer2 High- und Timer2 Low-Byte). T2CON (Timer2 Control) dient
zur Steuerung des Timers/Zählers.
7.1 der 8052 Seite 72
───────────────────────────────────────────────────────────────────────────
Bei einem Zählerüberlauf kann ein Interrupt generiert werden. Die Ein-
sprungadresse ist 002Bh. Dazu sind folgende Bits im Interrupt-Enable und
Interrupt-Priority Register definiert:
ET2 BIT 0ADh
PT2 BIT 0BDh
Timer2 CODE 002Bh
Timer2 läuft als 16 Bit Zähler/Timer wahlweise mit Reload oder Capture
Funktionen. Dazu besitzt Timer2 neben dem 16 Bit Zählregister (TH2:TL2) ein
Capture/Reload Register (RCAP2H:RCAP2L).
Im Reload Mode wird bei einem Überlauf oder bei einem externen Trigger an
Port1-Eingangspin T2EX der Wert aus RCAP2 in das Zählregister geladen.
Im Capture Mode wird bei einem externen Trigger der Wert aus dem Zählregi-
ster in RCAP2 geladen.
Wird Timer2 als Timer betrieben, so wird TH2:TL2 je CPU-Zyklus um 1 incre-
mentiert (Ausnahme bei Baud-Rate Generator siehe RCLK, TCLK). Beim Betrieb
als Counter wird er je negative Flanke an Port1-Eingangspin T2 incremen-
tiert.
T2EX BIT 091h ; = P1.1
T2 BIT 090h ; = P1.0
┌─────┬──────┬──────┬──────┬───────┬─────┬──────┬────────┐
│ TF2 │ EXF2 │ RCLK │ TCLK │ EXEN2 │ TR2 │ C_T2 │ CP_RL2 │ T2CON DATA 0C8h
└─────┴──────┴──────┴──────┴───────┴─────┴──────┴────────┘
CP_RL2 BIT 0C8h --> Mit CP_RL2 = 1 wird Capture Mode gewählt, sonst Re-
load Mode. Reload Mode wird auch gewählt, wenn Timer2 als Baud-Rate Genera-
tor für die serielle Schnittstelle verwendet wird. (siehe acuh EXEN2, RCLK
und TCLK).
C_T2 BIT 0C9h --> Mit C_T2 = 1 wird Timer2 als Counter betrieben, sonst
als Timer.
TR2 BIT 0CAh --> Timer2 Run Flag
EXEN2 BIT 0CBh --> Der externe Trigger Eingangspin T2EX ist nur aktiv,
mit EXEN2 = 1. Nur dann erfolgt bei einer negativen Flanke an T2EX ein
Übertrag in RCAP2 (im Capture Mode) bzw. eine Reload (in Reload Mode).
TCLK BIT 0CCh --> Ist TCLK gesetzt, dient der Timer2-Überlauf als Trig-
ger für den Baud-Rate Generator der seriellen Schnittstelle beim Sende.
7.1 der 8052 Seite 73
───────────────────────────────────────────────────────────────────────────
RCLK BIT 0CDh --> Ist RCLK gesetzt, dient der Timer2-Überlauf als Trig-
ger für den Baud-Rate Generator (DPLL) der seriellen Schnittstelle beim
Empfang.
EXF2 BIT 0CEh --> Extern Trigger Interrupt Flag. EXF2 wird von der Hard-
ware gesetzt, nachdem aufgrund einer negativen Flanke an T2EX ein Übertrag
erfolgte (also nur, wenn EXEN2 = 1). EXF2 muß von der Software gelöscht
werden.
TF2 BIT 0CFh --> Timer2 Überlauf Interrupt Flag. TF2 wird von der Hard-
ware gesetzt, nachdem ein Überlauf von 0FFFFh nach 0000 in TH2:TL2 erfolg-
te. TF2 muß von der Software gelöscht werden.
Timer2 Interrupt:
-----------------
Durch den Timer2 wird ein Interrupt ausgelöst, wenn TF2 oder EXF2 gesetzte
sind. Es genügt, das nur eines von beiden gesetzt ist. Die CPU rettet die
Return-Adresse auf den Stack und springt CODE 002Bh an. Beim Einsprung in
die Interrupt-Routine bleiben TF2 und EXF2 erhalten, denn nur so kann in
der Interrupt-Routine festgestellt werden, welche Ursache der Interrupt
hat:
TF2 --> Timer2 Überlauf
oder EXT2 --> externer Trigger und Übertrag
TF2 und EXF" müssen von der Software (in der Interrupt-Routine ) gelöscht
werden. Ansonsten erfolgte nach einem RETI ein erneuter Einsprung in die
Interrupt-Routine.
TF2 und/oder EXF2 könnten auch per Software gelöscht oder gesetzt werden.
Wird eines: EXF2 oder TF2 nicht benutzt, so kann dies zum Erzeugen eines
Software-Interrupts mißbraucht werden.
7.1.4 serielle Schnittstelle mit Timer2:
-----------------------------------------
Ist eines von TCLK und RCLK gesetzt, so wird Timer2 als Trigger für minde-
stens einen der Baud-Rate Generatoren (Senden oder Empfang oder beide) der
seriellen Schnittstelle verwendet. Timer2 läuft dann im Reload Mode (unab-
hängig von CP_RL2). Timer2 wird in dieser Betriebsart nicht je CPU-Zyklus
einmal incrementiert, sondern je System Takt (Quarztakt / 2) einmal. Er
läuft also schneller.
Voraussetzung für die Erzeugung des seriellen Schiebetaktes mit Timer 2
ist, daß die serielle Schnittstelle in Mode 1 (SM0:SM1 = 01) oder Mode 3
(SM0:SM1 = 11) betrieben wird. Default wird dann der Timer1-Überlauf zum
Triggern des Baud-Rate Generators verwendet. Ist TCLK oder/und RCLK ge-
setzt, wird zum Senden bzw. Empfang der Timer2-Überlauf verwendet.
TCLK = 0 RCLK = 0 --> Timer1 für Senden und Empfang
TCLK = 0 RCLK = 1 --> Timer1 zum Senden und Timer2 zum Empfang
TCLK = 1 RCLK = 0 --> Timer2 zum Senden und Timer1 zum Empfang
TCLK = 1 RCLK = 1 --> Timer2 zum Senden und Empfang.
7.1 der 8052 Seite 74
───────────────────────────────────────────────────────────────────────────
Mit Verwendung von Timer2 ergibt sich folgende Baud-Rate:
Oszi 1 1
BaudRate = ──── * ────────────── * ──── ; RCAP2 = RCAP2H:RCAP2L
2 10000h - RCAP2 16
│ │ └─ 16 Bit-Zähler / DPLL
│ └─────────────── Zählwert Timer 2
└───────────────────────────── Trigger für Timer je SystemTakt
Baud-Rate Tabelle:
------------------
Diese Tabelle enthält die Einstellwerte zur Erzielung einer bestimmten
Baud-Rate. Vergleiche auch Kapitel 6.3.4. Hier SM0:SM1 = 01 oder 11.
Quarz: RCAP2 BaudRate
------------------------------
12 MHz -1 375 kBit/s Mit einem 12 MHz Quarz lassen sich
12 MHz -2 187,5 kBit/s auch hier nicht alle üblichen Baud-
12 MHz -3 125 kBit/s Raten exakt einstellen
12 MHz -4 93750 Bit/s
12 MHz -5 75000 Bit/s
12 MHz -6 62500 Bit/s 11,0592 MHz -3 115200 Bit/s
12 MHz -7 53571 Bit/s 11,0592 MHz -6 57600 Bit/s
12 MHz -9 41667 Bit/s
12 MHz -10 37500 Bit/s 11,0592 MHz -9 38400 Bit/s
12 MHz -20 18750 Bit/s 11,0592 MHz -18 19200 Bit/s
12 MHz -39 9615 Bit/s 11,0592 MHz -36 9600 Bit/s
12 MHz -78 4808 Bit/s 11,0592 MHz -72 4800 Bit/s
12 MHz -156 2404 Bit/s 11,0592 MHz -144 2400 Bit/s
12 MHz -312 1202 Bit/s 11,0592 MHz -288 1200 Bit/s
12 MHz -625 600 Bit/s
12 MHz -1250 300 Bit/s
12 MHz -2500 150 Bit/s
12 MHz -3409 110 Bit/s
12 MHz -5000 75 Bit/s
Einstellbar bei PC:
115200, 57600, 38400, 28800, 23040, 19200, 16457,
Bei einem Assembler kann eine negative Zahl angegeben werden. Es kann aber
auch umgerechnet werden: -1 = 0FFFFh, -2 = 0FFFEh ....
Beispiel für 1200 Baud mit Timer2:
CLR ES ; seriellen Interrupt löschen
CLR ET2 ; Timer 2 Interrupt löschen
MOV RCAP2H, #HIGH(-312) ; Vorladewert für Timer 2
MOV RCAP2L, #LOW(-312)
MOV TH2, RCAP2H
MOV TL2, RCAP2L
MOV T2CON, #00110100b ; RCLK, TCLK, TR2
MOV SCON, #52h ; Mode 1, SM2=0,TB8=0,RB8=0, TI=1, REN=1,RI=0
A.1 ASCII-Tabelle Seite 75
───────────────────────────────────────────────────────────────────────────
A.1 ASCII-Tabelle:
-------------------
Die folgende Seite zeigt die Zuordnung von Zeichen zu Zahlenwerten (ASCII-
Tabele):
Die Zeichen unter 20h (Leerzeichen) sind im allgemeinen nicht druckbar,
auch werden sie von einigen Editoren und Textbearbeitungssystemen nicht
dargestellt. Sie dienen dort wie auch bei der Datenübertragung oft als
Steuerzeichen. Die wichtigsten:
BEL = Klingelzeichen (bell)
BS = rückwärts löschen (Back Space)
HT = horizontal TAB
VT = vertikal Tab
CR = an Zeilenanfang (Carriage Return)
LF = Zeilenvorschub (Line Feed)
FF = neue Seite (Form Feed)
Den Zeichen unter 20h ist zwar auch ein grafisches Zeichen zugeordnet, in
dieser Tabelle wurde aber nicht das Zeichen dargestellt, sondern die Bedeu-
tung für die Datenübertragung angegeben (denn auf vielen Editoren würde es
ohnehin Probleme geben die Darstellung zu betrachten).
A.1 ASCII-Tabelle Seite 76
───────────────────────────────────────────────────────────────────────────
NUL 0 00 BEL 7 07 SO 14 0E NAK 21 15 FS 28 1C
SOH 1 01 BS 8 08 SI 15 0F SYN 22 16 GS 29 1D
STX 2 02 HT 9 09 DLE 16 10 ETB 23 17 RS 30 1E
ETX 3 03 LF 10 0A DC1 17 11 CAN 24 18 US 31 1F
EOT 4 04 VT 11 0B DC2 18 12 EM 25 19
ENQ 5 05 FF 12 0C DC3 19 13 SUB 26 1A
ACK 6 06 CR 13 0D DC4 20 14 ESC 27 1B
32 20 0 48 30 @ 64 40 P 80 50 ` 96 60
! 33 21 1 49 31 A 65 41 Q 81 51 a 97 61
" 34 22 2 50 32 B 66 42 R 82 52 b 98 62
# 35 23 3 51 33 C 67 43 S 83 53 c 99 63
$ 36 24 4 52 34 D 68 44 T 84 54 d 100 64
% 37 25 5 53 35 E 69 45 U 85 55 e 101 65
& 38 26 6 54 36 F 70 46 V 86 56 f 102 66
' 39 27 7 55 37 G 71 47 W 87 57 g 103 67
( 40 28 8 56 38 H 72 48 X 88 58 h 104 68
) 41 29 9 57 39 I 73 49 Y 89 59 i 105 69
* 42 2A : 58 3A J 74 4A Z 90 5A j 106 6A
+ 43 2B ; 59 3B K 75 4B [ 91 5B k 107 6B
, 44 2C < 60 3C L 76 4C \ 92 5C l 108 6C
- 45 2D = 61 3D M 77 4D ] 93 5D m 109 6D
. 46 2E > 62 3E N 78 4E ^ 94 5E n 110 6E
/ 47 2F ? 63 3F O 79 4F _ 95 5F o 111 6F
p 112 70 Ç 128 80 É 144 90 á 160 A0 ░ 176 B0
q 113 71 ü 129 81 æ 145 91 í 161 A1 ▒ 177 B1
r 114 72 é 130 82 Æ 146 92 ó 162 A2 ▓ 178 B2
s 115 73 â 131 83 ô 147 93 ú 163 A3 │ 179 B3
t 116 74 ä 132 84 ö 148 94 ñ 164 A4 ┤ 180 B4
u 117 75 à 133 85 ò 149 95 Ñ 165 A5 ╡ 181 B5
v 118 76 å 134 86 û 150 96 ª 166 A6 ╢ 182 B6
w 119 77 ç 135 87 ù 151 97 º 167 A7 ╖ 183 B7
x 120 78 ê 136 88 ÿ 152 98 ¿ 168 A8 ╕ 184 B8
y 121 79 ë 137 89 Ö 153 99 ⌐ 169 A9 ╣ 185 B9
z 122 7A è 138 8A Ü 154 9A ¬ 170 AA ║ 186 BA
{ 123 7B ï 139 8B ¢ 155 9B ½ 171 AB ╗ 187 BB
| 124 7C î 140 8C £ 156 9C ¼ 172 AC ╝ 188 BC
} 125 7D ì 141 8D ¥ 157 9D ¡ 173 AD ╜ 189 BD
~ 126 7E Ä 142 8E ₧ 158 9E « 174 AE ╛ 190 BE
127 7F Å 143 8F ƒ 159 9F » 175 AF ┐ 191 BF
└ 192 C0 ╨ 208 D0 α 224 E0 ≡ 240 F0
┴ 193 C1 ╤ 209 D1 ß 225 E1 ± 241 F1 ASCII-Tabelle
┬ 194 C2 ╥ 210 D2 Γ 226 E2 ≥ 242 F2 -------------
├ 195 C3 ╙ 211 D3 π 227 E3 ≤ 243 F3 links: Zeichen
─ 196 C4 ╘ 212 D4 Σ 228 E4 ⌠ 244 F4 mitte: Dezimal
┼ 197 C5 ╒ 213 D5 σ 229 E5 ⌡ 245 F5 rechts: Hexa-
╞ 198 C6 ╓ 214 D6 µ 230 E6 ÷ 246 F6 dezimal
╟ 199 C7 ╫ 215 D7 τ 231 E7 ≈ 247 F7
╚ 200 C8 ╪ 216 D8 Φ 232 E8 ° 248 F8
╔ 201 C9 ┘ 217 D9 Θ 233 E9 ∙ 249 F9
╩ 202 CA ┌ 218 DA Ω 234 EA · 250 FA
╦ 203 CB █ 219 DB δ 235 EB √ 251 FB
╠ 204 CC ▄ 220 DC ∞ 236 EC ⁿ 252 FC
═ 205 CD ▌ 221 DD φ 237 ED ² 253 FD
╬ 206 CE ▐ 222 DE ε 238 EE ■ 254 FE
╧ 207 CF ▀ 223 DF ∩ 239 EF 255 FF
A.2 Befehlssatz Seite 77
───────────────────────────────────────────────────────────────────────────
A.2 Befehlssatz in Hexadezimaler Reihenfolge:
----------------------------------------------
Code Bytes Zyklus Mnemonic
00 1 1 NOP 30 3 2 JNB bitAdr, codeAdr
01 2 2 AJMP codeAdr 31 2 2 ACALL codeAdr
02 3 2 LJMP codeAdr 32 1 2 RETI
03 1 1 RR A 33 1 1 RLC A
04 1 1 INC A 34 2 1 ADDC A, #wert
05 2 1 INC dataAdr 35 2 1 ADDC A, dataAdr
06 1 1 INC @R0 36 1 1 ADDC A, @R0
07 1 1 INC @R1 37 1 1 ADDC A, @R1
08 1 1 INC R0 38 1 1 ADDC A, R0
09 1 1 INC R1 39 1 1 ADDC A, R1
0A 1 1 INC R2 3A 1 1 ADDC A, R2
0B 1 1 INC R3 3B 1 1 ADDC A, R3
0C 1 1 INC R4 3C 1 1 ADDC A, R4
0D 1 1 INC R5 3D 1 1 ADDC A, R5
0E 1 1 INC R6 3E 1 1 ADDC A, R6
0F 1 1 INC R7 3F 1 1 ADDC A, R7
10 3 2 JBC bitAdr, codeAdr 40 2 2 JC codeAdr
11 2 2 ACALL codeAdr 41 2 2 AJMP codeAdr
12 3 2 LCALL codeAdr 42 2 1 ORL dataAdr, A
13 1 1 RRC A 43 3 2 ORL dataAdr, #wert
14 1 1 DEC A 44 2 1 ORL A, #wert
15 2 1 DEC dataAdr 45 2 1 ORL A, dataAdr
16 1 1 DEC @R0 46 1 1 ORL A, @R0
17 1 1 DEC @R1 47 1 1 ORL A, @R1
18 1 1 DEC R0 48 1 1 ORL A, R0
19 1 1 DEC R1 49 1 1 ORL A, R1
1A 1 1 DEC R2 4A 1 1 ORL A, R2
1B 1 1 DEC R3 4B 1 1 ORL A, R3
1C 1 1 DEC R4 4C 1 1 ORL A, R4
1D 1 1 DEC R5 4D 1 1 ORL A, R5
1E 1 1 DEC R6 4E 1 1 ORL A, R6
1F 1 1 DEC R7 4F 1 1 ORL A, R7
20 3 2 JB bitAdr, codeAdr 50 2 2 JNC codeAdr
21 2 2 AJMP codeAdr 51 2 2 ACALL codeAdr
22 1 2 RET 52 2 1 ANL dataAdr, A
23 1 1 RL A 53 3 2 ANL dataAdr, #wert
24 2 1 ADD A, #wert 54 2 1 ANL A, #wert
25 2 1 ADD A, dataAdr 55 2 1 ANL A, dataAdr
26 1 1 ADD A, @R0 56 1 1 ANL A, @R0
27 1 1 ADD A, @R1 57 1 1 ANL A, @R1
28 1 1 ADD A, R0 58 1 1 ANL A, R0
29 1 1 ADD A, R1 59 1 1 ANL A, R1
2A 1 1 ADD A, R2 5A 1 1 ANL A, R2
2B 1 1 ADD A, R3 5B 1 1 ANL A, R3
2C 1 1 ADD A, R4 5C 1 1 ANL A, R4
2D 1 1 ADD A, R5 5D 1 1 ANL A, R5
2E 1 1 ADD A, R6 5E 1 1 ANL A, R6
2F 1 1 ADD A, R7 5F 1 1 ANL A, R7
A.2 Befehlssatz Seite 78
───────────────────────────────────────────────────────────────────────────
Code Bytes Zyklus Mnemonic
60 2 2 JZ codeAdr 90 3 2 MOV DPTR, #wert16
61 2 2 AJMP codeAdr 91 2 2 ACALL codeAdr
62 2 1 XRL dataAdr, A 92 2 2 MOV bitAdr, C
63 3 2 XRL dataAdr, #wert 93 1 2 MOVC A, @A+DPTR
64 2 1 XRL A, #wert 94 2 1 SUBB A, #wert
65 2 1 XRL A, dataAdr 95 2 1 SUBB A, dataAdr
66 1 1 XRL A, @R0 96 1 1 SUBB A, @R0
67 1 1 XRL A, @R1 97 1 1 SUBB A, @R1
68 1 1 XRL A, R0 98 1 1 SUBB A, R0
69 1 1 XRL A, R1 99 1 1 SUBB A, R1
6A 1 1 XRL A, R2 9A 1 1 SUBB A, R2
6B 1 1 XRL A, R3 9B 1 1 SUBB A, R3
6C 1 1 XRL A, R4 9C 1 1 SUBB A, R4
6D 1 1 XRL A, R5 9D 1 1 SUBB A, R5
6E 1 1 XRL A, R6 9E 1 1 SUBB A, R6
6F 1 1 XRL A, R7 9F 1 1 SUBB A, R7
70 2 2 JNZ codeAdr A0 2 2 ORL C, /bitAdr
71 2 2 ACALL codeAdr A1 2 2 AJMP codeAdr
72 2 2 ORL C, bitAdr A2 2 1 MOV C, bitAdr
73 1 2 JMP @A+DPTR A3 1 2 INC DPTR
74 2 1 MOV A, #wert A4 1 4 MUL AB
75 3 2 MOV dataAdr, #wert A5 - - reserved
76 2 1 MOV @R0, #wert A6 2 2 MOV @R0, dataAdr
77 2 1 MOV @R1, #wert A7 2 2 MOV @R1, dataAdr
78 2 1 MOV R0, #wert A8 2 2 MOV R0, dataAdr
79 2 1 MOV R1, #wert A9 2 2 MOV R1, dataAdr
7A 2 1 MOV R2, #wert AA 2 2 MOV R2, dataAdr
7B 2 1 MOV R3, #wert AB 2 2 MOV R3, dataAdr
7C 2 1 MOV R4, #wert AC 2 2 MOV R4, dataAdr
7D 2 1 MOV R5, #wert AD 2 2 MOV R5, dataAdr
7E 2 1 MOV R6, #wert AE 2 2 MOV R6, dataAdr
7F 2 1 MOV R7, #wert AF 2 2 MOV R7, dataAdr
80 2 2 SJMP codeAdr B0 2 2 ANL C, /bitAdr
81 2 2 AJMP codeAdr B1 2 2 ACALL codeAdr
82 2 2 ANL C, bitAdr B2 2 1 CPL bitAdr
83 1 2 MOVC A, @A+PC B3 1 1 CPL C
84 1 4 DIV AB B4 3 2 CJNE A, #wert, codeAdr
85 3 2 MOV dataAdr, dataAdr B5 3 2 CJNE A, dataAdr, codeAdr
86 2 2 MOV dataAdr, @R0 B6 3 2 CJNE @R0, #wert, codeAdr
87 2 2 MOV dataAdr, @R1 B7 3 2 CJNE @R1, #wert, codeAdr
88 2 2 MOV dataAdr, R0 B8 3 2 CJNE R0, #wert, codeAdr
89 2 2 MOV dataAdr, R1 B9 3 2 CJNE R1, #wert, codeAdr
8A 2 2 MOV dataAdr, R2 BA 3 2 CJNE R2, #wert, codeAdr
8B 2 2 MOV dataAdr, R3 BB 3 2 CJNE R3, #wert, codeAdr
8C 2 2 MOV dataAdr, R4 BC 3 2 CJNE R4, #wert, codeAdr
8D 2 2 MOV dataAdr, R5 BD 3 2 CJNE R5, #wert, codeAdr
8E 2 2 MOV dataAdr, R6 BE 3 2 CJNE R6, #wert, codeAdr
8F 2 2 MOV dataAdr, R7 BF 3 2 CJNE R7, #wert, codeAdr
A.2 Befehlssatz Seite 79
───────────────────────────────────────────────────────────────────────────
Code Bytes Zyklus Mnemonic
C0 2 2 PUSH dataAdr E0 1 2 MOVX A, @DPTR
C1 2 2 AJMP codeAdr E1 2 2 AJMP codeAdr
C2 2 1 CLR bitAdr E2 1 2 MOVX A, @R0
C3 1 1 CLR C E3 1 2 MOVX A, @R1
C4 1 1 SWAP A E4 1 1 CLR A
C5 2 1 XCH A, dataAdr E5 2 1 MOV A, dataAdr
C6 1 1 XCH A, @R0 E6 1 1 MOV A, @R0
C7 1 1 XCH A, @R1 E7 1 1 MOV A, @R1
C8 1 1 XCH A, R0 E8 1 1 MOV A, R0
C9 1 1 XCH A, R1 E9 1 1 MOV A, R1
CA 1 1 XCH A, R2 EA 1 1 MOV A, R2
CB 1 1 XCH A, R3 EB 1 1 MOV A, R3
CC 1 1 XCH A, R4 EC 1 1 MOV A, R4
CD 1 1 XCH A, R5 ED 1 1 MOV A, R5
CE 1 1 XCH A, R6 EE 1 1 MOV A, R6
CF 1 1 XCH A, R7 EF 1 1 MOV A, R7
D0 2 2 POP dataAdr F0 1 2 MOVX @DPTR, A
D1 2 2 ACALL codeAdr F1 2 2 ACALL codeAdr
D2 2 1 SETB bitAdr F2 1 2 MOVX @R0, A
D3 1 1 SETB C F3 1 2 MOVX @R1, A
D4 1 1 DA A F4 1 1 CPL A
D5 2 2 DJNZ dataAdr, codeAdr F5 2 1 MOV dataAdr, A
D6 1 1 XCHD A, @R0 F6 1 1 MOV @R0, A
D7 1 1 XCHD A, @R1 F7 1 1 MOV @R1, A
D8 2 2 DJNZ R0, codeAdr F8 1 1 MOV R0, A
D9 2 2 DJNZ R1, codeAdr F9 1 1 MOV R1, A
DA 2 2 DJNZ R2, codeAdr FA 1 1 MOV R2, A
DB 2 2 DJNZ R3, codeAdr FB 1 1 MOV R3, A
DC 2 2 DJNZ R4, codeAdr FC 1 1 MOV R4, A
DD 2 2 DJNZ R5, codeAdr FD 1 1 MOV R5, A
DE 2 2 DJNZ R6, codeAdr FE 1 1 MOV R6, A
DF 2 2 DJNZ R7, codeAdr FF 1 1 MOV R7, A