Erste Befehle - Mit Assembler das Laufen lernen
Materialbedarf
Anz. | Bezeichnung | Datenblatt |
1 | Batterie/Spannungsquelle 9V | |
1 | Spannungsregler 7805 |
![]() |
1 | ATMega8 AVR-Prozessor |
![]() |
1 | Widerstand 220 Ohm | |
1 | Widerstand 10 kOhm | |
1 | Elektrolytkondensator 100 µF/16V | |
1 | Kondensator 100nF | |
1 | Mikrotaster |
![]() |
1 | Standard-Leuchtdiode 3mm oder 5mm |
![]() ![]() |
Ohne Hardware geht es nicht
Bevor dem ATmega8 überhaupt den ersten Befehl gibt, sollte man an dem AVR auch ein bisschen Hardware anbringen, damit es überhaupt etwas gibt was wir steuern können.
Das Einfachste wäre hierzu eine Leuchtdiode und einen Taster zu verwenden.
.
Da es in diesen Lehrgang hauptsächlich um Assembler-Programmierung gehen soll, wird dies der einzige Aufbau in diesem Lehrgang sein. Wer näheres über die Portfunktionen wissen
möchte, sollte erst einmal den Bascom-Lehrgang
|
Die LED leuchtet auf
Nun soll die LED auch zum Leuchten gebracht werden. Hierzu starten wir das AVR-Studio und geben folgendes kleines Programm ein:
.include
"m8def.inc" Start: ldi r16,0xFF out DDRD,r16 ldi r16,0b00000001 out PORTD,r16 Loop: rjmp Loop |
Wenn dieses kleine Programm assembliert und übertragen wurde, leuchtet die LED auf. Auch wenn dies im ersten Moment nichts Besonderes sein mag, so ist dies doch der erste Schritt, den AVR in Assembler zu programmieren.
Wer den entsprechenden Bascom-Lehrgang durchgearbeitet hat, wird evtl. doch einige Parallelen finden. Als erstes fällt schon einmal die erste Zeile auf. Mit der Assembler-Direktive '.include "m8def.inc"' teilen wir dem Assembler mit, mit welchem Prozessor wir arbeiten möchten. In diesem Fall natürlich den ATMega8. Ab hier 'weiß' das AVR-Studio welche Ports, Timer etc. unser AVR hat.
Als nächstes folgt eine Sprungmarke: 'Start:'. In diesem Programm ist diese eigentlich nicht notwendig, da sie nie angesprungen wird. In größeren Projekten ist es aber dennoch sinnvoll, vor dem ersten Befehl eine passende Marke zu setzen. So kann man später erkennen, wo das eigentliche Programm beginnt.
Mit 'ldi r16,0xFF' haben wir den ersten richtigen Assembler-Befehl. Das Mnemonic 'ldi' sagt dem AVR das er einen Wert in ein Register laden soll. Hier ist es der Wert FF Hexadezimal (255 Dezimal). Dieser wird in das Register r16 geladen. Aber warum r16 und nicht r0?
Beim AVR sind die Register r0-r15 nur indirekt beschreibbar. Dies heißt, das r0-r15 keine direkte Verbindung zum Datenbus haben. Man müsste erst ein Register über r15 mit den gewünschten Wert laden und dann nach dem gewünschten unteren Register umkopieren. Dies ist aufwendiger und kostet mehr Rechenzeit und Speicherplatz.
Als Nächstes übertragen wir den, in r16 geladenen Wert, in das Port Register DDRB. Hiermit legen wir die Datenrichtung für den Port D fest. Beschreibt man ein Bit von DDRB mit 1 wird der entsprechende Port-Pin als Ausgang verwendet. Eine 0 an der entsprechenden Position setzt den Pin auf Eingang. Hier setzen wir einfach alle Pins des Port D auf Ausgang.
Einige werden sich vielleicht fragen, warum man nicht einfach 'out DDRD,0xFF' schreibt. Leider ist dies nicht erlaubt. Der AVR erlaubt nur maximal ein Festwert pro Befehl. Mit diesem out-Befehl hätten wir aber 2 Konstante. Somit müssen wir den Umweg über das Register r16 machen.
Die nächsten beiden Befehle sind ähnlich wie die letzten. Nur das wir hier das Register r16 mit 'ldi r16,0b00000001' mit einem Binärwert laden. Durch 'out PORTD,r16' wird nun der Port D Pin 0 gesetzt. Jetzt leuchtet die LED auf.
Nun ist der AVR mit der Arbeit fertig und er kann eigentlich stoppen. Nur so etwas wie anhalten des AVR gibt es nicht. Ein Mikroprozessor braucht immer etwas zu tun. Daher müssen wir dafür sorgen, dass der AVR auch etwas zu tun hat. Dies geschieht ab der Sprungmarke 'Loop'. Darauf folgt der Befehl 'rjmp Loop'. Dies heißt soviel wie: Springe zur angegeben Adresse und arbeite dort weiter. Er springt also wieder zur Sprungmarke Loop. Also, der AVR hängt in einer Endlosschleife. Diese wird hier nur verlassen, wenn wir einen Reset auslösen.
Taster steuert LED
Nur eine Leuchtdiode mit dem AVR einzuschalten ist relativ langweilig. Aber wir haben ja noch den Taster auf dem Steckboard, den wir an Port B Bit 0 geklemmt haben. Um die LED nun per Taster zu steuern, müssen wir unser Programm erweitern:
.include
"m8def.inc" ldi r16,0x00 out DDRB,r16 ldi r16,0xFF out PORTB,r16
Loop:
in
r16,PINB |
Hier wurde jetzt der Port B auch initialisiert. Mit 'ldi r16,0x00' und 'out DDRB,r16' haben wir dafür gesorgt, dass alle Pins an Port B als Eingänge fungieren sollen. Da wir an Port B noch die internen PullUp-Widerstände benötigen, wurde mit 'ldi r16,0xFF' und 'out PORTB,r16' dafür gesorgt, dass die internen Widerstände aktiviert werden.
Als eigentliche Neuerung ist der Befehl 'in r16,PINB' nach der Loop-Sprungmarke anzusehen. Hier lesen wir den Eingangsstatus des Port B ein und speichern das Ergebnis in das Register r16. Als nächstes wird der der so ermittelte Wert wieder an Port D geschickt.
Mit dem Sprungbefehl sorgen wir nun dafür, dass das Einlesen und Zuweisen immer wieder durchgeführt wird.
Wer dieses Programm übersetzt und an den AVR übertragen hat, wird ein ziemliches Manko feststellen. Die LED verhält sich genau umgekehrt wie die Tasterbetätigung. Ist der Taster nicht betätigt, leuchtet die LED auf. Betätigt man den Taster bleibt die Leuchtdiode dunkel. Wir müssen also dafür den ermittelten Wert von Port B invertieren.
Hierzu kann man den Assembler-Befehl 'com r16' verwenden. Dieser Befehl invertiert das angegebene Register. Zwar werden dann auch die nicht benötigten Bits invertiert. Das spielt bei dieser Anwendung aber keine große Rolle. Das geänderte Programm sieht nun so aus:
.include
"m8def.inc" ldi r16,0x00 out DDRB,r16 ldi r16,0xFF out PORTB,r16
Loop: in r16,PINB
com
r16 |
Nachdem nun das Programm assembliert und übertragen, kann man sehen, dass die Steuerung nun richtig funktioniert.