Erste Schritte mit dem 2051

Evaluation-Board   Grundfunktionen einsetzen   UART benutzen   Script zur Baudratenberechnung   Einsatz des Timers und des Interruptcontrollers   Bibliographie
Sie haben den Programmer gebaut, ausprobiert und alles hat funktioniert. Sie haben mal richtig investiert und nun 10 89C2051 auf der hohen Kante, inclusive Schwingquarz und allem drum und dran. Sie haben Software und Dokumentation aus dem Internet geladen und brennen nun voller Tatendrang darauf, alles auszuprobieren. Dieser Artikel beschreibt in Ansätzen, wie. Um Details und genaue Daten zu erhalten, die die benutzten Spezialregister erklären, laden Sie am besten '80C51 family programmers guide and instruction set' von Philips Semiconductors, diese Beschreibung ist kurz und prägnant.

Evaluation-Board

Evaluation-Board

Es ist nicht viel Hardware notwendig, um den 2051 zum Laufen zu bringen. Bis auf P1.0 und P1.1 haben alle I/O's integrierte Pullups. Diese sind für einfache Belange und zum ausprobieren völlig ausreichend, wenn auch für die direkte Ansteuerung von LED's unterdimensioniert. Der integrierte Oszillator ist schon mit nur 2 kleinen Kondenstoren und dem 24-MHz-Quarz zum Schwingen zu bewegen.
Es bietet sich aber an, auf dem Testboard eine automatische Resetschaltung und einen manuellen Resetknopf unterzubringen, die bei jedem Einschalten dafür sorgt, daß sich der Controller in einem definierten Zustand befindet. Der Reset-Knopf braucht nur den RST-Pin (Pin 1) nach Vcc zu schalten. Die automatische Resetschaltung besteht im einfachsten Falle aus einem Widerstand gegen Masse, der dafür sorgt, daß der Resetpin low ist, aus einem kleinen Kondensator gegen Vcc, der einen kurzen Puls beim Spannungswechsel beim Einschalten durchläßt, und einem hochohmigen Widerstand, der den Kondensator überbrückt und damit entlädt. 
Ein Entkopplungskondensator, der die Spannungsversorgung in unmittelbarer Chipnähe puffert, ist bei einem stabilisierten Netzteil in der Regel nicht notwendig und kann weggelassen werden.
Als praktikabel hat es sich erwiesen, die Ports im Layout (download hier) in geordneter Reihenfolge auf der Platine nach außen und die Spannungsversorgung zentral zu legen.
Benutzt man Lötstifte (1,3 mm oder 1 mm) als Anschlüsse, hat man zum einen die Möglichkeit, Meßgeräte leicht anschließen zu können, zum anderen, mit Kroko-verbindern oder Kabeln mit Lötschuhen sehr leicht Schaltungen zum Entwickeln zusammenstellen zu können.
Diverse Experimentierplatinen
zurück zum Anfang

Grundfunktionen benutzen

Nach dem Einsetzen eines programmierten 2051ers und dem Einschalten der Spannungsversorgung nimmt das Board dank der integrierten Resetschaltung umgehend seine Arbeit auf. Die primäre Funktion des Microcontrollers besteht darin, Eingaben von den I/O-Lines entgegenzunehmen und nach einer Verarbeitung wieder auszugeben. Dazu steht P1 als 8-Bit Ein- und Ausgang zur Verfügung, P3 bietet sich Aufgrund der sehr oft gebrauchten Spezialverschaltung mit UART, Timer und Interruptcontroller als I/O einzelner Signale auf den in der aktuellen Verschaltung nicht gebrauchten Pins an - prinzipiell ist die Ansteuerung beider Ports gleich.
Um ein einzelnes Byte auszugeben genügt ein einzelner mov-Befehl:
    mov P1,#123 ;Ausgabe der Zahl 123
    mov P1,R0   ;Ausgabe des Registers R0 beziehungsweise der RAM-Adresse 0
    mov P1, 0   ;ist daher prinzipiell dasselbe, auf der Registerbank 0 (Voreinstellung)
    

Für die Manipulation einzelner Bits dienen zum einen die Bitbefehle:

    setb P3.7   ;P3.7 (Pin 11) setzen
    clr P3.0    ;P3.0 löschen
    cpl P1.1    ; P1.1 negieren( 0->1, 1->0 )
    
Zum anderen lassen sich Bits auch mit Befehlen, die auf das ganze Byte wirken, verändern:
    mov a,#00010010b
    rl a        ;rotiert den ACC nach links, 00010010             ->  00100100
    anl a,#0Fh  ;logisches UND,              00100100 & 00001111  ->  00000100
    orl a,#0F0h ;logisches ODER,             00000100 | 11110000  ->  11110100
    
zurück zum Anfang

UART benutzen

Das Einsetzen der seriellen Schnittstelle ist nicht allzu komplex, da der integrierte UART die meiste Funktionalität in Hardware ausführt und nur einige Rahmenbedingungen geschaffen werden müssen. Dabei muß der Timer 1 zur Baudratengenerierung programmiert werden. Dazu genügt es, im 8 Bit-Modus mit Autoreload eine konstante Frequenz bereitzustellen, die der UART übernimmt. Wie dies genau geschieht, ist weiter unten oder in diversen anderen Quellen wie zum Beispiel der Intel-Homepage (Stichwort: A Simplified Guide to Using the MCS® 51 On-chip UART ) nachzulesen. Bei der Verbindung zu einem anderen Microcontroller ist nichts weiter zu beachten, außer daß beide mit der selben Baudrate und den selben Paritätseinstellungen programmiert wurden. Das dürfte nicht allzu schwierig sein, wenn beide über einen Schwingquarz der gleichen Frequenz betrieben werden. Bei der Verbindung zum PC jedoch muß beachtet werden, daß die serielle Schnittstelle nicht mit den TTL-Pegeln des Controlers, sondern üblicherweise mit +/-12V angesprochen werden muß, wobei -12V logisch 1 und +12V logisch 0 entspricht. Um nur eine Verbindung vom PC zum Controller zu etablieren,ist ein Widerstandsnetzwerk und eine Diode nach folgendem Schema vor RxD des 2051 zu schalten:
Anleitung zum Anschluß der seriellen Schnittstelle des PC

Die serielle Schnittstelle des PC erwartet weiterhin einen Handshake über die Steuerleitungen, so daß man mit einem dreiadrigen Kabel (TxD, RxD und Signalmasse) nicht auskommt - es sei denn, man verbindet am Stecker DTR,DSR,CD miteinander und RTS und CTS ebenfalls miteinander, dann wird eine Antwort des 'Modems' simuliert, indem ein 'request to send' der Schnittstelle automatisch an der Steuerleitung 'clear to send' anliegt, sowie 'data terminal ready' der Schnittstelle automatisch ein 'carrier detect' und 'data set ready' auslöst. Die Anschlußnummern der Leitungen im Stecker, die verbunden werden müssen, kann man der folgenden Aufstellung entnehmen:
Anschlußschema eines Microcontrollers an die serielle Schnittstelle des PC's
Allerdings wird dieser Handshake nicht von allen Betriebssystemen, Programm-API's oder Schnittstellenbausteinen verwendet, daher kann unter Umständen darauf verzichtet werden - aber sicherer ist es allemal, den geringen Aufwand mit den Verbindungen im Stecker in Kauf zu nehmen, insbesondere wenn die Schaltung nicht nur am eigenen Rechner korrekt arbeiten soll.
Die Programmierung des UART geschieht im wesentlichen im SCON-Register:
SM0SCON.7Operationsmodus
SM1SCON.6Operationsmodus
SM2SCON.5Wenn SM2=1: Im Modus 2 oder 3 wird RI nicht aktiviert wenn das 9. Datenbit 0 ist. Im Modus 1 wird RI nicht aktiviert, wenn kein gültiges Stop-Bit empfangen wurde. Im Modus 0 sollte SM2 0 sein.
RENSCON.4Gelöscht um den Empfang zu stoppen
TB8SCON.3Das 9. Bit, das in den Modi 2 und 3 übertragen wird.
RB8SCON.2Das 9. Bit, das in den Modi 2 und 3 empfangen wurde. Im Modus 1 bei SM2=0 das empfangene Stopbit.
TISCON.1Wird von der Hardware gesetzt, sobald ein Byte übertragen wurde und der Transfer abgeschlossen ist. Muß von der Software gelöscht werden, um das nächste Byte zu senden.
RISCON.0Wird von der Hardware gesetzt, sobald ein Byte empfangen wurde und in SBUF abgeholt werden kann. Muß von der Software gelöscht werden, um das nächste Byte empfangen zu können.

