Die Programmierung des 2051ers ist nicht schwer. Zunächst muß man sich jedoch über die einzusetzende Programmiersprache im klaren werden, bevor man daran gehen kann, eigene Projekte zu verwirklichen. |
Die Programmierung des Microcontrollers geschieht entweder über einen
geeigneten Assembler, C, C++ oder Basic. Da es nicht schwer ist, einen
fertigen Compiler auf die MCS-51 - Plattform umzusetzen, kann auch mit
weiteren Programmiersprachen gerechnet werden. Zumeist handelt es sich
jedoch dabei um kommerzielle Produkte, die als Zielgruppe professionelle
Entwickeler haben. Das bedeutet, daß der Preis für Hobbyentwickler
ins unerschwingliche tendiert. Es gibt jedoch auch gute freie Software,
die durchaus komfortabel zu bedienen ist. Da der Speicher des 2051 recht
klein ist und das Umsteigen von anderen Programmiersprachen bei der einfachen
Architektur nicht schwer fällt, ist Assembler die erste Wahl als Programmiersprache.
Zu empfehlen ist ASEM-51. Er ist kostenlos von http://plit.de/asem-51 zu beziehen. Der ASEM-51 ist ein sogenannter Cross-Assembler, das heißt, |
es ist ohne Änderungen am Code möglich, Maschinencode für
viele Plattformen zu erstellen.
Damit 2051-Code erzeugt wird, müssen
folgende Zeilen an den Anfang eines eigenen Programms geschrieben werden:
$NOMOD51 $INCLUDE (89c1051.mcu)Da sich der ASEM-51 vollständig über die Kommandozeile steuern läßt, ist das Integrieren in bestehende Entwicklungsumgebungen sehr einfach. Er müßte sogar im DOSEMU von Linux laufen, so daß auch Entwicklungen unter diesem Betriebssystem möglich sind. zurück zum Anfang |
Die Assemblersprache ist die Programmiersprache, die der Maschine und ihrer
Maschinensprache am nächsten steht. Daher sind auch nur Operationen
möglich, die der Prozessor direkt ausführen kann. In unserem
Falle heißt das, daß also nur 8-Bit-Variablen direkt in Rechnungen
eingesetzt werden können und Berechnungen nicht mit Hochsprachenkonstrukten
wie a=(127+34)*2 durchgeführt werden, sondern mit den low-Level-Befehlen
des Prozessors:
MOV A,#127 ADD A,#34 MOV B,#2 MUL ABZur Erklärung: zunächst wird in das Accumulator-Register (eine Art Variable, die für Berechnungen und Vergleiche jeder Art eingestetzt wird) die Konstente 127 geladen. Dann wird auf das A-Register die Konstante 34 addiert. Das Ergebnis befindet sich dann ebenfalls in A. Weiterhin wird die Konstante 2 in das B-Register geladen (Multiplikationen sind nur mit den A- und B-Registern möglich, nicht mit direkt amgegebenen Konstanten) und multipliziert. Das sieht zunächst umständlich aus, ist aber nach einer kleinen Eingewöhnungsphase leicht nachzuvollziehen. |
Und
es bietet den Vorteil, den Code direkt zu optimieren: man kann die letzten
beiden Zeilen auch durch ein
RLC Aersetzen. Dies ist ein Rotationsbefehl, der das A-Register um ein Bit binär nach links dreht. Da das Binärsystem auf der Basis der Zahl 2 aufbaut (n*2^0 + n*2^1 + n*2^2 ...) im Gegensatz zum Dezimalsystem (n*10^0 + n*10^1 + n*10^2 + ...) wirkt jede Verschiebung um eine Ziffer nach links wie eine Multilpikation mit 2. Gespart hat man dabei 2 Byte im Programmspeicher und ganze 4 Maschinenzyklen! Zudem ist es in Maschinensprache möglich, Timings exakt durch einfaches Auszählen der benötigten Maschinenzyklen festzulegen und so z.B. zwei beliebige I/O-Lines per Software zu einer vollwertigen I²C-Schnittstelle auszubauen und damit auf einfachste Weise Peripherie anzuschließen. (Wie das geht, können Sie in den Application-Notes von ATMEL unter dem Stichpunkt 'Two wire periphal expansion' nachlesen .) Weil die Asseblersprache auf die Hardware direkt, also ohne den Umweg eines Compilers, der Hochsprachenkonstrukte übersetzt, zugreift, können allerdings auch nur Funktionen ausgeführt werden, die im IC fest 'verdrahtet' sind. Dieser Umstand führt dazu, daß also wie schon oben angesprochen Multiplikationen nur zwischen dem A- und B-Register möglich sind und bedingte Sprünge nur das Carry-Flag oder den ACC als Bedingung haben. zurück zum Anfang |
Um diese Abhängigkeiten weiter zu verstehen, bietet es sich
an, die MCS-51 - Architektur näher zu erläutern.
Es gibt zum einen das Flash-ROM der MCU. Darin wird der statische Teil des Programms festgehalten, der von Assembler erzeugt wurde - das sind das Programm sowie Tabellen oder Datenfelder. Der 2051er enthält 2kByte PEROM. Weiterhin gibt es das RAM (128 Byte). Der RAM-Inhalt wird zur Laufzeit, also bein Ausführen des Programms beschrieben und dient als Variablen, Stapelspeicher und so weiter. Zur Manipulation und Bearbeitung von Daten und Kontrollstrukturen dienen die Register. Viele Befehle können auch direkt mit Daten aus dem RAM durchgeführt werden. Der Zugriff über die Register ist aber in einigen Fällen schneller. Der Accumulator (A-Register) ist das zentrale Arbeitsregister. Viele Befehle benötigen einen Operanden im A-Register. Das B-Register wird hauptsächlich für Multiplikationen benötigt. Weiterhin gibt es noch den Stack-Pointer SP, der als Zeiger auf den Stapelspeicher dient, welcher u.a. die Rücksprungadressen bei Unterprogrammaufrufen aufnimmt. Von elementarer Bedeutung sind die Register zur Steuerung der Hardware. Mit ihnen können z.B. die I/O-Ports des AT89C2051 angesprochen werden (P1, P3), die Zähler/Timer in ihrem Verhalten gesteuert werden (TCON, TMOD, TLn, THn...), die serielle Kommunikation gesteuert werden (SCON, SBUF) oder die Stromsparmechanismen ausgelöst werden (PCON). Alle diese Register werden über ihre Adresse scheinbar als RAM |
angesprochen (Adressen über 128 Byte). Die genaue Lage ist der SFR
Map aus dem ATMEL-Datenblatt aus Seite 4-18 zu entnehmen. Glücklicherweise
definiert aber der Assembler für uns symbolische Namen für die
Registeradressen, so daß das A-Register auch als 'A' oder 'ACC' in
Programmen eingesetzt werden kann, wobei der erste Ausdruck das Register
selbst, der zweite die Speicherstelle in der Special Function Map meint,
die dasselbe Register repräsentieren. Wichtig ist dieser Umstand
zum Beispiel bein PUSH-Befehl, der nicht mit dem A-Register, wohl aber
mit einer Speicherstelle wie ACC durchgeführt werden kann.
Weiter gibt es noch eine Serie von 8 allgemein verwendbaren Registern (R0 - R7), die auf 4 verschiedene Bereiche umgeschaltet werden können, so daß z.B. das Sichern von Registerinhalten bei Interruptaufrufen wie bei den Intel-CPUs bei geeigneter Programmierung entfallen kann. Diese Register liegen im RAM ab 00h bis 1Fh. Es ist also unbedingt darauf zu achten, diese RAM-Bereiche nicht leichtfertig zu überschreiben! Diese Gefahr besteht übrigens auch bei Unterprogrammaufrufen, da in diesem Fall der Stack beschrieben wird. Der SP muß also auf im Programm nicht benutzte RAM-Bereiche zeigen. Wo wir gerade beim RAM sind: die meisten allgemeinen Register sind direkt Bitadressierbar, indem man hinter den Regsiternamen einfach noch einen Punkt und die Bit-Nummer schreibt, z.B. P1.7, A.0 etc. Aber auch ein Teil des RAMs ist direkt Bitadressierbar, und zwar der Bereich von 20h bis 2Fh (Bit 00h bis Bit 7Fh). zurück zum Anfang |
Die Befehle gliedern sich grob in mehrere Bereiche:
|
Wem diese Informationen nicht ausführlich genug sind, der wird auf Intels Developer-Page fündig: An Introduction to the Intel MCS-51 Single-Chip Microcomputer Family ist eine ausgezeichnete Einführung, und wer sich die Online-Kosten für die 15 MB des "MCS-51 MICROCONTROLLER FAMILY USER’S MANUAL" leisten kann, bekommt auf 334 Seiten eine Referenz, die keine Fragen offen läßt. Man kann aber auch auf der Intel-Homepage eine CD kostenlos ordern, die sämtliche Datenblätter und Schaltungsbeispiele sowie auch diese Referenz enthält. zurück zum Anfang |
Als kleine Zugabe besitzt der 2051er einige spezielle Features, die bei
kleineren Projekten externe Logik einsparen hilft. Dies ist ein UART,
der eine serielle Verbindung ohne größere Programmierkenntnisse
zu anderen Microcontrollern oder sogar zum PC aufbauen kann.
Weiter verfügt der AT89C2051 über 2 Timer/Zähler, die zum einen Interrupts auslösen und zum anderen numerisch abgefragt werden können. Wenn der Timer 1 zur Baudratenerzeugung genutzt wird, steht selbstverständlich auch seine Zähler-Funktion nicht mehr zur Verfügung. |
Als drittes Goodie besitzt der 2051 einen Präzisions-Spannungsvergleicher,
dessen Ergebnisse aus Port P3.6 ausgelesen werden können. Seine Funktion
besteht darin, eine 1 auf P3.6 auszugeben, sobald die Spannung an P1.0
größer ist als die an P1.1. Das klingt zunächst simpel,
erlaubt aber sogar, einen Analog-Digital-Wandler mit einem Widerstandsnetzwerk
an den anderen Pins als Vergleichstwert aufzubauen.
zurück zum Anfang |
Hier finden sich die im Text verstreuten Literaturquellen und Links zum Thema noch einmal zum Nachschlagen und Wiederfinden | |
http://plit.de/asem-51 | Der Assembler ASEM-51 für DOS, Windows und Linux |
http://www.fsinc.com/devtools | Hier gibt's die Demo-Version der Franklin-IDE mit Assembler, C-Compiler, Debugger und Simulator. |
http://www.8052.com/tut8051.htm | Sehr gute Einführung in die Programmierung |
http://www.atmel.com/atmel/products/prod72.htm | Application Notes von ATMEL zum Anschluß von I2C-Bausteinen an den Microcontroller |
http://www.atmel.com/atmel/acrobat/doc0368.pdf | Atmel-Datenblatt 89C2051 |
http://www-us2.semiconductors.philips.com/acrobat/3082.pdf | '80C51 family programmers guide and instructuion set', Philips' Referenzdokument für 8051-Kompatible |
http://www.intel.com/design/mcs51/applnots/01502a01.htm | 'An Introduction to the Intel MCS-51 Single-Chip Microcomputer Family', Intels Einführung in den 8051er |
http://www.intel.com/design/mcs51/manuals/272383.htm | 'MCS-51 MICROCONTROLLER FAMILY USER’S MANUAL' Referenzhandbuch von Intel, ausführlicher geht es nicht mehr- (13 MB groß) |