Moin,
ich würde gerne eine Interrupt Routine auf meinem ATmega2560 per Hand
aufrufen. Konkret geht es um den Transmit Complete interrupt, welchen
ich gerne einmal per Hand auslösen würde.
Gruß
Justus
Justus Jonas schrieb:> ich würde gerne eine Interrupt Routine auf meinem ATmega2560 per Hand> aufrufen. Konkret geht es um den Transmit Complete interrupt, welchen> ich gerne einmal per Hand auslösen würde.
Das ist wenig sinnvoll. Wenn, dann musst du die Funktion im Interruopt
in eine normale Funktion packen, die man dann aus der ISR oder dem
normalen Programm heraus aufrufen kann. Das hat in der ISR den kleinen
Nachteill, daß dort realtiv viele Register gesichert werden müssen, so
um die 10. Das macht die ISR etwas langsamer. Wenn man das vermeiden
will, muss man die Funktionalität der ISR und der normalen Funktion
kopieren, sprich, doppelter Code an zwei Stellen im Quelltext. Nicht
schön, aber OK.
Justus Jonas schrieb:> Konkret geht es um den Transmit Complete interrupt, welchen> ich gerne einmal per Hand auslösen würde.
Such mal nach "Software Interrupt"
Justus Jonas schrieb:> ich würde gerne eine Interrupt Routine auf meinem ATmega2560 per Hand> aufrufen.
cli
rcall <symbolischer Name des Vectors, z.B.: UTXC0addr>
Das ruft natürlich wirklich nur die ISR auf, ändert nichts am Status der
Hardware.
Justus Jonas schrieb:> Konkret geht es um den Transmit Complete interrupt, welchen> ich gerne einmal per Hand auslösen würde.
Warum setzt Du nicht das entsprechende Interrupt-Flag?
Hugo H. schrieb:>> Konkret geht es um den Transmit Complete interrupt, welchen>> ich gerne einmal per Hand auslösen würde.>> Warum setzt Du nicht das entsprechende Interrupt-Flag?
Weil die nur die Hardware setzen kann. Die CPU kann sie nur löschen!
"Bit 6 – TXCn: USART Transmit Complete
This flag bit is set when the entire frame in the Transmit Shift
Register has been shifted out and there are no new data currently
present in the transmit buffer (UDRn). The TXCn Flag bit is
automatically cleared when a transmit complete interrupt is executed, or
it can be cleared by writing a one to its bit location. The TXCn Flag
can generate a Transmit Complete interrupt (see description of the
TXCIEn bit)."
Falk B. schrieb:> Weil die nur die Hardware setzen kann. Die CPU kann sie nur löschen!
Dann erkläre bitte mal, warum das bei einem Bit gehen soll und beim
anderen Bit nicht:
• Bit 7 – RXCn: USART Receive Complete
This flag bit is set when there are unread data in the receive buffer
and cleared when the receive buffer is empty
(that is, does not contain any unread data). If the Receiver is
disabled, the receive buffer will be flushed and consequently
the RXCn bit will become zero.
The RXCn Flag can be used to generate a Receive Complete interrupt
(see
description of the RXCIEn bit).
• Bit 6 – TXCn: USART Transmit Complete
This flag bit is set when the entire frame in the Transmit Shift
Register has been shifted out and there are no new
data currently present in the transmit buffer (UDRn). The TXCn Flag bit
is automatically cleared when a transmit
complete interrupt is executed, or it can be cleared by writing a one to
its bit location.
The TXCn Flag can generate a Transmit Complete interrupt (see
description of the TXCIEn bit).
Soll das nur am "Sprachgefüge" liegen? Wenn ich Spaß dran habe probiere
ich es mal aus :-)
Hugo H. schrieb:> The RXCn Flag can be used to generate a Receive Complete interrupt> (see> description of the RXCIEn bit).> Soll das nur am "Sprachgefüge" liegen?
Can be used heisst, dass das Bit intern mit der Interrupt-Einrichtung
verkabelt ist und es 'can' ein Interrupt-Enable-Bit gesetzt werden,
sodass jenes Bit den Interrupt ausloest.
Bei 8 Bit AVR macht der RETI Befehl genau das gleiche, wie RET. Deswegen
kann man theoretisch Interrupthandler wie ganz normale Funktionen
aufrufen. Ich habe allerdings nicht herausgefunden, wie man das in C
anstellt.
Stefan F. schrieb:> Bei 8 Bit AVR macht der RETI Befehl genau das gleiche, wie RET.
Falsch. RETI setzt das I-Bit und ermöglicht weitere Interrupts. RET tut
das nicht.
> Bei 8 Bit AVR macht der RETI Befehl genau das gleiche, wie RET.
?
RETI behaves differently in AVRe, AVRxm, and AVRxt devices. In the AVRe
series of devices, the Global Interrupt Enable bit is cleared by
hardware once an interrupt occurs, and this bit is set when RETI is
executed. In the AVRxm and AVRxt devices, RETI will not modify the
Global Interrupt Enable bit in SREG since it is not cleared by hardware
while entering ISR. This bit should be modified using SEI and CLI
instructions when needed.
Hugo H. schrieb:>> Weil die nur die Hardware setzen kann. Die CPU kann sie nur löschen!>> Dann erkläre bitte mal, warum das bei einem Bit gehen soll und beim> anderen Bit nicht:
Erklär mal, wie du das Bit setzen willst, wenn es heißt
"The TXCn Flag bit is automatically cleared when a transmit complete
interrupt is executed, or it can be cleared by writing a one to its bit
location."
Hugo Hurtig schrieb:
> Wenn ich Spaß dran habe probiere ich es mal aus
Wäre nicht das umgekehrte Vorgehen angebracht - wenn man sich unsicher
ist, erst ausprobieren, und danach sich hier äußern?
S. Landolt schrieb:> ?> RETI behaves differently in AVRe, AVRxm, and AVRxt devices.
Da hast du jetzt ein paar spezielle AVR Serien genannt, die ich dabei
nicht im Sinn hatte. Ehrlich gesagt sagen mir diese drei Abkürzungen
nichts. Wo hast du die her, dass ich da mal nachlesen kann?
Ob durch den Funktionsaufruf das globale I Flag gesetzt wird, spielt bei
dem angefragten Anwendungsfall keine Rolle. Denn es war vorher an und
soll auch nachher an sein (nehme ich an).
Die Screenshots stammen aus
https://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf
Stefan F. schrieb:> Ich habe allerdings nicht herausgefunden, wie man das in C> anstellt.
Ganz einfach:
1
cli();
2
USART0_TX_vect();
Der Funktionsname ist derselbe, der auch in der Definition der ISR an
das ISR-Makro übergeben wird.
Vor dem Aufruf sperrt man sinnvollerweise die Interrupts, wie dies auch
beim Auslösen eines echten Interrupts automatisch getan wird. Durch den
RETI am Ende der ISR werden die Interrupts wieder freigeschaltet, was
hier vermutlich auch gewünscht wird.
Justus Jonas schrieb:> ich würde gerne eine Interrupt Routine auf meinem ATmega2560 per Hand> aufrufen.AVRs kennen keine Software Interrupts!
Justus Jonas schrieb:> Konkret geht es um den Transmit Complete interrupt, welchen> ich gerne einmal per Hand auslösen würde.
Auf ein kaputtes Konzept, einen solchen Flicken setzen zu wollen, ist
99.999% der Fälle eine dumme Idee.
Mein Rat:
Mindestens 2 Schritte zurück gehen und neu nachdenken.
Yalu X. schrieb:> USART0_TX_vect();
Das habe ich probiert, aber zumindest im Simulator klappt das nicht.
Dann habe ich zum ATmega328p gewechselt, aber auch damit klappte es
nicht. Siehe Screenshot. Wenn ich dort noch einen schritt weiter
debugge, beginnt das Programm wieder am Anfang von main().
Offenbar springt er an die falsche Adresse. Man sieht das auch im *.lss:
1
00000080 <ISR>:
2
#include <avr/io.h>
3
4
ISR(USART_TX_vect) // ISR(USART_TX_vect)
5
{
6
PORTB |= 1; // PB0 auf HIGH
7
80: e5 e2 ldi r30, 0x25 ; 37
8
82: f0 e0 ldi r31, 0x00 ; 0
9
84: 80 81 ld r24, Z
10
86: 81 60 ori r24, 0x01 ; 1
11
88: 80 83 st Z, r24
12
}
13
8a: 08 95 ret
14
15
0000008c <main>:
16
17
18
int main()
19
{
20
DDRB |= 3; // PB0 und PB1: Ausgang
21
8c: e4 e2 ldi r30, 0x24 ; 36
22
8e: f0 e0 ldi r31, 0x00 ; 0
23
90: 80 81 ld r24, Z
24
92: 83 60 ori r24, 0x03 ; 3
25
94: 80 83 st Z, r24
26
USART_TX_vect();
27
96: 0e 94 3e 00 call 0x7c ; 0x7c <__bad_interrupt>
28
PORTB |= 2; // PB1 auf HIGH
29
9a: e5 e2 ldi r30, 0x25 ; 37
30
9c: f0 e0 ldi r31, 0x00 ; 0
31
9e: 80 81 ld r24, Z
32
a0: 82 60 ori r24, 0x02 ; 2
33
a2: 80 83 st Z, r24
34
}
35
a4: 80 e0 ldi r24, 0x00 ; 0
36
a6: 90 e0 ldi r25, 0x00 ; 0
37
a8: 08 95 ret
38
39
000000aa <_exit>:
40
aa: f8 94 cli
41
42
000000ac <__stop_program>:
43
ac: ff cf rjmp .-2 ; 0xac <__stop_program>
Anstatt "call 0x7c" würde ich hier "call 0x80" erwarten. Ich habe mit
-O1 compiliert.
> ... falsche Adresse. Man sieht das auch im *.lss
Also bei mir stimmt es im .lss-File.
Zu meiner C-Umgebung kann ich aber leider nichts sagen, die ist
irgendwie zusammengestoppelt.
Stefan F. schrieb:> Du musst die ISR vor main() platzieren.
Ahhhh! Manchmal ist es so einfach!
1
00000000 <__vectors>:
2
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
3
4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
4
8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
5
c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
6
10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
7
14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
8
18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
9
1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
10
20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
11
24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
12
28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
13
2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
14
30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
15
34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
16
38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
17
3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
18
40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
19
44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
20
48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
21
4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
22
50: 0c 94 40 00 jmp 0x80 ; 0x80 <__vector_20>
23
54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
24
58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
25
5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
26
60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
27
64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
28
29
00000068 <__ctors_end>:
30
68: 11 24 eor r1, r1
31
6a: 1f be out 0x3f, r1 ; 63
32
6c: cf ef ldi r28, 0xFF ; 255
33
6e: d8 e0 ldi r29, 0x08 ; 8
34
70: de bf out 0x3e, r29 ; 62
35
72: cd bf out 0x3d, r28 ; 61
36
74: 0e 94 52 00 call 0xa4 ; 0xa4 <main>
37
78: 0c 94 68 00 jmp 0xd0 ; 0xd0 <_exit>
38
39
0000007c <__bad_interrupt>:
40
7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
41
42
00000080 <__vector_20>:
43
ISR(USART_TX_vect) {
44
80: 1f 92 push r1
45
82: 0f 92 push r0
46
84: 0f b6 in r0, 0x3f ; 63
47
86: 0f 92 push r0
48
88: 11 24 eor r1, r1
49
8a: 8f 93 push r24
50
8c: 9f 93 push r25
51
PORTB ^= (1<<PORTB1);
52
8e: 85 b1 in r24, 0x05 ; 5
53
90: 92 e0 ldi r25, 0x02 ; 2
54
92: 89 27 eor r24, r25
55
94: 85 b9 out 0x05, r24 ; 5
56
}
57
96: 9f 91 pop r25
58
98: 8f 91 pop r24
59
9a: 0f 90 pop r0
60
9c: 0f be out 0x3f, r0 ; 63
61
9e: 0f 90 pop r0
62
a0: 1f 90 pop r1
63
a2: 18 95 reti
64
65
000000a4 <main>:
66
67
68
int main(void)
69
70
{
71
DDRB |= (1<<DDB1); //PORTB=0x00;
72
a4: 21 9a sbi 0x04, 1 ; 4
73
74
USART_TX_vect();
75
a6: 0e 94 40 00 call 0x80 ; 0x80 <__vector_20>
76
milliseconds can be achieved.
77
*/
Passt. Die ISR liegt auf Adresse 0x80, und der rcall ruft sie auf. Ganz
normal. Es wird NICHT der Interruptvektor __vector_20 auf Adresse 0x50
aufgerufen! In ASM kann man das machen, bringt aber auch nix extra.
S. Landolt schrieb:> Wäre nicht das umgekehrte Vorgehen angebracht - wenn man sich unsicher> ist, erst ausprobieren, und danach sich hier äußern?
Ja - hätte ich mal besser gemacht. Ich gelobe Besserung :-)
Falk B. schrieb:> Passt. Die ISR liegt auf Adresse 0x80, und der rcall ruft sie auf. Ganz> normal.
Warum zum Teufel geht das bei dir, und bei mir nicht?
Falk B. schrieb:> Da fehlt das #include "avr/interrupt.h"
Oh Mann, ja genau das war bei mir der Fehler.
Also halten wir fest: ISR kann man einfach wie normale Funktionen
aufrufen.
Stefan Frings schrieb:
> Oh Mann, ja genau das war bei mir der Fehler.
Merkwürdig: bei mir hier bringt der Compiler drei Warnungen, wenn ich
das '#include <avr/interrupt.h>' weglasse - bei Ihnen nicht?
S. Landolt schrieb:> Merkwürdig: bei mir hier bringt der Compiler drei Warnungen, wenn ich> das '#include <avr/interrupt.h>' weglasse - bei Ihnen nicht?
Siehe
Stefan F. schrieb:
Ausgaben des Compilers
Ich habe die Warnungen schon gesehen, aber nicht erkannt, was deren
Ursache ist.
Stefan F. schrieb:> Also halten wir fest: ISR kann man einfach wie normale Funktionen> aufrufen.
Man kann auch im 10.Stock aus dem Fenster springen. Ratsam ist beides
nicht.
Mal ganz deutlich gesagt: Interrupt-Services sind NICHT dafür gemacht,
im Programm von irgendwo her aufgerufen zu werden - auch wenn es formal
gehen sollte. Und wer da meint, daß er es dennoch braucht, der hat an
anderer Stelle etwas ganz verkehrt gemacht.
W.S.
Stefan F. schrieb:> Also halten wir fest: ISR kann man einfach wie normale Funktionen> aufrufen.
Ja. Sinnvoll ist das aber praktisch nie. I.d.R. hantieren ISRs ja
irgendwie mit der Hardware, die den Interrupt ausgelöst hat. Und deren
Status passt halt nicht. D.h.: es ist relativ wahrscheinlich, dass die
ISR "anders" läuft, als im echten Interruptfall.
Früher (TM) hätte man eine Rücksprungadresse auf den Stack gelegt und
dann einen Sprung auf die Interrupt-Routine gemacht. Aber das waren noch
6502-Tricksereien.
Christoph db1uq K. schrieb:> Früher (TM) hätte man eine Rücksprungadresse auf den Stack gelegt und> dann einen Sprung auf die Interrupt-Routine gemacht. Aber das waren noch> 6502-Tricksereien.
Nichts anderes passiert beim direkten Aufruf in C.
Justus Jonas schrieb:> Konkret geht es um den Transmit Complete interrupt, welchen> ich gerne einmal per Hand auslösen würde.
Die Frage ist, warum man sowas überhaupt machen sollte.
Er wird doch nur benötigt, um bei RS-485 die Richtung des Transceivers
umzuschalten. Initial läßt man den Transceiver erstmal auf Empfang.
Für das Senden selber nimmt man ja den USART Data Register Empty
Interrupt.
Peter D. schrieb:
[TXC-Interrupt]
> Er wird doch nur benötigt, um bei RS-485 die Richtung des Transceivers> umzuschalten.
Das mag die einzige nützliche Anwendung sein, die DU kennst, aber es
gibt noch etliche mehr...
Stefan F. schrieb:> Nichts anderes passiert beim direkten Aufruf in C.
Sei froh, daß es sich hier um einen AVR handelt. Bei anderen
Architekturen sieht sowas völlig anders aus, da wird ggf. auch der Stack
gewechselt, der Programmlevel von User auf System oder Supervisor
gewechselt, beim ARM7 ggf. auch Code (Arm/Thumb) und einige Register
(Schattenregister) gewechselt usw.
Von wegen nichts ...
Eben genau deshalb gibt es eine Reihe von Randbedingungen bei der
Verwendung von C, die eine saubere Trennung zwischen dem Grundprogramm
und den ISR erfordern. Und bei kleineren Plattformen (PIC, 8051 usw.)
hat man oftmals keine reentranten Funktionen, weil lokale Variablen eben
NICHT auf dem Stack stehen und RAM nur sehr begrenzt vorhanden ist, da
sollte man sowas auch besser bleiben lassen.
Und Leute, die offenbar mit den AVR aufgewachsen sind, haben sich dann
solche schlechten Manieren angewöhnt. Ja auch das ubiquitäre Verwenden
von DI/EI gehört dazu.
W.S.
W.S. schrieb:> Sei froh, daß es sich hier um einen AVR handelt. Bei anderen> Architekturen sieht sowas völlig anders aus
Ich weiss. Deswegen hatte ich das (siehe oben) extra nochmal
ausprobiert, bevor ich hier Blödsinn verzapfe.
Manche Dinge sind eben Hardware spezifisch. Man kann nicht alles völlig
generisch programmieren.
Stefan F. schrieb:> Manche Dinge sind eben Hardware spezifisch. Man kann nicht alles völlig> generisch programmieren.
Das allerdings ist, was die nixwissenden C-Apologeten immer wieder
versprechen, also inbesondere die, die nie nahe an der Hardware
programmieren...
Die richtigen C-Programmierer wissen natürlich sehr wohl, dass es da
eine Grenze gibt, die sich auch in C nicht wirklich überwinden läßt. Es
läuft dann halt auf die allseits beliebten #ifdef-Orgien hinaus, wenn
man nahe an der Hardware irgendwas "portabel" programmieren will.
Ich halte diese Art der "Portabilität" allerdings sowieso für einen
ziemlichen Euphemismus. Wie wohl jeder, der beim Kompilieren solchen
Codes in den #else-Zweig dieser #ifdef-Orgien gelangt...