SM0 SM1 Modus Benennung Baudrate
0 0 0 Register verschieben FOSC./12
0 1 1 8 Bit UART variabel
1 0 2 9 Bit UART FOSC/64 oder FOSC/32
1 1 3 9 Bit UART variabel
Eine Datenübertragung von der MCU zum PC ist nicht ganz so einfach, da wenigstens -5V zur Verfügung stehen müssen. Ein praktischer Weg ist es daher, einen darauf spezialisierten IC wie den MAX232 einzusetzen, der schon die NOT-Gatter für die Umsetzung enthält und +/-10V durch eine eingebaute Pumpschaltung erzeugt.
MAX232-Anschlußschema Eine gelungene Einführung zum MAX232 findet sich auf Die Anpassung der RS232 Schnittstellensignale.
Weil außerdem die serielle Schnittstelle des PC nicht allzu variabel Baudraten erzeugen und lesen kann, ist eine Baudrate zu programmieren, die der PC auch versteht, zum Beispiel 4800 bps, 9600 bps etc. Die in den Timer (das TH1 und in das TL1-Register) zu programmierenden Werte berechnen sich wie folgt:
                 K * Oszillatorfrequ.
   Baud-Rate=  ------------------------
                 32 * 12 * (256- TH1 )

Wobei K=1, wenn SMOD (PCON.7, Baudratenverdoppler) 0 ist und K=2 wenn SMOD=1. Genaue Baudraten sind freilich nur mit 'krummen' Quarzen wie 11,0592 MHz zu erreichen, aber die serielle Schnittstelle des PC ist nicht zimperlich - jedenfalls im Allgemeinen. Wenn jedoch die Abweichung vom Ideal fast 10% beträgt, ist das Stopbit noch nicht oder schon längst gesendet, wenn der UART der seriellen Schnittstelle es erwartet!
Zum Ausprobieren steht nun das folgende in JavaScript geschriebene Programm zur Verfügung:
Baudratenparameterberechnung
einzugebende Parameter Ergebnisse  
eingesetzter Quarz (in MHz) Wert für TH1/TL1
gewünschte Baudrate auf eine ganze Zahl gerundet
Taktverdoppler PCON.7 gesetzt tatsächliche Baudrate
(SMOD) nicht gesetzt damit beträgt der Fehler
Die Abfrage der seriellen Schnittstelle auf MCU-Seite sieht dann wie folgt aus (für einen 24MHz-Quarz und 2400 Baud):
    $NOMOD51
    $INCLUDE (89C1051.MCU)
    ; eingestellt sind ungefähr 2400 baud
    ; Startadresse des Codes:
           org 0h
    ; Stack auf 30h hinter Registerbänke und Bit-Area setzen
           mov sp,#30h
    ; Timer programmieren
           mov PCON,#00h
           mov TMOD,#22h
           mov TH1, #230
           mov TL1, #230
           setb TCON.6
           mov SCON,#052h
    
MAX232 - Abbildung
    start:
    ; auf empfangenes Byte warten
           jnb SCON.0, start
    ; wenn da, ausgeben
           mov P1,SBUF
    ; den UART wieder empfangsbereit machen
           clr SCON.0
           ajmp start
    END
    

