Der bekannte Mikrocontroller 8052AH-BASIC von Intel enthält einen kompletten 8-K-großen BASIC-Interpreter im internen ROM. Zum Betrieb werden in der Minimalkonfiguration ein RAM von 2K und ein Adreßlatch sowie Leitungstreiber und -empfänger für die serielle Schnittstelle benötigt. Der 8052AH-BASIC eignet sich damit zum Aufbau sehr kleiner und preiswerter Systeme. Die folgende Tabelle zeigt die Pinbelegung des Mikrocontrollers. Gegenüber dem 8052 unterscheiden sich einige Portanschlüsse durch zusätzliche Sonderfunktionen:
P1.0: T2, Zählereingang für den zusätzlichen Timer 2 des 8052 P1.1: T2EX, Eingang zur Torsteuerung von Timer 2 P1.3: PWM-Ausgang für den PWM-Befehl des BASIC-52 P1.4: /ALE-Disable, wird zur EPROM-Programmierung benötigt P1.5: /Programm-Impuls zur EPROM-Programmierung (PROG-Befehl) P1.6: /DMA-Acknowledge, für die DMA-Funktion des BASIC P1.7: Serieller Druckerausgang für PRINT#-Befehle P3.0: RXD, serieller Eingang vom PC P3.1: TXD, serieller Ausgang zum PC P3.2: /INT0 /DMA-Request, für BASIC-DMA-Funktion P3.3: /INT1, Interrupt-Eingang für ONEX1-Befehl P3.4: T0 P3.2: T1 P3.1: /WR P3.0: /RD
Die folgende Übersicht der wichtigsten Befehle und Funktionen des BASIC-52 zeigt, daß es sich um ein hochentwickeltes System handelt, dessen Sprachumfang kaum hinter weit größeren BASIC-Interpretern zurücksteht. Zahlreiche Sonderfuntionen sind speziell an die Umgebung des Mikrocontrollers angepaßt.
Kommandos:
RUN Ctrl-C CONT LIST LIST# LIST@ NEW
Operatoren:
+ - / * = > >= < <= <> .AND. .OR. .XOR. ABS() NOT() INT() SGN() SQR() RND LOG() EXP() SIN() COS() TAN() ATN()
Statements:
CALL DATA READ RESTORE DIM DO-WHILE DO-UNTIL END FOR-TO-STEP NEXT GOSUB ON-GOTO ON-GOSUB IF-THEN-ELSE INPUT LET ONERR PRINT REM STOP
Erweiterte Statements des BASIC-52:
BAUD Baudrate für List#, Print#
ONEX1 Unterprogrammaufruf nach Interrupt 1
ONTIME Timer-Interruptaufruf
RETI Ende eines Interrupt-Unterprogramms
PH0., PH1. Ausgabe einer Hexadezimalzahl ohne/mit Nullstellen
PH0.#, PH1.#,
PRINT#, LIST# Serielle Ausgabe über P1.7
PUSH, POP Daten zum, vom Argument-Stack
PWM Pulsweitenmodulation über Port 1.4
STRING Speicher für Textstrings reservieren
UO1, UO0 User-Output aktiv/inaktiv
IDLE Warten auf Interrupt
Spezialfunktions-Opreratoren und Systemvariablen:
CBY() DBY() XBY() GET IE IP PORT1 PCON RCAP2 T2CON TCON TMOD TIME TIMER0 TIMER1 TIMER2 PI XTAL MTOP LEN FREE
Der 8052AH-BASIC läßt sich problemlos im Enwicklungssystem ES51 nach Abb. 6.4 einsetzen. Jumper 1 muß dabei gesetzt sein, um das interne ROM des Controllers zu aktivieren. Abb. 8.1 zeigt das Gesamtsystem mit einem zusätzlichen LCD-Display und dem AD-Wandler MAX186. Port P1 bleibt hier für eigene Zwecke frei. Einige der Sonderfunktionen, wie z.B. das Brennen von BASIC-Programmen in ein EPROM oder die DMA-Funktion des BASIC sind in diesem System allerdings nicht nutzbar.
Abb. 8.1 Das BASIC-Board auf der Basis des ES51
Der Controller benötigt zu seinem Betrieb mindestens 2 Kilobyte Daten-RAM ab Adresse 0000. Die hier bereitgestellten 32 K werden ebenfalls erkannt und sind als Programm- und Datenspeicher im BASIC voll nutzbar. BASIC-Programme werden aus der Sicht des Controllers als Daten abgelegt, wie es bei jedem Interpretersystem üblich ist. Da das gesamte RAM in diesem System auch als Programmspeicher im eigentlichen Sinne ansprechbar ist, lassen sich Maschinen-Unterprogramme schreiben und vom BASIC ausaufrufen.
Zum Betrieb des BASIC-Interpreters wird ein Terminalprogramm benötigt, über das Kommandos und BASIC-Prorammzeilen eingegeben werden und Ausgaben empfangen werden können. Für erste Versuche genügt z.B. das folgende kleine Terminalprogramm in QBasic. Beim Öffnen der Schnittstelle muß beachtet werden, daß BASIC mit dem OPEN-Kommando die Handshakeleitungen RTS und DTR hochsetzt und damit einen RESET des Prozessors auslöst. Beide Leitungen werden daher in Zeile 30 zurückgesetzt.
10 REM Mini-Terminal für BASIC-52 (TERMINAL.BAS) 20 OPEN "COM2:9600,N,8,1,CS,DS" AS #1 30 OUT (&H2FC),8 :REM RTS und DTR zurücksetzen 40 A$=INKEY$ 50 IF A$ <> "" THEN PRINT#1,A$; 60 IF LOC(1) > 0 THEN B$ = INPUT$(1,#1): PRINT B$; 70 GOTO 40
Nach dem Neustart oder einem Reset erwartet der 8052AH-BASIC ein Leerzeichen, um sich auf die Baudrate des Terminals einzustellen. Er sendet dann seine Einschaltmeldung:
*MCS-51(tm) BASIC V1.1* READY >
Das MCS BASIC-52 verwendet den zusätzlichen Timer 2 des 8052 als Baudratengenerator. Im Gegensatz zu den Timern 0 und 1 des 8051 erhält Timer 2 die ungeteilte Taktfrequenz des Quarzosillators, hier also 11,0592 MHz. Das ermöglicht größere Teilerfaktoren und damit eine größere Flexibilität der Quarzfrequenzen. Nach einem Reset stellt BASIC-52 die Baudrate des angeschlossenen Terminals fest, indem es eine Impulslängenbestimmung des ersten gesendeten Zeichens vornimmt. Dabei muß es sich um ein Leerzeichen (ASCII 20h) handeln. Zusammen mit dem Startbit enthält es als erstes einen Impuls mit einer Länge von sechs Bitlängen. Dieser erste Impuls wird ausgemessen und damit der erforderliche Teilerfaktor für den Timer 2 bestimmt. So können im Prinzip beliebige Quarzfrequenzen und Baudraten verwendet werden. Das System geht standardmäßig von einem Quarz mit 11,0592MHz aus. Andere Frequenzen sollten dem BASIC-52 über den XTAL-Operator mitgeteilt werden, damit die interne Uhr und die zusätzliche serielle Ausgaberoutine korrekt ablaufen können.
Der automatisch eingestellte Teilerfaktor für Timer 2 kann mit dem Operator RCAP2 erfragt werden. Bei einer Übertragungsgeschwindigkeit von 9600 Baud und einer Taktfrequenz von 11,0592MHz ergibt PRINT RCAP2 das Ergebnis 65500. Das entspricht 65536-65500=36. Es werden also jeweils 36 Taktzyklen bis zum Überlauf gezählt. Da der interne Zähler der seriellen Schnittstelle dieses Signal durch 32 teilt, ergibt sich eine Bitrate von genau 9600 Baud.
Nach dem Einschalten und der Einstellung der Baudrate meldet das BASIC-52 seine Bereitschaft. Nun können in gewohnter Weise BASIC-Zeilen eingegeben und durch RUN gestartet werden. Ein erstes kleines Testprogramm könnte z.B. den Ausgabeport P1 ansprechen:
10 REM Portausgaben (TEST1.BAS) 20 FOR N=0 TO 255 30 PORT1=255-N 40 FOR I=1 TO 200 : NEXT I 50 NEXT N 60 GOTO 20
Ein übliches Terminalprogramm sendet jedes Zeichen von der Tastatur direkt an den Mikrocontroller. Alle Zeichen werden dort zunächst in einem Eingabepuffer mit einer Länge von 79 Zeichen gesammelt und erst nach <RETURN> ausgewertet. Jeder Fehler macht es leider erforderlich, die gesamte Zeile neu einzugeben. Um die Arbeit mit BASIC-52 etwas zu erleichtern, wurde das kleine Editor- und Terminalprogramm BASEDIT entwickelt.
BASEDIT hat im Grundmodus zwei Textfenster (vgl. Abb. 8.2). Im oberen Fenster arbeitet ein einfacher Texteditor mit vielen Editierfunktionen. Ins untere Fenster werden alle vom Mikrocontroller gesendeten Zeichen geschrieben. Jedesmal, wenn im Editor ein <RETURN> getippt wird, wird die komplette Zeile an den Controller geschickt. Sein Echo erscheint dann im Terminalfenster. Der Benutzer kann jede Zeile vor der Übertragung beliebig editieren. Dies gilt auch für Zeilen, die bereits weiter oben im Text stehen, und die nachträglich noch einmal geändert werden sollen. Im Terminalfenster läßt sich jeweils beobachten, ob eine Zeile angenommen wurde oder eine Fehlermeldung auslöst.
Abb. 8.2 Editier- und Ausgabefenster in BASEDIT.EXE
Der Editor arbeitet immer im Einfügemodus. RETURN innerhalb einer Zeile führt aber nicht wie in der Textverarbeitung üblich zu einem Teilen der Zeile, sondern der Cursor springt nur an den Anfang der nächsten Zeile. Der Cursor kann beliebig mit den Pfeiltasten, mit PgUp und PgDn sowie Home und End verschoben werden. Insgesamt können 512 Zeilen bearbeitet werden.
Spezielle Funktionen des Programms lassen sich über die Funktionstasten F1 bis F8 aufrufen:
F1 Hilfefunktion: Es erscheint ein Fenster mit einer Kurzübersicht
der BASIC-Befehle.
F2 Save: Das Programm im Editor wird unter einem wählbaren Dateinamen
gespeichert.
F3 Load: Ein Programm wird von der Diskette sowohl in den Editor als auch
in den Controller geladen.
F4 List: Der Programmtext wird aus dem Controller in den Editor übertragen.
F5 Insert: Eine leere Zeile wird in den Text eingefügt.
F6 Zoom: Es wird ein bildschirmfüllendes Terminalfenster geöffnet,
in dem alle Ausgaben des Controllers erscheinen. Eingaben werden direkt
weitergeleitet.
F7 RUN: Das Programm wird mit "RUN" gestartet.
F8 STOP: Das Programm wird mir CTRL-C unterbrochen.
Natürlich können neben Programmzeilen auch Direktkommandos wie NEW, LIST, RUN usw. eingeben werden. Zum Testen können auch direkte Abfragen erfolgen wie z.B. PRINT PORT1. Ein Besonderheit des Gesamtsystems ist es, daß geladene Programme schon im Controller vorhandene Programme nur insoweit überschreiben, wie sie gleiche Zeilennummern ersetzen. Man kann daher eine Sammlung von Unterprogrammen anlegen, die bei Bedarf hinzugeladen werden. Um ganz neu anzufangen, muß vor dem Laden eines Programms NEW eingegeben werden.
Der 8052AH-BASIC läßt sich nicht nur als programmierbarer Mikrocontroller, sondern auch als "passives" Interface einsetzen. Dabei verwendet man den Direktmodus des Controllers. Eine Aufforderung wie z.B. PRINT PORT1 auf der Kommandozeilenebene führt zu einer direkten Antwort, wobei hier der Portzustand von Port P1 gelesen und zurückgegeben wird. Zahlreiche Aktionen lassen sich daher von einem PC-Programm aus durchführen, das Kommandos absetzt und die Antwort des Controllers auswertet. Dieses Verfahren ist zwar nicht besonders schnell, hat jedoch einige Vorteile: Der Anwender kann ein Mikrocontroller-Interface nutzen, ohne selbst einen Mikrocontroller oder ein EPROM programmieren zu müssen. Das folgende kleine PASCAL-Programm demonstriert den Datenaustausch zwischen PC und BASIC-52. Jedes Kommando wird zeichenweise gesendet, wobei jedesmal das Echo des BASIC-52 abgewartet wird. Am Ende der Zeile sendet das BASIC CR und LF. Bei Kommandos ohne Werterückgabe wie z.B. PORT1=255 meldet sich das BASIC-52 mit dem Zeichen > zurück. In den anderen Fällen sendet das BASIC die Anwort als Textstring, gefolgt von CR, LF und dem Bereitschaftzeichen >.
Program Basic_Direktmodus; {BASDIREK.PAS} Uses DOS,CRT,COM2_96; var n : Word; procedure leeren; var dummy,n : Byte; begin for n:= 1 to 3 do begin delay(2); dummy := Port[BA]; end; end; procedure Ausgabe (Zeile: String); var n: Integer; b: Byte; begin leeren; for n:= 1 to length (Zeile) do begin sende (ord (Zeile [n])); b := Empfang; end; sende (13); repeat b:= Empfang until b= ord('>'); end; function Abfrage (Zeile: String): Real; var n: Integer; b: Byte; Antwort : String; Ergebnis: Real; begin leeren; for n:= 1 to length (Zeile) do begin sende (ord (Zeile [n])); b := Empfang; end; sende (13); for n:= 1 to 4 do b:=Empfang; Antwort := ''; repeat b:= Empfang; {write (chr(b));} if (b>32) then Antwort := Antwort + chr(b); until b in [13,32]; repeat b:= Empfang until b= ord('>'); val (Antwort,Ergebnis,n); Abfrage := Ergebnis; end; begin init; Ausgabe ('PORT1 = 15'); writeln (Abfrage ('PRINT PORT1'):3:0); Ausgabe ('PORT1= 255'); Ausgabe ('PWM 20000,20000,100'); repeat until KeyPressed; end.
Das Hauptprogramm verwendet den Port P1 des Interfaces zum Schreiben und
Lesen. Das PWM-Kommando wirkt auf das Bit 2 des Ports und dient zum Erzeugen
einer genau definierten Folge von Rechteckimpulsen. Die angegebenen Parameter
bedeuten: 20 000 Taktzyklen Highzustand, 20 000 Taktzyklen Lowzustand und
100 Impulse. Das PWM-Kommando läßt sich z.B. zur Tonerzeugung
nutzen.
Das BASIC-52 verfügt über Funktionen zum Beschreiben und Lesen
des Speichers. DBY zielt auf das interne RAM, XBY auf das externe RAM und
CBY auf den internen Programmspeicher des Controllers. Damit ist es sehr
einfach, eine Kopie des BASIC-52 auszulesen, um sie z.B. in ein EPROM zu
brennen. Mit einem 80C32 läßt sich dann ein CMOS-System mit
BASIC-52 aufbauen. Das Programm BASREAD.PAS nutzt den Direktmodus, um das
Betriebssystem auszulesen und in die Datei BASIC52.BIN zu schreiben.
Program Basic_Ausleseprogramm; {BASREAD.PAS} Uses DOS,CRT,COM2_96; var n : Word; Parameter: String; b : Byte; f : file of Byte; procedure leeren; var dummy,n : Byte; begin for n:= 1 to 3 do begin delay(2); dummy := Port[BA]; end; end; function Abfrage (Zeile: String): Real; var n: Integer; b: Byte; Antwort : String; Ergebnis: Real; begin leeren; for n:= 1 to length (Zeile) do begin sende (ord (Zeile [n])); b := Empfang; end; sende (13); for n:= 1 to 4 do b:=Empfang; Antwort := ''; repeat b:= Empfang; {write (chr(b));} if (b>32) then Antwort := Antwort + chr(b); until b in [13,32]; repeat b:= Empfang until b= ord('>'); val (Antwort,Ergebnis,n); Abfrage := Ergebnis; end; begin init; sende (32); {Basic initialisieren} delay (1000); leeren; assign (f,'BASIC52.BIN'); rewrite (f); for n:= 0 to 8191 do begin str (n,Parameter); b:=lo(trunc(Abfrage ('PRINT CBY('+Parameter+')'))); write (b,' '); write (f,b); end; close (f); end.
Die Datenausgabe des BASIC-52 erfolgt normalerweise über das angeschlossene Terminal. Für eigenständige Anwendungen ohne ein Terminal ist manchmal eine Anzeige erforderlich. Oft verwendet man gemultiplexte LED-Anzeigen. Bequemer ist jedoch der Einsatz intelligenter LCD-Anzeigen mit eigenem Display-Controller. Sie enthalten bereits einen Zeichengenerator und können ohne großen Aufwand zur Ausgabe von ASCII-Zeichen veranlaßt werden.
Fast alle intelligenten LCD-Displays folgen heute einem gemeinsamen Standard. Hier wird ein Standardtyp mit 2*16 Zeichen eingesetzt. Das Display verfügt über folgende Anschlüsse:
Pin 14...Pin 7: Datenbus
Pin 6: E, Enable-Signal, aktiv high
Pin 5: R/W, 0 = Schreiben, 1 = Lesen
Pin 4: RS, 1= Daten, 0 = Kommandos
Pin 3: V0, Kontrasteinstellung 0...2V
Pin 2: VDD, +5V
Pin 1: VSS, 0V
Die Datenübertragung folgt dem Bus-Protokoll eines 6800-Prozessors: Zunächst muß durch die R/W-Leitung die Datenrichtung festgelegt werden, dann erfolgt der eigentliche Zugriff durch einen positiven Enable-Impuls. An einem 8051-Prozessor muß daher eine Verknüpfung des /RD- und des /WR-Signals erfolgen. Die Datenrichtungs-Umschaltung kann durch eine Adreßleitung erfolgen. Eine weitere Adreßleitung ist erforderlich, um die interne Registerselektion des Displaycontrollers über die Leitung RS vorzunehmen. Hier wird zwischen Daten und Kommandos unterschieden.
Abb. 8.3 Detailschaltbild zum Anschluß des LCD-Displays
Der Anschluß des Displays an das Systemboard ES51 erfolgt nach Abb. 8.3 durch eine sehr einfache Ansteuerung ohne Adreßdecoder. Die Display-Adressen erscheinen ab Adresse 8000h und werden mehrfach gespiegelt. Schreibzugriffe sind nur bei A1=0 erlaubt, weil mit A1=1 das Display seine Daten auf den Bus legt. Da /WR und /RD UND-verknüpft werden, würde ein Schreibbefehl auf einer Leseadresse zu einem Buskonflikt führen. Für die Ansteuerung ergeben sich nun folgende Adressen:
8000: Kommando schreiben
8001: Daten schreiben
8002: Kommando lesen
8003: Daten lesen
Das Display kennt eine Vielzahl von Kommandos, die jeweils mit RS=0 übergeben werden. Dabei wird zwischen Typen von Kommandos unterschieden, die jeweils eine charakteristische Anzahl von Nullbits in den höherwertigen Stellen besitzen.
7 6 5 4 3 2 1 0 +-----------------------------------------------+ Display löschen ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 1 ¦ +-----+-----+-----+-----+-----+-----+-----+-----¦ Cursor home ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 1 ¦ x ¦ +-----+-----+-----+-----+-----+-----+-----+-----¦ verschieben ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 1 ¦ID ¦ S ¦ +-----------------------------------------------+ (ID=1/0: rechts/links, S=1/0: ohne/mit Text) +-----------------------------------------------+ Display, Cursor ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ 1 ¦ D ¦ C ¦ B ¦ +-----------------------------------------------+ (D,C,B=1/0: Display, Cursor, Blinken An/Aus) +-----------------------------------------------+ verschieben ¦ 0 ¦ 0 ¦ 0 ¦ 1 ¦SC ¦RL ¦ x ¦ x ¦ +-----------------------------------------------+ (SC=1/0: Text/Cursor nach RL=1/0: rechts/links) +-----------------------------------------------+ Initialisierung ¦ 0 ¦ 0 ¦ 1 ¦DL ¦ N ¦ x ¦ x ¦ x ¦ +-----------------------------------------------+ (DL=1/0: 8/4-Bit-Bus, N=1/0: beide/eine Zeile) +-----------------------------------------------+ Zeichengenerator¦ 0 ¦ 1 ¦ Zeichen ¦ Spalte ¦ +-----+-----------------------------------------¦ Adresse ¦ 1 ¦ Speicheradresse ¦ +-----------------------------------------------+
Das Kommandoregister des Displays kann auch zurückgelesen werden. Es liefert dann sein Busy-Flag BF und die aktuelle Cursor-Position. Vor jedem Schreibzugriff auf das Display muß unbedingt BF überprüft werden. Nur wenn BF low ist, darf ein Kommando oder ein Datenbyte in das Display geschrieben werden, andernfalls kann es zu einer Zerstörung des Display-Controllers kommen.
+-----------------------------------------------+ Bereitschaft? ¦BF ¦ Speicheradresse ¦ +-----------------------------------------------+
Nach dem Einschalten müssen einige Initialisierungsbytes in das Kommandoregister geschrieben werden. Hier ein Beispiel:
Initialisierung mit 8-Bit-Datenbus und zwei Zeilen: 0011 1000 = 38h = 56
Display einschalten, Cursor ausschalten: 0000 1100 = 0Ch = 12
Display löschen: 0000 0001 = 01h = 1
Das Display enthält einen internen Datenzeiger, der den einzelnen Zeichenpositionen zugeordnet ist. Bei einem 2*16-stelligen Display gilt:
Zeile 1: Adresse 00h bis 0Fh
Zeile 2: Adresse 40h bis 4Fh
Der Cursor rückt mit jedem geschriebenen Zeichen automatisch vor, kann aber durch Setzen einer Adresse gezielt auf eine gewünschte Position gestellt werden.
Das folgende kleine BASIC-Programm stellt ein erstes Beispiel für eine einfache Ansteuerung dar. Das Programm verzichtet auf eine Überprüfung des Busy-Flags. Dies ist unproblematisch, solange die
Ansteuerung aus einem reinen BASIC-Programm relativ langsam erfolgt.
1 REM Zeichenausgabe auf dem LCD-Display (LCD.BAS) 10 STRING 240,16 20 $(0)="Hallo Leute " 30 GOSUB 1000 40 GOSUB 1300 50 GOSUB 1400 60 END 1000 REM LCD-Reset 1010 XBY(8000H)=56 1020 XBY(8000H)=12 1030 XBY(8000H)=1 1040 RETURN 1100 REM Cursor-Position 0 1110 XBY(8000H)=128 1120 RETURN 1200 REM Zeichenausgabe 1210 XBY(8001H)=A 1220 RETURN 1300 REM Obere Zeile 1310 XBY(8000H)=128 1320 FOR I=1 TO 16 : XBY(8001H)=ASC($(0),I) : NEXT 1330 RETURN 1400 REM Untere Zeile 1410 XBY(8000H)=192 1420 FOR I=1 TO 16 : XBY(8001H)=ASC($(0),I) : NEXT 1430 RETURN
Die direkte Ausgabe einzelner Zeichen im BASIC ist wenig komfortabel. Das BASIC-52 sieht jedoch eine Erweiterung in Form eines eigenen Treibers vor, mit dem PRINT-Befehle umgeleitet werden können. Der Anwender muß dazu eine Ausgaberoutine bereitstellen, die Zeichen aus dem Register R5 (Bank 0) liest und an die Ausgabe, hier das LCD-Display leitet. Durch das Kommando UO 1 läßt sich die Ausgabe auf den eigenen Treiber umlenken. Bei der Abarbeitung eines PRINT-Befehls wird vom System für jedes Zeichen einmal die Adresse 4030h angesprungen, wo ein Sprungbefehl auf das Ausgabeprogramm stehen muß.
Eine andere Möglichkeit, den eigenen Treiber zu benutzen, bietet der PRINT@-Befehl. Hier wird die Adresse 403Ch angesprungen, wo ebenfalls ein Sprungbefehl auf die Ausgaberoutine stehen sollte. Der eigene Treiber wird allerdings nur dann wirksam, wenn Bit 39 des Internen RAMs (Bit 7 in Adresse 24h) gesetzt wurde. Dies kann z.B. in der ohnehin erforderlichen Initialisierungsroutine für das Display erfolgen.
Das im folgenden vorgestellte Programm BAS52_IO.ASM enthält einen LCD-Treiber sowie einen Treiber für den AD-Wandler MAX186 und die weiter unten beschriebenen Befehlserweiterungen. BASIC52 stellt bereits zahlreiche Hilfen zu seiner externen Erweiterung bereit. Maschinen-Unterprogramme müssen folgende Regeln einhalten: Alle Register außer den acht Registern der Bank 3 (18h ...1Fh) und der Akku sowie der Datapointer müssen unverändert bleiben. Der Benutzer darf Bank 3 komplett für eigene Zwecke belegen.
Der LCD-Treiber sichert den Akku und den Datapointer auf dem Stack, um sie vor dem Rücksprung wieder zu restaurieren. Register 4 der Bank 3 führt die jeweilige Cursorposition. Bei einem CR wird der Cursor an den Anfang der aktuellen Zeile zurückgesetzt. Bei zweizeiligen Displays kann der Anwender den Cursor auf den Anfang der zweiten Zeile (Position 64) stellen. Für jeden Datenzugriff auf das Display wird über die Busy-Routine die Bereitschaft geprüft. Vor der ersten Verwendung des Treibers muß das Display durch Aufruf der Initialisierungsroutine (CALL 4050h) vorbereitet werden. BASIC-52 erlaubt kurze Aufrufe mit CALL 00, CALL 01, CALL 03 usw. bis CALL 127, die zu den Adressen 4100h, 4102h, 4104h usw. bis 41FEh geleitet werden. Entsprechende Sprungvektoren an diesen Adressen ermöglichen den vereinfachten Aufruf der LCD-Initialisierung (CALL 00), der Cursor-Routine und zusätzlich der weiter unten beschriebenen AD-Wandler-Routine für den MAX186. Der Cursor muß über einen zusätzlichen Parameter gesetzt werden:
CALL 427FH 0 oder kurz CALL 01 0
Die Cursor-Routine wertet einen hinter der CALL-Adresse stehenden Ausdruck aus, der auch eine Berechnung wie z.B. CALL 01 (64+4*N) enthalten kann. Weiteres zur Datenübergabe zwischen BASIC-52 und eigenen Maschinenprogrammen ist in Kap. 11 zu finden.
;8052-AH-Basic-Erweiterung: LCD-Treiber (BAS52_IO.ASM) 4030 .org 4030H 4030 02 42 00 ljmp LCD ;Vektor für UO 1 403C .org 403CH 403C 02 42 00 ljmp LCD ;Vektor für PRINT@ 4100 .org 4100H 4100 41 50 ajmp LCDInit ;Vektor für CALL 0 4102 41 7F ajmp CURSOR ;für CALL 1 4104 61 00 ajmp AD ;für CALL 2 4200 .org 4200H 4200 C0 83 LCD push DPH 4202 C0 82 push DPL 4204 C0 E0 push ACC 4206 4206 51 2E acall Busy 4208 E5 1C mov A,28 ;Cusorposition 420A 44 80 orl A,#128 420C 90 80 00 mov DPTR,#8000H 420F F0 movx @DPTR,A ;Cursor setzen 4210 51 2E acall Busy 4212 E5 05 mov A,05 ;Zeichen holen 4214 B4 0D 02 cjne A,#0DH,J1 ;= CR? 4217 80 1D sjmp CR 4219 B4 0A 02 J1 cjne A,#0AH,J2 ;= LF? 421C 80 09 sjmp End 421E 90 80 01 J2 mov DPTR,#8001H 4221 F0 movx @DPTR,A ;Zeichen ausgeben 4222 E5 1C mov A,28 ;Bank3, R4, Cursorpos. 4224 04 inc A 4225 F5 1C mov 28,A ;Cursor erhöhen 4227 D0 E0 End pop ACC 4229 D0 82 pop DPL 422B D0 83 pop DPH 422D 22 ret 422E 422E 90 80 02 Busy mov DPTR,#8002H 4231 E0 movx A,@DPTR 4232 20 E7 F9 jb ACC.7,Busy 4235 22 ret 4236 4236 51 2E CR acall Busy 4238 74 20 mov a,#32 423A 90 80 01 mov DPTR,#8001h 423D F0 movx @DPTR,a 423E E5 1C mov A,28 4240 04 inc A 4241 F5 1C mov 28,a 4243 54 3F anl A,#63 4245 B4 28 EE cjne A,#40,CR 4248 E5 1C J3 mov a,28 424A 54 C0 anl a,#192 424C F5 1C mov 28,a 424E 80 D7 sjmp End 4250 4250 4250 C0 83 LCDInit push DPH 4252 C0 82 push DPL 4254 C0 E0 push ACC 4256 4256 51 2E acall Busy 4258 90 80 00 mov DPTR,#8000H 425B 74 38 mov A,#38H 425D F0 movx @DPTR,A 425E 51 2E acall Busy 4260 90 80 00 mov DPTR,#8000H 4263 74 06 mov A,#06H 4265 F0 movx @DPTR,A 4266 51 2E acall Busy 4268 90 80 00 mov DPTR,#8000H 426B 74 0C mov A,#0CH 426D F0 movx @DPTR,A 426E 51 2E acall Busy 4270 90 80 00 mov DPTR,#8000H 4273 74 01 mov A,#01H 4275 F0 movx @DPTR,A 4276 D2 27 setb 027H ;PRINT@ freigeben 4278 4278 D0 E0 pop ACC 427A D0 82 pop DPL 427C D0 83 pop DPH 427E 22 ret 427F 427F C0 E0 CURSOR push ACC ;Akku sichern 4281 74 39 Mov A,#57 4283 12 00 30 lcall 030h ;Ausdruck auswerten 4286 74 01 Mov A,#1 4288 12 00 30 lcall 030h ;als Integer in R3:R1 428B E9 mov A,R1 ;Startparameter holen 428C F5 1C mov 28,A 428E D0 E0 pop ACC ;Akku wiederherstellen 4290 22 ret
Der Displaytreiber kann z.B. ab der Adresse 4030h im EPROM stehen, wenn das EPROM über Jumper 2 aktiviert wurde. Ist Jumper 1 (EA) gesetzt, dann greift der Mikrocontroller in den unteren 8K automatisch auf das interne Interpreter-ROM zu, während Zugriffe auf höhere Adressen das externe EPROM verwenden. Zumindest in der Entwicklungsphase kann es aber sinnvoll sein, alle Maschinenroutinen im RAM zu testen. Dies ist bei offenem Jumper 1 möglich, da das gesamte RAM des Entwicklungssystems auch als Programmspeicher dient. Der BASIC-RAM-Bereich muß allerdings z.B. durch MTOP=8191 verkleinert werden, um Konflikte mit der systemeigenen Speicherverwaltung zu vermeiden. Das BASIC-Programm LCD2.BAS stellt den LCD-Treiber im RAM bereit:
3999 REM LCD-Treiber (LCD2.BAS) 4000 MTOP=8191 4029 REM LCD-Vektoren 4030 DATA 002H,042H,000H,000H,000H,000H,000H,000H 4038 DATA 000H,000H,000H,000H,002H,042H,000H,000H 4039 FOR N=0 TO 15 : READ D : XBY(04030H+N)=D : NEXT N 4099 REM CALL-Vektoren 4100 DATA 041H,050H,041H,07FH,061H,000H,000H,000H 4101 FOR N=0 TO 7 : READ D : XBY(04100H+N)=D : NEXT N 4199 REM LCD 4200 DATA 0C0H,083H,0C0H,082H,0C0H,0E0H,051H,02EH 4208 DATA 0E5H,01CH,044H,080H,090H,080H,000H,0F0H 4210 DATA 051H,02EH,0E5H,005H,0B4H,00DH,002H,080H 4218 DATA 01DH,0B4H,00AH,002H,080H,009H,090H,080H 4220 DATA 001H,0F0H,0E5H,01CH,004H,0F5H,01CH,0D0H 4228 DATA 0E0H,0D0H,082H,0D0H,083H,022H,090H,080H 4230 DATA 002H,0E0H,020H,0E7H,0F9H,022H,051H,02EH 4238 DATA 074H,020H,090H,080H,001H,0F0H,0E5H,01CH 4240 DATA 004H,0F5H,01CH,054H,03FH,0B4H,028H,0EEH 4248 DATA 0E5H,01CH,054H,0C0H,0F5H,01CH,080H,0D7H 4250 DATA 0C0H,083H,0C0H,082H,0C0H,0E0H,051H,02EH 4258 DATA 090H,080H,000H,074H,038H,0F0H,051H,02EH 4260 DATA 090H,080H,000H,074H,006H,0F0H,051H,02EH 4268 DATA 090H,080H,000H,074H,00CH,0F0H,051H,02EH 4270 DATA 090H,080H,000H,074H,001H,0F0H,0D2H,027H 4278 DATA 0D0H,0E0H,0D0H,082H,0D0H,083H,022H,0C0H 4280 DATA 0E0H,074H,039H,012H,000H,030H,074H,001H 4288 DATA 012H,000H,030H,0E9H,0F5H,01CH,0D0H,0E0H 4290 DATA 022H 4291 FOR N=0 TO 144 : READ D : XBY(04200H+N)=D : NEXT N 4292 CALL 00H
Die Anwendung des LCD-Treibers ist relativ einfach. Im Normalfall wird die Ausgabe mit UO 1 umgeleitet. Dann wirken alle PRINT-Befehle auf das Display. Zusätzlich kann mit CALL 01 die Cursorposition verändert werden. Das Beispielprogramm LCDDEMO.BAS stellt den Zustand von Port P1 am Display dar. Zusätzlich wird ein Text ausgegeben. Mit jedem Zeilenende durch den PRINT-Befehl gelangt der Cursor wieder an den Anfang der aktuellen Zeile.
100 REM Anwendung des LCD-Treibers (LCDDEMO.BAS) 110 UO 1 : REM User Output freigeben 120 CALL 01H 0 : REM Cursor auf Zeile 1 130 PRINT " PORT P1 = " 140 CALL 01H 64 : REM Cursor auf Zeile 2 150 PRINT PORT1 160 GOTO 150 200 REM Hier LCD2.BAS hinzuladen
Auf dem Entwicklungssystem ES51 kann der 12-Bit-Wandler MAX186 eingesetzt werden. Die Ansteuerung wurde bereits in Kap. 7.4 genau erläutert. Eine Verwendung in BASIC-Programmen eröffnet zahlreiche Möglichkeiten für spezielle Meßprogramme. Der Wandler wird hier vollständig von Leitungen des Ports P3 angesteuert, so daß der Port P1 frei bleibt. Port P3 ist aber durch keinen BASIC-Befehl ansprechbar, weil alle Leitungen auch Spezialfunktionen haben. Der Wandler kann also nur durch eine Maschinenroutine eingebunden werden, was ja wegen der seriellen Datenübertragung auch aus zeitlichen Gründen sinnvoll ist.
Das Programm BAS52_IO.ASM enthält eine geeignete Routine zur Ansteuerung des MAX186. Sie verarbeitet ein Steuerwort, das ihr vom aufrufenden BASIC-Programm übergeben wird, und liefert einen 12-Bit-Integerwert zurück. Das BASIC-52 bietet zahlreiche Hilfen der Einbindung eigener Routinen. So läßt sich z.B. ein Wert an die Routine übergeben, der im BASIC-Programm sogar in Form eines komplexen Ausdrucks stehen kann. Die Auswertung des Ausdrucks wird vom BASIC52 übernommen, wenn der Anwender geeignete Systemroutinen aufruft. Alle internen Funktionen des Systems werden mit CALL 030h aufgerufen, nachdem der Akku mit einem Steuerkommando geladen wurde. Hier werden die folgenden Funktionen benutzt:
57: Ausdruck auswerten
01: In Integer wandeln und in R3,R1 ablegen
157: Integer aus R2,R0 auf den Argument-Stack legen
Im BASIC wird die AD-Routine dann z.B. so aufgerufen:
10 CALL 4300H (206+16* Kanal) : REM AD-Wandler aufrufen 20 POP A : REM Wert abholen 30 PRINT A : REM Meßwert ausgeben
Das Maschinenprogramm ist voll relocatibel, weil durchgehend relative Sprungbefehle verwendet werden. Aus Gründen der günstigen Speichernutzung wird es hier hinter die LCD-Programme ab Adresse 4300h geschrieben. Das Programm entspricht dem MC51-Programm in Kap. 7.4.
;Ansteuerung des MAX186 (BAS52IO.ASM) 4300 C0 E0 AD push ACC ;Akku sichern 4302 74 39 Mov A,#57 4304 12 00 30 lcall 030h ;Ausdruck auswerten 4307 74 01 Mov A,#1 4309 12 00 30 lcall 030h ;als Integer in R3:R1 430C E9 mov A,R1 ;Startparameter holen 430D 430D C2 B5 clr 0B5h ;Steuerbyte zum Wandler 430F 75 18 08 mov 18h,#08 ;Zähler: R1, Bank3 4312 20 E7 04 S1 jb ACC.7,S2 4315 C2 B4 clr 0B4h ;Di=0 4317 80 02 sjmp S3 4319 D2 B4 S2 setb 0B4h ;Di=1 431B D2 B5 S3 setb 0B5h ;Clk=1 431D C2 B5 clr 0B5h ;Clk=0 431F 23 rl A ;nächstes Bit 4320 D5 18 EF djnz 18h,S1 4323 4323 75 18 0A mov 18h,#10 ;Warteschleife 4326 D5 18 FD S4 djnz 18h,S4 4329 4329 74 00 mov A,#0 ;Highbyte auslesen 432B 75 18 04 mov 18h,#4 ;4 mal 432E D2 B5 S5 setb 0B5h ;Clk-Impuls 4330 C2 B5 clr 0B5h 4332 23 rl A 4333 30 B2 01 jnb 0B2h,S6 ;Do lesen 4336 04 inc A 4337 D5 18 F4 S6 djnz 18h,S5 433A FA mov R2,A ;Highbyte in R2 433B 433B 74 00 mov A,#0 ;Lowbyte lsen 433D 75 18 08 mov 18h,#8 ;8 mal 4340 D2 B5 S7 setb 0B5h ;Clk-Impuls 4342 C2 B5 clr 0B5h 4344 23 rl A 4345 30 B2 01 jnb 0B2h,S8 ;Do lesen 4348 04 inc A 4349 D5 18 F4 S8 djnz 18h,S7 434C F8 mov R0,A ;Lowbyte in R0 434D 434D 74 9A mov A,#154 434F 12 00 30 lcall 030h ;auf den Ergebnisstack 4352 D0 E0 pop ACC ;Akku wiederherstellen 4354 22 ret
Der Treiber wird z.B. über das BASIC-Ladeprogramm AD.BAS in das RAM des Systems eingetragen.
4299 REM AD-Wandler MAX186 (AD.BAS) 4300 DATA 0F5H,019H,074H,039H,012H,000H,030H,074H 4308 DATA 001H,012H,000H,030H,0E9H,0C2H,0B5H,075H 4310 DATA 018H,008H,020H,0E7H,004H,0C2H,0B4H,080H 4318 DATA 002H,0D2H,0B4H,0D2H,0B5H,0C2H,0B5H,023H 4320 DATA 0D5H,018H,0EFH,075H,018H,00AH,0D5H,018H 4328 DATA 0FDH,074H,000H,075H,018H,004H,0D2H,0B5H 4330 DATA 0C2H,0B5H,023H,030H,0B2H,001H,004H,0D5H 4338 DATA 018H,0F4H,0FAH,074H,000H,075H,018H,008H 4340 DATA 0D2H,0B5H,0C2H,0B5H,023H,030H,0B2H,001H 4348 DATA 004H,0D5H,018H,0F4H,0F8H,074H,09AH,012H 4350 DATA 000H,030H,0E5H,019H,022H,000H,000H,000H 4351 FOR N=0 TO 87 : READ D: XBY(04300H+N)= D: NEXT N
Zusammen mit der schon vorgestellten Erweiterung für das LCD-Display ergeben sich nun die folgenden CALL-Adressen:
LCD-Initialisierung: CALL 427FH oder CALL 00H
LCD-Cursor: CALL 427FH oder CALL 01H
AD-Wandler: CALL 4300H oder CALL 02H
Das folgende kleine Programm ADLCD.BAS verwendet die Erweiterung zum Aufbau eines Vierkanal-Voltmeters. Diesmal wird die Ausgabe nicht mit UO umgeleitet, sondern die Ergebnisse gelangen mit PRINT @ zum LCD-Display.
100 REM Vierkanal-Voltmeter (ADLCD.BAS) 110 CALL 00H: REM LCD-Init 120 FOR N=0 TO 3 130 CALL 02H (128+64+16*N+14) : REM AD, Kanal 1,3,5,7 140 POP D 150 IF N<2 THEN CALL 01H (8*N): REM Cursor 160 IF N>1 THEN CALL 01H (64+8*(N-2)): REM Cursor 170 PRINT @ D,"mV", 180 NEXT N 190 FOR I=1 TO 500 : NEXT I 200 GOTO 120
Das BASIC-52 ist für komfortable Befehlserweiterungen vorbereitet. Der Benutzer kann also eigene BASIC-Schlüsselwörter festlegen und mit eigenen Maschinenprogrammen verbinden. Im Prinzip wird dabei ein Aufruf wie z.B. CALL 4300H 254 durch eine bessere Schreibweise wie z.B. AD 254 ersetzt. Die Erweiterungen umfassen Kommandos und Statements, denen Parameter übergeben werden können. Nicht möglich sind leider Funktionen, bei denen ein Wert zurückgeliefert wird. Als Hilfsmittel ist aber die Möglichkeit vorgesehen, mit einem Maschienprogramm eine Realzahl auf den sog. Argument-Stack zu legen, von wo sie mit dem BASIC-Befehl POP abgeholt werden kann.
Zum Erzeugen eigener Befehle sind die folgenden Schritte nötig: Der Benutzer muß das Byte 5Ah an die Adresse 2002h des Programmspeichers legen, um dem System die Erweiterung mitzuteilen. Zusätzlich muß an der Adresse 2048h ein Unterprogramm stehen, das Bit 45 des internen RAM setzt. An den Adressen 2070h und 2078h sind kurze Unterprogramme bereitzustellen, die den Datapointer jeweils auf den Anfang der Tabellen für Sprungvektoren und für Schlüsselworte stellen. Die Vektortabelle besteht aus einer Aneinanderreihung von Adressen. Die Tabelle der neuen Schlüsselworte enthält jeweils ein neues BASIC-Token (10h, 11h usw. bis 1Fh), das Schlüsselwort im ASCII-Text und ein Trennzeichen 00. Das letzte Wort der Liste wird von FFh gefolgt.
;BASIC-Befehlserweiterung (BAS52_IO.ASM) 2002 .org 2002H 2002 5A .byte 5AH ;Erweiterung vorhanden 2003 2048 .org 2048H 2048 D2 2D setb 45 ;Erweiterung aktiv 204A 22 ret 204B 2070 .org 2070H 2070 90 20 7C MOV DPTR,#VECTOR_TABLE ;Vektortabelle 2073 22 ret 2074 2078 .org 2078H 2078 90 20 82 MOV DPTR,#USER_TABLE ;Schlüsselworte 207B 22 ret 207C 207C .msfirst ;Highbyte - Lowbyte 207C 42 50 VECTOR_TABLE .word LCDInit 207E 42 7F .word CURSOR 2080 43 00 .word AD 2082 2082 10 USER_TABLE .byte 10H ;Basic-Token 2083 4C4344494E49 .text "LCDINIT" ;Schlüsselwort 2089 54 208A 00 .byte 00H ;end 208B 11 .byte 11H 208C 435552534F52 .text "CURSOR" 2092 00 .byte 00H 2093 12 .byte 12H 2094 41 44 .text "AD" 2096 FF .byte 0FFH ;Tabellenende 2097
Der Maschinencode der Befehlserweiterung läßt sich mit einigen BASIC-Zeilen in das RAM kopieren.
1000 REM Spracherweiterungen LCD und AD-Wandler (TOKEN.BAS) 1010 MTOP=8191 1999 REM 0 1 2 3 4 5 6 7 2000 DATA 000H,000H,05AH,000H,000H,000H,000H,000H 2001 FOR N=0 TO 7 : READ D: XBY(02000H+N)= D: NEXT N 2048 DATA 0D2H,02DH,022H,000H,000H,000H,000H,000H 2049 FOR N=0 TO 7 : READ D: XBY(02048H+N)= D: NEXT N 2070 DATA 090H,020H,07CH,022H,000H,000H,000H,000H 2078 DATA 090H,020H,082H,022H,042H,050H,042H,07FH 2080 DATA 043H,000H,010H,04CH,043H,044H,049H,04EH 2088 DATA 049H,054H,000H,011H,043H,055H,052H,053H 2090 DATA 04FH,052H,000H,012H,041H,044H,0FFH,000H 2091 FOR N=0 TO 39 : READ D: XBY(02070H+N)= D: NEXT N
Die Spracherweiterung läßt sich ohne weiteres auch in einem EPROM unterbringen. Brennt man zusätzlich die unteren 8 K des BASIC-52-Systems in dasselbe 32-K-EPROM, dann läßt sich ein CMOS-System mit einem 80C32 aufbauen. Die Begleitdiskette enthält das komplette System in der Datei BASIC52X.BIN.
Das kleine Beispielprogramm ADLCD2.BAS demonstriert die Anwendung der neuen Befehlsworte LCDINIT, AD und CURSOR.
100 REM Vierkanal-Voltmeter (ADLCD2.BAS) 110 LCDINIT 120 FOR N=0 TO 3 130 AD (128+64+16*N+14) : REM Kanal 1,3,5,7 140 POP D 150 IF N<2 THEN CURSOR (8*N) 160 IF N>1 THEN CURSOR (64+8*(N-2)) 170 PRINT @ D,"mV", 180 NEXT N 190 FOR I=1 TO 500 : NEXT I 200 GOTO 120
Der BASIC-52 verfügt über eine zweite, softwaremäßig realisierte serielle Schnittstelle zum Anschluß eines Druckers. Der Datenausgang liegt an P1.7 und muß durch einen Leitungstreiber invertiert werden. Dazu könnte man den noch freien Treiber im MAX232 einsetzen. Abb. 8.4 zeigt dagegen den Anschluß über einen einfachen Transistortreiber.
Abb. 8.4 Anschluß eines Druckers an den 8052AH-BASIC
Die Zeichenausgabe über die zweite serielle Schnittstelle erfolgt mit PRINT#. Über das BAUD-Kommando muß zuvor die Übertragungsrate eingestellt werden.
100 REM 4-Kanal-Messung und Ausgabe (PRINTER.BAS) 110 BAUD 1200 120 TIME=0 : CLOCK 1 : REM Echtzeituhr starten 130 ZEIT=60 : REM Messung nach 60 Sekunden 140 T=1 : REM 1. Messung 150 DO : WHILE TIME<(ZEIT*T) 160 GOSUB 500 170 T=T+1 180 GOTO 150 500 REM Messung und Ausgabe 510 PRINT #T," ", 520 FOR N=0 TO 3 530 AD (128+64+16*N+14) : REM Kanal 1,3,5,7 540 POP D 550 PRINT #D, 560 NEXT N 570 PRINT #"mV" 580 RETURN
Abb. 8.5 zeigt einen Beispielausdruck des Programms. Mit einem Papierverbrauch von etwa einer Seite pro Stunde lassen sich Langzeitprotokolle für bis zu acht Meßkanäle aufnehmen.
Abb. 8.5 Ein Beispielausdruck des Programms PRINTER.BAS