Interrupt aus main aufrufen AVR

#7338703
Lesenswert?

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.

#7338750
Lesenswert?

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)."

#7338767
Lesenswert?

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 :-)

Gast #7338773
Lesenswert?

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.

Gast #7338797
Lesenswert?

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.

Gast #7338831
Lesenswert?

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.

#7338835
Lesenswert?

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."

Gast #7338844
Lesenswert?

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?

Gast #7338852
Lesenswert?

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

Angehängte Dateien:
Moderator #7338872
Lesenswert?

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.

Gast #7338876
Lesenswert?

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.

Gast #7338914
Lesenswert?

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.

Angehängte Dateien:
#7338942
Lesenswert?

Yalu X. schrieb:

Ganz einfach: cli(); USART0_TX_vect();

Das mag der Compiler nicht 8-0

1
#define F_CPU 8000000UL
2

3
#include <avr/io.h>
4
#include <avr/interrupt.h>
5
#include <util/delay.h>
6

7
int main(void)
8

9
{
10
    DDRB |= (1<<DDB1);  //PORTB=0x00;
11

12
    USART_TX_vect();
13

14
    while (1) 
15
    {
16
        PORTB |= (1<<PORTB1);
17
        _delay_ms(50);
18
        PORTB &= ~ (1<<PORTB1);
19
        _delay_ms(50);
20
    }
21
}
22

23
ISR(USART_TX_vect) {
24
    PORTB ^= (1<<PORTB1);
25
}
1
../test_simulation.c: In function 'main':
2
../test_simulation.c:12: warning: implicit declaration of function '__vector_20'
3
../test_simulation.c: At top level:
4
../test_simulation.c:23: warning: conflicting types for '__vector_20'
5
../test_simulation.c:12: warning: previous implicit declaration of '__vector_20' was here
Gast #7338987
Lesenswert?

Ich habe unter Linux den gleichen Fehler:

1
stefan@stefanspc:~/Downloads$ cat > test.c
2
#include <avr/io.h>
3

4
ISR(USART_TX_vect) // ISR(USART_TX_vect) 
5
{
6
    PORTB |= 1; // PB0 auf HIGH
7
}
8

9

10
int main() 
11
{
12
    DDRB |= 3;  // PB0 und PB1: Ausgang
13
    USART_TX_vect();
14
  PORTB |= 2; // PB1 auf HIGH
15
}
16

17
stefan@stefanspc:~/Downloads$ avr-gcc -Wall -gdwarf-2 -std=gnu99 -O1 -mmcu=atmega328p test.c
18
test.c:3:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
19
 ISR(USART_TX_vect) // ISR(USART_TX_vect) 
20
 ^
21
test.c: In function ‘ISR’:
22
test.c:3:1: warning: type of ‘__vector_20’ defaults to ‘int’ [-Wimplicit-int]
23
test.c:6:1: warning: control reaches end of non-void function [-Wreturn-type]
24
 }
25
 ^
26
stefan@stefanspc:~/Downloads$ avr-objdump -h -S a.out
27

28
a.out:     file format elf32-avr
29

30
...
31

32
0000007c <__bad_interrupt>:
33
  7c:  0c 94 00 00   jmp  0  ; 0x0 <__vectors>
34

35
00000080 <ISR>:
36
#include <avr/io.h>
37

38
ISR(USART_TX_vect) // ISR(USART_TX_vect) 
39
{
40
    PORTB |= 1; // PB0 auf HIGH
41
  80:  28 9a         sbi  0x05, 0  ; 5
42
}
43
  82:  08 95         ret
44

45
00000084 <main>:
46

47

48
int main() 
49
{
50
    DDRB |= 3;  // PB0 und PB1: Ausgang
51
  84:  84 b1         in  r24, 0x04  ; 4
52
  86:  83 60         ori  r24, 0x03  ; 3
53
  88:  84 b9         out  0x04, r24  ; 4
54
    USART_TX_vect();
55
  8a:  0e 94 3e 00   call  0x7c  ; 0x7c <__bad_interrupt>
56
  PORTB |= 2; // PB1 auf HIGH
57
  8e:  29 9a         sbi  0x05, 1  ; 5
58
}
59
  90:  80 e0         ldi  r24, 0x00  ; 0
60
  92:  90 e0         ldi  r25, 0x00  ; 0
61
  94:  08 95         ret
62

63
00000096 <_exit>:
64
  96:  f8 94         cli
65

66
00000098 <__stop_program>:
67
  98:  ff cf         rjmp  .-2        ; 0x98 <__stop_program>
68

69
stefan@stefanspc:~/Downloads$ avr-gcc --version
70
avr-gcc (GCC) 5.4.0
71
Copyright (C) 2015 Free Software Foundation, Inc.
72
This is free software; see the source for copying conditions.  There is NO
73
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#7338988
Lesenswert?

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.

Gast #7339005
Lesenswert?

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?

Persönliche Seite #7339014
Lesenswert?

Warum nicht einfach "normalen" Code schreiben?

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3

4
void machwas (void);
5

6
ISR (USART_TX_vect, ISR_FLATTEN)
7
{
8
    machwas();
9
}
10

11
int main (void)
12
{
13
    machwas();
14
}
15

16
void machwas (void)
17
{
18
    __asm ("nop");
19
    // ...
20
}

Falls "machwas" in der ISR nicht geinlinet werden soll, dann einfach flatten weglassen.

Gast #7339027
Lesenswert?

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 im Beitrag #7338987: Ausgaben des Compilers

Ich habe die Warnungen schon gesehen, aber nicht erkannt, was deren Ursache ist.

Gast #7339117
Lesenswert?

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.

Gast #7339245
Lesenswert?

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.

#7339291
Lesenswert?

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.

Gast #7339339
Lesenswert?

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.

Gast #7339362
Lesenswert?

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.

Gast #7339418
Lesenswert?

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...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren