Forum: Mikrocontroller und Digitale Elektronik Philips/NXP LPC214x 2 Interrupts gleichzeitig


von Marek S. (marekthestudent)


Lesenswert?

Hallo Leute,

ich habe folgende Frage an euch:

Ich habe hier ein Philips/NXP EVA-Board von Keil, das MCB2140. µC ist 
ein LPC2148. Ich schreibe an einem Programm und bin auf ein Problem 
gestoßen wo ich nicht mehr weiter weiß. Folgendes....

Ich habe ein Programm geschrieben, das 2 vektorisierte Interrupts 
enthält. Der eine reagiert auf ein externes Ereignis (Prio 1) und der 
andere ist ein Timer-Interrupt (Prio 0). Der Timer-Interrupt hat 
lediglich die Aufgabe eine "wait"-Funktion darzustellen (wie in dem 
Beispiel von der Keil Homepage). Wenn jetzt das Programm in den externen 
Interrupt reinspringt mit Prio 1, wird darin die Warte-Funktion mit dem 
Timer0-Interrupt gestartet. Und hier beginnt auch schon das Problem. Die 
ISR des Timer0, die die höhere Priorität hat, löst nicht aus. Warum?

Ich habe schon mehrere Dinge probiert wie Interrupt disablen, 
"VICVectAddr = 0;", usw.... nichts hilft und im Forum konnte ich leider 
auch nichts dazu finden. Weiß mir jemand einen guten Rat? Wäre sehr 
dankbar!

Grüße
Marek


PS: Ich weiß, eine Warte-Funktion kann man auch anders realisieren und 
auch vernünftiger und das werde ich auch aber dennoch gehts einfach um 
das Problem. Ich möchte für die Zukunft wissen warum das so ist, da ich 
neu in der µC Welt bin. Naja so fast neu. ;-) Danke!

von Tobi O. (der_ossi)


Lesenswert?

poste mal bitte deinen code (zumindest den wichtigen teil)...

gruss
Tobias

von Peter (Gast)


Lesenswert?

Kenne den LPC2148 nicht, aber den LPC2129 und nehme mal an die sind im 
Bereich Interrupthandling identisch.
Es handelt sich NICHT um einen Nested Interrupt Controller; d.h. 
Vectored Interrupts können sich nicht gegenseitig unterbrechen. Nur ein 
Fast Interrupt kann das.
Hilft nur eins, Interrupt Routinen sehr kurz halten und mit dem 
restlichen Jitter leben.

von Peter D. (peda)


Lesenswert?

Ja, das Mißverständnis gibts regelmäßig auch bei den AVRs.

Die meisten Leute verstehen unter Priorität das stoppen des gerade 
laufenden Interrupts und sofortiges Ausführen der hohen Priorität.

Leider verstehen die Autoren der Datenblätter von MCs ohne Priortäten 
das genau anders.
Sie meinen damit nur die Annahmereihenfolge, wenn beide Interrupts exakt 
gleichzeitig auflaufen, also wer zuerst rangenommen wird.

Aber es gibt bei den LPCs nen Trick, den fast-interrupt, der ist quasi 
eine 2. hohe Prioritätsstufe.
Er hat nämlich ein eigenes Freigabebit im Statuswort und das bleibt an 
bei den anderen Interrupts.


Peter

von Marek S. (marekthestudent)


Lesenswert?

Hallo Tobias,

hier der Code:

void main (void)
{
  IODIR1 = LED1 | LED2;
  IOCLR1 = LED1 | LED2;
  init_timer();
  fnInitIOLinkIRQ();
  while(1)
  {
    // Hier trifft externes Ereignis irgendwann ein.
  }
}

void fnInitIOLinkIRQ(void) // EINT1
{
  // Bits für P0.3 löschen (GPIO Modus).
  PINSEL0 &= 0xFFFFFF3F;
  // P0.3 als Interrupt Pin wählen
  PINSEL0 |= 0x000000C0;
  // Edge or Level Sensitivity
  EXTMODE |= 0x02; // Edge sensitive
  // Low or High active
  EXTPOLAR |= 0x02; // Falling-Edge sensitive.
  // Funktion Interrupt Adresse zuweisen
  VICVectAddr1 = (unsigned int)&fnIOLinkISR;
  // number of interrupt request assigned to vectored IRQ slot
  VICVectCntl1 = 0x20 | 15; // Channel1 on Source#15 ... enabled
  // Clear the peripheral interrupt flag
  EXTINT |= 0x02;
  // Acknowledge Interrupt
  VICVectAddr = 0;
  // Enable Interrupt
  VICIntEnable = 0x8000;
}

