Microcontrollerprogrammierung in Assembler

1.    Einleitung

1.1   Mikrocontroller oder Mikrorechner?

Ein Mikrocontroller ist ein Prozessor, bei dem im Unterschied zu PC-Prozessoren (Mikrorechnern) Speicher, wichtige Baugruppen wie Zeitgeber, digitale sowie analoge Ein- und Ausgabegeräte, auf einem einzigen Chip integriert sind, so dass eine Mikrocontroller-Lösung oft mit einigen wenigen externen Bauteilen auskommt.

Ein PC-Prozessor verfügt nicht über eigene Eingabe- und Ausgabekomponenten, sondern über eine Verbindung zu einem externen Systembus, an dem die Ein und Ausgabegeräte zum Beispiel als Steckkarten angeschlossen sind. In immer mehr Geräten des Alltags werden die Aufgaben von analogen Schaltungen durch Mikrocontroller realisiert. Damit lassen sich vor allem die Produktionskosten der Hardware drastisch senken. Prinzipiell kann die Struktur und Arbeitsweise von Mikrorechnern und Mikrocontrollern mit der von-Neumann-Architektur erklärt werden.

1.2   von-Neumann-Architektur (ausgewählte Aspekte):

  • Ein Rechner besteht aus einer Zentraleinheit mit Rechenwerk und Steuerwerk, Eingabegeräten, Ausgabe- geräten und einem Arbeitsspeicher
  • Die intern verwendete Signalmenge ist binär codiert (Digitalrechner)
  • Der Rechner verarbeitet Worte fester Länge (Verarbeitungsbreite, Bussystem, Register, Speicher)
  • Die Programmbefehle werden sequenziell in der Reihenfolge ihrer Speicherung abgearbeitet.

      Befehlszyklus:

  1. POWER-ON oder RESET
  2. lade Befehlszeiger mit fester Startadresse (z. B.: OxOO)
  3. lese Befehl (von Adresse im Befehlszeiger)
  4. decodiere Befehl (Steuerwerk)
  5. führe Befehl aus (Rechenwerk)
  6. erhöhe den Befehlszeiger um 1, weiter mit 3.
  • Der Rechner arbeitet taktgesteuert;
  • Die sequenzielle Verarbeitung von Befehlen kann durch Sprunganweisung geändert werden;
  • Befehlszeiger = Sprungadresse, Sprung mit oder ohne Bedingung, Unterprogrammaufruf mit Speicherung der Rücksprungadresse.

Prozessoren werden als erstes an der Verarbeitungsbreite der internen Architektur unterschieden: 8 Bit, 16 Bit, 32 Bit, 64 Bit usw. Diese Bit-Zahl kann man als die Länge der Datenworte interpretieren, die der Controller/Prozessor in einem Befehl verarbeiten kann. Die größte in 8 Bit (=1 Byte) darstellbare Zahl ist die 255. Somit kann ein 8-Bit-Mikrocontroller z. B. in einem einfachen Additionsbefehl nur Zahlen von 0 bis 255 verarbeiten und das Ergebnis kann ebenfalls nicht größer als 255 sein, sonst liegt ein Ergebnis-Überlauf vor. Zur Bearbeitung von größeren Zahlen werden dann mehrere Befehle hintereinander benötigt, was bei diesem Prozessor natürlich länger dauert.

Ein Prozessor benötigt eine meist extern eingespeiste Taktfrequenz. Dieses Taktsignal gibt dem Prozessor vor, wann der nächste Befehlsschritt ausgeführt werden soll. Da die Baugruppen innerhalb des Prozessors weder unendlich schnell noch gleich schnell arbeiten, ist dieses Synchronisationssignal für die Zusammenarbeit von Steuerwerk, Rechenwerk, Ein- und Ausgabegeräten notwendig. Die maximale Frequenz, mit der ein Prozessor betrieben werden kann, reicht heute von 1 MHz bis hin zu über 3 GHz.

Bei Mikrocontrollern sind im Moment Taktfrequenzen von 1 bis 300 MHz üblich. Diese Taktfrequenz sagt jedoch noch nichts über die tatsächliche Geschwindigkeit eines Prozessors aus. Es ist auch entscheidend, ob für den oben beschriebenen Befehlszyklus viele, wenig oder gar nur ein Taktzyklus benötigt wird.

Bei vielen Prozessoren wird die Frequenz intern geteilt, ein mit 24 MHz getakteter Mikrocontroller der Intel Reihe 8051 arbeitet eigentlich nur mit 2 MHz, da der externe Takt durch 12 geteilt wird. Benötigt dieser dann für einen Befehl durchschnittlich 2 Taktzyklen, so bleiben "nur" noch 1 Mio. Befehle pro Sekunde übrig (1 MIPS, Million Instructions per Second). Dieser Sachverhalt trifft besonders auf Prozessoren zu, die eine CISC-Architektur besitzen (Complex Instruction Set Computer). Betrachten wir dazu im Gegensatz eine RISC-Architektur (Reduced Instruction Set Computer), finden wir hier kaum interne Taktteilungen und für die Ausführung eines Befehls wird oft nur ein Taktzyklus benötigt. Ein moderner RISC-Mikrocontroller, der ungeteilt mit 8 MHz arbeitet und für viele Befehle nur einen Prozessorzyklus braucht, schafft gegenüber dem mit 24 MHz getakteten CISC Controller fast 8 Mio. Befehle pro Sekunde (8 MIPS). Ein RISC-Prozessor hat dafür einen geringeren Befehlssatz. Komplexe Befehle wie eine Division, müssen deshalb algorithmisch umgesetzt werden.

1.3   Wozu ist ein Mikrocontroller gut?

Welche Aufgaben Mikrocontroller haben und warum sie eine zunehmende Bedeutung erlangen, soll folgender Einsatzfall verdeutlichen. Uns allen sind Detektoren unterschiedlichster Art bekannt. Am auffälligsten sind die Portalmetalldetektoren in Flughäfen oder wichtigen öffentlichen Gebäuden, aber auch der stationäre Blitzer um die Ecke verfügt als Sensor über einen Metalldetektor in der Fahrbahn. Ein anderer Anwendungsfall ist zum Beispiel die Lebensmittelindustrie. Es ist sehr wichtig für den Hersteller von Backmischungen, dass keine Nägel, Schrauben, Metallsplitter oder ähnliches während des Produktionsprozesses in das Nahrungsmittel gelangt. Auf der Verpackungsstrecke werden alle Packungen, in denen der Metalldetektor Metallteile ortet, automatisch ausgesondert. Metalldetektoren werden in großen Stückzahlen für unterschiedlichste Anwendungsfälle benötigt. Das folgende Schema zeigt die Funktionseinheiten eines Metalldetektors nach dem Pulsinduktionsverfahren (PI).


Abbildung: Metalldetektor nach dem Pulsinduktionsverfahren (PI)

Der klassische Aufbau eines solchen Detektors als diskrete analoge Schaltung erfordert trotz Verwendung integrierter Schaltungen einen bestimmten, kaum weiter reduzierbaren Materialaufwand an aktiven und passiven Bauelementen. Die Materialkosten für diesen konkreten Metalldetektor belaufen sich auf ca. 25 €.


