mikrocontroller.net

Forum: Compiler & IDEs Daewoo-Fernbedienungscode / Interrupt lässt sich nicht ausschalten


Autor: Guido S. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Folgendes kleines Programm habe ich geschrieben, um einen 
Fernbedienungscode zu empfangen und zu entschlüsseln. So wie das 
Programm dort steht, funktioniert es. Allerdings hätte ich gerne den 
Timerinterrupt gesperrt, wenn der Code komplett empfangen wurde. Leider 
zeigen die Befehle "TIMSK &= ~(1 << OCIE1A);" und "cli();" keine 
Wirkung. Woran könnte das liegen. Ich möchte eigentlich nur den 
Timerinterrupt sperren - das wäre also "TIMSK &= ~(1 << OCIE1A);". Kann 
ich das während des externen Interrupts nicht machen? Warum?
Wenn ihr dieses Verhalten testen möchtet, dann könnt ihr am Anfang des 
Quelltextes wahlweise "#define Test_OCIE1A", "#define Test_CTC" oder 
"#define Test_CLI" herein- oder herausnehmen. Bei mir wird der 
Timerinterrupt (Compare) auch ausgelöst, wenn ich "cli();" benutze.
Kann mir jemand dieses Verhalten erklären?

Danke und Gruß aus Oranienburg
Guido

Autor: Uwe Nagel (ulegan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein cli()im Interrupt geht nicht, da der reti Befehl am Ende der ISR den 
gesperrten Interrupt wieder freigibt. Siehe 
http://www.mikrocontroller.net/articles/AVR-Tutori...

Gruß Uwe

Autor: Guido S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

@Uwe

Danke für die Antwort. Mir ist bekannt, dass ein RETI den Interrupt 
wieder freigibt. Doch aber nur für den Vektor der Interruptroutine, die 
damit beendet wird. Einen Einfluss auf das globale Interruptregister hat 
RETI doch nicht? Oder ist es in C anders als in ASM?
Das würde ja bedeuten, dass ein Interrupt nicht durch einen anderen 
Interrupt unterbrochen werden kann. Ist das so?

Gruß
Guido

Autor: Uwe Nagel (ulegan)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Im Instruction set steht:

RETI
Returns from interrupt. The return address is loaded from then STACK and 
the Global Interrupt Flag is set.

Beim Eintritt in die ISR wird das globale Flag gelöscht und durch den 
RETI wieder gesetzt. Also kann eine ISR nicht durch einen anderen 
Interrupt unterbrochen werden. Es sei denn, man hat selbst das Flag 
durch sei() innerhalb der ISR wieder gesetzt.
So ist es auch in der AVR libc Dokumentation beschrieben.

Gruß Uwe

Autor: Guido S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das mit dem I im SREG habe ich inzwischen mit Hilfe von AVR Studio 
herausbekommen. Trotzdem großen Dank.
Ich habe jetzt verstanden, dass beim AVR ein INT nicht durch einen 
anderen unterbrochen werden kann. Es sei denn, ich erlaube es explizit. 
Warum ich den Timer-INT nicht abschalten konnte, kann ich trotzdem nicht 
erklären.
Da der Timer Interrupt für dieses Programm nicht wirklich benötigt wird, 
habe ich ihn entfernt.

Ich  danke  Allen,  die  sich  mit  mir  Gedanken 
gemacht  haben.

Hier ist noch der geänderte Code. Viel Spaß bei der weiteren Verwendung.

Ach, wen es interessiert: auf meiner Universalfernbedienung (URC22D-8A) 
ist der DEAWOO-Code auf VCR mit dem Code 040.
/*#########################################################################################

Auswertung des Daewoo-Fernbedienungscodes

Achtung! Dies ist nur eine Studienversion, die noch keine weitere Funktionalität hat.

Das Programm wurde erstellt von Guido S.

ATMega8

#########################################################################################*/

#include <avr/io.h>
#include <avr/interrupt.h>

uint8_t FlankenZaehler;
uint16_t Adr_OPCode;
uint16_t VergZeit;


void Ausgabe(uint8_t Wert)
{
  // an PortB2 bis B6 und PortC0 bis C2 sind LEDs angeschlossen
  // somit kann ein Byte angezeigt werden

  // Aufbereitung der Daten und Ausgabe an die richtigen Pins
  // die Bits werden negiert, damit man auf eine richtige Darstellung kommt
  // => Bit von Wert = "1" - LED an; Bit von Wert = "0" - LED aus
  PORTB = ~((Wert <<  1) & 0b00111110);
  PORTC = ~(Wert >> 5);
}


ISR(INT1_vect)
{
  /* Interrupt Code */

  // hier findet die Zeitmessung statt
  // Messung beginnt beim Eintreffen des ersten EXT1-Interrupts

  void Fehlerbehandlung(void)
  {
    // Flankenzähler wird auf 0 gestellt - eine neue Sequenz kann beginnen
    FlankenZaehler = 0;
  }

    // Timer auslesen
  VergZeit = TCNT1;  // Read TCNT1   // seit dem Int ist TCNT1 um 43 erhöht
  TCNT1 = 0;      // Timer soll von 0 beginnen; für diesen Befehl werden weitere 6 Takte benötigt

#define Tolleranz 8 // Werte zwischen 5 und 15 haben sich als sinnvoll gezeigt

#if (F_CPU <= 6553500)
  // Diese Berechnung geht nur bis 6,5535 MHz
  #define T10ms (F_CPU/100)
  #define T8msh (((F_CPU/1000)*8)+((F_CPU/10000)*Tolleranz))
  #define T8msl (((F_CPU/1000)*8)-((F_CPU/10000)*Tolleranz))
  #define T4msh (((F_CPU/1000)*4)+((F_CPU/10000)*Tolleranz))
  #define T4msl (((F_CPU/1000)*4)-((F_CPU/10000)*Tolleranz))
  #define T055msh (((F_CPU/100000)*55)+((F_CPU/100000)*3*Tolleranz))
  #define T055msl (((F_CPU/100000)*55)-((F_CPU/100000)*3*Tolleranz))
  #define T045msh (((F_CPU/100000)*45)+((F_CPU/100000)*3*Tolleranz))
  #define T045msl (((F_CPU/100000)*45)-((F_CPU/100000)*3*Tolleranz))
  #define T145msh (((F_CPU/100000)*145)+((F_CPU/100000)*3*Tolleranz))
  #define T145msl (((F_CPU/100000)*145)-((F_CPU/100000)*3*Tolleranz))
#else
  // Bei F_CPU > 6,5535 MHz wird der Vorteiler auf 8 gestellt und die Zeiten berechnen sich so:
  #define T10ms (F_CPU/800)
  #define T8msh (((F_CPU/8000)*8)+((F_CPU/80000)*Tolleranz))
  #define T8msl (((F_CPU/8000)*8)-((F_CPU/80000)*Tolleranz))
  #define T4msh (((F_CPU/8000)*4)+((F_CPU/80000)*Tolleranz))
  #define T4msl (((F_CPU/8000)*4)-((F_CPU/80000)*Tolleranz))
  #define T055msh (((F_CPU/800000)*55)+((F_CPU/800000)*3*Tolleranz))
  #define T055msl (((F_CPU/800000)*55)-((F_CPU/800000)*3*Tolleranz))
  #define T045msh (((F_CPU/800000)*45)+((F_CPU/800000)*3*Tolleranz))
  #define T045msl (((F_CPU/800000)*45)-((F_CPU/800000)*3*Tolleranz))
  #define T145msh (((F_CPU/800000)*145)+((F_CPU/800000)*3*Tolleranz))
  #define T145msl (((F_CPU/800000)*145)-((F_CPU/800000)*3*Tolleranz))
#endif

  // hier beginnt die Abfrage des Flankenzählers
  if (FlankenZaehler == 0)
  {
    // Timer 1 einstellen - Timer wird als CTC genutzt
      // Mode 4
      // CLKi/o / 1   oder   CLKi/o / 8
    TCCR1A = 0; 
    #if (F_CPU <= 6553500)
    TCCR1B = (1 << WGM12) | (1 << CS10); 
    #else
    TCCR1B = (1 << WGM12) | (1 << CS11); 
    #endif
    FlankenZaehler++;    // Flankenzähler erhöhen und auf neue Flanke warten
  }
  else if (FlankenZaehler == 1) 
  {  // die erste Periode müssen 8 ms lang sein
    if ((VergZeit > T8msl) & (VergZeit < T8msh))
    {
      FlankenZaehler++;  // Flankenzähler erhöhen und auf neue Flanke warten
    }
    else
    {
      // Flanke ist nicht im erwarteten Zeitraum gekommen
      Fehlerbehandlung();
    }
  }
  else if ((FlankenZaehler == 2) | (FlankenZaehler == 20))
  {  // diese Perioden müssen 4 ms lang sein
    if ((VergZeit > T4msl) & (VergZeit < T4msh))
    {
      FlankenZaehler++;  // Flankenzähler erhöhen und auf neue Flanke warten
    }
    else
    {
      // Flanke ist nicht im erwarteten Zeitraum gekommen
      Fehlerbehandlung();
    }
  }
  else if ((FlankenZaehler & 1) == 1)
  {  // wenn Flankenzähler ungerade, dann muss die Zeit 0,55 ms sein
    if ((VergZeit > T055msl) & (VergZeit < T055msh))
    {
      if (FlankenZaehler == 37)
      {  // wenn Flankenzähler 37 ist, dann ist das Stopp-Bit erreicht
        // hier wird nicht mehr auf die nächste Flanke gewartet, 
        // da das Stopp-Bit nur eine halbe Welle ist
        // an dieser Stelle wurde der Code komplett empfangen
        // JUCHU!!!
        TCCR1A = 0;          // CTC abschalten 
        TCCR1B = 0;          // CTC abschalten
        Ausgabe((uint8_t)(Adr_OPCode >> 8));   // Ausgabe des OP-Codes
        //Ausgabe((uint8_t)(Adr_OPCode));    // das ist die Geräteadresse
        FlankenZaehler = 0;
      }
      else
      {  // bei allen anderen Flanken ist alles OK und der Flankenzähler wird erhöht
        FlankenZaehler++;  // Flankenzähler erhöhen und auf neue Flanke warten
      }
    }
    else
    {
      // Flanke ist nicht im erwarteten Zeitraum gekommen
      Fehlerbehandlung();
    }
  }
  else if ((FlankenZaehler & 1) == 0)
  {  // hier kommt die Auswertung der 2. Halbwelle, die die Information beinhaltet,
    // ob es eine eine "0" oder "1" ist
    if ((VergZeit > T045msl) & (VergZeit < T145msh))
    {
      FlankenZaehler++;  // Flankenzähler erhöhen und auf neue Flanke warten
      if ((VergZeit > T045msl) & (VergZeit < T045msh))
      // das erkannte Bit ist eine "0"
      {
        Adr_OPCode = (Adr_OPCode >> 1);
      }
      else if ((VergZeit > T145msl) & (VergZeit < T145msh))
      // das erkannte Bit ist eine "1"
      {
        Adr_OPCode = (Adr_OPCode >> 1) | (1 << 15);
      }
      else
      {
        // Flanke ist nicht im erwarteten Zeitraum gekommen
        Fehlerbehandlung();
      }
    }
    else
    {
      // Flanke ist nicht im erwarteten Zeitraum gekommen
      Fehlerbehandlung();
    }
  }
}


int main(void)
{
  // Variablen initialisieren
  FlankenZaehler = 0;

  // Interrupts initialisieren 
  MCUCR |= (1 << ISC10); MCUCR &= ~(1 << ISC11);  //EXT1 soll bei jeder Flanke ausgelöst werden
  GICR |= (1 << INT1);                  // EXT1 Interrupt erlauben        
  sei();                         // global Interrupt erlauben

  // Ports initialisieren
  // PB1 bis PB5 als Ausgang; an diesen Ports ist je eine LED mit Vorwiderstand gegen +5V angeschlossen
  DDRB |= (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4) | (1 << PB5);
  // PC0 bis PC2 als Ausgang; an diesen Ports ist je eine LED mit Vorwiderstand gegen +5V angeschlossen
  DDRC |= (1 << PC0) | (1 << PC1) | (1 << PC2);

  // diese Ausgabe ist nur zum Test und soll mir zeigen, dass die CPU bereit ist
  Ausgabe(0b10100101);

  // Das Hauptprogramm ist eine Endlosschleife, es wird durch Interrupts unterbrochen
  while(1) 
  {
  }
}

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.