void fnIOLinkISR(void)__irq
{
  VICIntEnClr = 0x8000; // Disable EINT1 in the VIC
  //*** CODE
  IOSET1 = LED1;
  wait(1000000);              IOSET1 = LED2;
  //*** CODE
  EXTINT |= 0x02; // Clear the peripheral interrupt flag
  VICIntEnable = 0x8000; // Enable EINT1 in the VIC
  VICVectAddr = 0; // reset VIC
}

unsigned long timeval;

void wait (ULONG mikrosec)
{
  unsigned long i;

  VICIntEnable |= 0x00000010;             // Enable Timer0 Interrupt
  if (mikrosec < 10)
  {
      mikrosec = 10;
  }
  mikrosec = mikrosec / 10;
  i = timeval;
  while ((i + mikrosec) != timeval);    // wait ...
  VICIntEnClr |= 0x00000010;             // Disable Timer0 Interrupt
}

void init_timer (void)            /* Timer Initialization */
{
    T0PR = 0;                // Teiler. Bei "1" doppelte Zählzeit.
  T0MR0 = 600;                           // 10µSec
    T0MCR = 3;                // Interrupt and Reset on MR0
  T0TCR = 0x02;                           // Reset
  T0TCR = 0x00;                           // Reset aufheben
  T0CTCR = 0x00;              // Timer Mode
    T0TCR = 1;                              // Timer0 Enable
    VICVectAddr0 = (unsigned long)&tc0;     // set interrupt vector in 0
    VICVectCntl0 = 0x20 | 4;                // use it for Timer 0 
Interrupt
    //VICIntEnable |= 0x00000010;             // Enable Timer0 Interrupt
}

void tc0 (void) __irq
{
    VICVectAddr = 0;                       // Acknowledge Interrupt
  timeval++;
    T0IR = 1;                              // Clear interrupt flag
}

Gruß,
Marek.

von Marek S. (marekthestudent)


Lesenswert?

Hallo Peter(s), ;-)

ah ok danke! Das stand leider nirgendwo drin, deshalb mein Posting. Hm, 
ok thanx. In meinen Augen leider eine große Schwachstelle. Schade. 
Vielen Dank!

Gruß,
Marek.

von A.K. (Gast)


Lesenswert?

Der Interrupt Controller unterstützt durchaus Nesting, und wider 
erwarten sogar nachweislich korrekt (die Doku legt das nicht wirklich 
nahe).

Es ist die ARM7 CPU die im Nesting einen Design-Fehler hat. Man muss 
dafür im Interrupt-Handler den Modus wechseln. Gibt's diverse 
Source-Codes, Application Notes und Kommentare von ARM, Philips/NXP, ... 
dazu.

Naturgemäss ist im Interrupt-Handler der Interrupt erst mal 
ausgeschaltet. Ebendrum.

Überdies gibt es noch den Fast Interrupt. Und da ist sogar seitens der 
CPU das Nesting kein Problem.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Das Design ist verbesserungsbedürftig.

IN einem Interrupthandler eine Funktion namens wait aufzurufen ...
nein, das ist keine gute Idee.

von Marek S. (marekthestudent)


Lesenswert?

Hallo A.K,

ah cool danke! Habs gefunden! Wußte nicht, dass sich das "nested" 
Interrupts nennt. Wie gesagt, Newbee. ^^ Danke!



Hallo Rufus,

ich weiß, es klingt dumm ABER es ist nötig. Es geht um einen neuen 
Feldbus der dies verlangt, da dieser seine Kommunikation mit einem 
Kurzschluß zum Laufen bringt, weshalb es da nötig ist, den 
Transistor/FET etwas abzukühlen bzw. ihm, nach detektiertem Kurzschluß, 
ne Auszeit zu göhnen.




Grüße und danke!
Marek.

von Marek S. (marekthestudent)


Lesenswert?

So hallo nochmal,

möchte mich vielmals bedanken. Habs gefunden und ausprobiert. Cool, 
funktioniert. Danke und schönes Wochenende! :-)

Gruß,
Marek.

von A.K. (Gast)


Lesenswert?

Auch wenn man den FET abkühlen muss - man tut so etwas einfach nicht in 
einem Interrupt-Handler.

