Forum: Mikrocontroller und Digitale Elektronik Raspi: Interrupts gehen nach 12h nicht mehr


von Markus R. (maggggus)


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?

von Stefan F. (Gast)


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.

von Peter D. (peda)


Lesenswert?

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.

von Peter D. (peda)


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.

von Bernd W. (berndwiebus) Benutzerseite


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

von Mikro 7. (mikro77)


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.

von Martin K. (maart)


Lesenswert?

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.

von Mikro 7. (mikro77)


Lesenswert?

Was soll der Blödsinn mit dem Bagger. Das ist ganz normale 
ereignisgesteuerte Programmierung. Das ist keine Hardware ISR!

von Martin K. (maart)


Lesenswert?

Ich habe den Bagger einfach nur aufgegriffen, da die Schreiber weiter 
oben ihn erwähnt hatten und am eigentlichen Problem vorbeigelabert 
haben.

von Bernd W. (berndwiebus) Benutzerseite


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

von Markus R. (maggggus)


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.

von Peter D. (peda)


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.

von Markus R. (maggggus)


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.

von lalala (Gast)


Lesenswert?

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)

von Helmut H. (helmuth)


Lesenswert?

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.

: Bearbeitet durch User
von Peter D. (peda)


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?

von Felix P. (fixxl)


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.

von Alex W. (a20q90)


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)

von Johannes S. (Gast)


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.

von Felix P. (fixxl)


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.

von S. R. (svenska)


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.

von Mikro 7. (mikro77)


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.

von Philipp K. (philipp_k59)


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?

von Micha (Gast)


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

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.