Die Blinkschaltung wird in diesem Abschnitt über eine andere Methode verwirklicht. Ausgenutzt wird, dass der Zähler, sobald er 255 erreicht hat, wieder auf 0 zurückspringt. In dem Moment springt das Programm in eine sogenannte Interrupt Service Routine (ISR) und führt das dort festgelegte Programm aus (zum Beispiel kann es die Zustände von LEDs umkehren. LEDs, die leuchten, werden ausgeschaltet oder umgekehrt). Anschließend kehrt das Programm in den Hauptteil des Programms zurück und setzt seine Arbeit fort.
Um das folgende Programm zu verstehen, sind vorher ein paar neue Begriffe zu erklären.
Interruptvektor - Interrupt Vektortabelle
Je nach verwendetem Prozessor stellt AVR unterschiedliche Interrupt-Quellen zur Verfügung. Bei einem ATmega 8515 gibt es insgesamt 17 sogenannte Interruptvektoren, das sind schlicht Speicherplätze im Programmspeicherraum, die als Einsprungadressen reserviert sind und im Datenblatt in einer Interrupt-Vektortabelle dokumentiert sind. Dabei ist die niedrigste Adresse, sie hat auch die höchste Priorität, dem RESET-Vektor zugeordnet, während die nachfolgenden 16 weiteren Interruptvektoren sich nach oben anschließen. Es gilt:
Der ATmega 8515 verfügt z. B. über vier Interruptvektoren zum timer1 und einem zum timer0 mit der Adresse $007, die über TIM0OVF angesprochen werden kann.
Im Datenblatt jedes Controllers finden sich unter dem Abschnitt "Interrupts" die spezifischen Angaben. Deshalb wird hier darauf nicht weiter eingegangen.
Für den Einsatz von Timer0 und Interrupt benötigt man die Register TCCR0, TIFR und TIMSK.
Sobald beim Timer/Counter0 ein Überlauf stattfindet (der Zähler springt von 255 zurück auf 0 und fängt von vorne an zu zählen), wird im Register TIFR das TOV0-Flag gesetzt, der zugehörige Interrupt aus der Interrupt-Vektortabelle ($007) ausgeführt und das TOV0-Flag wieder gelöscht.
Bit 1 (TOIE0) - "Timer/Counter0 Overflow Interrupt Enable einschalten" im Register TIMSK
Bit1 (TOV0-Flag) im Register TIFR wird vom Controller gesetzt, sobald im timer0 ein Überlauf stattfindet und wieder gelöscht, sobald die ISR ausgeführt wird. Der Rücksprung aus einer ISR ist ein Rücksprung von einem Interrupt (return from Interrupt command) und muss deshalb mit RETI abgeschlossen werden.
Blinkerschaltung mit timer0 und Interrupt | |
Material |
|
Aufgaben |
|
Schaltungsaufbau
Das Programm led_timer0OV_1.asm
Das aufgenommene Oszillogramm am Ausgang PB0 ergibt bei einem Vorteiler von 1024 eine Ausgangsfrequenz von ca. 1,9 Hz.
Bei einem Vorteiler 1024, einem Zählbereich von 256 und einer Taktfrequenz von 1.000.000 Hz ergibt sich rechnerisch eine Ausgangsfrequenz von
Wir arbeitet das Programm ov_interrupt1.asm?
Dies wird mit Hilfe des Debuggers genauer untersucht. Über Debug - Start Debugging aus dem Hauptmenü von AVR Studio wird der Debugger in Gang gesetzt. Der gelbe Programmzeiger markiert die aktuell zu bearbeitende Stelle im Quellprogramm. Er wird mit mit jedem Tastendruck auf F11(Trace Modus) um jeweils eine Befehlszeile weiter gestellt.
Nach Aufruf des Debuggers landet der Programmzeiger nach dem Programmkopf bei der ersten Direktive
Der Taktzähler (Cycle Counter) im Fenster Processor ist 0. Der Zahlenwert des Taktzählers bezieht sich immer auf das Kommando der vorherigen Befehlszeile.
Cycle Counter |
Beschreibung des Programmablaufs |
Aktion |
0 | .ORG 0x0000 | Start des Programms |
0 | RJMP main | Verzweigung zur Einsprungmarke main |
2 | Main: | Einsprungmarke |
2 | LDI R16, HIGH(ramend) | Highbyte von RAMEND -> R16 |
3 | OUT SPH, R16 | Inhalt von R16 in Register SPH ablegen |
4 | LDI R16, LOW(ramend) | Lowbyte von Ramend -> R16 |
5 | OUT SPL, R16 | R16-Inhalt in Register SPL ablegen |
6 | LDI R17, 0xFF | 0xFF -> R17 |
7 | OUT DDRB, R17 | DDRB auf Ausgang |
8 | OUT PORTB, R17 | Alle LEDs OFF (active low) |
9 | LDI R16, 0x01 | Für den Debugger 0x01 laden |
10 | OUT TCCR0, R16 | TCNT0 beginnt zu zählen |
11 | LDI R16, 0x02 | 0x02 -> R16 |
12 | OUT TIMSK, R16 | T/C0 OV Interrupt einschalten |
13 | SEI | |
14 |
Loop: RJMP loop |
Taktzähler wird bei jedem Schleifendurchlauf um 2 erhöht |
...
... 266 |
...
... ... |
... die Schleife loop: rjmp loop wird solange durchlaufen, bis das TCNT0 Register den Wert 0xFF überschritten hat und wieder bei 0 anfängt. Dann wird das I-Flag gelöscht und die Interrupt Service Routine aufgerufen. |
271 |
.ORG OVF0addr RJMP isr_tim0_ov |
ISR wird aufgerufen |
273 |
Isr_tim0_ov: OUT PORTB, R17 |
R17 -> PORTB |
274 | COM R17 | Inhalt von R17 wird invertiert |
275 | RETI | Abschluss der ISR |
279 |
Loop: RJMP loop |
Schleife wird solange durchlaufen, bis ein erneuter Überlauf im TCNT0 Register erfolgt. |
Lösung zu Teilaufgabe 2
Mit LDI r16, 0x04 im Programmblock "fo div 1024" wird der Vorteiler auf 256 eingestellt. Das Oszillogramm zeigt eine Frequenz von ca. 7,6 Hz, während der rechnerische Wert nach (1) bei ca. 7,63 Hz liegt.
Lösung zu Teilaufgabe 3
Das Programm muss im Block "PORTB auf Ausgang" angepasst werden. Da nur die vierte LED blinken soll, wird die folgende Programmanpassung vorgenommen:
Im Datenrichtungsregister B (DDRB) wird Port 4 auf Ausgang geschaltet und auf HIGH gezogen. Damit ist zunächst die LED4 ausgeschaltet, weil active low. Über die ISR wird bei jedem Overflow deren Zustand invertiert.
Ohne dies in einem weiteren Programm zu dokumentieren, sei hier nur angemerkt, dass man, wie bereits in Übung 1 beschrieben, auch bei einem timer0-Interrupt den Zähler mit einem Startwert (preload) versehen kann.
Statt dessen werden wir uns jetzt das timer0-compare-match-Interrupt etwas intensiver ansehen. Ohne Interrupt kennen wir dieses Verfahren bereits aus AVR Assembler Teil 4 - Übung 2 - timer0 im CTC-Modus.
Wie wir das bereits aus dem CTC-Modus kennen, muss der timer0/counter nicht immer von 0 bis 255 zählen. Gibt man einen Vergleichswert in ein Compare-Register ein, dann wird so lange hochgezählt, bis Vergleichs- und Zählwert identisch sind.
Besteht Gleichheit zwischen Compare-Register und Timer0/Counter-Register, wird ein Compare-Match-Interrupt ausgelöst. Anschließend startet der Zählvorgang wieder bei 0.
Bei einem Timer0-Compare-Match-Interrupt sind die folgenden Register beteiligt
Als viertes kommt das TIFR-Register (Timer/Counter0 Interrupt Flag Register) ins Spiel; dort wird vom Controller das OCF0-Bit gesetzt, wenn der Inhalt des Output Compare Registers mit dem Timer/Counter0 Register übereinstimmt. Vielleicht ein bisschen viel auf einmal, aber so läuft es nun mal ab.
Blinkerschaltung mit timer0-Compare-Match-Interrupt | |
Material |
|
Aufgaben |
|
Arbeiten mit dem Datenblatt
Das Programm led_timer0OV_1.asm
Ergebnisse zu Teilaufgabe 1
Nicht alle LEDs blinken. LED1 ist permanent auf HIGH. Die gemessene Blinkfrequenz der restlichen LEDs liegt bei ca. 2 Hz.
Ergebnisse zu Teilaufgabe 2
Die Einstellung im Output Compare Register muss auf 0xA0 geändert werden.
Die Blinkfrequenz stellt sich dann auf ca. 3 Hz ein.
Wie arbeitet das Programm led_timer0OV_1.asm?
Zum besseren Verständnis wird das Programm mit dem Debugger Programmzeile für Programmzeile durchlaufen. Dabei werden der Programmzähler (PC), das Statusregister (SReg) und der Stack-Pointer genauer angeschaut. Die einzelnen Schrittfolgen lauten:
Einschub: Aufbau des SRAM
Die unteren 608 Adressen im Datenspeicherraum sprechen die 32 General Purpose Register, 64 I/O Register und den internen SRAM an. Dabei gilt:
Im diesem Abschnitt wurden die Op-Codes
und die Direktive
eingeführt.
Aufgabe
Das Programm lässt die LEDs mit einem festen Takt blinken. Nur LED1 macht nicht mit; sie ist permanent eingeschaltet. Wo muss das Programm verändert werden, damit alle LEDs im Takt blinken?
Lösung: Über das TCCR0-Register kann dieser "Fehler" behoben werden.