Beispiel: Es gibt einen Systemtimer, der Millisekunden abzählt und bei 
Bedarf irgendwas tut. In deinem Interrupt wird ein globaler Zähler 
gesetzt, der vom Timer-Interrupt bis 0 runtergezählt wird. Erst nach 
Ablauf dieses Zählers darf der FET wieder eingeschaltet werden. 
Ergebnis: Keine Zeitschleife mehr drin und kein Nesting nötig.

So ungefähr erledigt man zeitliche Abhängigkeiten. Normalerweise sind 
nur Wartzeiten im Mikrosekundenbereich als Zeitschleifen in Interrupts 
sinnvoll, alles andere kann man getrost als Designfehler abbuchen.

von Marek S. (marekthestudent)


Lesenswert?

Ja gut in meinem Fall sind es "nur" 420µSek. Das Nesting habe ich jetzt 
so oder so abgeschafft aber interessiert hat es mich allemal sowieso 
warum das nicht ging oder wie es denn prinzipiell gehen tut. (Ich weiß, 
Nested Interrupts machen Probleme sobald RWM-Zugriffe auftauchen und auf 
dieselbe Variable zugreifen. Außerdem verliert man dann die Übersicht 
usw...)

Zu Deinem Lösungvorschlag verstehe ich, dass sobald Interrupt 1 
gestartet ist, dieser Interrupt 2 aktiviert, der nach einer best. Zeit 
startet und dort ein Flag setzt. Somit muß ich an entprechender 
Programmstelle einbauen auf das Flag zu warten (while-Schleife). 
Richtig?

von A.K. (Gast)


Lesenswert?

Lass den Systemtimer immer mit festem Raster durchlaufen und Erbsen 
zählen. Ob er nun Arbeit hat oder keine. Den wirst du über kurz oder 
lang noch anderswo verwenden können.

Beispiele für zeitliche Steuerungen:

Global:
  typedef unsigned long long uptime_t
  volatile uptime_t uptime;
  volatile uptime_t fet_zu_heiss_bis;

Systemtimer-Interrupt, 100µs oder 1ms oder ...
  uptime += 1;

Irgendeine Stelle an der gewartet werden muss:
  uptime_t zukunft = uptime + 10000;
  while (uptime < zukunft)
     ;

Dein Interrupt:
  fet_zu_heiss_bis = uptime + 1000;

Irgendein Code der den FET verwenden will:
  while (uptime < fet_zu_heiss_bis)
     ;

von Marek S. (marekthestudent)


Lesenswert?

Ja gut ok, so geht das natürlich auch ABER was mich an diesem hier 
"stört", ist dass der Interrupt ständig aufgerufen wird, das heißt 
ständig das laufende Programm unterbricht. (ok, in meinem Fall muß ich 
keine Performance-Rakete bauen von daher egal.)

Ansonsten, klar gebe ich Dir Recht. Das geht.

Auch möglich wäre immer wieder den Interrupt in einem anderen Interrupt 
wieder zu aktivieren, oder?! Wäre er halt nur für diese eine Aufgabe 
belegt. Ok doof, aber man könnte diesen auch immer wieder neu 
initialisieren...

Alles so "wenn und aber"-Geschichten aber für mich als Neuling zu Beginn 
doch sehr interessant.

PS: Ich könnt auch einfach nur ein Flag setzen und sobald ISR beendet 
wird/ist, da ich diesen Fall nur an einer Stelle im Programm-Code 
erwarte, könnte ich da auch den Timer pollen (mit Abbruchkriterium). 
Würde auch gehen. Glaube das werde ich auch machen. Somit hätte ich 
einen fließenden Betrieb und müßte nicht allzuviele Dinge in Betracht 
ziehen.

von A.K. (Gast)


Lesenswert?

Solche Systemtimer sind völlig normal. Und es liegt in der Natur von 
Interrupts, dass sie unterbrechen. Das stört normalerweise nicht.

Der gesamte Overhead eines normalen Interrupts liegt bei LPC2000/60MHz 
in der Grössenordnung von 1µs, ohne IRQ-Nesting die Hälfte. Da kannst du 
den Timer sogar im 10µs-Zyklus laufen lassen und du spürst es nicht mal.

von A.K. (Gast)


Lesenswert?

PS: Kleiner Fehler oben. besser 32bit verwenden (Vorsicht: Überlauf) 
oder um die Abfragen herum die Ints abschalten. Das kommt halt davon 
wenn man selber sowas meist mit C++ encapsulation macht und nun in 
allgemeinverständliches C übersetzt. ;-)

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.