Abbildung: vollst. Aufbau analoger Metalldetektor

Einen nicht unerheblichen Teil dieser diskreten Schaltung macht die Realisierung des "Timings", also der Steuerung der verschiedenen Komponenten für den Detektor aus. Der folgende Detektor hat das gleiche Arbeitsprinzip und die gleiche Leistungsfähigkeit wie der oben gezeigte Detektor. Der Materialaufwand ist jedoch um ein vielfaches geringer (Materialkosten ca. 8 €). Dies wurde erreicht, indem die Steuerung des Detektors von einem Mikrocontroller und der entsprechenden Software darauf übernommen wurde.


Abbildung: vollst. Aufbau Metalldetektor mit Microcontroller


Abbildung: Funktionsverlagerung von der Hardware zur Software

Die zuvor diskret mit analogen Bauelementen realisierten Funktionen des Detektors werden in der mikrocontrollerbasierenden Lösung in die Software verlagert. Damit reduziert sich die Anzahl der aktiven und passiven analogen Bauelemente.

Die Komplexität und Größe der Leiterplatte konnte reduziert werden. In der Gesamtheit konnten die Kosten für die Herstellung eines Detektors auf ein Drittel reduziert werden. Die wirtschaftliche Bedeutung der Reduzierung der Stückkosten, in diesem Fall um 60%, braucht man nicht weiter zu betonen. Aber selbst wenn die Kostenreduzierung durch den Mikrocontrollereinsatz pro System nur einige Cent beträgt, liegt der Kostenreduzierungseffekt in der Massenfertigung schnell bei Millionen Euro.

Ganz klar, die Softwarelösung für den Tongenerator lässt sich auf jedes einzelne System kopieren und verursacht keine Beschaffungskosten mehr. Die diskrete Lösung benötigt eine integrierte Schaltung (IC), 3 Widerstände und einen Kondensator mehr und diese müssen für jedes zu produzierende Gerät neu beschafft werden. Und das ist noch nicht alles. Die Mikrocontrollerlösung könnte sogar beliebige Melodien abspielen, die der Hersteller im Internet zum Herunterladen anbieten kann. Solche zusätzlichen Eigenschaften werden von den Anwendern der Lösungen gern angenommen, wie der aktuelle Trend bei Klingeltönen und Handylogos zeigt. Es werden nicht nur Kosten, Platz und Gewicht gespart, sondern zusätzliche Möglichkeiten eröffnet. Daher auch der ungebrochene Trend, selbst einfache Geräte wie eine Kaffeemaschine mit einem Mikrocontroller auszustatten.

Hier ein paar Beispiele, für welche Aufgaben Mikrocontroller verwendet werden können: Roboter, CD-, MP3- und DVD-Player, Temperaturregler, Füllstandsregler, Motorsteuerungen, Signaldecoder für Satellitenempfang, Fernbedienung, Alarmanlagen, Schaltuhren, Ladegeräte, Waschmaschinen, Geschirrspüler, Fernseher, Radio, Wecker/Uhr, Messwerterfassung (z. B. Drehzahlmessung im Auto), intelligente Geräte in der Auto- matisierungstechnik, intelligente Sensoren, intelligente Aktaren z. B. die Airbags im PKW, Handy, alle Formen von Heimelektronik, Geräte der Medizintechnik, Spielzeug u.v.m. .

Anforderungen und Möglichkeiten von Mikrocontrollerlösungen:

  • programmierbar (Update, Optimierung, Wartung)
  • flexible Schnittstellen (vielfältig, integriert, standardisiert)
  • Selbstdiagnose, Fehlerkorrektur, Debuginterface
  • Echtzeitfähigkeit (schnelle Reaktionszeiten)
  • Timer, Interruptfähigkeit
  • deterministisch (bestimmbares, berechenbares Verhalten)
  • geringe Kosten, geringer Leistungsverbrauch

Die Anwendungsgebiete von Mikrocontrollern sind schier unendlich. In allen Bereichen unseres Lebens lassen sich heute "versteckte" Mikrocontroller finden.

1.4   Welchen Mikrocontroller verwenden?

Mikrocontroller werden von unterschiedlichen Firmen angeboten. Weit verbreitete Mikrocontroller sind die der Intel-Reihe 8051, der Zilog-Reihe Z80, Z86, die PICController der Firma Microsystems und die AVR-Controller der Firma Atmel. Die AVR-Reihen von Atmel haben eine innovative RISC-Architektur, die schnell und einfach zu erlernen ist. Sie sind inzwischen sehr weit verbreitet. Sie sind elektrisch robust und bis zu 10.000-mal programmierbar. Da die AVR-Prozessoren zu den modernsten Controllern am Markt gehören und enorme Zuwachsraten aufweisen, sollen sich alle Ausführungen und die Experimentierhardware auf diese Controller beziehen. Prinzipiell lassen sich jedoch alle Aussagen auf alle anderen Mikrocontroller übertragen.

Ein pikanter Hintergrund ist, dass der AVR-Kern eine Entwicklung von zwei Studenten der Universität Trontheim in Norwegen ist. Atmel kaufte die Lizenz und entwickelte dieses innovative Konzept weiter. Hartnäckig hält sich das Gerücht, dass die Abkürzung "AVR" etwas mit den Vornamen der beiden inzwischen nicht mehr Studenten Alf Egil Bogen und Vegard Wollan zu tun hat, die diesen RISC-Prozessor entwickelt haben.

1.5   Welche Programmiersprache benutzen?

Assembler ist für den Einstieg in die Programmierung von Mikrocontrollern zwar recht schwierig, aber am besten geeignet. Da Assemblerprogramme auf Maschinencodeebene angesiedelt sind, lernt man den Aufbau und Funktionsweise eines Prozessors richtig kennen und kann ihn dadurch optimal ausnutzen. Zudem stößt man bei jedem Compiler irgendwann einmal auf Problemstellungen, die sich nur durch das Verwenden von Assemblercode lösen lassen. Die zunehmende Leistungsfähigkeit und immer mehr Speicher auf den Mikrocontrollern erlauben inzwischen auch die Anwendung von höheren Programmiersprachen. Die Sprachen C/C++ bieten sich auf Grund ihrer Effizienz und Hardwarenähe für die Programmierung von komplexen Mikrocontrollerlösungen an.

Der AVR Assembler wird Ihnen Schritt für Schritt und verständlich vermittelt. Mit den so erworbenen Kenntnissen ist die Programmierung von AVR-Controllern mit jeder anderen Sprache noch effizienter. Die Auseinandersetzung mit den Maschinenbefehlen und der Programmierung in Assembler führt zum besseren Verständnis des Aufbaus und der Arbeitsweise des Prozessors/Mikrocontrollers.


Abbildung: AVR- Assemlerprogramm

2.   Grundlagen zu Mikrocontrollern

In diesem Kapitel soll nochmals kurz auf ausgewählte Aspekte der Hardware und Software eingegangen werden. Das myAVR Board wird als Referenzhardware vorgestellt.

2.1   Hardwaregrundlagen

Mikrocontroller gehören zu der Klasse der frei programmierbaren Universalprozessoren. Programmierbare Prozessoren gibt es in unterschiedlichen Ausprägungen und Einsatzfeldern. Im Folgenden eine Auswahl von verschiedenen Prozessorklassen:

Mikroprozessor:
Ein "herkömmlicher" Prozessor, wie er auch in PCs zu finden ist. Die Verbindung mit der Außenwelt erfolgt ausschließlich über weitere Bausteine in einem Bussystem. Fokus: allgemeine Aufgaben, Leistung, Flexibilität, Standardhardware, Standardsoftware.

Mikrocontroller:
Ein Mikrocontroller beinhaltet in einem Chip bereits alle Komponenten, die ihn zu einem funktionsfähigen 1-Chip-IJRechner machen. Er besitzt also neben einem Prozessor auch Speicher, diverse Schnittstellencontroller, Timer und einen Interruptcontroller. Er kann über digitale und analoge Ein- und Ausgabeleitungen Mess- und Steueraufgaben ausführen. Fokus: Spezialisierung auf konkrete Aufgaben, Platzbedarf, Energieverbrauch.

Signalprozessor:
Digitaler Signalprozessor (DSP), Mixed-Signal-Controller. Darunter versteht man Mikrocontroller, die sowohl digitale als auch analoge Signale sehr schnell verarbeiten können. Fokus: Spezialisierung auf Signalverarbeitung (Audio, Video, Datenübertragung), Geschwindigkeit, Platzbedarf, Energieverbrauch.

Embedded Prozessor:
Embedded System: Mikrocontroller oder DSP werden häufig als eingebettete Systeme verwendet. Das sind Systeme, in denen die Steuereinheit im Zielsystem integriert ist. Ein Beispiel wäre ein Mobiltelefon, hier ist der steuernde Controller im Gerät selbst integriert.


Abbildung: Prinzipschaltbild Mikrocontroller

2.1.1   Prozessorkern (Zentraleinheit)

Die Zentraleinheit setzt sich zusammen aus dem Steuerwerk und dem Rechenwerk. Im Steuerwerk befindet sich der Befehlsdecoder. Dieser "erkennt" alle Befehle, die die Zentraleinheit ausführen kann, verteilt die Aufgaben und synchronisiert die einzelnen Elemente der Zentraleinheit sowie die der Ein- und Ausgabeeinheiten.

Das Rechenwerk, die Arithmetic Logic Unit (ALU), führt alle logischen und mathematischen Operationen durch. Für diese Berechnungen sind die Operationen Addition und Subtraktion zuständig. Das Rechenwerk liefert als Ergebnis der ausgeführten Operationen die sogenannten Flags (Ereignisspeicher), die nach einem bestimmten Zustand einer Funktion oder einer Berechnung gesetzt oder zurückgesetzt werden. Diese werden dann als Bedingungen für den Algorithmus benutzt.

Die komplette Zentraleinheit wird in der Fachsprache als Mikroprozessor bezeichnet. Wenn nun alle drei Baugruppen, die Zentraleinheit, der Zentralspeicher und entsprechende Peripheriebausteine, in einem Gehäuse integriert sind, bezeichnet man diesen Prozessor als Mikrocontroller, Ein-Chip-Mikrorechner oder Embedded Computer.


Abbildung: Integration aller Baugruppen auf eiem Chip

Die Integration auf einem Chip hat offensichtlich einen enormen Vorteil in Bezug auf den Platzbedarf. Stellen Sie sich vor, Ihr Handy würde auf einem PC-Mainboard mit den entsprechenden Komponenten basieren. Den hohen Integrationsgrad erkauft man sich aber mit einer viel geringeren Gesamtleistung. Vor allem die Geschwindigkeit und die Speicherkapazität sind wesentlich geringer als bei PC-Systemen. Wo man beim PC auf 512 MB oder 1 GB Arbeitsspeicher zurückgreifen kann, haben Mikrocontroller zum Beispiel 128 Byte oder 512 Byte Arbeitsspeicher. Ein 8 MHz getakteter Mikrocontroller erscheint zuerst viel langsamer als ein mit 3000 MHz arbeitende PC. Betrachtet man aber das Einsatzfeld und die konkreten Aufgaben der Mikrocontroller, reichen oft auch schon 1 MHz aus.

Ein immer wieder zu lösendes Problem der vorgestellten Mikrocontrollerlösungen ist es, das mit 3,6 MHz getaktete System per Wartefunktionen zu bremsen. Unterprogramme und Funktionen wie wait und delay spielen für viele Programme von Mikrocontrollern eine wichtige Rolle. Die Entwicklung bei den Mikrocontrollern geht aber genauso wie bei allen anderen Rechnern zu immer höherer Geschwindigkeit und mehr Speicherkapazität.

2.1.2   Peripherie-Bausteine

Digitale Ein-/Ausgabe-Bausteine

Die wichtigsten Ein-/Ausgabegeräte sind digitale Ein- und Ausgabebausteine. Diese werden als Ports bezeichnet und können digitale Signale (HI/LO, 1/0, 5/0 V) ausgeben oder einlesen. Ein Port besteht meist aus mehreren Lines (Leitungen,Bits). Typisch sind 4, 6 oder 8 Lines je Port. Damit können EIN/AUS-Zustände, Schaltvorgänge, Impulse oder Frequenzen erzeugt bzw. gelesen werden. Die Leitungen sind als Pins des Controllers nach außen geführt. Um analoge Messwerte erfassen zu können, also beliebige Spannungswerte zum Beispiel zwischen 0 und 5 V, benötigt man spezielle Bausteine. Typisch sind hier der Comparator und der Analog-Digital-Wandler (A/D-Wandler, engl.: ADC Analog-Digital-Converter).

Comparator

Ein Comparator kann zwei "beliebige" Spannungen miteinander vergleichen und anzeigen, ob eine Eingabespannung größer oder kleiner als eine vorgegebene Referenzspannung (Schwellwert) ist. Comparatoren sind sehr genau und schnell.

A/D-Wandler

Ein A/D-Wandler kann einen "beliebigen" analogen Spannungswert in einen Zahlenwert umwandeln. Der Wert wird als ganzzahlige Zahl dem Prozessor zur weiteren Verarbeitung bereitgestellt. Die Genauigkeit (Resolution) der Umwandlung des analogen Wertes in einen digitalen Wert entspricht der Anzahl der Bit-Stellen des vom A/D-Wandler gelieferten Digitalwertes. Typisch sind Auflösungen von 4, 6, 8, 10, 12, 16 Bit. A/D-Wandler benötigen für die Konvertierung (Conversion) des Analogwertes eine von Fall zu Fall unterschiedliche Zeit (Conversion Time).

D/A-Wandler Wenn beliebige (analoge) Spannungen ausgegeben werden sollen, kann dies über einen Digital-Analog-Wandler (D/A-Wandler) erfolgen, der aus einer digitalen Zahl des Mikrocontrollers eine entsprechende Ausgangsspannung erzeugt. Es ist aber auch möglich, mit einfachen passiven Bauelementen (Kondensator, Widerstand) aus einer Frequenz unterschiedliche Spannungswerte zu erzeugen.

Timer-Baustein

