Forum: PC-Programmierung Mit RPi auf SPI zugreifen


von oldtimer (Gast)


Lesenswert?

Hallo,
eine Kollegin hat ein Programm geschrieben, wo sie mit einem Raspberry 
und Python-Programm über I2C und SPI mit anderen Mikrocontrollern 
kommuniziert.
Dabei gibt es manchmal Unregelmässigkeiten im Betrieb, d.h. dass I2C/SPI 
Kommandos nicht erkannt werden von den Mikrocontrollern.
Dabei verwendet sie
als OS das Standard-OS raspbian stretch,
und
https://pypi.org/project/pigpio/
für die HW-Schnittstellen.
und tkinter als Python-GUI

Dabei sind ihre Aufrufe verstreut im Code, meistens in den Callbacks von 
Button-Clicks.
Die Aufrufe der I2C/SPI-Zugriffe sind auch mit Sleep-Befehlen versehen 
innerhalb der Button-Callbacks.
Es schien zu funktionieren
Frühers habe ich mal gelernt, dass man in solche Callbacks nur Befehle 
mit kurzer Ausführungsdauer schreibt. Längere Sequenzen müssten in einem 
anderen Thread abgearbeitet werden.

Kann das die Ursache für den unzuverlässigen Programmablauf sein?
Wie könnte man das begründen?

von Mikro 7. (mikro77)


Lesenswert?

Mehr Details. Was wird genau gemacht? Welche Funktionen von pigpio 
werden wie für was genutzt. Einfach Mal einen Use-Case beschreiben, den 
Source Code zeigen und das aufgetretene Problem. Schon mit eine LA 
angeschaut?

Bis vor einem Jahr, als ich mit dem Rpi noch was gemacht hatte, gab es 
von Joan (Autor von pigpio) schnelle und fundierte Hilfestellung bei 
Problemen. Also mit den erforderlichen Details im raspberry.org Forum 
posten (englisch, im Interfacing Subforum).

von oldtimer (Gast)


Lesenswert?

Der Source-Code ist 15.000 Zeilen lang und in einem 3/4 Jahr entstanden, 
ich weiß nicht warum der so lang ist. Die Funktionalität ist nicht 
sonderlich umfangreich.

Ich muss da noch exemplarisch paar typische Stellen raussuchen.

Die Fehler sehen z.B. so aus, dass der Raspberry einen Befehl über i2c 
an einen STM32-Mikrocontroller schickt, eine LED anzumachen, aber die 
LED geht manchmal nicht an. Manchmal reproduzierbar, manchmal nicht.

Wo genau das Problem auftritt ist unbekannt, und es ist sehr schwierig 
aufgrund der Länge des Codes zu lokalisieren.

Hier ist der SPI Zugriff kurz skizziert.
1
#!/usr/bin/env python3
2
import tkinter as tk
3
import pigpio
4
5
...
6
pi=pigpio.pi()
7
h=pi.spi_open(0, 500000, 0x100)
8
...
9
10
#SPI-Zugriff
11
            resp2 = pi.spi_xfer(h, [0x10, 0,0, 0])
12
            time.sleep(0.002)
13
            a = [0]*len
14
            resp2 = pi.spi_xfer(h, a )

Kann morgen mehr Code-Stellen anbieten.

von STK500-Besitzer (Gast)


Lesenswert?

oldtimer schrieb:
> Der Source-Code ist 15.000 Zeilen lang und in einem 3/4 Jahr entstanden,
> ich weiß nicht warum der so lang ist. Die Funktionalität ist nicht
> sonderlich umfangreich.

Sowas kenne ich auch. Copy-and-paste-Grab.
Leider kann ich zum eigentlichen Problem nichts sagen.

von oldtimer (Gast)


Lesenswert?

Ich selber würde alle pigpio-Zugriffe von einem einzelnen Thread (d.h. 
"worker-thread") ausführen. Und die tkinter-Button-Callbacks posten 
asynchron entsprechende Nachrichten zu diesem worker-Thread.
Wenn ich diese vielen pigpio und sleep Funktionen in den 
Button-Callbacks sehe, wird es mir ungemütlich.

So wie ich das sehe, startet Python für jeden Callback einen neuen 
Thread, weil die GUI nie hängen bleibt bei einer längeren 
Callback-Ausführung.
D.h. die meisten pigpio-Zugriffen geschehen von ständig von neuen 
Threads(?)

von Mikro 7. (mikro77)


Lesenswert?

Ich kann da keine Hilfestellung geben. Ich mache kein Python. Und bin 
aus dem Rpi Thema auch raus.

