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
:
Bearbeitet durch User
Vielen Dank für den Hinweiß Mikro 77, hier eine auf die Grundfunktion beschränkte variante meines Programmes :
1 | #!/usr/bin/python
|
2 | # -*- coding: latin-1 -*-
|
3 | import os, sys |
4 | import RPi.GPIO as GPIO |
5 | import time |
6 | from threading import Thread |
7 | GPIO.setmode(GPIO.BOARD) |
8 | |
9 | def main () : |
10 | |
11 | try : |
12 | def MotorSteuerung1(MotorSchritteWeg, iDelay, Drehrichtung): |
13 | print("Thread 1 opened") |
14 | print(GPIO.getmode()) |
15 | |
16 | GPIO.setup(15, GPIO.OUT) |
17 | GPIO.setup(8, GPIO.OUT) |
18 | |
19 | |
20 | while True: |
21 | |
22 | if Drehrichtung == 1: |
23 | |
24 | |
25 | GPIO.output(8, GPIO.HIGH) |
26 | time.sleep((iDelay / 1000)) |
27 | GPIO.output(8, GPIO.LOW) |
28 | time.sleep((iDelay / 1000)) |
29 | print("1") |
30 | |
31 | elif (Drehrichtung == 2): |
32 | |
33 | |
34 | GPIO.output(18, GPIO.HIGH) |
35 | time.sleep((iDelay / 1000)) |
36 | GPIO.output(18, GPIO.LOW) |
37 | print("2") |
38 | |
39 | |
40 | def MotorSteuerung2(MotorSchritteWeg, iDelay, Drehrichtung): |
41 | |
42 | print("Thread 2 opened") |
43 | print(GPIO.getmode()) |
44 | |
45 | GPIO.setup(23, GPIO.OUT) |
46 | GPIO.setup(24, GPIO.OUT) |
47 | |
48 | while True: |
49 | |
50 | if Drehrichtung == 1: |
51 | |
52 | |
53 | GPIO.output(24, GPIO.HIGH) |
54 | time.sleep((iDelay / 1000)) |
55 | GPIO.output(24, GPIO.LOW) |
56 | time.sleep((iDelay / 1000)) |
57 | |
58 | elif (Drehrichtung == 2): |
59 | |
60 | |
61 | GPIO.output(24, GPIO.HIGH) |
62 | time.sleep((iDelay / 1000)) |
63 | GPIO.output(24, GPIO.LOW) |
64 | |
65 | iMotorSchritteWeg = 1.8 |
66 | iDelay = 5 |
67 | iDrehrichtung = 1 |
68 | |
69 | t1 = Thread(target=MotorSteuerung1, args=(iMotorSchritteWeg, iDelay, iDrehrichtung)) |
70 | t2 = Thread(target=MotorSteuerung2, args=(iMotorSchritteWeg, iDelay , (iDrehrichtung))) |
71 | t1.start() |
72 | t2.start() |
73 | |
74 | t1.join() |
75 | t2.join() |
76 | |
77 | except KeyboardInterrupt : |
78 | print ("Execution Interrupted") |
79 | |
80 | finally: |
81 | GPIO.cleanup() |
82 | |
83 | if __name__ == '__main__' : |
84 | main () |
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.
:
Bearbeitet durch User
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
:
Bearbeitet durch User
Vielen dank für die Verbesserungsvorschläge, ich habe das Programm entsprechend angepasst das Problem besteht aber leider weiterhin.
1 | #!/usr/bin/python
|
2 | # -*- coding: latin-1 -*-
|
3 | import os, sys |
4 | import RPi.GPIO as GPIO |
5 | import time |
6 | from threading import Thread |
7 | GPIO.setmode(GPIO.BOARD) |
8 | |
9 | def main () : |
10 | GPIO.setup(8, GPIO.OUT) |
11 | GPIO.setup(24, GPIO.OUT) |
12 | GPIO.setup(23, GPIO.OUT) |
13 | GPIO.setup(10, GPIO.OUT) |
14 | |
15 | try : |
16 | def MotorSteuerung(MotorSchritteWeg, iDelay, Drehrichtung, Pin): |
17 | print("Thread 1 opened") |
18 | print(GPIO.getmode()) |
19 | |
20 | |
21 | while True: |
22 | |
23 | if Drehrichtung == 1: |
24 | |
25 | |
26 | GPIO.output(Pin, GPIO.HIGH) |
27 | time.sleep((iDelay / 1000)) |
28 | GPIO.output(Pin, GPIO.LOW) |
29 | time.sleep((iDelay / 1000)) |
30 | |
31 | |
32 | elif (Drehrichtung == 2): |
33 | # GPIO.output(16, GPIO.LOW)
|
34 | |
35 | GPIO.output(Pin, GPIO.HIGH) |
36 | time.sleep((iDelay / 1000)) |
37 | GPIO.output(Pin, GPIO.LOW) |
38 | time.sleep((iDelay / 1000)) |
39 | |
40 | |
41 | iMotorSchritteWeg = 1.8 |
42 | iDelay = 5 |
43 | iDrehrichtung = 1 |
44 | Pin_1 = 8 |
45 | Pin_2 = 24 |
46 | |
47 | t1 = Thread(target=MotorSteuerung, args=(iMotorSchritteWeg, iDelay, iDrehrichtung, Pin_1)) |
48 | t2 = Thread(target=MotorSteuerung, args=(iMotorSchritteWeg, iDelay , (iDrehrichtung), Pin_2)) |
49 | t1.start() |
50 | t2.start() |
51 | |
52 | t1.join() |
53 | t2.join() |
54 | |
55 | except KeyboardInterrupt : |
56 | print ("Execution Interrupted") |
57 | |
58 | finally: |
59 | GPIO.cleanup() |
60 | |
61 | if __name__ == '__main__' : |
62 | main () |
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
Hast du das Programm nur mit einem Thread laufen lassen. Also nur für Pin-8. Ergebnis ok? Dann nur für Pin-24. Ergebnis ok?
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.