Viele Aufgaben, die durch Mikrocontroller zu erfüllen sind, erfordern eine zeitabhängige Steuerung. Aber auch umfangreiche Zählvorgänge oder die Verarbeitung von Frequenzen erfordern einen Timer/Counter-Baustein. Für die Messung von Uhrzeit und Tagesdatum gibt es Echtzeit-Uhren-Bausteine (Real Time Clock,RTC).

Sender/Empfänger-Baustein

Zur Übertragung von größeren Datenmengen an andere Systeme oder Bausteine werden spezielle Schnittstellenbausteine eingesetzt, die entsprechende Standardübertragungsprotokolle unterstützen. Aufgrund der unterschiedlichen Einsatzfelder ergibt sich die Notwendigkeit zweier unterschiedlicher Schnittstellenarten. Typische Schnittstellenbausteine sind UART (Universal, Asynchron, Receiver, Transmitter, für RS232 oder USB), SPI (Serial Prozessor Interface für externe Speicher und Programmierung), 12C-BUS oder CAN-BUS.

Interruptcontroller-Baustein

Um kurze Reaktionszeiten des Mikrocontrollers bei Veränderungen an Ein-/Ausgängen oder internen Bausteinen zu erhalten, gibt es eine Unterbrechungslogik (Interruptcontroller) für die laufenden Programme. Diese überwacht ständig alle Interruptquellen, unterbricht das aktuelle Programm, wenn von irgendeiner Quelle ein Interrupt ausgelöst wird und startet eine zugehörige Interruptserviceroutine (ISR). Ist die Abarbeitung dieser Routine beendet, kehrt die CPU wieder zum ursprünglichen Programm zurück.

Watchdog-Timer-Baustein

Der Watchdog-Timer (Wachhund) ist ein interner Zähler, der an einem bestimmten Punkt im Programm auf das maximale Zeitintervall zurückgesetzt wird um das Programm komplett durchlaufen zu können. Wenn jetzt das Programm einen Fehler hat (z. B.: nicht vorgesehene Unendlichschleife) und den Watchdog-Timer nicht mehr zurücksetzen kann, wird der Controller automatisch neu gestartet (Watchdog-Timer-Reset).

2.1.3   Speicherarten und Speicherarchitektur

In einem Mikrocontroller-System gibt es verschiedene Arten von Speichereinheiten. Im Gegensatz zur von-Neumann-Architektur, die einen Arbeitsspeicher für Programme und Daten gleichzeitig vorsieht, verfügen Mikrocontroller meist über eine Harvard-Architektur. Diese sieht getrennte Speicher für Programmcode und für Daten vor.


Abbildung: getrennter Speicher eines Mikrocontrollers

RAM-Speicher, SRAM-Speicher

RAM-Speicher (Random Access Memory) bzw. SRAM-Speicher (Static Random Access Memory) können vom Prozessor beschrieben, wieder gelesen und gelöscht werden. Daher sind sie als Datenspeicher in einem Mikrocontroller-System geeignet. Jedoch verlieren die RAM-Bausteine ihren Inhalt, wenn die Betriebsspannung abgeschaltet wird. RAM Speicher sind sehr schnell.

Register

Das sind Speicherzellen, die direkt mit dem Prozessorkern verbunden sind. Sie sind die Operanden und Ergebnisspeicher für die Maschinenbefehle. Bei Mikrocontrollern sind die Register oft ein spezieller Bereich im SRAM, dieser wird als Registerfile bezeichnet. Register haben eigene Bezeichner, das können Buchstaben (A, B, C, AX, BX, CX, ... X, Y, Z) sein oder die Register sind durchnummeriert (R1, R2, R3, R4, R5, ..,).

ROM-Speicher

In die ROM-Bausteine (Read Only Memory) können die gewünschten Speicherinhalte fest eingebrannt werden. War das Programm fehlerhaft, kann man diesen Baustein jedoch nicht mehr verwenden, da seine Informationen nicht mehr gelöscht werden können.

EPROM-Speicher

In die EPROM-Bausteine (Erasable Programmable Read Only Memory) können die gewünschten Speicherinhalte fest eingebrannt werden. Im Unterschied zu einem ROM lassen diese sich jedoch mit UV-Licht wieder löschen.

EEPROM-Speicher

Die EEPROM-Speicher (Electrically Erasable Programmable Read Only Memory) beinhalten sowohl Eigenschaften der EPROM- als auch RAM-Bausteine. Sie können direkt vom Prozessor beliebig beschrieben, gelesen und gelöscht werden. Sie verlieren jedoch nicht ihren Inhalt, wenn die Betriebsspannung ausgeschaltet wird. Deshalb sind sie besonders gut zur Ablage von Daten des Mikrocontroller-Systems geeignet, die bei Störung der Betriebsspannungsversorgung nicht verloren gehen dürfen. EEPROM-Speicher sind im Vergleich zu RAM bzw. SRAM Speichern langsam.

FLASH-Speicher

FLASH-Bausteine sind besonders schnelle EEPROMs und stehen für blitzschnelle Umprogrammierung. Unterschiede gegenüber herkömmlichen EEPROMs sind zum Beispiel, dass das Speichern im FLASH nur blockweise und nicht byteweise erfolgen kann und dass die Lebensdauer mit 1.000 bis 10.000 Schreibzyklen geringer sein kann als bei EEPROMs, die heute 100.000 Schreibzyklen garantieren können. Mit diesem Speicher ist es möglich, den Controller im Zielsystem zu programmieren. Dafür stehen spezielle, meist serielle, Schnittstellen zur Verfügung.

2.1.4   Prozessoren

CISC-Prozessoren

Ein CISC-Prozessor (Complex Instruction Set Computer) verfügt über einen großen,komplexen Satz an Befehlen. Jeder Befehl ist ein eigenes Mikroprogramm, das eine Reihe von Aktionen auslöst. Dadurch wird die Programmierung komfortabler. Der größte Nachteil ist, dass die CISC-Prozessoren in der Regel vier bis zehn Takte benötigen, bis ein Befehl komplett ausgeführt ist.

RISC-Prozessoren

Ende der siebziger Jahre stellten die US-Universitäten Berkeley und Stanford fest, dass in rund 80 Prozent aller Anwendungen lediglich 20 Prozent der möglichen Befehle verwendet werden. Dabei ist auffällig, dass die einfachsten Befehle am häufigsten verwendet werden. Aus dieser Erkenntnis heraus wurden Prozessoren gebaut, die einen eingeschränkten Befehlssatz haben. Dadurch waren die Prozessoren in der Lage, relativ schnell zu arbeiten. Man bezeichnet diese Prozessoren als RISC-Prozessoren (Reduced Instruction Set Computer).

Diese erzielen vor allem dann beachtliche Leistungen bei den Anwendungen, wenn für die Rechnerarchitektur optimierte Übersetzer (Compiler) vorliegen. Ein optimierender Compiler versucht in mehreren Phasen den Maschinencode so umzustellen, dass seine Ausführungen auf der Architektur möglichst schnell erfolgen können.

Durch den geringeren Befehlssatz der RISC-Prozessoren ist auch ihre Herstellung einfacher und günstiger als bei CISC-Prozessoren. Diese Punkte führen dazu, dass alle Befehle eines Programms in einem einzigen Maschinenzyklus abgearbeitet werden können. Heutzutage gibt es kaum mehr Prozessoren, die keine RISC-Elemente beinhalten.


Abbildung: Zeitverhalten versch. Mikrokontrollertypen

2.1.5   Signale am AVR

Digitale Signale am AVR

Die digitalen Signale (0/1), welche durch den AVR-Mikrocontroller als Eingabe oder Ausgabe verarbeitet werden können, sind Spannungen zwischen 0 V (Masse, Low, logische 0) und typisch 5 V (Versorgungsspannung, High, logische 1).

Die logische 1 (High) entspricht immer der Versorgungsspannung. Je nach Controllertyp können Spannungen zwischen 2,3 V und 6 V als Betriebsspannung dienen.

Die CMOS Eingänge und Ausgänge des AVR sind TTL kompatibel. Eine einzelne Ausgabeleitung kann je nach Controllertyp mit 3 mA bis 10 mA high-aktiv belastet werden und mit bis zu 20 mA low-aktiv.


Abbildung: Pegel 5V Logik

Analoge Signale am AVR

Die analogen Signale, die durch den Comparator und Analog/Digital-Wandler des AVR verarbeitet werden können, sind beliebige Spannungen zwischen 0 V und der Betriebsspannung (typisch 5 V) des Controllers.

2.2   Experimentierhardware myAVR Board

AVR RISC-Mikrocontroller

Die kleinen und preiswerten AVR-Prozessoren gehören zu den derzeit modernsten Universal-Mikrocontrollern. Das Design wurde an der Universität für Technologie in Trondheim/Norwegen entwickelt und von der Firma Atmel erworben. Der AVR-Kern ist außergewöhnlich klein und enthält nur rund 4000 Gates. Atmel bietet zwei AVR-Serien an: tinyAVR (tiny=winzig) und megaAVR. Die Serie AVR (AT90x) ist auf die Tiny- und Mega-Serie aufgeteilt. Die Produktion der AT90x-Baureihe läuft nach und nach aus.

AVR Prozessoren sind hervorragend geeignet für Schaltungen, die früher auf Prozessoren wie dem Motorola 6805, dem Intel 8051, MSP 430 oder dem PIC von Microchip beruhten. Der AVR ist ein RISC-Prozessor. Die verschiedenen Serien unterscheiden sich in der Grundarchitektur sowie Art und Weise der Programmierung nicht. Die Unterschiede liegen insbesondere in der Ausstattung mit Peripherie und Speicher.

Das myAVR Board verwendet Controller der Mega-Serie im 28 PIN DIP-Gehäuse.


Abbildung: AVM-Mikrokontroller der Tiny-Serie


Abbildung: AVM-Mikrokontroller der Mega-Serie

Beschreibung der Experimentier-Hardware

Alle folgenden Kapitel werden Beispiele und Übungsaufgaben enthalten, die mit der Entwicklungsumgebung SiSy AVR bzw. myAVR Workpad und dem myAVR Board nachvollziehbar sind. Das myAVR Board verfügt über einen RISC AVRMikrocontroller (ATmega8) der Firma ATMEL. Auf dem Board befindet sich ein USB Interface.


Abbildung: Das myAVM-Board

Der auf dem Board vorgesehene Controller gehört zur Reihe megaAVR und verfügt bereits über alle wesentlichen Baugruppen. Der weitere Umfang an Ausstattung der noch "größeren" AVR ist zumeist auf die Speichergröße und die Anzahl der 1/0-Ports bezogen.


Abbildung: Aufbau des AVR ATmega8 von Amtel

3.   Programmierung von Mikrocontrollern

3.1   Programmiersprachen, Programmiergeräte, Schnittstellen

Für die Programmierung von Mikrocontrollern stehen unterschiedliche Entwick- lungsumgebungen für verschiedene Sprachen zur Verfügung. Diese weisen eine Reihe gemeinsamer Merkmale auf. Der wichtigste Aspekt, der hier angesprochen werden muss, ist, dass im Gegensatz zur Programmierung eines PC-Programms das fertige Programm nicht auf der Entwicklungsplattform, also unserem PC-Arbeitsplatz gestartet, ausgeführt und getestet werden kann. Für das Ausführen der entwickelten Controllersoftware ist es notwendig, das ausführbare Programm in geeigneter Weise auf den Controller und diesen in das Zielsystem zu bringen.

Neben dem üblichen Übersetzungslauf des Quellcodes ist ein weiterer Arbeitsschritt notwendig. Dabei wird das übersetzte Controllerprogramm in den Programmspeicher (FLASH) des Mikrocontrollers übertragen. Dieser Vorgang wird "Programmieren" oder "Brennen" genannt und erfordert eine spezielle Schnittstelle für das Programmieren des Controllers. Diese zusätzliche Hardware wird "Programmer" genannt.

Eine Ausnahme bilden Simulatoren der Zielhardware. Bei diesen ist es möglich, ohne den Brennvorgang, das fertige Programm auf den PC zu testen.

Die am weitesten verbreiteten Sprachen für die Programmierung von AVR-Mikrocontrollern sind:

  • Assembler
  • C/C++
  • BASIC (BASCOM)

Eine Entwicklungsumgebung enthält typischerweise folgende Komponenten:

  • Quellcode-Editor
  • Übersetzungsprogramm (Assembler, Compiler)
  • Programmier- /Brenn-Programm
  • Testwerkzeuge (Terminal, Simulator, Debugger)

Folgende Dateitypen finden Verwendung bei der Programmierung von Mikrocontrollern:
*.ASM, *.S, *.C, *.CC, *.BAS

Quellcode, wird im Editor erstellt und übersetzt (Compiler, Assembler) zu ...
*.0, *.OBJ

Objektdateien, entstehen beim Übersetzen, werden zu ausführbaren Dateien gebunden (Linker) ...
*.HEX, *.BIN

Binärdatein, entstehen beim Linken, werden beim Brennen in den Programmspeicher des Controllers übertragen.

Die folgenden Abbildung zeigt die allgemeine Grundstruktur eines Mikrocontrollerprogramms in Assembler:


Abbildung: AVR Assemlerprogramm

Für das Brennen des fertigen Mikrocontrollerprogramms (*.HEX oder *.BIN) gibt es grundsätzlich zwei Möglichkeiten:

  1. Zum einen kann man ein Programmiergerät verwenden, in das man den Chip einsetzt und programmiert. Dazu muss das Programmiergerät zum Beispiel über die serielle Schnittstelle oder USB an den PC angeschlossen werden.

    Der Mikrocontroller ist aus dem Zielsystem zu entfernen und auf den entsprechenden Sockel des Programmiergerätes zu stecken. Dann kann das Programm in den FLASH-Speicher des Controllers übertragen werden. War dieser Vorgang erfolgreich, kann der Controller aus dem Programmiergerät entnommen und wieder in das Zielsystem eingebaut werden.

    Ein häufig verwendetes Programmiergerät ist das STK500 von Atmel.


