Forum: Mikrocontroller und Digitale Elektronik AT Mega 88 Interrupt


von Amateur (Gast)


Lesenswert?

Hallo,

ich möchte über einen Tastendruck (fallende Flanke) den Interrupt INT1 
auslösen.

passen die Registereinstellungen?
1
 //Interrupt INT1 auf fallende Flanke definiert
2
  EICRA = 0x08;
3
  EIMSK = 0x02;
das ist dann der Interrupt Aufruf:
1
ISR (INT1_vect)
2
{
3
  ChargeCurrent_Write_Speed();
4
  ChargeVoltage_Write();
5
  InputCurrent_Write();
6
}
Muss ich noch irgendwas einstellen?? Es tut sich nichts wenn ich auf die 
Taste drücke es wird kein Interrupt ausgelöst...

von Johannes O. (jojo_2)


Lesenswert?

sei(); eingegeben? Aktiviert die Interrupts global.

sonst sind die Interrupts nicht aktiviert!

Zeig ansonsten mal bitte deinen ganzen Code, dann kann man besser 
helfen.

Ersetze die Konstanten wie z.B. 0x12 usw. in Zukunft durch so etwas wie 
z.B. (1 << INTF0) oder was du da gerade machst. Man sieht es viel besser 
was passiert.

von Amateur (Gast)


Lesenswert?

Johannes O. schrieb:
> sei(); eingegeben? Aktiviert die Interrupts global.
>
> sonst sind die Interrupts nicht aktiviert!
>
> Zeig ansonsten mal bitte deinen ganzen Code, dann kann man besser
> helfen.
>
> Ersetze die Konstanten wie z.B. 0x12 usw. in Zukunft durch so etwas wie
> z.B. (1 << INTF0) oder was du da gerade machst. Man sieht es viel besser
> was passiert.

hier ist die Anpassung:
 //Interrupt INT1 auf fallende Flanke definiert
  EICRA = (1 << ISC11);
  EIMSK = (1 << INT1);

ISR (INT1_vect)
{
  ChargeCurrent_Write_Speed();
  ChargeVoltage_Write();
  InputCurrent_Write();
}

geht leider aber immer noch nicht.

Muss ich noch was definieren ? Habe ich was übersehen?

von Johannes O. (jojo_2)


Lesenswert?

Bitte mal den GANZEN Code. Hast du sei() drin?

von Amateur (Gast)


Lesenswert?

Johannes O. schrieb:
> Bitte mal den GANZEN Code. Hast du sei() drin?


das ist der gesamte relevante code:
1
//Interrupt INT1 auf fallende Flanke definiert
2
  EICRA = (1 << ISC11);
3
  EIMSK = (1 << INT1);
4
5
6
ISR (INT1_vect)
7
{  __enable_interrupt;
8
  ChargeCurrent_Write_Speed();
9
  ChargeVoltage_Write();
10
  InputCurrent_Write();
11
}
Passt das soweit mit den Registern?

von Karl H. (kbuchegg)


Lesenswert?

Amateur schrieb:
> Johannes O. schrieb:
>> Bitte mal den GANZEN Code. Hast du sei() drin?
>
>
> das ist der gesamte relevante code:

glaub ich kaum.

