Forum: Compiler & IDEs Atmega16 Interrupt kruzzeitig deaktiveren


von Benedikt D. (metalhead)


Lesenswert?

Hi,

ich habe folgendes Problem: ich möchte den externen Interrupt kurzzeitig 
nach dessen Auslösen deaktivieren und nach einer bestimmten Zeit mittels 
eines Compare-Interrupts wieder deaktivieren. Sitzt schon ewig dran und 
versteh einfach nicht was da falsch läuft. Hier mal der Quellcode kurz 
und knapp:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <util/delay.h>
#include "lcd.h"

unsigned char counter;


ISR(INT0_vect)
{
  cli();
  TCNT1H = 0x00;      //Timer zurücksetzen
  TCNT1L = 0x00;


  OCR1AH = 0x05;  //Compare nach ca 50ms
  OCR1AL = 0xDC;


  GICR &= ~(0x40); //INT0 deaktivieren

  sei();

  char buffer[10];
  counter++;
  itoa(counter, buffer, 10 );
  set_cursor(0,1);
  lcd_string(buffer);

}

ISR(TIMER1_COMPA_vect)
{
  if(!(GICR | 0x40))
  {
    cli();
    GIFR = 0x40;
    GICR |= 0x40; //INT0 aktivieren
    sei();
  }
}

ISR(TIMER1_OVF_vect)
{
  //auf Overflow reagieren
}

int main(void)
{
  cli();

  GIFR = 0x40;
  GICR |= 0x40;  //INT0 aktivieren
  MCUCR = 0x0f;  //auf steigende Flanken reagieren

  OCR1AH = 0x05;  //Compare nach ca 50ms
  OCR1AL = 0xDC;  //bei Taktung von 1MHz/64
  TIMSK |= 0x04;  //Overflow-Int aktivieren
  TCCR1A = 0x00;  //Steigende Flanken, kein clear bei Compare-Match
  TCCR1B = 0x03;  //Prescaler mit 64; Timer starten



  sei();

    lcd_init();

    set_cursor(0,2);

    lcd_string("Hello World!");

    while(1)
    {

    }

    return 0;
}

Der Controller reagiert brav auf den ersten Interrupt, wird aber nach 
einer gewissen Zeit noch einer angelegt, passiert gar nichts mehr ...
Jemand eine Idee, was da schief läuft ?

Grüße Beni

von Falk B. (falk)


Lesenswert?

@ Benedikt Dietrich (metalhead)

>ISR(INT0_vect)
>{
>  cli();

Ist Unsinn, in einer ISR sind die Interrupts automatisch ausgeschaltet.

>  sei();

Hat in einer ISR nix zu suchen (jaja, es gint Ausnahmen, aber nicht in 
diesem Fall)

>ISR(TIMER1_COMPA_vect)
>{
>  if(!(GICR | 0x40))
>  {
>    cli();
>    GIFR = 0x40;
>    GICR |= 0x40; //INT0 aktivieren
>    sei();

Das selbe hier, kein cli() und sei(), ist sinnlos und gefährlich.


>int main(void)
>{
>  cli();

Hat du Paranoia? Beim Start von main() sind die Interrupts inaktiv.

>Der Controller reagiert brav auf den ersten Interrupt, wird aber nach
>einer gewissen Zeit noch einer angelegt, passiert gar nichts mehr ...
>Jemand eine Idee, was da schief läuft ?

>ISR(TIMER1_COMPA_vect)
>{
>  if(!(GICR | 0x40))

Das muss so heissen

>  if(!(GICR & 0x40))

Siehe Bitmanipulation.

MFG
Falk

von Benedikt D. (metalhead)


Lesenswert?

Danke für die Antwort. Die Paranoia haben mich überfallen nachdem es 
nach ewigem Suchen nicht ging.

Der Fehler bei der Bitmanipulation ist peinlich, aber ändert nichts in 
diesem Fall. Das Ganze funktioniert nach wie vor nicht :-( Der Interrupt 
wird nur einmal ausgelöst

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Benedikt Dietrich wrote:
> Der Interrupt
> wird nur einmal ausgelöst

Kein Wunder, wenn du diese Zeile in der Interruptroutine (in beiden) 
hast:

 GICR &= ~(0x40); //INT0 deaktivieren

Anmerkung:

Diesen aufwändigen Kram

  char buffer[10];
  counter++;
  itoa(counter, buffer, 10 );
  set_cursor(0,1);
  lcd_string(buffer);

macht man nicht im Interrupthandler sondern im Hauptprogramm. Im 
Interrupthandler schafft man nru die Daten zur Seite auf die das 
Hauptprogramm (oder eine vom Hauotprogramm aufgerufene Funktion) 
auswertet und weiterverarbeitet.

von Falk B. (falk)


Lesenswert?

@  Benedikt Dietrich (metalhead)

>Der Fehler bei der Bitmanipulation ist peinlich, aber ändert nichts in
>iesem Fall. Das Ganze funktioniert nach wie vor nicht :-( Der Interrupt
>wird nur einmal ausgelöst

Nimm mla die Deaktivierung aus der ISR und prüfe, ob dann die ISR 
mehrfach ausgelöst wird.

MfG
Falk

von Falk B. (falk)


Lesenswert?

@ Stefan "stefb" B. (stefan) Benutzerseite

>Kein Wunder, wenn du diese Zeile in der Interruptroutine (in beiden)
>hast:

> GICR &= ~(0x40); //INT0 deaktivieren

Augen auf beim Eierkauf. Er hat das schon richtig.

>ISR(TIMER1_COMPA_vect)
>{
>  if(!(GICR | 0x40))
>  {
>    cli();
>    GIFR = 0x40;
>    GICR |= 0x40; //INT0 aktivieren

ODER, nicht UND.

>    sei();
>  }
>}

MfG
Falk

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ich seh's, sorry!

INT0 kommt => INT0 deaktivieren + Timer Countdown auf 50ms => Timer 
kommt => INT0 scharf machen (# s.u.) => INT0 kommt => ...

#)

  if(!(GICR | 0x40))

ist falsch

  if(!(GICR & 0x40))

ist richtig

von Falk B. (falk)


Lesenswert?

Dumme Frage. Wie Erzeugst du denn deinen exteren Interrupt? Mit einem 
taster nach Masse? Hast du da auch einen Pull-Up am Pin?

MFg
Falk

von Benedikt D. (metalhead)


Lesenswert?

Also ohne die Deaktivierung wird der Interrupt ohne Probleme öfters 
aufgerufen.
Der INT0 wird mit nem 10k Widerstand nach GND gezogen. Den Interrupt 
erzeuge ich mit nem Reed-Kontakt, der dann INT0 auf 5V zieht, wenn er 
durchschaltet

Grüße Beni

von Falk B. (falk)


Lesenswert?

Lass mal in

ISR(TIMER1_OVF_vect)

Eine LED togglen. Dann sieht man, ob der Timer evt. nicht läuft.

Mal dein code aufgeräumt

1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <stdlib.h>
4
#include <util/delay.h>
5
#include "lcd.h"
6
7
unsigned char counter;
8
9
10
ISR(INT0_vect)
11
{
12
  TCNT1 = 0x00;      //Timer zurücksetzen
13
  OCR1A = 0x05DC;  //Compare nach ca 50ms
14
  GICR &= ~(0x40); //INT0 deaktivieren
15
16
  char buffer[10];
17
  counter++;
18
  itoa(counter, buffer, 10 );
19
  set_cursor(0,1);
20
  lcd_string(buffer);
21
}
22
23
ISR(TIMER1_COMPA_vect)
24
{
25
  if(!(GICR & 0x40))
26
  {
27
    GICR |= 0x40;  // INT0 aktivieren
28
    GIFR = 0x40;   // INT0 löschen
29
  }
30
}
31
32
ISR(TIMER1_OVF_vect)
33
{
34
  //auf Overflow reagieren
35
  // LED hier togglen!
36
}
37
38
int main(void)
39
{
40
  MCUCR = 0x0f;  //auf steigende Flanken reagieren
41
  GICR  |= 0x40;  //INT0 aktivieren
42
  GIFR   = 0x40;
43
  OCR1A  = 0x05DC;  //Compare nach ca 50ms
44
  TIMSK |= 0x04;  //Overflow-Int aktivieren
45
  TCCR1A = 0x00;  //Steigende Flanken, kein clear bei Compare-Match
46
  TCCR1B = 0x03;  //Prescaler mit 64; Timer starten
47
48
  sei();
49
50
  lcd_init();
51
  set_cursor(0,2);
52
  lcd_string("Hello World!");
53
  while(1) {
54
  }
55
}

Mfg
Falk

von Benedikt D. (metalhead)


Lesenswert?

Gute Idee Falko!

Jetzt weiss ich dass der Timer läuft (hab mit nem Overflow das Lämpchen 
erlöschen lassen), aber die Compare-Match ISR wird nicht angesprungen. 
Mal schaun was das Datenblatt sagt...

von Falk B. (falk)


Lesenswert?

Ein 'O' zuviel. Naja ;-)

von Benedikt D. (metalhead)


Lesenswert?

So ich hab des Rätsels Lösung: habe vergessen im TIMSK das Bit 4 – 
OCIE1A: Timer/Counter1, Output Compare A Match Interrupt Enable - zu 
setzen ... doofer Fehler :-)

Danke für die Hilfe Falko - o und Stefan ;-)

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.