Abbildung: Programmiergerät STK500 von Amtel

  1. Eine elegante Lösung für die Programmierung des Mikrocontrollers ist das sogenannte "In System Programming" (ISP). Dabei muss der Controller nicht aus dem Zielsystem ausgebaut werden, sondern kann direkt im System programmiert werden. Dafür muss das Zielsystem eine ISP-Schnittstelle bereitstellen. Mit einer recht einfachen Hardware, dem sogenannten ISP-Programmer, der an den LPT-Port, die COM-Schnittstelle oder den USB-Port angeschlossen wird, kann aus der Entwicklungsumgebung heraus das Programm direkt in das Zielsystem übertragen werden. Mögliche Schnittstellen für diese Art der Programmierung sind ISP, JTAG und I2C.


Abbildung: ISP-Anschlüsse, ISP Standards Amtel 10 pol., Amtel 6 pol., TwinAVR

Dieser ISP-Anschluss am Zielgerät muss an den pe des Entwicklers oder Servicetechnikers mit einem der besagten ISP-Programmer angeschlossen werden. Im Folgenden sehen Sie Beispiele für die Schaltung unterschiedlicher Programmer. Auf dem myAVR Board ist ein SP12 kompatibler Programmer voll integriert (USB).

3.2   Mikrocontroller-Entwicklungsumgebung SiSy AVR

Schauen wir uns als nächstes kurz in der Entwicklungsumgebung SiSy AVR um.

SiSy AVR ist ein allgemeines Entwicklungswerkzeug, mit dem man von der Konzeption eines Systems, bis zur Realisierung die verschiedensten Arbeitsschritte unterstützen kann. Für die Entwicklung von Mikrocontrollerlösungen bietet sich die einfache Programmierung an.


Abbildung: Bildschirmaufbau SiSy AVR

Beim Kompilieren, Linken und Brennen des Schnellstart-Beispiels öffnete sich ein Ausgabefenster und zeigte Protokollausgaben der Aktionen an. Wenn die Hardware ordnungsgemäß angeschlossen, von der Software erkannt und das Programm erfolgreich auf den Programmspeicher des Mikrocontrollers übertragen wurde, muss die letzte Ausschrift folgenden Inhalt haben:


Abbildung: Ausgabefenster mit Brennprotokoll

3.3   Schnellstart mit SiSy AVR (Systemtest)

Voraussetzungen:

Für die Bearbeitung der folgenden Aufgaben benötigen Sie die aufgeführte Software und Hardware.

Software:
• SiSy AVR ab Version 2.17

Hardware:
• Ein bestücktes myAVR Board
• USB-Kabel A-B
• 9 V Batterie / 9 V Netzteil bei Bedarf (z. B.: autonomer Einsatz)

Zielstellung: Der Schnelleinstieg zur Mikrocontroller-Programmierung soll Ihnen helfen, das Add-On AVR kennen zu lernen und erste Schritte in der hardwarenahen Programmierung mit SiSy AVR zu gehen. Die Funktion des nachfolgenden Programms ist es, bei Programmstart alle optischen Ausgabegeräte (LED) auf dem myAVR Board zum Leuchten zu bringen.

Ein neues Projekt anlegen

  • Starten Sie SiSy AVR und wählen Sie Assistent öffnen.
  • wählen Sie den Menüpunkt Neues Projekt anlegen
  • vergeben Sie den Projektnamen "Alle_lichter_an" und bestätigen Sie mit OK.
  • wählen Sie das Vorgehensmodell "Programmierung" und laden Sie keine Diagrammvorlage.

Erstellen Sie ein Programm für den AVR Mikrocontroller, in dem Sie per Drag & Drop aus der Objektbibliothek ein "kleines Programm" in das Diagrammfenster ziehen. In dem aufgeblendeten Dialogfenster vergeben Sie den Namen "Alle_lichter_an". Der Datei- und Programmname wird dabei automatisch vergeben. Wählen Sie die Sprache "AVR Assembler". Über die Registerkarte "Programmgerüst" können Sie die Vorlage "Grundgerüst" für ein AVR Assemblerprogramm laden.




Abbildung: Grundgerüst für AVR-Assemblerprogramm

Quelleode in Assembler erstellen

Die Ausgabegeräte LED sollen vom Prozessorport D gesteuert werden. Die Realisierung erfolgt über Bits im Register R16. Geben Sie folgenden Quelleode ein bzw. ergänzen Sie die Programmvorlage.


Abbildung: Quelltextänderungen

Übersetzen und binden

Der eingegebene Quellcode muss nun in Maschinencode für den AVR-Prozessor übersetzt werden.
Wählen Sie dazu die Schaltflächen "Compilieren" und "Linken".

Bei fehlerfreier Übersetzung liegt das Programm als "Alle_Lichter_An.hex" vor und kann auf den FLASH-Programmspeicher des Prozessors gebrannt werden.

Hardware anschließen und brennen

Das myAVR Board verfügt über eine ISP (In System Programming) Schnittstelle. Der Prozessor muss also nicht für die Programmierung aus dem System entfernt werden, um ihn in einem gesonderten Programmiergerät zu brennen, sondern kann im myAVR Board direkt programmiert werden. Dazu schließen Sie das Programmierkabel an den USB-Port Ihres Rechners an.

Zum Brennen wählen Sie die Schaltfläche "Brennen". Bei erfolgreichem Brennvorgang erhalten Sie im Ausgabefenster folgende Meldung:


Abbildung: Brennprotokoll

Mikrocontrollerlösung testen

Um das Programm zu testen ist es nötig, den Port D mit den Ausgabegeräten LED

  • Wenn vorhanden, ziehen Sie die Batterie bzw. das Netzteil und das Programmierkabel ab.
  • Verbinden Sie die LEDs mit dem Prozessorport D ent- sprechend dem folgenden Schema.
  • Nutzen Sie die Patchkabel!
  • Prüfen Sie die Verbindungen und schließen Sie die Batterie an und nehmen Sie die Mikrocontrollerlösung in Betrieb.
  • Es sind jetzt alle LEDs an.
  • Gratulation! Das ist Ihre erste Mikrocontrollerlösung mit dem myAVR Board.

4.   Mikrorechnerprogrammierung in Assembler

4.1   Einführung in den AVR Assembler

Was ist ein Assembler?

Letztlich ist es egal, welche Programmiersprache man verwendet. Ein Mikroprozessor oder auch Mikrocontroller verarbeitet intern immer Maschinenbefehle im Binärformat. Ein solcher Maschinenbefehl ist zum Beispiel:

1001 0101 00001000

Dieses Zahlenformat ist recht schwer lesbar. Dafür steht dem hardwarenahen Programmierer eine etwas freundlichere Darstellung zur Verfügung, welche je ein Halbbyte als ein Zeichen darstellt. Ein Halbbyte umfasst 4 Bit und kann somit 16 verschieden Werte (0 ...15) enthalten. Diese werden mit den Zahlen 0-9 und den Buchstaben A-F kodiert. Befehle in Hexadezimaldarstellung sind schon besser vom Programmierer zu erkennen.

Der Hexdump Ox9508 wird vom erfahrenen AVR-Programmierer recht schnell als der Rücksprungbefehl aus einem Unterprogramm identifiziert, da dieser häufig verwendet wird.

