Alle hier dargestellten Vorgänge und Erklärungen lassen sich auf alle anderen Atmel-Controller übertragen. Im Einzelnen muss immer das Datenblatt hinzugezogen werden; die Bezeichnungen unterscheiden sich - wenn überhaupt - nur geringfügig voneinander.
In einer ersten Übung soll eine LED mit Vorwiderstand blinken. Die Schwierigkeit in dieser einfachen Schaltung besteht darin, dass zwischen dem Ein- und Ausschalten der LED eine Pause eingebaut werden muss. Siehe hierzu auch in Kapitel "8-Bit Arithmetik - Teil 2" die "Übung 4 - Aufwärtszähler mit Warteschleife - Instruktionen INC, DEC".
Blinkende LED | |
Material |
|
Schaltskizze
Blinkende LED | |
Aufgaben |
|
Vorüberlegungen zum Programmentwurf
Würde man eine LED in einer endlosen Schleife ein- und anschließend wieder ausschalten, geschieht das mit einem Assemblerprogramm so schnell, dass das menschliche Auge den Eindruck hat, die LED leuchtet dauerhaft.
Eine Methode, die Blinkfrequenz einer LED zu senken, besteht darin, den Controller zwischen den beiden Zuständen LED HIGH und LED LOW in eine Warteschleife zu schicken. Zum Beispiel dadurch, dass der Controller - nachdem er die LED auf 1 gezogen hat - von 0 bis 10.000 aufwärts oder von 10.000 bis 0 abwärts zählt und erst dann die LED auf 0 zieht. Durch das Zählen ist soviel Zeit vergangen, dass zwischen "LED leuchtet" und "LED leuchtet nicht" eine endliche Zeitspanne liegt.
Für das Aufwärtszählen gibt es u.a. den Befehl ADIW (add immediate to Word), für das Abwärtszählen den Befehl SBIW (Subtract immediate from Word); die Ausführungszeit für beide Instruktionen ist vom Hersteller ATMEL angegeben: 2 CLKs.
Wir entscheiden uns für das Abwärtszählen SBIW, da hier die Abbruchbedingung (Stopp des Schleifendurchlaufs und weiter im Programm) über das Setzen des Z-Flags im Statusregister mit einem Verzweigungsbefehl einfach abzufragen ist.
Wenn also zum Beispiel von einer Zahl 10.000 bis auf 0 in 1-er Schritten zurückgezählt und anschließend das Zero-Flag gesetzt wird und die LED umschaltet, dann werden dafür 2 x 10.000 = 20.000 CLK benötigt; das entspricht je nach Taktung des Controllers einer bestimmten Zeit. Das wird der Ansatz im folgenden Programm sein.
Das Programm led_1.asm
Wie arbeitet das Programm led_1.asm?
Die Direktiven .NOLIST, .LIST, .INCLUDE sind uns bereits bekannt. Das Programm enthält vier Einsprungadressen: Start, Wiederhole, Warten1 und Warten2.
Einsprungstelle Start:
Im Datenrichtungsregister B wird Port2 auf Ausgang (1) gesetzt.
SBI DDRB, 2 Set Bit in I/O Register |
Setzt Bit 2 im DDRB-Register auf 1. | 2 CLK |
Einsprungsstelle Wiederhole:
Wiederhole: |
Einsprungstelle | 0 CLK |
SBI PORTB, 2 Set Bit in I/O Register |
Setzt Bit 2 im PORTB-Register. Als Folge davon geht LED2 AUS; sie ist LOW ACTIVE geschaltet. |
2 CLK |
Einsprungsstelle Warten1:
SBIW ZL, 1 SuBtract Immediate from Word |
Der Inhalt des Doppel-Registers ZH:ZL startet bei Null und wird bei jedem Schleifendurchlauf um den Wert 1 dekrementiert. ZL ist das Lower-Byte, ZH das Higher Byte. Die Instruktion SBIW ist auf die letzten drei Pointer X, Y und Z und R24:R25 im General Purpose Register anwendbar. |
2 CLK |
BRNE Warten1 BRunch if Not Equal |
Solange das Z-Flag nicht gesetzt ist, mache weiter bei Warten1: |
2 CLK |
CBI PORTB, 2 Clear Bit in I/O Register |
Setzt Bit 2 im PORTB-Register auf 0 zurück. Als Folge davon geht die LED2 AN. |
2 CLK |
Einsprungsstelle Warten2:
Die Abfolge der Instruktionen bei der Einsprungstelle Warten2: ist identisch mit der bei Warten1. Erst nachdem diese Schleife ebenfalls durchlaufen wurde, geht es mit RJMP Wiederhole zurück auf Anfang.
RJMP Wdh Relative JuMP |
Springt zurück an die Marke Wiederhole: und alles beginnt von vorne. |
2 CLK |
Über den internen Ablauf eines Assemblerprogramms kann man mit dem Einsatz des Debuggers (Strg + F7) viel lernen. Ist das Programm fehlerfrei, erscheint nach kurzer Zeit im Editorfenster ein gelber Programmzeiger. In dieser Übung wird das Programm led_1.asm mit dem Debugger näher untersucht. Mit dem Start erkennt man:
Im Datenrichtungsregister B (DDRB) ist Pin 2 auf Ausgang gesetzt und Portpin B.2 auf HIGH gezogen. LED2 wird ausgeschaltet.
Mit F11 wird jetzt zeilenweise jede Instruktion des Assemblerprogramms durchlaufen. Man erkennt, dass sich der Inhalt des Z-Registers (Z-Pointer) mit jedem Schleifendurchlauf um 1 erniedrigt.
Gestartet wird bei 0x0000, geht mit 0xFFFF, 0xFFFE, ... weiter und erreicht ein vorläufiges Ende mit 0x0000.
Um nicht alle Zählschritte mit F11 durchlaufen zu müssen, wird der Z-Pointer im Fenster "Processor" durch Anklicken mit der Maus auf 0x0001 gesetzt und mit F11 die nächste Instruktion aufgerufen. In dem Moment, wo der Z-Pointer auf 0x0000 springt, wird im SRegister das Zero-Flag (Z-Flag) gesetzt.
Das Z-Flag ist gesetzt, die BRNE-Bedingung nicht mehr erfüllt; das Programm macht weiter mit der Instruktion CBI. portb, 2. LED2 wird eingeschaltet und das Programm setzt seine Arbeit im Block Warten2: fort. Der Ablauf dort ist identisch mit dem in Block Warten1:.
Ist Block Warten2: abgearbeitet (dies dauert genau so lange wie bei Block Warten1:), dann kehrt das Programm zurück an die Stelle Wiederhole: und das heißt: LED2 wird ausgeschaltet.
Die erste Schleife mit der Einsprungmarke Warten1 gibt die Zeit für die eingeschaltete LED vor. Der ATmega8515 ist mit 1,0 MHz getaktet (Werkseinstellung). Jeder CLK verbrät somit 1,0.. µs (nach Formel 1). Bei 4 CLKs pro Schleifendurchlauf (SBIW - 2CLK, BRNE - 2CLK) und 65536 Zählzyklen ergibt das insgesamt 262.144 CLKs. Hinzu kommen noch 2 CLK von der CBI-Instruktion. Macht insgesamt also 262.146 CLKs.
Insgesamt entspricht das einer Zeit von 262.146 µs oder 262,146 ms oder 0,262146 s für eine Halbperiode (LED an). Die gleiche Berechnung gilt für die zweite Halbperiode (LED aus). Eine Periode dauert somit 0,524292 s; umgerechnet in eine Frequenz über die Formel
ergibt sich f = 1,907.. Hz.
Das an der Schaltung über PB2 und GND aufgenommene Oszillogramm zeigt folgenden Verlauf
Nachdem wir wissen, wie man Zeitverzögerungen programmieren kann, soll jetzt ein Programm entworfen werden, das ein Lauflicht erzeugt. Die acht LEDs auf dem STK200 werden nacheinander ein- und wieder ausgeschaltet, so dass der Eindruck eines Lauflichtes entsteht.
Lauflicht mit 8 LEDs | |
Material |
|
Aufgaben |
|
Schaltungsaufbau und Schaltskizze
Vorüberlegung zum Programmaufbau
Die Ablauffolge bei einem Lauflicht könnte so aussehen:
Geht man in der hier skizzierten Reihenfolge vor, wird das Assemblerprogramm sehr zeilenintensiv.
Das Programm led_2.asm
...
Das Programm lässt sich deutlich verkürzen, wenn man Teile davon in einem Macro ablegt. Wie man es anlegt, wird in Kapitel AVR Studio - Teil 3 gezeigt. Ein Macro könnte zum Beispiel das folgende Aussehen haben:
Das Programm led_2.asm mit Macroaufruf zeigt die folgende Abbildung.
Kleine Nachbetrachtung
Die kleinste Zeitperiode des mit 1,0 MHz getakteten ATmega8515 beträgt nach (1) ca. 1 µs. Es lassen sich mit der hier vorgestellten Schleifenmethode nur Vielfache dieser "Einheitszeit" erzeugen. Es gilt:
Um zum Beispiel eine Pause von 1s zu realisieren, wird der Prozessor in sinnlose Schleifendurchläufe gezwungen, in denen er - außer sie stupide abzuarbeiten - nichts tut. Für einen so kleinen mit vielen Optionen vollgepfropften Arbeitsbolzen ist das klassische Ressourcenverschwendung.
Für eine LED, die im Sekundentakt blinken soll, wird der Controller 500 ms in eine Zeitschleife geschickt, ändert dann den Zustand der LED zum Beispiel von HIGH auf LOW und taucht dann wieder ab in eine zweite Zeitschleife von 500 ms, an deren Ende der Zustand der LED umgeschaltet wird. Während die Zeitschleife läuft, ist der Controller für andere Aufgaben blockiert.
Eine wesentlich bessere Lösung wäre, wenn der Controller eine innere Uhr in Gang setzte, die alle 500 ms ein Signal abgibt, dass den Zustand einer angeschlossenen LED umschaltet. Bis zum nächsten Zeitsignal widmet er sich wieder anderen Dingen. Wie man das umsetzt, wird im folgenden Kapitel Timer/Counter gezeigt.
Weiter gehts mit AVR Studio - Teil 3.