Als kleines Beispiel auf PC-Seite kann das folgende BASIC-Programm dienen.

    OPEN "COM2:2400,N,8,1,BIN" FOR OUTPUT AS #1
         DO
           FOR z = 0 TO 255
           PRINT #1, CHR$(z);
           PRINT z
           FOR q = 1 TO 100000: NEXT
           NEXT
         LOOP
    

Einsatz des Timers und des Interruptcontrollers

Der Timer und der Interrupt-Controller sind noch einfacher zu programmieren als der UART. Nach dieser Beschreibung sind Sie also in der Lage, im Prinzip jede Funktion des 2051 erfolgreich einzusetzen. (Der integrierte Spannungsvergleicher ist so trivial zu programmieren, daß es sich nicht lohnt, darüber auch nur ein Wort zu verlieren.)
Die beiden integrierten Timer arbeiten prinzipiell gleich. Sie zählen von einem in TLn und THn eingestellten (16 oder 13 Bit-Modus) Wert
bis 65535 (16 Bit-Modus), 8191 (13 Bit-Modus) oder 255 (8 Bit-Modus). Gezählt wird jede 12. Oszillatorschwingung, also jeder Maschinenzyklus oder bei einem 24 MHz Quarz mit 2 MHz. Einzig der 8 Bit-Modus ist in der Lage, einen Autoreload durchzuführen, das heißt den Wert von THn nach TLn zu kopieren, wenn der maximale Zählerstand erreicht ist. Die Modi werden im TMOD-Register nach folgender Übersicht eingestellt.
M1 M0  Operationsmodus
0 0 0 13-bit Timer (8048 kompatibel, unüblich zu benutzen)
0 1 1 16-bit Timer/Zähler
1 0 2 8-bit Auto-Reload Timer/Zähler
1 1 3 (Timer 0) TL0 ist ein 8-Bit Timer/Zähler, der von den Timer0-Kontrollbits gesteuert wird TH0 ist ein 8-Bit Timer/Zähler, der von den Timer1-Kontrollbits gesteuert wird
  (Timer 1) Timer/Zähler angehalten

Weiterhin kann im TMOD-Register noch in GATE-Bit eingestellt werden, ob der Timer nur laufen soll, während INTn high ist (GATE=1, Hardware-Kontrolle) oder nur laufen soll, wenn TRn im TCON-Register 1 ist (GATE=0, Software-Kontrolle).
76543210
GATEC/TM1M0GATEC/TM1M0
Timer 1 Timer 0

Gesteuert wird der Timer vom TCON-Register. In diesem wird das Verhalten des Timers zur übrigen Hardware geregelt. Von Bedeutung ist zunächst nur TRn, mit dem der Timer (TRn=1) eingeschaltet wird. TR0 liegt an Adresse TCON.4, TR1 an Adresse TCON.6 . Genaueres zum TCON-Register kann dem Intel-Manual oder dem 80C51 family programmers guide and instruction set von Philips.
Nun zum Interrupt-Controller: er kann von 5 Quellen ausgelöst werden, von den I/O-Ports P3.2, P3.3, von den beiden Timern und von dem integrierten UART, der damit Bescheid gibt, ob der Puffer leer bzw. ein Datenbyte gesendet oder empfangen wurde. Damit kann verhindert werden, das der 2051 kostbare Rechenzeit in sinnlosem warten auf irgendwelche Ereignisse verbringt. Leider hat man den MCS-51-Microcontrollern nur einen Interrupt spendiert, um auf den UART zu reagieren. Das bedeutet, daß die Software anhand SCON.1 (Byte gesendet) oder SCON.0 (Byte empfangen) feststellen muß, welches Ereignis denn nun vorliegt.
Die Interrupts brauchen nur eingeschaltet zu werden, um eingesetzt werden zu können. Dies geschieht im IE (interrupt enable) Register. Wird ein Interrupt ausgelöst, wird vom Programm weg zu dem jeweiligen Interruptbereich im Flash-ROM gesprungen. Dabei wird nur der alte Instruktion Pointer auf dem Stack gesichert, sonst nichts. Deswegen muß also jedes Register, das in der Interrupt-Serviceroutine benutzt wird, unbedingt auch auf dem Stack gesichert werden. Der Interruptbereich ist für jeden Interrupt 8 Byte groß. Wenn die Behandlungsroutine darin keinen Platz findet, muß zu einem Bereich weiter hinten im Flash-ROM verzweigt werden. Die Interruptbehandlung wird im Gegensatz zu einem normalen Unterprogrammrücksprung mit einem RETI beendet. Interruptvektoren und die damit korrespondierenden Enable-Bits entnehmen Sie der unten stehenden Tabelle.
 NameVektoradresseBeschreibung