Trotzdem sind die ca. 130 Maschinenbefehle eines AVR-Mikrocontrollers als Hexcodes immer noch schwer auswendig zu lernen. Dafür wurde eine besser lesbare Darstellungsform für jeden Maschinenbefehl entwickelt, die aus einer Abkürzung auf die Bedeutung des Befehls schließen lässt. So wird der Rücksprungbefehl mit dem Kürzel RET dargestellt und entspricht dem binären Maschinenbefehl 1001 0101 0000 1000.

Die Schreibweise, in der genau ein Maschinenbefehl mit genau einer symbolischen Darstellung abgebildet wird, nennt man Mnemonik.

Es handelt sich dabei nicht um eine Programmiersprache im eigentlichen Sinne bei der die Sprachelemente nach einer vorgegebenen Syntax (Grammatik) in beliebigen Kombinationen verwendet werden können, welche dann von einem Compiler aufgelöst werden.

Das Übersetzungsprogramm für diese Maschinenprogramme ist ein Assembler. Dieser setzt lediglich einen bekannten Maschinenbefehl nach dem anderen in das entsprechende Binärformat um und berechnet die konkreten Adressen für Sprungbefehle und Speicherzugriffe.

Ein Assemblerprogramm wird mit einem Texteditor geschrieben und muss in einfachem ASCII-Format vorliegen. In der Regel wird der Quellcode in mehreren Spalten organisiert. Das dient der Übersichtlichkeit, ist aber nicht zwingend notwendig. Die Spalten werden einfach durch das Einfügen von Tabulatoren oder eine Folge von Leerzeichen erzeugt (white space).

  • Die erste Spalte enthält Bezeichner für Adressen, die zum Beispiel bei Sprunganweisungen genutzt werden.
    Man bezeichnet diese als Marken, Sprungmarken oder auch Label.
    Eine Marke sollte ab Spalte 1 geschrieben werden und muss immer mit einem Buchstaben beginnen und einem Doppelpunkt enden. Vergleichen Sie dazu im folgenden Quellcode die Marke mainloop:.

  • Die zweite Spalte enthält die Maschinenbefehle, gefolgt von den Operanden.
    Die Operanden können in einer eigenen Spalte stehen und stehen immer in der Reihenfolge Ziel, Quelle der Operation. Das heißt also, dass immer dann, wenn die Operation ein Ergebnis liefert, dieses im ersten Operanden vorliegt. Der Transportbefehl ldi (load immediately) ist eine Wertzuweisung einer Konstante an ein Register. Vergleichen Sie den Befehl ldi r16 , 128 im folgenden Quelltext. Dieser entspricht der Wertzuweisung r16=128.

    Kommentare haben ein vorangestellten Semikolon gekennzeichnet und enden immer am Zeilenende.

Im AVR-Assembler können Zahlen in verschiedenen Formaten dargestellt werden.
Dezimalzahlen werden ohne weitere Kennzeichnung geschrieben.
Binär und Hexadezimalzahlen erhalten einen entsprechenden Präfix Ob oder Ox.

Beachten Sie, dass die führende Null nicht zum Wert gerechnet wird, sondern zum Präfix gehört !

In anderen Systemen werden Hexadezimalzahlen oft mit einem $-Zeichen gekennzeichnet.

In manchen Darstellungen findet sich diese Schreibweise auch in diesem Buch. Dabei handelt es sich meist um Abbildungen aus den Datenblättern von Atmel.

Was ist ein Register?

Register sind besondere Speicherzellen, in unserem Fall mit je 8 Bit. Man kann in einem solchen 8-Bit-Register Zahlen von 0 bis 255 (Ganzzahl ohne Vorzeichen), von -128 bis +127 (Ganzzahl mit Vorzeichen-Bit 7), ein Acht-Bit-ASCII-Zeichen wie z. B. 'A' oder auch acht einzelne Bits speichern. Das Besondere an diesen Registern (im Gegensatz zu anderen Speichern) ist, dass nur diese direkt in Maschinenbefehlen angesprochen werden können. Sie sind direkt an das Rechenwerk angeschlossen und können sowohl Quelle von Daten, als auch Ziel des Ergebnisses von Operationen sein. Register sind die schnellsten Speicherplätze, die dem Programmierer zur Verfügung stehen.

Die Register stellen also die Variablen dar, mit denen ein Assemblerprogramm in erster Instanz arbeitet. Ein AVR-Mikrocontroller besitzt 32 allgemeine Register. Sie werden mit R0 bis R31 bezeichnet. Beachten Sie, dass nicht alle Register uneingeschränkt verwendet werden können. Nur die Register R16 bis R31 lassen sich zum Beispiel mit einem konstanten Wert direkt laden, die Register RO bis R15 nicht.

Diese Register benutzt man für spezielle Aufgaben. Als allgemeine Arbeitsregister (Variablen) dienen die Register R16 bis R31.

Zeiger-Register

Eine wichtige Sonderrolle spielen die Registerpaare R26+R27, R28+R29 und R30+R31. Diese werden zu den 16-Bit-Adressregistern X, Y und Z zusammengefasst. Sie werden bei Adressierungen für den internen SRAM benötigt.

Was ist ein Port?

Port ist die allgemeine Bezeichnung für Eingabe- und Ausgabegeräte, speziell sind damit die digitalen Ein- und Ausgänge gemeint. Aber auch die Steuerregister für die unterschiedlichsten Aufgaben sind bei AVR-Mikrocontrollern als I/O-Ports realisiert und werden somit auch wie diese angesprochen. Im Umkehrschluss sind die Ein- und Ausgänge beim AVR als Register realisiert. Daraus ergibt sich eine einheitliche Programmierung aller Steueraufgaben intern und extern.

Ports haben eine feste Adresse, über die sie angesprochen werden können. Die Adressen sind für alle AVR-Typen einheitlich festgelegt. So befindet sich der Ausgabeport der Parallelschnittstelle B bei allen Prozessoren der AVR-Familie an der Portadresse Ox18.

Ports werden üblicherweise über Alias-Namen angesprochen. Diese sind in der Datei "avr.h" festgelegt. Die I/O-Adresse Ox18 besitzt demzufolge den Alias- Namen PORTB. Der Bezeichner PORTB wird in den Befehlen wie die entsprechende Adresse gehandhabt. Die folgende Liste zeigt eine Auswahl der I/O-Register:

Statusregister / Flagregister SREG Ox3F
Stackpointer SP Ox3D
General Control Register MCUCR Ox34
General Interrupt Control Register GICR(GIMSK) Ox3B
Generallnterrupt Flag Register GIFR Ox3A
Timer Interrupt Mask Register TIMSK Ox39
Timer Interrupt Flag Register TIFR Ox38
Timer / Counter 0 Control Register TCCRO Ox33
Timer / Counter 0 TCNTO Ox32
Watchdog-Timer Control Register WDTCR Ox21
Port B PORTB Ox18
Data Direction Register Port B DDRB Ox17
Input Register Port B PINB Ox16
UART Data Register UDR OxOC
UART Status Register USR OxOB
UART7 Control Register UCR OxOA
UART Baud Rate Register UBRR Ox09
Analog Comparator Control ACSR Ox08

4.2   Grundaufbau eines AVR-Assembler Programms

Aus dem bisher Besprochenen ergibt sich zum AVR-Mikrocontroller folgendes Bild:

Auf einem Chip sind neben dem Prozessor auch alle notwendigen Peripheriebausteine integriert. Der Speicher, der dem AVR-Mikrocontroller zur Verfügung steht, ist in der folgenden Abbildung dargestellt.

Beachte: Je nach Controllertyp können sich die Registerbezeichnungen unterscheiden.

Zum Beispiel ist das Steueregister General Interrupt Control Register GICR des ATmega8 gleichbedeutend mit dem General Interrupt Mask Register GIMSK des ATtiny2313. Wenn hier Register besprochen werden, die in unterschiedlichen Controllern nicht gleich bezeichnet sind, steht die alternative Bezeichnung in Klammern dahinter.


Abbildung: Speicher des AVR - Mikrocontrollers

Die Programme werden im AVR immer im FLASH-Speicher ab Adresse OxOOOO abgelegt. Diese Adresse wird automatisch beim Start (POWER ON) oder beim RESET des Controllers angesprungen. Es ist also die Startadresse für jedes AVRProgramm, egal in welcher Sprache es geschrieben wird.

Nach dem Start soll ein Controller, entsprechend seiner Aufgabe, Initialisierungsaufgaben durchführen und dann solange, bis er abgeschaltet wird, die Eingabegeräte überwachen (Eingabe), Ereignisse und Daten auswerten (Verarbeitung) und entsprechend reagieren bzw. agieren (Ausgabe). Wir halten also fest, dass beim Start oder Neustart (RESET) des Controllers, die Abarbeitung ab der Startadresse OxOOOO beginnt und nach einer Initalisierung des Systems in einer Unendlichschleife die Hauptaufgabe (Eingabe -> Verarbeitung -> Ausgabe) des Controllers ausgeführt wird. Damit ist die Grundstruktur einer Mikrocontrolleranwendung umrissen.

Für die effektive Arbeit des Controllers stehen Möglichkeiten zur Verfügung, schnell auf bestimmte Ereignisse zu reagieren. Dafür sind, je nach Controllertyp, eine unterschiedliche Anzahl von programmierbaren Interruptquellen verfügbar. Diese sind in der Lage, auf Ereignisse außerhalb oder innerhalb des Mikrocontrollers in der Form zu reagieren, dass die laufende Programmabarbeitung automatisch kurz unterbrochen wird, um spezielle Unterprogramme zur Ereignisauswertung aufzurufen und danach die aktuelle Verarbeitung fortzusetzen. Dieser Mechanismus wird Interrupt oder auch Unterbrechungsanforderung genannt. Für jedes auszuwertende Ereignis werden eine Einsprungadresse (Vektor) und eine Serviceroutine programmiert. Daraus ergibt sich folgendes typisches Bild im Programmspeicher.

Der Aufbau aller AVR-Mikrocontrollerprogramme ist letztlich immer gleich. Daraus lässt sich die folgende Grundstruktur eines AVR-Assemblerprogramms ableiten:

Der Programmkopf

Dieser dient der Dokumentation. Besonders wichtig ist die Beschreibung der Funktion und der Schaltung. Jedes Mikrocontrollerprogramm macht nur im Zusammenhang mit der dafür vorgesehenen Schaltung des Controllers Sinn.

Einsprungpunkt und Interruptvektoren

Die sogenannte Interruptvektortabelle ist ein Element, welches in allen interruptfähigen Systemen realisiert werden muss (auch im PC). Bei AVR-Mikrocontrollern ist diese Tabelle fest ab Adresse OxOOOO mit einer vorgegebenen Größe (abhängig vom Controllertyp) vorgesehen. Jede Position in dieser Tabelle ist ein fester Ansprungpunkt des Controllers für ein bestimmtes Ereignis. Die erste Adresse ist zum Beispiel der Vektor für das Ereignis POWER-ON-RESET. Die Interruptvektortabelle muss immer vollständig realisiert sein. Nicht belegte Interrupts sind mit dem Maschinenbefehl RETI (return from interrupt) zu versehen. Das bewirkt, dass selbst wenn ein nicht vorgesehener Interrupt ausgelöst wurde, das System dann ordnungsgemäß nichts tut, denn jeder Interrupt muss immer mit RETI beendet werden, damit die normale Programmabarbeitung fortgesetzt wird.

Vergleichen Sie dazu die folgende Darstellung. Nur der erste Vektor ist belegt und führt einen Sprung zum Hauptprogramm aus. Die anderen Vektoren sind als Platzhalter notwendig. In Hochsprachen wie C/C++ oder BASIC wird die Interruptvektortabelle vom Compiler automatisch generiert.

Initialisierungssequenz

Nach dem Einschalten oder dem Neustart (RESET) des Systems sind immer die entsprechenden Initialisierungsschritte für die konkrete Lösung auszuführen. Bis auf wenige AVR-Controller der Tiny-Serie, die über keinen SRAM verfügen, ist die minimale Initialisierungsaufgabe, den Stack (Stapelspeicher) zu initialisieren. Der Stack wird vom Prozessorkern benötigt, um die Rücksprungadressen für Unterprogramme und Interruptroutinen zu speichern, sowie Registerinhalte zwischen zu speichern.

Der Stack arbeitet nach dem UFO-Prinzip (last in first out). Das Register SP (Ox3D) ist der Stackpointer und zeigt auf die letzte (aktuelle) Adresse im Stapelspeicher. Wird ein Wert auf den Stack gelegt (z. B.: Befehle call f push), verringert sich der Stackpointer entsprechend. Wird ein Wert vom Stack abgeholt (z. B.: Befehle ret f pop), wird die Adresse im Stackpointer erhöht. Der Stack wächst also bildlich gesprochen rückwärts bzw. von oben nach unten. Deshalb liegt die Startadresse des Stack nicht am Beginn des SRAM, sondern an dessen Ende. Der Stapelspeicher wächst dann sozusagen vom Ende des SRAM in Richtung Anfang des SRAM. Das Ende des SRAM ist die Adresse des letzen Bytes und abhängig vom Controllertyp. Beim ATmega8 ist es zum Beispiel die 1024 (1 kByte SRAM), beim ATtiny26 die 128 (128 Byte SRAM). Für diese Werte ist ein Alias-Name definiert: RAMEND.

Hauptschleife (das Betriebssystem)

Die Verarbeitungs- bzw. Steueraufgabe soll vom Mikrocontroller solange ausgeführt werden, bis er abgeschaltet wird. Das geschieht durch wiederholtes Durchlaufen der vorgegebenen Verarbeitungsschritte. Verallgemeinert bestehen die Verarbeitungsschritte aus drei Elementen: Eingabe, Verarbeitung, Ausgabe. Da es nicht wirklich eine Endbedingung für diesen Zyklus gibt, wird diese Hauptschleife (rnainloop) ohne Abbruchbedingung formuliert.

Unterprogramme und Interruptserviceroutinen

Für die Wahrung der Übersichtlichkeit und für Aufgaben, die mehrfach benötigt werden, wie zum Beispiel eine Warteroutine, benutzt man die Unterprogrammtechnik. Interruptroutinen sind ebenfalls Unterprogramme. Diese werden in unserem Fall am Ende angefügt.

Seite drucken  Seite aktualisieren  zurück  zum Textanfang