Momentan versuche ich zwei Schrittmotoren parallel über eine
Pythonsteuerung auf einem RaspberryPi zu steuern. Die beiden Threads
greifen jeweils auf eine Funktion Motorsteuerung1 und 2 zu und schalten
unterschiedliche GPIO Pins (18 und 24), allerdings wenn ich nachmesse
wird immer nur einer der GPIO Pins geschalten und am anderen tut sich
gar nichts.
Die vermutung ist das der Raspi nicht zulässt dass ich von zwei Threads
gleichzeitig auf GPIO's zugreife, allerdings weiß ich nicht wie ich das
umgehen kann.
Mit vielen Grüßen,
Manuel
Was ist, wenn die Ports nur Byteweise (oder noch mehr Bits gleichzeitig)
angesprochen werden und dieses Byte irgendwo als Variable vorgehalten
wird?
Thread 1:
1) Port x in Variable y einlesen.
2) Ein Bit in y ändern.
3) Variable y auf Port x Port ausgeben.
4) Ein Bit in y ändern.
5) Variable y auf Port x Port ausgeben.
6) Ein Bit in y ändern.
7) Variable y auf Port x Port ausgeben.
Thread 2:
1) Port x in Variable z einlesen.
2) Ein Bit in z ändern.
3) Variable z auf Port x Port ausgeben.
4) Ein Bit in z ändern.
5) Variable z auf Port x Port ausgeben.
6) Ein Bit in z ändern.
7) Variable z auf Port x Port ausgeben.
Diesen Code müsste man so ändern, dass vor jedem Bit-Ändern der Port
erneut gelesen wird. Vielleicht liegt es prinzipiell daran.
Ein Objekt erstellen, was das GPIO abbildet. Dann beide Threads mit dem
Objekt arbeiten lassen, und den Zugriff über ein Lock verrriegeln.
So wäre mein Standard-Vorgehen.
Grundsätzlich würde ich auch zur Objekt-Methode mit dem Lock greifen,
allerdings ist dies für meine Anwendung nicht sehr praktisch da die
Geschwindigkeit der Schrittmotoren über ein delay-Zeit zwischen zwei
Schritten geregelt wird. Muss jetzt der eine Thread warten wird dadurch
die Geschwindigkeit meiner Motoren verändert.
Was mir bei weiteren versuchen aufgefallen ist, ist eine ungewöhnliche
abhängigkeit der GPIO's die ich mir nicht erklären kann. Beispielsweiße
wenn ich in Thread 1 versuche den GPIO 24 zu steuern und in Thread 2
GPIO 8 (BOARD Layout) Funktioniert Pin 8 und 24 nicht, ändere ich
allerdings den Pin 24 in der Software auf GPIO 18 funktioniert auf
einmal gar kein Pin mehr.
Außerdem bekomme ich bei manchen Pins wie z.B. GPIO 4 eine meldung Pins
is invalid on Raspberry Pi obwohl es sich bei diesem um einen normalen
Pin handeln sollte. Vielleicht haben Sie eine Idee woher dieses Phänomen
kommen könnte.
Mit vielen Grüßen,
Manuel
Die Ports werden über separate Register zum Setzen (GPIO Pin Output Set
Registers) und Rücksetzen (GPIO Pin Output Clear Registers) angesprochen
[BCM2835 ARM Peripherals,§6]: "Separating the set and clear functions
removes the need for read-modify-write operations." Einer Ansteuerung
aus unterschiedlichen Threads/Prozessen sollte technisch nichts entgegen
stehen.
Versuche das Problem zu isolieren. / Zeige ein Minimalprogramm, das
anderen ermöglicht, dein Problem zu reproduzieren. / Poste direkt im
raspberry.org Forum wo du wahrscheinlich mehr Feedback bekommst.
Edit: BCM2835
Vielleicht liegt der Hase ganz woanders im Pfeffer.
Python führt Threads nicht parallel aus, sondern immer nur genau einen.
Das Scheduling der Threads ist darauf optimiert, mehrere I/O-abhängige
Aufgaben nebenläufig abzuarbeiten.
Das Schlag- und Suchwort zum Thema ist "GIL" (Global Interpreter Lock)
und die Diskussion darum, ob und wie man dieses Lock loswerden
kann/soll/muß wird mit viel emotionalem Einsatz geführt.
Wenn es also Python sein soll, dann kann man entweder selbst die beiden
Aufgaben schedulen, oder man nimmt zwei Python-Prozesse.
Was ist denn daran gut, fragt man sich? Wenn man eine C-Bbliothek
schreibt, die von Python aufgerufen werden soll, dann kann nur ein
Thread in der C-Bibliothek sein und man muß sich um Race-Conditions etc.
keine Gedanken machen. Das heißt aber auch, daß ein Call in eine
C-Bibliothek durch Thread A verhindert, daß Thread B gescheduled werden
kann. Das trifft hier auf alle Calls der GPIO-Bibliothek zu - und
hoffentlich nicht auf time.sleep(), da solltest Du mal schauen, ob man
explizit den Current Thread schlafen legen kann, damit der andere zum
Zuge kommt.
ManuH schrieb:> def MotorSteuerung1(MotorSchritteWeg, iDelay, Drehrichtung):> print("Thread 1 opened")> print(GPIO.getmode())>> GPIO.setup(15, GPIO.OUT)> GPIO.setup(8, GPIO.OUT)
Dieses Setup sollte besser in main() passieren.
> while True:> if Drehrichtung == 1:> GPIO.output(8, GPIO.HIGH)> time.sleep((iDelay / 1000))> GPIO.output(8, GPIO.LOW)> time.sleep((iDelay / 1000))> print("1")
Dieses print dauert lang und hat evtl. Einfluß auf das
Thread-Scheduling.
> elif (Drehrichtung == 2):> GPIO.output(18, GPIO.HIGH)> time.sleep((iDelay / 1000))> GPIO.output(18, GPIO.LOW)
Hier fehlt das Delay.
Die beiden Funktionen sind ja mehr oder weniger identisch. Die
Pin-Nummern könnten Parameter werden, dann bräuchte man nur eine.
time.sleep() scheint ok zu sein:
https://docs.python.org/3.5/library/time.html#time.sleep
Ich bin mir nicht ganz sicher wie die Sache mit dem GPIO scheduling
gemeint war, ist der Zugriff auf die GPIO's begrenzt (es kann also nur
ein Thread gleichzeitig darauf zugreifen) oder eben nicht ? Falls ja
könnte es ja sein dass der zweite Thread seine angegebene Delayzeit
wartet, in der Zeit allerdings nicht auf die GPIO's zugreifen kann da
sie bereits verwendet werden und sich dann nach ablauf der Delayzeit
verabschiedet ohne wirklich etwas gemacht zu haben ( das würde zumindest
das Phänomen erklären warum immer nur ein Pin gesetzt wird).
Mit vielen Grüßen
Manuel
ManuH schrieb:> ...das Problem besteht aber leider weiterhin.
Wie sind die Pins physisch beschaltet?
Wie stellst du das Problem fest? Loggst du mit einem anderen Programm
den Pin Level? Hast du einen Logikanalyzer angeschlossen?
Dir ist der Unterschied zwischen GPIO.setmode(GPIO.BOARD) und
GPIO.setmode(GPIO.BCM) bewußt?
Ich messe die Pins mit einem Mulimeter von Hand aus (darum auch der
zweite Delay damit die LOW Zeiten auch messbar sind). Wenn ich Nachmesse
lösche ich den Teiler von 1000 dann liegt das High und das Low Signal
jeweils 5 sekunden an und können gemessen werden.
1
GPIO.output(Pin, GPIO.HIGH)
2
time.sleep((iDelay ))
3
GPIO.output(Pin, GPIO.LOW)
4
time.sleep((iDelay ))
An einem der Pins funktioniert es mit dieser Methode auch.
Auf das BOARD Layout habe ich auch geachtet und habe mich dabei an
diesem Pinout orientiert.
https://de.pinout.xyz/#
Mit vielen Grüßen
Manuel
Problem gelöst, mir war der Unterschied zwischen BOARD und BCM Layout
zwar bewusst allerdings habe Ich ihre Bedeutung vertauscht.
Vielen Dank für den Hinweiß zu diesem Thema und auch die allgemeinen
Tipps zum Aufbau.
Mit vielen Grüßen
Manuel