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!
poste mal bitte deinen code (zumindest den wichtigen teil)... gruss Tobias
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.
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
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.
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.
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.
Das Design ist verbesserungsbedürftig. IN einem Interrupthandler eine Funktion namens wait aufzurufen ... nein, das ist keine gute Idee.
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.
So hallo nochmal, möchte mich vielmals bedanken. Habs gefunden und ausprobiert. Cool, funktioniert. Danke und schönes Wochenende! :-) Gruß, Marek.
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.
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?
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) ;
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.