IE.7EA Wenn EA=0 wird kein Interrupt zugelassen. Damit können also alle Interruptquellen auf einmal aus- und eingeschaltet werden.
IE.6-  nicht belegt
IE.5-  nicht belegt
IE.4ES0023h schaltet den Interrupt des UART ein
IE.3ET1001BhTimer 1
IE.2EX10013hexterner Interrupt 1 (P3.3)
IE.1ET0000BhTimer 0
IE.0EX00003hexterner Interrupt 0 (P3.2)
Eine Besonderheit gibt es noch bei den externen Interruptquellen: sie lassen sich in TCON-Register zwischen 2 Modi umschalten. Im einen wird ein Interrupt ausgelöst, wenn die INTn-Leitung auf low gezogen wird (ITn im TCON-Register = 0), im anderen wird ein Interrupt bei einer Veränderung auf der Leitung ausgelöst (edge-triggered, ITn=1). IT0 liegt auf TCON.0, IT1 auf TCON.2 .
Die Berechnung des Timer-Aufrufes geschieht wie folgt:

                                Oszillatorfrequenz
preload-Wert  =  max. Wert  -  --------------------
                                  12 * Frequenz

Der Wert für das unten stehende Beispiel wurde so ermittelt: PR = 65535 - 24.000.000 / (12 * 500)
Das folgende Beispiel illustriert die Benutzung des Timers und des Interrupt-Controllers. Es wird ein Signal etwa 500 mal pro Sekunde invertiert. Um das Timing exakt zu machen, müßte man zum Preload-Wert noch die Maschinenzyklen, die die Interruptserviceroutine benötigt, dazuaddieren.
    $NOMOD51
    $INCLUDE (89C1051.MCU)

    ;Startadresse des Codes:
            org 0h
            ajmp start

    ;Interruptbehandlung
            org 0Bh
    ; Wert wieder in den Timer laden
            mov TH0, #0F0h
            mov TL0, #05Fh
    ; Bit verändern
            cpl P1.0
            reti

    ;Stack auf 30h hinter Registerbänke und Bit-Area setzen
            mov sp,#30h
    start:
    ; timer programmieren
            mov PCON,#0
            mov TMOD,#1
            mov TH0, #0F0h
            mov TL0, #05Fh
            setb IE.1
            setb IE.7
            setb TCON.4

    ; anhalten
    halt:   ajmp halt

    END
    

Bibliographie

Hier finden sich die im Text verstreuten Literaturquellen und Links zum Thema noch einmal zum Nachschlagen und Wiederfinden
http://www.intel.com/design/mcs51/applnots/2047.htm 'A Simplified Guide to Using the MCS® 51 On-chip UART', Intels Einführung mit Beispielen.
ftp://ftp.philipsmcu.com/PUB/BBS/examples/intrupts.asm Demo of extra external interrupts on C51 von Philips
ftp://ftp.philipsmcu.com/PUB/BBS/examples/51serial.zip Beispielcode für die Ansteuerung des UART von Philips
ftp://ftp.philipsmcu.com/PUB/BBS/examples/math51.zip Code für die Ausführung von Berechnungen mit Zahlen > 8 Bit
ftp://ftp.philipsmcu.com/PUB/BBS/examples/float51.zip Code zum Rechnen mit Fließkommazahlen
Die Anpassung der RS232 Schnittstellensignale Erläuterungen zum MAX232 und zur seriellen Schnittstelle.
zurück zum Anfang

Seite zurück Startseite
Erik Buchmann
EMail an: Owner@ErikBuchmann.de