>
> //Interrupt INT1 auf fallende Flanke definiert
>   EICRA = (1 << ISC11);
>   EIMSK = (1 << INT1);
>
>
> ISR (INT1_vect)
> {  __enable_interrupt;

in der ISR die Interrupts freigeben?
Das kann aber nicht dein Ernst sein.

Zeig halt mal alles oder bau dir ein abgespecktes Beispiel, wenn dir der 
Code insgesamt als zu viel vorkommt.

Ein einfaches Beispiel, bei dem der Interrupt eine LED einschaltet und 
sonst nichts im Programm ist, reicht ja schon um die Funktionsgruppe 
'externer Interrupt' zu testen, zu debuggen und in Betrieb zu nehmen. 
Das was man dann daran lernt wendet man aufs eigentliche Programm an.

von Timmo H. (masterfx)


Lesenswert?

Ohne sei() kein Interrupt. Da kannst du in den anderen Registern 
rumfummeln wie du willst.

von Thomas E. (thomase)


Lesenswert?

Amateur schrieb:
> Johannes O. schrieb:
>> Bitte mal den GANZEN Code. Hast du sei() drin?
>
>
> das ist der gesamte relevante code:
Den ganzen Code. Nicht was du für relevant hältst.
Wie oft denn noch?
>
> //Interrupt INT1 auf fallende Flanke definiert
>   EICRA = (1 << ISC11);
>   EIMSK = (1 << INT1);
>
>
> ISR (INT1_vect)
> {  __enable_interrupt;
Was ist denn das für ein Unsinn?

>   ChargeCurrent_Write_Speed();
>   ChargeVoltage_Write();
>   InputCurrent_Write();
> }
>
> Passt das soweit mit den Registern?

mfg.

von Amateur (Gast)


Lesenswert?

1
#ifndef _DEFINITIONEN_H_
2
#define _DEFINITIONEN_H_
3
4
// ***** Anfang allgemeine Includes
5
#include <avr/io.h>    // ATmega88 Definitionen
6
//#include <inavr.h>    // IAR Definitionen
7
#include <stdio.h>    // Standard I/O Definitionen
8
// ***** Ende allgemeine Includes
9
10
11
// ***** Beginn Systemkonfiguration
12
13
// ***** Ende Systemkonfiguration
14
15
16
// ***** Anfang Direktiven
17
#ifndef setbit                                         // Bit setzen durch ODER-Maskierung
18
#define setbit(p,n) (p|=(1<<n))
19
#endif
20
21
#ifndef clearbit                                       // Bit rücksetzen durch UND-Maskierung
22
#define clearbit(p,n) (p&=~(1<<n))
23
#endif
24
// ***** Ende Direktiven
25
26
27
// ***** Beginn diverses
28
#define ON            1               // Definition On
29
#define OFF           0               // Definition Off
30
#define SW_VERSION    0x01
31
32
33
#define __no_operation() __asm__ volatile ("nop")
34
#define __enable_interrupt() __asm__ volatile ("sei")
35
#define __disable_interrupt() __asm__ volatile ("cli")
36
#define __watchdog_reset() __asm__ volatile ("wdr")
37
38
#endif /*_DEFINITIONEN_H_*/
1
 //Interrupt INT1 auf fallende Flanke definiert
2
  EICRA = (1 << ISC11);
3
  EIMSK = (1 << INT1);
4
5
__enable_interrupt;
6
ISR (INT1_vect)
7
{
8
9
  PORTC |= ( 1 << PC0);
10
11
}

Was fehlt noch was damit diese "einfache" InterruptFunktion aufgerufen 
wird?

von Johannes O. (jojo_2)


Lesenswert?

Amateur schrieb:
> Was fehlt noch was damit diese "einfache" InterruptFunktion aufgerufen
> wird?

Eine main Funktion.

von Roger (Gast)


Lesenswert?

Das __enable_interrupt() nicht in die Interrupt-Routine, sondern in die 
main-loop, dann lüpt es auch.
Bzw. sei(), wie oben schon geschrieben.

von Timmo H. (masterfx)


Lesenswert?

Johannes O. schrieb:
> Amateur schrieb:
>> Was fehlt noch was damit diese "einfache" InterruptFunktion aufgerufen
>> wird?
>
> Eine main Funktion.
in der zumindest ein "sei()" steht

von Thomas E. (thomase)


Lesenswert?

Amateur schrieb:
> Was fehlt noch was damit diese "einfache" InterruptFunktion aufgerufen
> wird?
Der Pullup für den Taster.

mfg.

von Karl H. (kbuchegg)


Lesenswert?

Den Port C auf Ausgang schalten.


Sag mal wird das eine Verarsche?
Was ist denn bitte an
1
....
2
3
int main()
4
{
5
  DDRC |= ( 1 << PC0 );
6
7
  DDRD &= ( 1 << PD3 );
8
  PORTD |= ( 1 << PD3 );
9
10
  EICRA = (1 << ISC11);
11
  EIMSK = (1 << INT1);
12
13
  __enable_interrupt;
14
15
  PORTC &= ~( 1 << PC0 );
16
17
  while( 1 )
18
    ;
19
}
das große Problem? Jeder der schon mal eine LED ein/ausgeschaltet hat, 
muss doch die restlichen Zutaten für ein simples Testprogramm im Schlaf 
runterbeten können.

von M. N. (Gast)


Lesenswert?


von Amateur (Gast)


Lesenswert?

Hallo hier ist der Quellcode der soweit auch funktioniert beim betätigen 
der Taste wird der Interrupt ausgelöst und die LED leuchtet.

Jetzt möchte ich aber aus dem Interrupt raus und die LED ausmachen. Wie 
setze ich das um?
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#define __enable_interrupt() __asm__ volatile ("sei")
4
#define __disable_interrupt() __asm__ volatile ("cli")
5
6
7
8
9
int main(void)
10
{
11
 
12
    DDRB = 0x06;
13
    PORTB = 0;
14
    
15
  
16
    DDRC = 0x3F;
17
    PORTC = 0;
18
    
19
    
20
    DDRD = 0x00;
21
    PORTD = 0x1F;
22
    
23
   
24
    
25
    //Interrupt INT1 
26
    __enable_interrupt();
27
    EICRA = (1 << ISC11);
28
    EIMSK = (1 << INT1);
29
   
30
   
31
    while(1)
32
    {
33
        
34
    }
35
}
36
37
38
ISR (INT1_vect)
39
{
40
  PORTB |= ( 1 << PB2);
41
  __disable_interrupt();
42
  
43
}


P.S bitte nicht gleich "steinigen" wenn es zu einfach ist .....

von Karl H. (kbuchegg)


Lesenswert?

Amateur schrieb:

> Jetzt möchte ich aber aus dem Interrupt raus und die LED ausmachen. Wie
> setze ich das um?

Du willst nicht 'aus dem Interrupt raus'.
Du willst dir im Interrupt in einer globalen Variablen (die volatile 
sein muss) vermerken, dass die LED auzuschalten ist.

Und die Hauptschleife sieht sich diese globale Variable an und wenn dort 
der 'Befehl' zum ausmachen der LED auftaucht, dann macht sie das und 
setzt den 'Befehl' wieder zurück.


> P.S bitte nicht gleich "steinigen" wenn es zu einfach ist .....

Es geht nicht um "einfach".
Es geht darum, dass diese Taktik der Problem-Präsentation in 
homöopathischen Dosen einfach nur nervt. Speziell wenn schon x-mal nach 
komplettem Code nachgefragt wurde und du dann immer noch nicht 
reagierst.

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
ISR (INT1_vect)
2
{
3
  PORTB |= ( 1 << PB2);
4
  __disable_interrupt();
5
  
6
}
(gemeint ist der _disable_interrupt) bringt dir genau gar nichts.
Denn der Code ist hier in einer ISR.
* während des Betretens der ISR werden die Interrupts abgeschaltet. d.h. 
alleine dadurch, dass das eine ISR ist, ist damit implizit verknüpft, 
dass vor deinem Code ein __disable_interrupt() gemacht wird
* beim Verlassen der ISR werden Interrupts automatisch wieder 
zugelassen. D.h. alleine dadurch, dass das eine ISR ist, ist damit 
implizit verknüpft, dass am Ende der Funktion, als allerletztes (also 
nachdem alle Aufräumarbeiten der Funktion gemacht wurden) als Teil der 
Returns ein __enable_interrupt() durchgeführt wird. Tatsächlich ist es 
so, dass es für diesen Fall eine spezielle Prozessorinstruktion gibt, 
die beides gleichzeitig macht: Return und Interrupts freigeben. Weil es 
eben wichtig ist, das hier die zeitliche Reihenfolge stimmt.

Fazit:
1) dein __disable_interrupt() bringt überhaupt nichts
2) vergreifst du dich innerhalb einer ISR an dieser Freigabe, dann 
kannst du maximal auf die Schnauze fallen (wenn du nicht exakt und 100% 
weißt was du tust).

