www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik IRQ hängt ab und zu - Warum?


Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo

Ich versuche einen ADUC7000 (ARM7) zu programmieren.
Im Moment hänge ich an den Uart-Funktionen.

Ich habe ein FIFO im SRam zum Senden und Empfangen angelegt.

Wenn Daten gesendet werden sollen, landen diese im FIFO.
So bald das Uart Senderegister leer ist, wird ein IRQ
ausgelöst. In der IRQ-Funktion wird ein Byte aus dem FIFO
ins Senderegister kopiert.

Wenn Daten in das FIFO schneller geschrieben werden, als die
Daten über die IRQ-Funktion gesendet werden können, wird eine
Statusvariable auf 2 gesetzt, damit im Programm erkannt werden
kann, dass gewartet werden muss, bis wieder Platz im Puffer ist.

Das funktioniert fast.
Dies ist meine Sendefunktion:

char UartWrite (char *ptr, char wait) { // Sende String ueber uart, Wenn 
wait==1, wird gewartet, bis der String vollstaendig
    // gesendet werden konnte. Soll nicht ewartet werden und der 
TX-Puffer laeuft ueber, wird die Position des Zeichen im
    // String zurueckgegeben, die nicht mehr gesendet werden konnte
  char *origptr = ptr;
  char CharSent;
  while (*ptr!='\0') {
    CharSent = UartPutChar (*ptr); // Uebergebe einzelnes Zeichen an 
Sendefunktion
    ptr++;
    if (CharSent==0) { // Puffer ist voll. Warte oder gebe Position des 
letzten gesendeten Zeichen zurueck
      if (wait){
        while (uarttxbufferstatus==2) {
          int bla1 = 7;
          int bla2 = 80;
        } // Warte bis Platz in TX-Puffer ist
      }
      else
        return *ptr; // Gebe Position des noch zu sendenden Zeichen aus
    }
  }
  return *origptr; // Gebe Pointer auf String zurueck, wenn alles 
gesendet wurde.
}



Was ich nicht ganz verstehe ist, dass der Code so funktioniert. Ich 
musste
in die inntere while-Schleife Variablen definieren. Eigentlich hätte 
folgender
Code gereicht:
        while (uarttxbufferstatus==2) {
        } // Warte bis Platz in TX-Puffer ist
Das Problem ist, dass dann der IRQ nicht mehr ausgelöst wird.
Hat einer von euch eine Idee wieso?


Beim Empfangen von Zeichen habe ich ebenfalls IRQ-Probleme. So bald 
Daten
im Empfangsregister liegen, wird ein IRQ ausgelöst. Die Daten werden 
dann
in einen FIFO kopiert. Einzelne Zeichen werden mit der Funktion 
UartRXGet()
empfangen:

char UartGetChar(void) { // Lese Zeichen aus Empfangspuffer
  char RETURN = 0;
  if (uartrxbufferstatus!=0) { // Zeichen in Empfangsringspeicher
    RETURN = uartrxbuffer[readuartrxbuffer];
    readuartrxbuffer++; // Aendere Position im Empfangsringspeicher
    readuartrxbuffer %= sizeof(uartrxbuffer);
    if (uartrxbufferstatus==2) uartrxbufferstatus = 1;
    if (writeuarttxbuffer == readuarttxbuffer) { // Empfangspuffer ist 
nun leer, setzte auf 0 zurueck
      uartrxbufferstatus = 0;
    }
  }
  return RETURN; // Gebe 0 zurueck, wenn kein Zeichen empfangen wurde
}

char UartRXGet(void) { // Warte bis Zeichen empfangen wurde und gebe 
dieses aus
  char ReturnChar = 0;
  while (!ReturnChar) {
    ReturnChar = UartGetChar();
    UartIrq(); // Warum muss hier diese Funktion aufgerufen werden?
  }
  return ReturnChar;
}

Hier habe ich ein ähnliches Problem. Der IRQ wird nicht immer ausgelöst.
Mein Programm hängt ewig in der while-Schleife fest. Desshalb habe ich 
in
die while-Schleife meine IRQ-Funktion für den Uart eingefügt. So 
funktioniert
der Code dann, aber ich weiß nicht wieso.

Dies ist meine IRQ-Funktion:

void UartIrq (void) {
  if (COMSTA0 & 0x01) { // Zeichen in RX Puffer
    if (uartrxbufferstatus!=2) { // Es ist noch Platz im Puffer
      uartrxbuffer[writeuartrxbuffer] = COMRX;
      writeuartrxbuffer++; // Aendere Position im Empfangsringspeicher
      writeuartrxbuffer %= sizeof(uartrxbuffer);;
      if (writeuartrxbuffer == readuartrxbuffer) { // Sendepuffer ist 
nun voll -> Gebe als Fehler 0 zurueck
        uartrxbufferstatus=2; // Puffer ist voll
      }
      else
        uartrxbufferstatus = 1; // Im Puffer liegen nun Daten
    }
    else { // Ueberschreibe aeltesten Wert in Puffer, damit das aelteste 
Zeichen verloren geht
      uartrxbuffer[writeuartrxbuffer] = COMRX;
      writeuartrxbuffer++; // Aendere Position im Empfangsringspeicher
      writeuartrxbuffer %= sizeof(uartrxbuffer);;
      readuartrxbuffer++; // Aendere Position im Empfangsringspeicher. 
Leseposition muss ebenfalls geaendert werden
      readuartrxbuffer %= sizeof(uartrxbuffer);;
    }
  }
  if (COMSTA0 & 0x40) { // TX Puffer leer.
    if (uarttxbufferstatus!=0) { // Zeichen in Senderingspeicher
      COMTX = uarttxbuffer[readuarttxbuffer]; // Sende Zeichen
      readuarttxbuffer++; // Aendere Position im Senderingspeicher
      readuarttxbuffer %= sizeof(uarttxbuffer);
      if (readuarttxbuffer==writeuarttxbuffer) { // Puffer ist nun leer
        COMIEN0 = 0x01; // Schalte TX-IRQ ab, Senderingspeicher leer.
        uarttxbufferstatus = 0; // Setzte Status = 0, also keine Daten 
mehr in Puffer
      }
      else
        uarttxbufferstatus = 1; // Puffer kann nicht mehr voll sein, da 
ein Element gesendet wurde
    }
    if (uarttxbufferstatus==0) { // Keine Zeichen in Puffer, Schalte IRQ 
ab.
      COMIEN0 = 0x01;
    }
  }

}

void IRQ_Function (void) {
  if (IRQSIG & UART_BIT) { // Uart
    UartIrq(); // Zeichen wurde empfangen
  }
}


Ich habe versucht herauszufinden, warum der IRQ nicht ausgelöst wird.
Für den Uart gibt es die Statusregister COMSTA0 und COMIEN0. In COMIEN0
wird eingestellt, ob ein volles RX oder ein leeres TX Register einen IRQ
auslöst. Mit COMSTA0 kann abgefragt werden, ob RX voll oder TX leer ist.

Ich habe in Eclipse das Programm an den entsprechenden Stellen 
angehalten
und die Register COMSTA0 und COMIEN0 angeschaut. Laut diesen Registern 
müsste
ein IRQ ausgelöst werden.

Ich kenne mich mit der Materie noch nicht so gut aus.

Hat einer von euch einen Tip für mich, wie ich weitermachen könnte?
Ich habe mir überlegt die Register abzufragen, in denen gespeichert 
wird,
ob sich die CPU gerade im Interrupt Mode befindet. DAs könnte ein Grund
sein, warum kein IRQ mehr ausgelöst wird. Ich habe nur leider keine 
Ahnung,
wie man so etwas machen könnte.

Bis dann, Tilo

Autor: Jeff (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Spurious and Surprise Interrupts berücksichtigt?
http://water.cse.unsw.edu.au/esdk/lpc2/spurious-irq.html

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Neine habe ich nicht. Ich wusste nicht einmal, dass es so etwas
gibt. Ich habe den Text durchgelesen aber noch nicht verstanden,
worum es genau geht. Da werde ich mich noch ein wenig reinhängen müssen.

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bins nochmal.

Also ich habe versucht den Text zu verstehen.
Der ARM7 hat eine Piepline, die abgearbeitet wird.
Erst wenn die Pipeline abgearbeitet ist, wird er IRQ
ausgeführt. Wenn nun einer der Befehle in der Pipeline
den IRQ abschaltet, wird er trotzdem ausgeführt?
Jedenfalls habe ich den Text so verstanden, dass genau dies ein
"surprise interrupt" ist.

Müsste in diesem Fall die IRQ-Funktion zu oft aufgerufen werden?

spurious interrupts:
Zitat aus deinem Link:  "On page 2-2 it states VIC does not handle 
interrupt sources with transient behaviour.  An interrupt exhibits 
transient behaviour if it is asserted and then deasserted before the 
software can clear the interrupt source."

Das ganze verstehe ich auf deutsch so, dass ein IRQ nicht abgearbeitet
wird, wenn der IRQ gesetzt und gelöscht wurde, bevor die IRQ-Quelle
zurückgesetzt werden konnte.

Was bedeutet das in meinem Fall?
Ich habe eine Vermutung: So bald keine Daten mehr im FIFO liegen, soll
der TX-Empty IRQ abgeschaltet werden. Der RX-Full IRQ soll aber an
bleiben. Hierfür setzte ich das entsprechende Register COMIEN0 in der
IRQ-Funktion auf 0x01. Nun vergeht zwischen "Schiebe letzte Daten in
TX-Senderegister" und deaktiviere TX-Empty IRQ ein wenig Zeit.
Während dieser Zeit kann es sein, dass das TX-Senderegister wieder leer
ist und das entsprechende Bit in COMSTA0 gesetzt ist, also bereits in 
der
IRQ-Funktion der nächste IRQ ausgelöst wird. Setze ich nun COMIEN0=0x1,
würde dies einen "spurious interrupt" auslösen.
Da der Uart nur eine Interruptquelle darstellt, egal was am Uart den IRQ
ausgelöst hat, reagiert der Interruptcontroller gar nicht mehr auf den 
Uart.

Was haltet ihr von der Vermutung?

Vielen Dank,

Tilo

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Thema Spurious Interrupts ist in diesem Kontext schnell abgehandelt, 
denn m.W. hat der ADUC7000 keinen Interrupt-Controller an Bord. Und ohne 
den gibt es die nicht, jedenfalls nicht in der beschriebenen Form.

Suprise Interrupts wirken sich unangenehm auf FIRQs aus. Wenn man die 
nicht benutzt, kann man auch dieses Thema vorerst auf Halde legen.

Quelltexte bindet man besser mit den vorgesehene Tags ein, dann bleiben 
sie einigermassen lesbar.

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke Andreas.

Ich wollte den Quelltext mit
 und 
 einbinden,
so wie fast überall. Sind die Tags hier anders?

Irgend welche anderen Ideen?

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sperr die Augen auf und lies nach. Es steht direkt auf dieser Seite 
drauf.

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Arg, Sorry. Ich brauche dringend eine neue Brille. Ich hab überall
nachgesehen nur nicht dort, wo es offensichtlich ist.
Ich denke gerade viel zu kompliziert.
Also nochmal den entsprechenden Code unten angehängt. Den ersten
Beitrag kann ich leider nicht mehr editieren.
char UartPutChar (char ch) {
  char RETURN;
  if (uarttxbufferstatus!=2) { // Puffer ist nicht voll
    uarttxbuffer[writeuarttxbuffer] = ch; // Schreibe Zeichen in Sendepuffer
    writeuarttxbuffer++;
    writeuarttxbuffer %= sizeof(uarttxbuffer);
    uarttxbufferstatus = 1;
    if (writeuarttxbuffer == readuarttxbuffer) { // Sendepuffer ist nun voll -> Gebe als Fehler 0 zurueck
      uarttxbufferstatus=2;
      RETURN = 0; // Puffer voll
    }
    else RETURN = 1; // Alles OK
  }
  else RETURN = 0; // Puffer voll
  COMIEN0 = 0x03; // Aktiviere TX-Interrupt
  return RETURN;
}

char UartGetChar(void) { // Lese Zeichen aus Empfangspuffer
  char RETURN = 0;
  if (uartrxbufferstatus!=0) { // Zeichen in Empfangsringspeicher
    RETURN = uartrxbuffer[readuartrxbuffer];
    readuartrxbuffer++; // Aendere Position im Empfangsringspeicher
    readuartrxbuffer %= sizeof(uartrxbuffer);
    if (uartrxbufferstatus==2) uartrxbufferstatus = 1;
    if (writeuarttxbuffer == readuarttxbuffer) { // Empfangspuffer ist nun leer, setzte auf 0 zurueck
      uartrxbufferstatus = 0;
    }
  }
  return RETURN; // Gebe 0 zurueck, wenn kein Zeichen empfangen wurde
}

char UartRXGet(void) { // Warte bis Zeichen empfangen wurde und gebe dieses aus
  char ReturnChar = 0;
  while (!ReturnChar) {
    ReturnChar = UartGetChar();
    UartIrq(); // Warum muss hier diese Funktion aufgerufen werden?
  }
  return ReturnChar;
}

void UartIrq (void) {
  if (COMSTA0 & 0x01) { // Zeichen in RX Puffer
    if (uartrxbufferstatus!=2) { // Es ist noch Platz im Puffer
      uartrxbuffer[writeuartrxbuffer] = COMRX;
      writeuartrxbuffer++; // Aendere Position im Empfangsringspeicher
      writeuartrxbuffer %= sizeof(uartrxbuffer);;
      if (writeuartrxbuffer == readuartrxbuffer) { // Sendepuffer ist nun voll -> Gebe als Fehler 0 zurueck
        uartrxbufferstatus=2; // Puffer ist voll
      }
      else
        uartrxbufferstatus = 1; // Im Puffer liegen nun Daten
    }
    else { // Ueberschreibe aeltesten Wert in Puffer, damit das aelteste Zeichen verloren geht
      uartrxbuffer[writeuartrxbuffer] = COMRX;
      writeuartrxbuffer++; // Aendere Position im Empfangsringspeicher
      writeuartrxbuffer %= sizeof(uartrxbuffer);;
      readuartrxbuffer++; // Aendere Position im Empfangsringspeicher. Leseposition muss ebenfalls geaendert werden
      readuartrxbuffer %= sizeof(uartrxbuffer);;
    }
  }
  if (COMSTA0 & 0x40) { // TX Puffer leer.
    if (uarttxbufferstatus!=0) { // Zeichen in Senderingspeicher
      COMTX = uarttxbuffer[readuarttxbuffer]; // Sende Zeichen
      readuarttxbuffer++; // Aendere Position im Senderingspeicher
      readuarttxbuffer %= sizeof(uarttxbuffer);
      if (readuarttxbuffer==writeuarttxbuffer) { // Puffer ist nun leer
        COMIEN0 = 0x01; // Schalte TX-IRQ ab, Senderingspeicher leer.
        uarttxbufferstatus = 0; // Setzte Status = 0, also keine Daten mehr in Puffer
      }
      else
        uarttxbufferstatus = 1; // Puffer kann nicht mehr voll sein, da ein Element gesendet wurde
    }
    if (uarttxbufferstatus==0) { // Keine Zeichen in Puffer, Schalte IRQ ab.
      COMIEN0 = 0x01;
    }
  }

}

char UartWrite (char *ptr, char wait) { // Sende String ueber uart, Wenn wait==1, wird gewartet, bis der String vollstaendig
    // gesendet werden konnte. Soll nicht ewartet werden und der TX-Puffer laeuft ueber, wird die Position des Zeichen im
    // String zurueckgegeben, die nicht mehr gesendet werden konnte
  char *origptr = ptr;
  char CharSent;
  while (*ptr!='\0') {
    CharSent = UartPutChar (*ptr); // Uebergebe einzelnes Zeichen an Sendefunktion
    ptr++;
    if (CharSent==0) { // Puffer ist voll. Warte oder gebe Position des letzten gesendeten Zeichen zurueck
      if (wait){
        while (uarttxbufferstatus==2) {
          int bla1 = 7;
          int bla2 = 80;
        } // Warte bis Platz in TX-Puffer ist
      }
      else
        return *ptr; // Gebe Position des noch zu sendenden Zeichen aus
    }
  }
  return *origptr; // Gebe Pointer auf String zurueck, wenn alles gesendet wurde.
}



Wie bereits oben geschrieben mein Code hängt an 2 Stellen.
1. Wenn in UartRXGet() UartIrq() auskommentiert ist
2. Wenn in UartWrite() die innere while-Schleife leer ist.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Tilo Lutz (katagia)

>Arg, Sorry. Ich brauche dringend eine neue Brille. Ich hab überall
>nachgesehen nur nicht dort, wo es offensichtlich ist.

Un doch nur die Hälfte gelesen :-( Solche langen Quelltexte gehören in 
den Anhang! Als *.c Dateien. Könnte kaum einfacher sein.

MFG
Falk

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja, das hängt davon ab, was man unter lang versteht.
Ich fand den noch recht kurz.

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du solltest dir mal in den Get/Put Routinen Zeile für Zeile überlegen, 
was passiert, wenn exakt in/nach dieser Zeile ein UART-Interrupt 
erfolgt. Dann wirst du vielleicht darauf kommen, dass es fatal ist, wenn 
direkt nach dem Test der Statusvariablen aber vor dem übrigen Code der 
Interrupt erfolgt und sich der Status ändert. Fachwort dafür: "race 
condition".

Einfachste Abhilfe: Um den entsprechenden Code in den Get/Put-Routinen 
herum den jeweiligen UART-Interrupt zeitweilig abschalten.

Autor: Tilo L. (katagia)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Hinweis. Ich habe den Code angepasst.
Leider funktioniert es immer noch nicht.

Beim Empfangen von Daten hängt das Programm immer noch in
UartRXGet(). In der Funktion werden folgende Register in
lokalen Variablen gesichert:
    int bla1 = COMIEN0;
    int bla2 = COMSTA0;
    int bla3 = IRQEN;
    int bla4 = IRQSIG;
Wenn das Programm "hängt" halt es über JTAG in Eclipse an
und debugge schrittweise. In COMATA0 ist der Urart IRQ aktiviert,
wenn RX-Register voll oder TX-Register leer ist. Laut den Variablen
ist IQRSIG gesetzt und ein IRQ sollte ausgelöst werden. Es sollte also
zumindest die IRQ-Routine ausgeführt werden.
Wenn ein Haltepunkt in der IRQ-Routine gesetzt wird, wird dieser nie
erreicht.

Woran kann das liegen?
Wo kann ich weitere Informationen zu diesem Thema finden?
Bei Analog selbst scheint es da nicht viel zu geben.
Bei ARM bin ich gerade am suchen, habe aber bisher noch nichts
gefunden.

Ich habe das Projekt vollständig angehängt.

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe das Problem gefunden!

Im Programm aktiviere ich bei Bedarf den IRQ für das
RX-Register. Wenn dieses voll ist, wird ein IRQ ausgelöst.
Ist das Register schon voll und ich aktiviere den IRQ, wird
dieser nicht ausgelöst.

Die Lösung war, nach dem aktivieren des IRQ die IRQ Funktion
1x auszuführen.

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.