Hallo miteinander,
ich habe eine kurze frage zu Interrupts.
Mit dem 16 Bit Timer des ATmega8 möchte ich gerne ein Zahl hoch zählen.
Der Interrupt funktioniert soweit auch. Allerdings habe ich das Problem,
dass der Interrupt nur ein einziges mal ausgeführt wird und dann nichts
mehr.
Kann ich innerhalb eines Interupthandlers bestimmte Funktionen mir rcall
aufrufen und dann mit RET wider zurück springen? Wenn der Interrupt in
einer Endlosschleife ausgelöst wurde springe ich dann mit RETI zurück in
diese Schleife oder zurück zur Interrupt Adresse?
Konrad M. schrieb:> Kann ich innerhalb eines Interupthandlers bestimmte Funktionen mir rcall> aufrufen und dann mit RET wider zurück springen?
Ja.
> Wenn der Interrupt in> einer Endlosschleife ausgelöst wurde springe ich dann mit RETI zurück in> diese Schleife oder zurück zur Interrupt Adresse?
RETI springt exakt dorthin zurück, wo untergebrochen wurde. Interrupts
halten also den Programmfluss im Hauptprogramm nur temporär auf, ändern
ihn aber nicht.
Konrad M. schrieb:> Kann ich innerhalb eines Interupthandlers bestimmte Funktionen mir rcall> aufrufen und dann mit RET wider zurück springen?
Kannst du, aber ob du das solltest, müsstest du dich selbst
hinterfragen. Um ein halbwegs gescheites Timing hinzukriegen sollten
deine ISRs auch von einer überschaubaren Komplexität sein.
> Wenn der Interrupt in> einer Endlosschleife ausgelöst wurde springe ich dann mit RETI zurück in> diese Schleife oder zurück zur Interrupt Adresse?
Vergegenwärtige Dir doch mal, was die eigentliche Aufgabe von RET ist
(was passiert da auf dem Stack). Da die Interruptbehandlung eine
Unterbrechung des normalen Programmablaufs ist, erfordert diese einen
speziellen Befehl um wieder in den normalen Programmablauf
zurückzuspringen - und das macht RETI.
Danke soweit für die Hilfe.
Das Timing ist nicht ganz so kritisch. Das größere Problem was ich habe,
ist, dass der Interrupt nur einmalig ausgeführt wird obwohl ich ein RETI
am ende des Interupthandlers habe und momentan auch keine weiteren
Adressen im Interrupt anspringe.
Eventuell kann mir von euch jemand sagen was das faul im Programm ist.
Das soll jetzt ein erster versuch werden ein LCD anzusteuern. Ich würde
im Grunde nur ganz gerne eine Zahl hoch zählen und ausgeben. Leider
wird der Interrupt nur einmalig ausgelöst. Mir ist bewusst, dass
pause_lang kürzer ist als pause_weniger_lang, dass sollte bei diesem
Problem aber nichts ändern.
1
/*
2
* PD5 = E (enable)
3
* PD4 = RS (register select | Befehle / Daten)
4
* PD0 = DB4
5
* PD1 = DB5
6
* PD2 = DB6
7
* PD3 = DB7
8
*
9
*/
10
11
12
.org 0x000
13
rjmp main
14
15
.org 0x007
16
rjmp interrupt
17
18
19
20
21
main:
22
23
;------------------------------- Variablen
24
.def temp = r16
25
.def temp1 = r18
26
.def temp3 = r3
27
.def temp4 = r17
28
.def temp5 = r4
29
.def lcd1 = r20
30
.def lcd2 = r21
31
32
.def zahl = r22
33
34
;------------------------------- Stackpointer
35
ldi temp, LOW(RAMEND)
36
out SPL, temp
37
ldi temp, HIGH(RAMEND)
38
out SPH, temp
39
40
;------------------------------- PORTD Ausgang
41
ldi temp, 0xff
42
out DDRD, temp
43
out DDRB, temp
44
out PORTB, temp
45
ldi zahl, 0x30
46
47
;------------------------------- Timer 1
48
ldi temp, (1<<OCIE1B) ; Interupt bei clear timer on compare match
49
out TIMSK, temp
50
ldi temp, (1<<WGM12) | (1<<CS12) | (0<<CS11) | (1<<CS10) ; CTC Modus einschalten, so wie vorteiler setzen
51
out TCCR1B, temp
52
53
ldi temp, HIGH(1000) ; CTC wert
54
out OCR1BH, temp
55
ldi temp, LOW(1000)
56
out OCR1BL, temp
57
58
59
60
;------------------------------- Vor initialisierung Pause machen
61
rcall pause_lang
62
63
64
lcd_init: ; LCD Initialisierung
65
ldi lcd1, 0b00000011 ; muss 3mal hintereinander gesendet
66
out PORTD, lcd1 ; werden zur Initialisierung
67
rcall lcd_enable ; 1
68
rcall pause_lang
69
rcall lcd_enable ; 2
70
rcall pause_lang
71
rcall lcd_enable ; 3
72
rcall pause_lang
73
74
ldi lcd1, 0b00000010 ; 4bit-Modus einstellen
75
out PORTD, lcd1
76
rcall lcd_enable
77
rcall pause_lang
78
79
ldi lcd1, 0b00101000 ; 4Bit / 2 Zeilen / 5x8
80
rcall lcd_command
81
ldi lcd1, 0b00001111 ; Display ein / Cursor ein / Blinken
82
rcall lcd_command
83
ldi lcd1, 0b00000100 ; inkrement / kein Scrollen
84
rcall lcd_command
85
ldi lcd1, 0b00000010 ; Cursor home
86
rcall lcd_command
87
ldi lcd1, 0b00000001 ; LCD clear
88
rcall lcd_command
89
sei
90
rjmp loop
91
92
93
94
lcd_enable:
95
sbi PORTD, 5 ; Enable high
96
nop
97
nop
98
nop
99
cbi PORTD, 5 ; Enable low
100
ret
101
102
;-------------------------------- Ausgabe des Bytes als Befehl
103
lcd_command:
104
mov lcd2, lcd1 ; Kopie für Übertragung des zweiten Nibbles
105
swap lcd1 ; Vertauschen (erst Bit 4 - 7 Ausgeben!) --> LCD Datenleitungen DB4 bis DB7 angeschlossen
106
; ARV Datenleitungen PD0 bis PD3 angeschlossen.
107
andi lcd1, 0b00001111 ; Rest auf 0 setzen (RS = 0 also befehl)
108
out PORTD, lcd1 ; Ausgabe Bit 4 - 7
109
rcall lcd_enable
110
andi lcd2, 0b00001111 ; Das gleich mit den Bits 0 - 3 wiederholen
111
out PORTD, lcd2 ; Kein vertauschen nötig. Nibble bereits an richtiger stelle
112
rcall lcd_enable
113
rcall pause_lang
114
ret
115
116
;-------------------------------- Ausgabe eines Bytes als Befehl
117
lcd_data:
118
mov lcd2, lcd1 ; Kopie für Übertragung des zweiten Nibbles
119
swap lcd1 ; Vertauschen (erst Bit 4 - 7 Ausgeben!) --> LCD Datenleitungen DB4 bis DB7 angeschlossen
120
; ARV Datenleitungen PD0 bis PD3 angeschlossen.
121
andi lcd1, 0b00001111 ; Rest auf 0 setzen
122
sbr lcd1, 1<<4 ; RS = 1, also Daten!!!
123
out PORTD, lcd1 ; Ausgabe Bit 4 - 7
124
rcall lcd_enable
125
andi lcd2, 0b00001111 ; Das gleich mit den Bits 0 - 3 wiederholen
126
sbr lcd2, 1<<4 ; RS = 1, also Daten!!!
127
out PORTD, lcd2 ; Kein vertauschen nötig. Nibble bereits an richtiger stelle
Konrad M. schrieb:> Allerdings habe ich das Problem,> dass der Interrupt nur ein einziges mal ausgeführt wird und dann nichts> mehr.
SP wurde richtig initialisiert? Besteht das Hauptprogramm wirklich nur
aus einer Endlosschleife? Falls nicht, könnte es sein, dass die ISR
Auswirkungen auf den Lauf des Hauptprogramms hat -- alle doppelt
genutzten Register müssen gesichert werden.
Vielleicht liegt der Fehler aber auch in Zeile 42...? ;-)
Habe den Fehler beseitigen können.
Ich fürchte da war wohl irgend eine Schwarze Magie am werk.
Habe den Abschnitt Timer1 in folgendes umgeändert:
1
;------------------------------- Timer 1
2
ldi temp, (1<<OCIE1A) ; Interupt bei clear timer on compare match
3
out TIMSK, temp
4
ldi temp, (1<<WGM12) | (1<<CS12) | (0<<CS11) | (1<<CS10) ; CTC Modus einschalten, so wie vorteiler setzen
5
out TCCR1B, temp
6
7
sei
8
9
ldi temp, HIGH(1000) ; CTC wert
10
out OCR1AH, temp
11
ldi temp, LOW(1000)
12
out OCR1AL, temp
Also ich benutze nun den CTC Kanal A. Wieso das nun einen Unterschied
macht weiß ich auch nicht. Kann mir das evtl. jemand erklären?
Natürlich habe ich die Interruptadresse auch geändert.
Konrad M. schrieb:> Also ich benutze nun den CTC Kanal A. Wieso das nun einen Unterschied> macht weiß ich auch nicht. Kann mir das evtl. jemand erklären?
Es gibt kein "CTC Kanal B". Wenn du den CTC-Modus wählst, und OCR1A auf
0 lässt (wie in deinem ersten Code), dann zählt der Timer nur
0-0-0-0-0-... und erreicht nie die 1000 um den OCR1B-Interrupt
auszulösen.