Interrupt aus main aufrufen AVR

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.

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

Gast

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

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

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

Gast

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

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

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

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

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:

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

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

Gast

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.

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

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

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

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

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

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.

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

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

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

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

Eine eigene Frage? Dieser Thread ist über 3 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.