Anyway, bei SPI kann der RPI nur Master. Da wird also in deinem Bsp. ein 
Kommando gesendet (und nach >= 2ms die Antwort abgeholt). Bei den kurzen 
Kommandos sollte es kein Problem geben (da die FIFO nicht vollläuft). 
Ich weiß nicht, wie Joan das implementiert hat: Wenn die aus dem 
Userspace gefüllt wird, könnte es potentiell (sehr unwahrscheinliche) 
Probleme geben, wenn die FIFO nicht gefüllt ist, bevor der SPI startet. 
Wenn der Kernel Treiber (oder DMA aus dem Userland) benutzt wird, sollte 
es immer klappen. (Bei I2C nutzt Joan glaube ich immer den Kernel 
Treiber.) Grundsätzlich ist die Lib von Joan ausgereift. Da sollte es 
nie Probs geben.

Neben dem Rpi gibt es als Fehlerquellen noch die Leitung und die 
Gegenseite. Am besten einen LA mitlaufen lassen und gucken ob das Signal 
auf der Leitung ok ist. Vielleicht schaltet ihr auch selbst die LED 
wieder aus?!

Ansonsten (wie immer bei Fehleranalyse) Problem auf einen Minibsp. 
reduzieren (ihr habt da ja euer LED-Anschalten Use Case), Stress Test 
machen (wenn es nicht immer reproduzierbar ist), und mit dem Ergebnis 
in's Rpi Forum gehen (also zu Joan).

btw: Single, Multi whatever Thread sehe ich nicht als Problem, da es nur 
eine 4-byte Kommandosequenz ist. Ob die LED-an geht oder nicht hängt ja 
nicht von was anderem ab, oder?!

: Bearbeitet durch User
von Torben (Gast)


Lesenswert?

>Wo genau das Problem auftritt ist unbekannt, und es ist sehr schwierig
>aufgrund der Länge des Codes zu lokalisieren.

1. Habt Ihr schon die Mikrocontroller ausgeschlossen?
2. Hast Du schon den RPI Code auf SPI/I2C reduziert und einen 
Dauerlauftest getätigt?

von oldtimer (Gast)


Lesenswert?

> 1. Habt Ihr schon die Mikrocontroller ausgeschlossen?

Im Prinzip ja. Sie sind schon im Feld 1/2 Jahr, und damit Dauerbetrieb. 
Gab schon ein paar Rückläufer, aber Hardware-Versager.


> 2. Hast Du schon den RPI Code auf SPI/I2C reduziert und einen Dauerlauftest 
getätigt?

Nee. Die Kollegin hat die 15.000 Zeilen verfasst, und ich kann den Code 
nicht in absehbarer Zeit auseinandernehmen und verstehen, sondern suche 
strukturelle Fehler. D.h. ich lese das quer durch.
Ihr scheint das Konzept von Unterfunktionen nicht so vertraut zu sein, 
weil sie von oberster Ebene auf die low-level Funktionen zugreift.
(Durch Unterfunktionen mit Parameterübergabe könnte man Code 
übersichtlicher und wartbarer schreiben, weil man mögliche 
Fehlerkorrekturen und Anpassungen nur an einer Stelle machen muss und 
nicht an 15 ähnlichen.)
Mein Vorschlag an Projekt-Leitung war "Neu schreiben", aber wurde nicht 
umgesetzt.

Denkbar wäre einen Parser zu schreiben, der die ähnlichen Stellen mit 
diff-tools vergleicht.

Ich habe immer noch die vielen Threads im Verdacht, und vermute, dass 
dann doch einmal zwei threads gleichzeitig auf die pigpio-Objekte 
zugreifen, und dann Daten durcheinander würfeln.
Bisher habe ich immer den Einsatz eines "worker-threads" empfohlen, der 
zentral alle I2C/SPI-Zugriffe erledigt.

von Olaf (Gast)


Lesenswert?

> Die Fehler sehen z.B. so aus, dass der Raspberry einen Befehl über i2c
> an einen STM32-Mikrocontroller schickt, eine LED anzumachen, aber die
> LED geht manchmal nicht an. Manchmal reproduzierbar, manchmal nicht.

Die Funktionen werden doch bestimmt ACK vom I2C auswerten und dann eine 
Fehlermeldung zurueckliefern oder? Wenn nicht dann alles neuschreiben.
Wenn ja dann wuerde da erstmal ein Leitung setzen und die als Postrigger 
fuer den Oszi verwenden um sich mal so eine fehlerhafte 
Datenuebertragung anzuschauen.

Olaf

von PittyJ (Gast)


Lesenswert?

Normalerweise kapselt man HW-Zugriffe in eigenen Klassen.
Dann kann man dort auch gezielt Debuggen und Loggen.

Wenn das nicht der Fall ist, dann sollte man das Programm neu 
strukturieren. Und wenn das die Kollegin nicht kann, dann ist sie falsch 
am Platz.

Vielleicht sollte man dem Management mitteilen, dass sie die richtigen 
Leute für die richtigen Jobs nehmen sollten.

von oldtimer (Gast)


Lesenswert?

Die Kollegin hat schon einiges drauf, ich möchte sie nicht kritisieren. 
In Sachen SW-Entwicklung und SW-Architektur fehlt ihr einfach die 
Erfahrung. In der Firma gibt es keinen SW-Senior, der sie einweisen 
könnte. Der Firma meint, ein Abgänger von der FH kann bereits alles, um 
professionelle SW zu schreiben und braucht keine weiteren Einweisungen.
(Ich bin in der Firma teilzeit-Freiberufler)

von oldtimer (Gast)


Lesenswert?

Wie die Kollegin angefangen hat vor einem Jahr, hatte sie keine Ahnung 
von Raspberry, von Linux und von Python glaube ich auch nicht.
Ich denke, sie hat weit mehr geschafft als der Durchschnitt.
Sie damit ziemlich weit gekommen.

von Ingo W. (uebrig) Benutzerseite


Lesenswert?

oldtimer schrieb:
> So wie ich das sehe, startet Python für jeden Callback einen neuen
> Thread, weil die GUI nie hängen bleibt bei einer längeren
> Callback-Ausführung.

Kenne mich mit Python nicht aus, aber das würde mir etwas zu denken 
geben:
Wenn wirklich überlappende Zugriffe von verschiedenen Threads auf die 
gleiche SPI-Schnittstelle stattfinden, könnte es Kauderwelsch 
(Kollisionen) geben.
Normalerweise würde man versuchen, dies mit einem Semaphor oder einer 
Lockdatei zu verhindern.
Würde allerdings auch nur auftreten, wenn der Anwender schon die nächste 
Funktion auslöst, bevor die Erste abgearbeitet ist. In diesem Falle sind 
absichtlich eingebaute Verzögerungen (Sleep/Delay) wohl auch eher 
kontraproduktiv, das sie diese Gefahr erhöhen.

oldtimer schrieb:
> Ich selber würde alle pigpio-Zugriffe von einem einzelnen Thread (d.h.
> "worker-thread") ausführen. Und die tkinter-Button-Callbacks posten
> asynchron entsprechende Nachrichten zu diesem worker-Thread.

Das wäre wohl sicher die bessere Lösung, da so sichergestellt ist, das 
die zweite Aktion erst begonnen wird, wenn die erste abgearbeitet ist.

von oldtimer (Gast)


Lesenswert?

> Wenn wirklich überlappende Zugriffe von verschiedenen Threads

Sollte normalerweise nicht passieren, weil die callbacks nacheinander 
gefeuert werden. Es ist aber keine Lock-Sicherung vorhanden.
Aber wenn z.B. System überlastet ist, ein Thread ist blockiert, oder 
irgendwelche Nebeneffekte, dann könnte es eventuell schon passieren.

Ich hätte gedacht, dass bei GUI-Programmen alle Button-Callbacks von 
einem einzigen GUI-Thread aufgerufen werden. So war das bei der 
WIN32-API bis Windows XP, oder Linux-GTK.
Da wäre nichts passiert mit überlappenden Threads, nur dass die 
Oberfläche unresponsive ist, solange der Callback in der Ausführung 
hängt.

von Dirk (Gast)


Lesenswert?

>Die Kollegin hat schon einiges drauf, ich möchte sie nicht kritisieren.

Machst Du indirekt schon. Lass am besten solche Informationen in einem 
Forum weg, weil es nichts mit deinem Problem zu tun hat.

Beispiel:

Hallo,
wir haben ein Programm geschrieben, wo sie mit einem Raspberry
und Python-Programm über I2C und SPI mit anderen Mikrocontrollern
kommuniziert.

>Ich habe immer noch die vielen Threads im Verdacht, und vermute, dass
>dann doch einmal zwei threads gleichzeitig auf die pigpio-Objekte
>zugreifen, und dann Daten durcheinander würfeln.
>Bisher habe ich immer den Einsatz eines "worker-threads" empfohlen, der
>zentral alle I2C/SPI-Zugriffe erledigt.

Vermutungen und etwas verdächtigen sind keine Fakten, bringt ein paar 
Logfunktionen rein oder teile den Code auf, ansonsten wird die 
Fehlerursache zu finden nicht einfacher.


Ja, ein Daemon wäre besser gewesen, aber auch viel Arbeit und später 
weiss man es immer besser, aber auch das hilft dir gerade null.

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.