von hilmar (Gast)


Lesenswert?

Kurz nachdem du die LED eingeschaltet hast, bist du doch schon wieder 
aus dem Interrupt raus.
Warum hast du eigentlich
1
 __disable_interrupt();
in der Interruptroutine stehen?
Und ganz abgesehen davon, warum ersetzt du
1
cli();
2
durch
3
 __disable_interrupt();
und
1
sei();
2
durch
3
 __enable_interrupt();

Ist dir cli(); und sei(); zu kurz? :-)

LED ausschalten:
Fallende Flanke auswerten und dann mit
1
PORTB &= ~(1 << PB2);
die LED wieder ausmachen.

von Amateur (Gast)


Lesenswert?

hilmar schrieb:
> Kurz nachdem du die LED eingeschaltet hast, bist du doch schon
> wieder
> aus dem Interrupt raus.
> Warum hast du eigentlich __disable_interrupt();
> in der Interruptroutine stehen?
> Und ganz abgesehen davon, warum ersetzt ducli();
> durch
>  __disable_interrupt();
> undsei();
> durch
>  __enable_interrupt();

>
> Ist dir cli(); und sei(); zu kurz? :-)
hat der Compiler(AVR-Studio 6.1) erst nicht genommen jetzt komischer 
weise schon
>
> LED ausschalten:
> Fallende Flanke auswerten und dann mitPORTB &= ~(1 << PB2);
> die LED wieder ausmachen.


wie werte ich denn die fallende Flanke aus?
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
5
6
volatile unsigned int Merker;
7
8
int main(void)
9
{
10
 
11
    DDRB = 0x06;
12
    PORTB = 0;
13
    
14
  
15
    DDRC = 0x3F;
16
    PORTC = 0;
17
    
18
    
19
    DDRD = 0x00;
20
    PORTD = 0x1F;
21
  
22
  Merker;
23
    
24
   
25
    
26
    //Interrupt INT1 
27
    sei();
28
  
29
    EICRA = (1 << ISC11);
30
    EIMSK = (1 << INT1);
31
   
32
   
33
    while(1)
34
    {
35
        
36
    }
37
}
38
39
40
ISR (INT1_vect)
41
{
42
  PORTB |= ( 1 << PB2);
43
  
44
  Merker = PORTB &= ~ ( 1 << PB2);
45
}

von hilmar (Gast)


Lesenswert?

Du mußt in deiner Interrupt-Routine umschalten von steigender auf 
fallende Flanke und umgedreht. Also immer abwechselnd. Dafür hast du ja 
den Marker eingeführt. Das Bit ISC11=1 und ISC10=0 sagt dem Interrupt, 
daß er die fallende Flanke auswerten soll (so wie du es jetzt hast). 
Wenn du ISC11 auch auf 1 setzt, wird die steigende Flanke ausgewertet. 
Und das mußt du immer umschalten. Das heißt, erst fallende Flanke und 
beim nächten Interrupt steigende Flanke usw.

Du kannst auch nur die fallende Flanke auswerten und dann in der 
Interrupt-Routine die LED einmal ein- und das nächste Mal ausschalten.

Bei deinen Versuchen wirst du bestimmt merken, daß du manchmal mehrmals 
drücken mußt, damit was passiert. Dann kommt das Thema Entprellung zum 
Tragen. Aber dazu wurde hier im Forum schon eine ganze Menge 
geschrieben.

von Karl H. (kbuchegg)


Lesenswert?

1
ISR (INT1_vect)
2
{
3
  PORTB |= ( 1 << PB2);
4
  
5
  Merker = PORTB &= ~ ( 1 << PB2);
6
}

du schaltest die LED ein und gleich darauf wieder aus?
(oder umgekehrt, je nachdem wie die LED angeschlossen ist)

Wenn du nicht sehr gute Augen hast, dann wirst du das nicht wirklich 
feststellen können. Eigentlich, das kann nur Superman.

Gemeint war das ganze zb so
1
...
2
3
#define LED_ON   0x01
4
5
volatile uint8_t Command;
6
7
int main()
8
{
9
10
  ....
11
12
  while( 1 )
13
  {
14
15
    if( Command == LED_ON )
16
    {
17
      Command = 0;
18
      _delay_ms( 1000 );
19
      PORTB &= ~(1 << PB0);   // und wieder aus
20
    }
21
  }
22
}
23
24
ISR( .... )
25
{
26
  PORTB |= (1 << PB0);   // LED ein ....
27
  Command = LED_ON;      // Hauptschleife benachrichtigen, dass die LED brennt
28
}

der delay_ms da drinnen ist natürlich nicht der Weisheit letzter 
Schluss. Er dient nur dazu, dass die LED eine gewisse wahrnehmbare Zeit 
leuchtet. Um mit externen Interrupts zu experimentieren reicht das, mehr 
aber auch nicht. Zumal man Tasten sowieso anders auswertet und nicht mit 
externen Interrupts. Aber so wie ich das im Moment noch verstehe, geht 
es nicht im eigentlichen Sinn um Tasten (die Taste ist nur Mittel zum 
Test-Zweck) sondern darum, dass irgendein Signal einen Interrupt 
auslösen soll.


Edit: Ach ja. Weil ich es gesehen habe.
Gewöhn dir diese int-Rundumschläge gleich wieder ab. Es ist sinnlos, 
wenn du den µC in 16-Bit Arithmetik treibst, wenn es 8 Bit auch tun. UNd 
an dieser Stelle ist das sogar ziemlich wichtig, weil der µC einen 8-Bit 
Wert atomar (in einem Rutsch ohne Unterbrechung) auswerten kann, einen 
16 Bit Wert aber nicht.

von Amateur (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> ISR (INT1_vect)
> {
>   PORTB |= ( 1 << PB2);
>
>   Merker = PORTB &= ~ ( 1 << PB2);
> }
>
> du schaltest die LED ein und gleich darauf wieder aus?
> (oder umgekehrt, je nachdem wie die LED angeschlossen ist)
>
> Wenn du nicht sehr gute Augen hast, dann wirst du das nicht wirklich
> feststellen können. Eigentlich, das kann nur Superman.
>
> Gemeint war das ganze zb so
> ...
>
> #define LED_ON   0x01
>
> volatile uint8_t Command;
>
> int main()
> {
>
>   ....
>
>   while( 1 )
>   {
>
>     if( Command == LED_ON )
>     {
>       Command = 0;
>       _delay_ms( 1000 );
>       PORTB &= ~(1 << PB0);   // und wieder aus
>     }
>   }
> }
>
> ISR( .... )
> {
>   PORTB |= (1 << PB0);   // LED ein ....
>   Command = LED_ON;      // Hauptschleife benachrichtigen, dass die LED
> brennt
> }
>
> der delay_ms da drinnen ist natürlich nicht der Weisheit letzter
> Schluss. Er dient nur dazu, dass die LED eine gewisse wahrnehmbare Zeit
> leuchtet. Um mit externen Interrupts zu experimentieren reicht das, mehr
> aber auch nicht. Zumal man Tasten sowieso anders auswertet und nicht mit
> externen Interrupts. Aber so wie ich das im Moment noch verstehe, geht
> es nicht im eigentlichen Sinn um Tasten (die Taste ist nur Mittel zum
> Test-Zweck) sondern darum, dass irgendein Signal einen Interrupt
> auslösen soll.
>
> Edit: Ach ja. Weil ich es gesehen habe.
> Gewöhn dir diese int-Rundumschläge gleich wieder ab. Es ist sinnlos,
> wenn du den µC in 16-Bit Arithmetik treibst, wenn es 8 Bit auch tun. UNd
> an dieser Stelle ist das sogar ziemlich wichtig, weil der µC einen 8-Bit
> Wert atomar (in einem Rutsch ohne Unterbrechung) auswerten kann, einen
> 16 Bit Wert aber nicht.

okay super Danke! Funktioniert soweit.

Jetzt möchte ich gerne das bei Tastendruck die Anweisungen in der 
Interruptroutine dauerhaft in eine Schleife ausgeführt wird.

Finde aber keinen Ansatz ...
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
5
// Prototyp
6
void schnellladen (void);
7
8
 volatile uint8_t Merker;
9
 #define Zustand 0x01
10
11
int main()
12
{
13
    DDRB = 0x06;
14
    PORTB = 0;
15
    
16
    
17
    DDRC = 0x3F;
18
    PORTC = 0;
19
    
20
    
21
    DDRD = 0x00;
22
    PORTD = 0x1F;
23
           
24
    
25
    //Interrupt INT1
26
    sei();
27
    
28
    EICRA = (1 << ISC11);
29
    EIMSK = (1 << INT1);
30
  
31
32
  while( 1 )
33
  {
34
35
    schnellladen();
36
  }
37
}
38
39
ISR( INT1_vect )
40
{
41
  Merker = Zustand;
42
}
43
44
void schnellladen (void)
45
{
46
while ( Merker == Zustand)
47
  {
48
    Merker = 0;
49
    ChargeCurrent_Write_Speed()
50
    ChargeVoltage_Write();
51
    InputCurrent_Write()
52
  }
53
}

von hilmar (Gast)


Lesenswert?

> Jetzt möchte ich gerne das bei Tastendruck die Anweisungen in der
> Interruptroutine dauerhaft in eine Schleife ausgeführt wird.

Also immer wieder
> Merker = Zustand;
bis zum Sanktnimmerleinstag...

Grundsatz: So wenig wie möglich im Interrupt machen und alles andere 
außerhalb. Flags setzen (so wie du es machst) ist ok. Und dann außerhalb 
auf die Flags reagieren.

Also was soll wiederholt werden?

von hilmar (Gast)


Lesenswert?

Das "schnelladen()" wird ja schon wiederholt durch das "while(1)". Oder 
meinst du was anderes?

von Thomas E. (thomase)


Lesenswert?

Amateur schrieb:
>> Ist dir cli(); und sei(); zu kurz? :-)
> hat der Compiler(AVR-Studio 6.1) erst nicht genommen jetzt komischer
> weise schon
Das ist nicht komisch. Ein Compiler ist das humorloseste, was es gibt.
Die beiden sind in "interrupt.h" definiert. Und die muß natürlich auch 
included sein.

War sie aber vorher nicht:
> // ***** Anfang allgemeine Includes
> #include <avr/io.h>    // ATmega88 Definitionen
> //#include <inavr.h>    // IAR Definitionen
> #include <stdio.h>    // Standard I/O Definitionen
> // ***** Ende allgemeine Includes

Wenn der Compiler etwas nicht kennt, was er kennen sollte, bastelt man 
sich keine Würgeraunds. Damit kann man sich ganz üble Probleme schaffen.

mfg.

von Karl H. (kbuchegg)


Lesenswert?

Amateur schrieb:

> void schnellladen (void)
> {
> while ( Merker == Zustand)
>   {
>     Merker = 0;
>     ChargeCurrent_Write_Speed()
>     ChargeVoltage_Write();
>     InputCurrent_Write()
>   }
> }


Nicht
while ( Merker == Zustand )

in deinem Fall dann eben einfach
1
  if ( Merker == Zustand )
2
    ChargeCurrent_Write_Speed()
3
    ChargeVoltage_Write();
4
    InputCurrent_Write()
5
  }
die Schleife drummherum besorgt dann sowieso die Hauptschleife in main.

Du musst dir dann halt nur überlegen, wann 'Merker' wieder auf 0 
zurückgesetzt werden soll. Also: Wann genau sollen die Texte wieder 
verschwinden bzw. nicht mehr upgedatet werden? Was ist die Bedingung 
dafür? Könnte es sein, dass dieses genau dann der Fall ist, wenn die 
Akkus voll sind? Dann kommt dann eben dort das Rücksetzen dieses Flags 
dazu, wo dieses festgestellt wird.

PS: Nenn das nicht Merker. Das ist ein selten dämlicher Name, der genau 
gar nichts aussagt. Was ist seine Funktion? Und nach der benennst du 
ihn. Zb ShowValues oder ChargeAkku oder .... Benenne Variablen nach 
ihrer logischen Funktion und nicht danach, wie sie intern implementiert 
sind.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.