Hallo,
Ich nutze einen NXP Cortex Controller LPC1764.
Sporadisch kommt es vor das aus einer Interrupt-Routine Port-Pins an P0
nicht gelöscht werden, obwohl der Befehl (LPC_GPIO0->FIOPIN &= ~0x01) in
der Firmware korrekt aufgerufen wird.
Was kann das Problem sein?
Würde mich über Antworten sehr freuen.
Gruß
Justus
Justus schrieb:> Sporadisch kommt es vor das aus einer Interrupt-Routine Port-Pins an P0> nicht gelöscht werden, obwohl der Befehl (LPC_GPIO0->FIOPIN &= ~0x01) in> der Firmware korrekt aufgerufen wird.
Wenn im Vordergrundprogramm oder einem anderen Interrupt-Handler
ebenfalls auf dieses Register schreibend zugegriffen wird, können sich
die Zugriffe gegenseitig unterbrechen. Z.B. liest das Programm den alten
Inhalt aus, wird dann durch den Interrupt-Handler unterbrochen, der P0
zurücksetzt, und arbeitet nach Beendigung des Interrupt-Handlers auf
seinem alten Registerinhalt mit noch gesetztem P0 weiter. Also werden
solche Peripherie- und/oder Registeroperationen nicht atomar ausgeführt.
Viele aktuelle Microcontroller haben daher separate Periherieregister,
mit denen einzelne (oder mehrere) Bits gesetzt werden können, und
solche, mit denen die entsprechenden Bits zurückgesetzt werden können.
Dies vermeidet zum einen auf Programmseite viel Aufwand durch
Lesen->verunden->verodern->schreiben, und zum anderen sind die
Schreibzugriffe atomar. Der einzige Nachteil besteht darin, dass man
nicht gleichzeitig Bits setzen und zurücksetzen kann.
Ansonsten besteht noch die Möglichkeit, die Peripheriezugriffe durch
Interruptsperren o.ä. zu schützen.
<glaskugelmodus>
In Deinem Programm befindet sich der Fehler übrigens in Zeile 42.
</glaskugelmodus>
Justus schrieb:> Was kann das Problem sein?
Vermutlich ist es das übliche Semaphorenproblem: du greifst von 2
Stellen nicht atomar auf das LPC_GPIO0->FIOPIN Register zu.
Dein Interupt unterbricht dabei einen solchen Zugriff auf das
LPC_GPIO0->FIOPIN, der gerade in der Hauptschleife am Laufen ist.
Das passiert natürlich während der Testphase nur 1 einziges Mal, laut
Murphy beim Kunden dann aber zuverlässig jede Viertelstunde. Bis der
Techniker kommt.
EDIT: hoppla, Absenden vergessen... ;-)
Andreas S. schrieb:> Der einzige Nachteil besteht darin, dass man nicht gleichzeitig Bits> setzen und zurücksetzen kann.
Doch, das geht durchaus - z.B. beim STM32F4 mit dem BSRR-Register.
Justus schrieb:> obwohl der Befehl (LPC_GPIO0->FIOPIN &= ~0x01) in> der Firmware korrekt aufgerufen wird.
Um einen Pin zu löschen, nimmt man besser die passende Lib-Funktion, die
kann das dann atomar.
Lothar M. schrieb:> Dein Interupt unterbricht dabei einen solchen Zugriff auf das> LPC_GPIO0->FIOPIN, der gerade in der Hauptschleife am Laufen ist.
Auch NXP hat sich dafür Set/Reset-Register ausgedacht: FIOxSET, FIOxCLR
Damit geht's nicht nur atomar, sondern auch noch einfacher/schneller.
Man muss sie halt nur verwenden ...
>> wäre strenggenommen korrekt.
Nicht nur strenggenommen, sondern einfach 'korrekt'. Zwar sind FIOxSET
und FIOxCLR lt. UM R/W, allerdings steht nicht ausdrücklich da, dass
beim Lesen 0 herauskommt: "Reading the FIOxSET register returns the
value of this register, as determined by previous writes to FIOxSET and
FIOxCLR (or FIOxPIN as noted above). This value does not reflect the
effect of any outside world influence on the I/O pins."
Diese Beschreibung ist reichlich obskur, ich verstehe sie jedenfalls
nicht.
Und erst das Register lesen, ein Bit auf '1' setzen und dann das zurück
schreiben ist doch etwas sonderbar und höchst ineffizient - sowohl für
den Compiler als auch für den uC.
Nochmals vielen Dank für die ergäzenden Hinweise.
Werde morgen im Labor die Firmware daraufhin ändern und den Port Pin mit
dem Oszilloskop überwachen. Bin gespannt auf das Ergebnis.
Viele Grüße
Justus
Habe die Befehle von
LPC_GPIO0->FIOPIN |= 0x01;
nach
LPC_GPIO0->FIOCLR = 0x01;
geändert.
Jetzt funktioniert der Port-Pin zuverlässig.
Vielen Dank für den guten Support!
Gruß
Justus
A. B. schrieb:> Andreas S. schrieb:>>> LPC_GPIO0->FIOCLR = 0x01;>>>>>> wäre strenggenommen korrekt.>> Nicht nur strenggenommen, sondern einfach 'korrekt'. Zwar sind FIOxSET> und FIOxCLR lt. UM R/W, allerdings steht nicht ausdrücklich da, dass> beim Lesen 0 herauskommt: "Reading the FIOxSET register returns the> value of this register, as determined by previous writes to FIOxSET and> FIOxCLR (or FIOxPIN as noted above). This value does not reflect the> effect of any outside world influence on the I/O pins."> Diese Beschreibung ist reichlich obskur, ich verstehe sie jedenfalls> nicht.
Ich würde mal vermuten, dass der Wert des Ausgaberegisters gelesen wird
(nicht des Pins, das wäre dann "outside influence"). Ausprobiere tu
ich's jetzt aber nicht.
Das hieße dann, mit LPC_GPIO0->FIOCLR|= werden ALLE gesetzten Bits
zurückgesetzt, was definitiv falsch ist.