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


You were forwarded to this site from EmbDev.net. Back to EmbDev.net
von Tilo (Gast)


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

von Jeff (Gast)


Lesenswert?

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

von Tilo (Gast)


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.

von Tilo (Gast)


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

von Andreas K. (a-k)


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.

von Tilo (Gast)


Lesenswert?

Danke Andreas.

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

Irgend welche anderen Ideen?

von Andreas K. (a-k)


Lesenswert?

Sperr die Augen auf und lies nach. Es steht direkt auf dieser Seite 
drauf.

von Tilo (Gast)


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.
1
char UartPutChar (char ch) {
2
  char RETURN;
3
  if (uarttxbufferstatus!=2) { // Puffer ist nicht voll
4
    uarttxbuffer[writeuarttxbuffer] = ch; // Schreibe Zeichen in Sendepuffer
5
    writeuarttxbuffer++;
6
    writeuarttxbuffer %= sizeof(uarttxbuffer);
7
    uarttxbufferstatus = 1;
8
    if (writeuarttxbuffer == readuarttxbuffer) { // Sendepuffer ist nun voll -> Gebe als Fehler 0 zurueck
9
      uarttxbufferstatus=2;
10
      RETURN = 0; // Puffer voll
11
    }
12
    else RETURN = 1; // Alles OK
13
  }
14
  else RETURN = 0; // Puffer voll
15
  COMIEN0 = 0x03; // Aktiviere TX-Interrupt
16
  return RETURN;
17
}
18
19
char UartGetChar(void) { // Lese Zeichen aus Empfangspuffer
20
  char RETURN = 0;
21
  if (uartrxbufferstatus!=0) { // Zeichen in Empfangsringspeicher
22
    RETURN = uartrxbuffer[readuartrxbuffer];
23
    readuartrxbuffer++; // Aendere Position im Empfangsringspeicher
24
    readuartrxbuffer %= sizeof(uartrxbuffer);
25
    if (uartrxbufferstatus==2) uartrxbufferstatus = 1;
26
    if (writeuarttxbuffer == readuarttxbuffer) { // Empfangspuffer ist nun leer, setzte auf 0 zurueck
27
      uartrxbufferstatus = 0;
28
    }
29
  }
30
  return RETURN; // Gebe 0 zurueck, wenn kein Zeichen empfangen wurde
31
}
32
33
char UartRXGet(void) { // Warte bis Zeichen empfangen wurde und gebe dieses aus
34
  char ReturnChar = 0;
35
  while (!ReturnChar) {
36
    ReturnChar = UartGetChar();
37
    UartIrq(); // Warum muss hier diese Funktion aufgerufen werden?
38
  }
39
  return ReturnChar;
40
}
41
42
void UartIrq (void) {
43
  if (COMSTA0 & 0x01) { // Zeichen in RX Puffer
44
    if (uartrxbufferstatus!=2) { // Es ist noch Platz im Puffer
45
      uartrxbuffer[writeuartrxbuffer] = COMRX;
46
      writeuartrxbuffer++; // Aendere Position im Empfangsringspeicher
47
      writeuartrxbuffer %= sizeof(uartrxbuffer);;
48
      if (writeuartrxbuffer == readuartrxbuffer) { // Sendepuffer ist nun voll -> Gebe als Fehler 0 zurueck
49
        uartrxbufferstatus=2; // Puffer ist voll
50
      }
51
      else
52
        uartrxbufferstatus = 1; // Im Puffer liegen nun Daten
53
    }
54
    else { // Ueberschreibe aeltesten Wert in Puffer, damit das aelteste Zeichen verloren geht
55
      uartrxbuffer[writeuartrxbuffer] = COMRX;
56
      writeuartrxbuffer++; // Aendere Position im Empfangsringspeicher
57
      writeuartrxbuffer %= sizeof(uartrxbuffer);;
58
      readuartrxbuffer++; // Aendere Position im Empfangsringspeicher. Leseposition muss ebenfalls geaendert werden
59
      readuartrxbuffer %= sizeof(uartrxbuffer);;
60
    }
61
  }
62
  if (COMSTA0 & 0x40) { // TX Puffer leer.
63
    if (uarttxbufferstatus!=0) { // Zeichen in Senderingspeicher
64
      COMTX = uarttxbuffer[readuarttxbuffer]; // Sende Zeichen
65
      readuarttxbuffer++; // Aendere Position im Senderingspeicher
66
      readuarttxbuffer %= sizeof(uarttxbuffer);
67
      if (readuarttxbuffer==writeuarttxbuffer) { // Puffer ist nun leer
68
        COMIEN0 = 0x01; // Schalte TX-IRQ ab, Senderingspeicher leer.
69
        uarttxbufferstatus = 0; // Setzte Status = 0, also keine Daten mehr in Puffer
70
      }
71
      else
72
        uarttxbufferstatus = 1; // Puffer kann nicht mehr voll sein, da ein Element gesendet wurde
73
    }
74
    if (uarttxbufferstatus==0) { // Keine Zeichen in Puffer, Schalte IRQ ab.
75
      COMIEN0 = 0x01;
76
    }
77
  }
78
79
}
80
81
char UartWrite (char *ptr, char wait) { // Sende String ueber uart, Wenn wait==1, wird gewartet, bis der String vollstaendig
82
    // gesendet werden konnte. Soll nicht ewartet werden und der TX-Puffer laeuft ueber, wird die Position des Zeichen im
83
    // String zurueckgegeben, die nicht mehr gesendet werden konnte
84
  char *origptr = ptr;
85
  char CharSent;
86
  while (*ptr!='\0') {
87
    CharSent = UartPutChar (*ptr); // Uebergebe einzelnes Zeichen an Sendefunktion
88
    ptr++;
89
    if (CharSent==0) { // Puffer ist voll. Warte oder gebe Position des letzten gesendeten Zeichen zurueck
90
      if (wait){
91
        while (uarttxbufferstatus==2) {
92
          int bla1 = 7;
93
          int bla2 = 80;
94
        } // Warte bis Platz in TX-Puffer ist
95
      }
96
      else
97
        return *ptr; // Gebe Position des noch zu sendenden Zeichen aus
98
    }
99
  }
100
  return *origptr; // Gebe Pointer auf String zurueck, wenn alles gesendet wurde.
101
}


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.

von Falk B. (falk)


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

von Tilo (Gast)


Lesenswert?

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

von Andreas K. (a-k)


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.

von Tilo (Gast)


Angehängte Dateien:

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.

von Tilo (Gast)


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.

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.