Raspi: Interrupts gehen nach 12h nicht mehr

OP #5007489
Lesenswert?

Hi!

Ich habe folgenden Pyhon-Code (Auszug), der auf einen Interrupt wartet:
1
def isr(channel):
2
    e.set()
3

4
def waitForEvent(e):
5
    e.wait()
6
    handleData()
7
    e.clear()
8

9
if __name__ == "__main__":
10
    e = threading.Event()
11
    GPIO.add_event_detect(36, GPIO.RISING, callback=isr)
12

13
    try:
14
        while True:
15
            waitForEvent(e)
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
        while True:
2
            inp433 = GPIO.input(36)
3
            if inp433 != 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?
Gast #5007828
Lesenswert?

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.
#5008434
Lesenswert?

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.
Persönliche Seite #5008449
Lesenswert?

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
#5008457
Lesenswert?

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?

>
1
> def isr(channel):
2
>     e.set()
3
> 
4
> def waitForEvent(e):
5
>     e.wait()
6
>     handleData()
7
>     e.clear()
8
> 
9
> if __name__ == "__main__":
10
>     e = threading.Event()
11
>     GPIO.add_event_detect(36, GPIO.RISING, callback=isr)
12
>

> 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.
Persönliche Seite #5008493
Lesenswert?

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
OP #5009670
Lesenswert?

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
        while True:
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.
#5010367
Lesenswert?

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.
OP #5010527
Lesenswert?

Hab den Code jetzt nochmal auf Folgendes geändert:
1
while True:
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.
#5010897
Lesenswert?

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?
#5010916
Lesenswert?

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.
#5010919
Lesenswert?

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)
Gast #5010985
Lesenswert?

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.
#5011011
Lesenswert?

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.
#5011201
Lesenswert?

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.
#5011367
Lesenswert?

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.
#5011393
Lesenswert?

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?
Gast #5011582
Lesenswert?

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:
1
GPIO.remove_event_detect(self.irq_pin)
2
GPIO.add_event_detect(self.irq_pin, GPIO.RISING)

Auszug aus der Hauptschleife:
1
while True :
2
    rfm.rxon()
3
    if rfm.wait_msg(100) :
4
        rfm.rxon() # Empfaenger wieder einschalten
5

6
        [Paket bearbeiten]

Auszug aus rfm:
1
def wait_msg(self, timeout) :
2
    if GPIO.input(self.irq_pin) == True or 
3
    self.irq_pin == GPIO.wait_for_edge(self.irq_pin, GPIO.RISING,
4
    timeout = timeout) :
5
        if self.rfm_mode == RFM69_MODE_RX and (self.status() & 
6
        STATE_MODE_RDY) :
7
            self.read_rssi()
8
            self.receive()     # Paket lesen
9
            return True
10
    #RX Restart nach RSSI-Timeout
11
    status = self.status()
12
    if not (status & STATE_PAYLOAD_RDY) and (status & STATE_TIMEOUT) :
13
        self.writeReg(0x3D,(self.readReg(0x3D) | 0x04))
14
    return False

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren