Am Interrupt-Pin hängt ein RFM69-Funkmodul, das signalisiert, dass es
ein Paket empfangen hat und dass der Python-Code es abholen soll.
Das Ganze funktioniert wunderbar, ich kann ca einen halben Tag lang
Pakete empfangen. Irgendwann kommen aber keine Interrupts mehr durch,
das Programm hängt bei e.wait() fest, weil isr() nicht mehr aufgerufen
wird, obwohl das RFM69 ankommende Pakete meldet. Das Ganze habe ich
mehrere Tage hintereinander beobachtet (dazwischen neu gestartet), es
war immer wieder dasselbe.
Wenn ich hingegen auf den Interrupt-Pin polle
1
whileTrue:
2
inp433=GPIO.input(36)
3
ifinp433!=0:
4
handleData()
dann funktioniert es ohne Probleme auch viel länger, erzeugt allerdings
100% CPU-Last.
Woran kann es liegen, dass die Interrupts nach etwa 12 Stunden nicht
mehr kommen?
Ich kann Dir dabei nicht wirklich helfen. Allerdings möchte ich darauf
hinweisen, dass ich die Idee schon ziemlich abwegig finde, Interrupts
mit einer Scriptsprache zu bedienen.
Das ist in etwa so, als ob man seine Cola-Flasche mit einem Bagger
öffnet.
Der Unterschied ist, der Interrupt reagiert nur auf eine Flanke, das
Polling aber auf den Pegel.
Wenn keine neue Flanke kommt, weil der Pegel schon 1 ist, dann kann der
Interrupt nichts dafür.
Du mußt den Interrupt auf Pegeltriggerung umstellen.
Wenn die Hardware keinen pegelgetriggerten Interrupt hat, kann man sich
dadurch behelfen, daß man am Ende des Handlers die Leitung pollt:
1
ISR()
2
{
3
do{
4
handle_interrupt();
5
clear_pending_flag();
6
}while(inputpin_active());
7
}
Typisch ist, daß es mit Fehler sehr lange gut gehen kann, bis mal
zufällig viele Interrupts zusammen auftreten oder höher priorisierte
Interrupts die Bearbeitung verzögern.
Ich hab das bei anderen Peripherie-ICs benötigt, z.B. SJA1000, ENC28J60.
Hallo Stefan.
Stefan U. schrieb:> Ich kann Dir dabei nicht wirklich helfen. Allerdings möchte ich darauf> hinweisen, dass ich die Idee schon ziemlich abwegig finde, Interrupts> mit einer Scriptsprache zu bedienen.>
Python ist ja keine reine Skriptsprache. Es können durchaus
Programmeteile, die in irgedwas geschrieben sind, auch C oder Fortran,
eingebunden werden.
> Das ist in etwa so, als ob man seine Cola-Flasche mit einem Bagger> öffnet.
Wenn es halt gerade opportun ist? Weil es sich einfach schreiben lässt,
und die Geschwindigkeit langt?
Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
http://www.l02.de
Markus R. schrieb:> Das Ganze funktioniert wunderbar, ich kann ca einen halben Tag lang> Pakete empfangen. Irgendwann kommen aber keine Interrupts mehr durch,
Kannst du die Schnittstelle etwas genauer beschreiben? Ich vermute der
RFM69 hat eine separate Signalleitung die er raised wenn Daten anliegen?
Du wartest auf die Flanke und startest danach einen SPI Dialog zur
Datenübertragung?! Wer setzt wann die Signalleitung zurück?
>
> das Programm hängt bei e.wait() fest, weil isr() nicht mehr aufgerufen> wird, obwohl das RFM69 ankommende Pakete meldet. Das Ganze habe ich
Was passiert denn wenn bevor e.clear() neue Datenankommen? Kann man nach
(!) dem clear() per SPI prüfen ob neue Daten vorliegen?
Woran siehst du dass neue Pakete "gemeldet" werden? Sieht du die Flanken
auf der Signalleitung?
> Wenn ich hingegen auf den Interrupt-Pin polle> dann funktioniert es ohne Probleme auch viel länger, erzeugt allerdings> 100% CPU-Last.
Als Workround, was spricht dagegen ein Sleep einzubauen?
Zur Fehlereingrenzung kannst du auch mal gucken, ob die Python Lib für
die PIGPIO andere Ergebnisse liefert.
Grundsätzlich würde ich sagen, im Forum der Raspberry Org bekommt du
wohl mehr/besseres Feeback.
Die Frage oder das Problem des TO besteht doch hierin:
Er kann 12 Stunden lang die Flasche mit dem Bagger öffnen, dann
funktioniert es nicht mehr. Erst nachdem der Bagger neu gestartet wurde,
klappt es wieder 12 Stunden lang.
Hallo Martin.
Martin K. schrieb:> Die Frage oder das Problem des TO besteht doch hierin:> Er kann 12 Stunden lang die Flasche mit dem Bagger öffnen, dann> funktioniert es nicht mehr. Erst nachdem der Bagger neu gestartet wurde,> klappt es wieder 12 Stunden lang.
Ja. So ein Problem hatte ich auch mal. Und der Grund war der gleiche wie
von Peter Dannegger geschildert.
Und das kann in Assembler so auch passieren, das hat nichts mit der Art
der Sprache zu tun. ;O)
Man muss halt wissen, dass man eine Flanke und keinen Pegel auswertet,
und dann geht das sowohl in Assembler als auch in Python.
Mit freundlichem Gruß: Bernd Wiebus alias dl1eic
http://www.l02.de
Der Aspekt Flanke vs Pegel ist interessant, das könnte es sein. Trotzdem
wundere ich mich, der Linux-Kernel wird doch sicher durch Hardware
getriggert, wenn eine Flanke auftritt. Mich wundert es, dass Flanken
"übersehen" werden können. Vor allem, da ich Zig Pakete erfolgreich
empfangen kann und dann von einem Moment auf den anderen gar keines
mehr.
Das RFM69-Modul ist über SPI mit dem Raspi verbunden, dieser ist der
SPI-Master. Eine Signalleitung vom RFM69 geht auf high, wenn ein PAket
angekommen ist, und wird auch von diesem wieder nach low gezogen.
Folgender Code scheint besser zu funktionieren:
1
try:
2
whileTrue:
3
GPIO.wait_for_edge(RFM69_433_DIO0,GPIO.RISING)
4
handleData()
Ich werde das aber noch ein wenig länger beobachten und berichte dann
nochmal.
Markus R. schrieb:> Eine Signalleitung vom RFM69 geht auf high, wenn ein PAket> angekommen ist, und wird auch von diesem wieder nach low gezogen.
Ich kenne speziell den RFM69 nicht, aber bei den genannten SJA1000,
ENC28J60 kann es passieren, daß wärend der Behandlung eines Ereignisses
schon das nächste auftritt. Dann geht der Interruptpin nicht auf inaktiv
und es gibt demzufolge auch keine Flanke!
Der Pin bleibt auf aktiv.
Hab den Code jetzt nochmal auf Folgendes geändert:
1
whileTrue:
2
# Turn receiver back on
3
RFM.rfm_rxon()
4
GPIO.wait_for_edge(RFM69_433_DIO0,GPIO.RISING)
5
handleData()
Außerdem habe ich geprüft, dass in handleData() nirgends in den RX-Modus
geschaltet wird.
Wenn nun ein Paket empfangen wird, wird der RX-Modus automatisch vom
RFM69-Modul verlassen und erst dann wieder aktiviert, wenn der
Python-Code bereit dazu ist. Somit sollte sich das Problem lösen lassen,
dass ein Paket ankommt, wenn das vorherige noch verarbeitet wird.
Trotzdem erklärt das meiner Meinung nur sporadische Paketverluste, nicht
aber dass plötzlich überhaupt keine Interrupts mehr funktionieren.
Vielleicht mal das try: weglassen? Nicht das irgendetwas nicht geht und
Du keine exception bekommst (wobei ich ehrlich gesagt keine Ahnung habe
wie ein try : while auf eine Exception reagiert)
lalala schrieb:> wobei ich ehrlich gesagt keine Ahnung habe> wie ein try : while auf eine Exception reagiert
Würde auch das try innerhalb der while Schleife machen, aber noch viel
wichtiger die exception fangen und entsprechende Meldung
ausgeben/loggen.
Markus R. schrieb:> Trotzdem erklärt das meiner Meinung nur sporadische Paketverluste, nicht> aber dass plötzlich überhaupt keine Interrupts mehr funktionieren.
Du hast das Problem noch immer nicht verstanden. Du kriegst keinen
Flankeninterrupt, wenn der Pegel auf aktiv bleibt bzw. vor dem Löschen
des Pending-Bits wieder aktiv wird.
Ein Flankeninterrupt benötigt, wie ja der Name schon sagt, zwingend eine
Flanke.
Was ist denn so schlimm daran, am Ende des Handlers den Pegel nochmal zu
testen?
Mir kommt dieses Problem nur allzu bekannt vor, auch mit Raspberry Pi
und RFM69.
Eine Überprüfung der Leitungen mittels Logic-Analyzer hat ergeben, dass
das Signal gegeben wird, aber die Software das nicht immer registriert.
Egal, wie ich es gedreht und gewendet habe, mit Interrupts wurde das nie
stabil. Da es für meine Anwendung keine Rolle gespielt hat, bin ich
daher auf Polling der Statusregister umgestiegen, das funktioniert
problemlos.
Felix P. schrieb:> Da es für meine Anwendung keine Rolle gespielt hat, bin ich> daher auf Polling der Statusregister umgestiegen, das funktioniert> problemlos.
Ja, das ist in etwa so, als würde der Kunde beim Zulieferer 10x am Tag
anrufen um nachzufragen wie weit seine Platinen sind, anstatt zu warten
bis er sich meldet! Kostet Zeit, belastet beide, bringt aber nur
unnötige Last mit. Keiner hat Ruhe (CPU geht wohl auf 100% bei dem
Skript)
Ich habe ein RFM69 mit RPi3 laufen, Software ist
https://github.com/abouillot/HomeAutomation, ein Gateway zu MQTT. Das
läuft jetzt seit mehreren Monaten ohne Probleme, uptime 116 Tage.
Diese Lösung benutzt die wiringPi Library, aber in C programmiert, auch
mit Interrupt. Die Interruptantwortzeit ist bei dieser Lösung 200 µs und
funktioniert zuverlässig. Vorher hatte ich eine Lib benutzt die in
JavaScript geschrieben wurde, aber das Interrupt quittieren hat bis zu 4
ms gedauert und das ist natürlich sehr suboptimal.
Alex W. schrieb:> Ja, das ist in etwa so, als würde der Kunde beim Zulieferer 10x am Tag> anrufen um nachzufragen wie weit seine Platinen sind, anstatt zu warten> bis er sich meldet! Kostet Zeit, belastet beide, bringt aber nur> unnötige Last mit. Keiner hat Ruhe (CPU geht wohl auf 100% bei dem> Skript)
Wenn die Alternative aber ist, dass der Zulieferer nicht in der Lage
ist, den Kunden zu informieren, dass die Platine fertig ist, und der
Kunde seine Platine nie bekommt, ist es auch nicht im Sinne des
Erfrinders. Was ist "nötiger"? Eine geringe CPU-Last oder ein
funktionierendes Programm?
Mir ist klar, dass diese Lösung ganz generell nicht gut und
zufriedenstellend ist, aber die "schöne" Lösung hat wie gesagt nicht
zuverlässig funktioniert und für meine Anwendung - RaspberryPi fragt zu
Beginn ab, welche Knoten in Reichweite sind und sendet denen dann zu
bestimmten Zeitpunkten Befehle, während er gleichzeitig ein Musikstück
abspielt und die Zeit bis zum nächsten Befehl auf einem Display ausgibt,
das Programm läuft alle paar Wochen für maximal 30 Minuten - war es
tolerierbar, zu Zeiten, wenn Daten erwartet wurden oder gesendet werden
sollten, das Statusregister zu pollen. Dass es für andere Anwendungen
anders ist, bestreite ich nicht. Leider scheint die event_detection von
RPi.GPIO noch irgendwo fehlerhaft zu sein, auf sourceforge gibt es immer
wieder Tickets in diesem Zusammenhang.
Markus R. schrieb:> Trotzdem wundere ich mich, der Linux-Kernel wird doch sicher> durch Hardware getriggert, wenn eine Flanke auftritt.
Der Linux-Kernel unterstützt sowohl flanken- als auch pegelgetriggerte
Interrupts. Ich hatte den Spaß mit dem UIO-Framework mal.
Felix P. schrieb:> Eine geringe CPU-Last oder ein> funktionierendes Programm?
Grundlos(!) verschwendete CPU-Zeit ist in meinen Augen kein Zeichen für
ein funktionierendes Programm. Das Problem hier ist bekannt, die Abhilfe
ist bekannt, also ist Polling keine sinnvolle Alternative.
Da das mit der Pegeltriggerung jetzt ziemlich oft kam: Wir sind hier im
Userland! Es handelt sich wie oben bereits gesagt nicht um eine Hardware
ISR.
Der BCM2835 unterstützt zwar pegel-getriggerte Interrupts, die laufen
dann allerdings im Linux-Kernel auf. Wie oft soll denn die Kernel ISR
auslösen bis irgendwo vielleicht irgendwann eine Anwendung im Userland
darauf reagiert?
Dementsprechend unterstützt das Python Modul auch nur Flanken. Und wie
oben beschrieben reicht das auch vollkommen aus (mit der beschriebenen
zusätzlichen Abfrage), wenn denn da überhaupt das Problem lag.
Peter D. schrieb:> Du mußt den Interrupt auf Pegeltriggerung umstellen.Markus R. schrieb:> Der Aspekt Flanke vs Pegel ist interessant, das könnte es sein.S. R. schrieb:> Der Linux-Kernel unterstützt sowohl flanken- als auch pegelgetriggerte> Interrupts.
Also mich hat das nun auchmal interessiert und google sagt das es einen
Interrupt Queue gibt und man abfragen kann ob interrupts
gesperrt/blockiert sind.
Mir ist da noch der Gedanke zum Realtime RT-Kernel gekommen, ob das was
bringen würde? Wenn man sowieso nur wichtige Services laufen hat wäre
das ja kein Nachteil, oder ist das beim Rpi schon grundsätzlich
Realtime?
Hi,
das ist mein erster Post hier, man möge nachsichtig sein.
Ich bin ebenfalls grade dabei einen Raspi mit einem RFM69 zu verheiraten
und kann die Probleme bestätigen. Es ist so wie Peter schreibt, der
Interruptpin hat wieder High bevor die Bearbeitung der ISR beendet ist.
Vielleicht werden auch wirklich Flanken vom Kernel übersehen, das habe
ich aber nicht weiter untersucht.
In meinem Code nutze ich den Pin allerdings nicht um eine ISR
aufzurufen, sondern starte aus der while True eine Funktion die den Pin
abfragt und mit Timeout blockiert. Der Trick ist nun, wie schon
vorgeschlagen, vor Aufruf von GPIO.wait_for_edge() nochmal den Pin
abzufragen.
Damit polle ich mit der Timeoutzeit den Pin. Geht der Pin vorher auf
High, endet die Blockierung und ich kann das Paket auslesen.
Wichtig ist auch, dass nach einem RSSI-Timeout der Empfaenger neu
gestartet wird. Das hat zwar nichts mit dem bespochenen Problem zu tun,
aber mit dem danach.
Mit dem unten skizzierten Code läuft der Empfang sehr stabil und erzeugt
keine nenenswerte CPU-Last.
Initialisierung: