Seite 1 von 1
Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Sonntag 26. Dezember 2021, 08:46
von kaytec
Hallo,
kann man mit der seriellen Schnittstelle/USB auch ohne queue kommunizieren, wenn auf die GUI verzichtet wird ?
ohne queue :
Code: Alles auswählen
#/usr/bin/env python
# -*- coding: utf-8
import time
from threading import Thread, Event
from pylibftdi import BitBangDevice
class ToggleLed(object):
def __init__(self):
self.run_event = None
def __enter__(self):
return self
def __exit__(self, *args):
self.release()
def stop(self):
self.run_event.set()
def toggle(self, port):
self.port = port
def off(self):
self.port = self.port_off
def start(self):
self.run_event = Event()
self.thread = Thread(target=self.worker_thread)
self.thread.daemon = True
self.thread.start()
def worker_thread(self):
with BitBangDevice() as bb:
while not self.run_event.is_set():
bb.port = self.port
def release(self):
if self.run_event:
self.stop()
def main():
with ToggleLed() as toggle_led:
toggle_led.start()
while True:
toggle_led.toggle(0)
time.sleep(0.5)
toggle_led.toggle(1)
time.sleep(0.5)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print("KeyboardInterrupt")
mit queue:
Code: Alles auswählen
#/usr/bin/env python
# -*- coding: utf-8
import time
from threading import Thread, Event
from pylibftdi import BitBangDevice
import queue
class ToggleLed(object):
def __init__(self, queue):
self.queue = queue
self.run_event = None
def __enter__(self):
return self
def __exit__(self, *args):
self.release()
def stop(self):
self.run_event.set()
def start(self):
self.run_event = Event()
self.thread = Thread(target=self.worker_thread)
self.thread.daemon = True
self.thread.start()
def worker_thread(self):
with BitBangDevice() as bb:
while not self.run_event.is_set():
if not self.queue.empty():
while True:
try:
bb.port = self.queue.get_nowait()
except queue.Empty:
break
def release(self):
if self.run_event:
self.stop()
def main(queue):
queue = queue.Queue()
with ToggleLed(queue) as toggle_led:
toggle_led.start()
while True:
time.sleep(0.5)
queue.put(0)
time.sleep(0.5)
queue.put(1)
if __name__ == '__main__':
try:
main(queue)
except KeyboardInterrupt:
print("KeyboardInterrupt")
Gruß Frank
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Sonntag 26. Dezember 2021, 09:23
von __deets__
Klar kann man. Was ich nicht verstehe ist der extra Thread im Queue-losen Beispiel. Der ballert 100% der Rechenzeit raus, und ist doch unnötig.
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Sonntag 26. Dezember 2021, 10:26
von kaytec
Danke deets ,
ohne GUI :
Code: Alles auswählen
#/usr/bin/env python
# -*- coding: utf-8
import time
from pylibftdi import BitBangDevice
def main():
with BitBangDevice() as bb:
while True:
bb.port = 0
time.sleep(0.5)
bb.port = 1
time.sleep(0.5)
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print("KeyboardInterrupt")
Alle Versionen funktionieren bei mir ohne merkliche Probleme bei der Leistung und so poste ich alle meine Versuche, da ohne Code die Fragen immer sehr oberflächlich bleiben. Welche Version richtig oder falsch ist ...?
Gruß Frank
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Sonntag 26. Dezember 2021, 10:35
von __deets__
Extra Aufwand, ohne erkennbaren Nutzen, ist tendenziell falsch. Mehr Code heißt mehr Fehlerquelle, mehr CPU heisst mehr Anfälligkeit wenn die anderweitig angefordert wird.
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Montag 27. Dezember 2021, 10:00
von kaytec
Eine einfache Pulweitenmodulation erfordert erfordert doch schon diese Version ?
Code: Alles auswählen
#/usr/bin/env ^
# -*- coding: utf-8
import time
from threading import Thread, Event
from pylibftdi import BitBangDevice
class PwmLed(object):
HERZ50 = 20
def __init__(self):
self.run_event = None
self.width = 0
def __enter__(self):
return self
def __exit__(self, *args):
self.release()
def stop(self):
self.run_event.set()
def set_pwm(self, width):
if width >= 0 and width <= self.HERZ50:
self.width = width
def start(self):
self.run_event = Event()
self.thread = Thread(target = self.worker_thread)
self.thread.daemon = True
self.thread.start()
def worker_thread(self):
with BitBangDevice() as bb:
while not self.run_event.is_set():
for port, t in ((1, 0.001 * self.width),
(0, 0.001 * (self.HERZ50 - self.width))):
bb.port = port
time.sleep(t)
def release(self):
if self.run_event:
self.stop()
with PwmLed() as pwm_led:
pwm_led.start()
while True:
for step in range(20):
time.sleep(0.2)
pwm_led.set_pwm(step)
for step in range(20, 0, -1):
time.sleep(0.2)
pwm_led.set_pwm(step)
Gruß Frank
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Montag 27. Dezember 2021, 11:14
von __deets__
Ja, allerdings wird die in Bezug auf Wiederholgenauigkeit katastrophal sein. Für sowas ist weder Python noch USB und dein OS gedacht. Darum würde ich das nie so machen, sondern dedizierte Hardware dafür verwenden.
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Montag 27. Dezember 2021, 11:45
von __blackjack__
@kaytec: `run_event` ist irritierend weil das genau das Gegenteil ist: ein Ereignis das gesetzt wird, wenn gestoppt werden soll.
Dass das nicht in der `__init__()` erstellt wird, macht den Code unnötig kompliziert weil man eine `stop()`-Methode hat die auf die hart auf die Nase fällt wenn vorher nicht `start()` aufgerufen wurde, und deshalb eine zusätzliche `release()`-Methode da ist.
Das `thread`-Attribut wird ausserhalb der `__init__()` eingeführt und der Code kommt nicht damit klar wenn `start()` mehr als einmal aufgerufen wird.
Durch das setzen von `daemon` auf `True` wird der Thread hart mittendrin beendet. Damit greift das ``with`` für das `BitBangDevice` nicht mehr. Man sollte also beim stoppen explizit auf das Ende des Threads warten.
Ungetestet:
Code: Alles auswählen
#/usr/bin/env python3
import time
from threading import Event, Thread
from pylibftdi import BitBangDevice
class PwmLed:
HERZ_50 = 20
def __init__(self):
self._width = 0
self.run_event = Event()
self.thread = None
def __enter__(self):
return self
def __exit__(self, *args):
self.stop()
@property
def width(self):
return self._width
@width.setter
def width(self, value):
if not 0 <= value <= self.HERZ_50:
raise ValueError(f"value must be in 0..{self.HERZ_50}")
self._width = value
def stop(self):
self.run_event.set()
if self.thread:
self.thread.join()
self.thread = None
def start(self):
if not self.thread:
self.run_event.clear()
self.thread = Thread(target=self._worker_thread, daemon=True)
self.thread.start()
def _worker_thread(self):
with BitBangDevice() as bit_bang_device:
while not self.run_event.is_set():
for state, delay in [
(1, 0.001 * self.width),
(0, 0.001 * (self.HERZ_50 - self.width)),
]:
bit_bang_device.port = state
time.sleep(delay)
def main():
with PwmLed() as pwm_led:
pwm_led.start()
while True:
for step in range(20):
time.sleep(0.2)
pwm_led.width = step
for step in range(20, 0, -1):
time.sleep(0.2)
pwm_led.width = step
if __name__ == "__main__":
main()
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Montag 27. Dezember 2021, 15:16
von kaytec
Hallo deets,
ist nur eine Spielerei, doch man hat ja acht Ein/Ausgänge und ein Kran aus Fischertechnik könnte damit angesteuert werden. Schöner Spaß für meine Kinder ! Mein selbstgebasteltes 2-Kanalsoundkartenoszilloskop zeigt mal die Frequenz von 49Hz an. Wird genauso ungenau sein. Möchte der Elektroniker Genauigkeit, dann greift auch er auf die Mechanik zurück.
Gruß Frank
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Montag 27. Dezember 2021, 18:25
von __deets__
Bei 8 Ausgängen werden die Ungenauigkeiten sich allerdings aufaddieren. Denn dann musst du ja die potentiell 8 verschiedenen Zeitpunkte mit ggf kaum unterschiedlichen Zykluszeiten abwarten.
Wenn’s dir reicht, dann kannst du das ja so machen. Nur nicht wundern, wenn es eben nicht reicht.
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Montag 27. Dezember 2021, 22:26
von kaytec
Hallo deets,
wenn man die Geschwindigkeit nicht über ein PWM-Signal steuert, sonder über die Untersetzung "drosselt", dann haben die alten F-Technikmotoren auch genug Kraft. So würde man pro Motor zwei Leitungen für eine H-Brücke brauchen. Auf eine Encoderscheibe könnte auch verzichtet werden, da es ja auf Sicht bespielt wird. Falls es nicht klappt, dann ist es ja auch ok und man nimmt den guten alten Batteriestab. Es gibt tolle Hardware, doch mir langen solche Bastellösungen und diese passen auch besser zu meinen finanziellen Möglichkeiten.
Gruß Frank
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Montag 27. Dezember 2021, 22:37
von __deets__
Na „Hardware“ ist ein arduino für 5€. Oder ein PCA-irgendwas, der via I2C über dein FTDI angesteuert wird.
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Dienstag 28. Dezember 2021, 00:30
von kaytec
Hallo deets,
mehr als 5-10 Euro gebe ich nicht für Spielereien im Monat nicht aus und das gebrauchte Lernpaket hat schon 7 Euro gekostet. Da war der FDTI- Adapter drin und ich versuche die VB-Beispiele mit Python nachzubasteln. Ist für mich eine entspannende Beschäftigung ! Mit der beiliegenden Zusatzplatine lassen sich auch Microcontroller beschreiben. Damit bin ich bestimmt noch lange beschäftigt und falls ich es nicht hinbekomme, zerstöre etc. geht die Welt auch nicht unter.
Hallo BlackJack,
Danke für das fertige Script. Das mit dem“daemon“ habe ich noch nicht verstanden. Versuche ich mal nachzulesen.
Gruß Frank
Re: Kommunnizieren mit der seriellen Schnitstelle /USB
Verfasst: Dienstag 28. Dezember 2021, 09:55
von kaytec
deamon = True:
Das Hauptprogramme beendet die threads und sie laufen nach dem Beenden nicht im Hintergrund weiter.
thread.join():
Ordnet die threads und wartet bis alle beendet sind.
Warum werden sie geordnet ?
Class PwmLed:
Alle Klassen sind Objekte und die explizite Angabe ist nicht mehr notwendig.
@property und @width.setter macht die wichtigen Merkmale öffentlich und „erleichtern“ den Zugriff bzw. Veränderung.
Würde es mit Gui so aussehen ?
Code: Alles auswählen
#/usr/bin/env python3
import time
import tkinter as tk
import queue
from threading import Event, Thread
from pylibftdi import BitBangDevice
class PwmLed:
HERZ_50 = 20
def __init__(self, queue):
self.queue = queue
self._width = 0
self.run_event = Event()
self.thread = None
def __enter__(self):
return self
def __exit__(self, *args):
self.stop()
def stop(self):
self.run_event.set()
if self.thread:
self.thread.join()
self.thread = None
def start(self):
if not self.thread:
self.run_event.clear()
self.thread = Thread(target=self._worker_thread, daemon=True)
self.thread.start()
def _worker_thread(self):
with BitBangDevice() as bit_bang_device:
while not self.run_event.is_set():
if not self.queue.empty():
try:
self._width = self.queue.get_nowait()
if not 0 <= self._width <= self.HERZ_50:
raise ValueError(
f"value must be in 0..{self.HERZ_50}")
except queue.Empty:
break
else:
for state, delay in [
(1, 0.001 * self._width),
(0, 0.001 * (self.HERZ_50 - self._width)),
]:
bit_bang_device.port = state
time.sleep(delay)
class PwmLedUI(tk.LabelFrame):
def __init__(self,
parent,
queue,
pwm_led):
tk.LabelFrame.__init__(self,
parent,
text = "PWM-LED",
relief = "solid")
self.parent = parent
self.queue = queue
self.pwm_led = pwm_led
self.pwm_led.start()
self.rate_scale = tk.Scale(self, from_ = 0,
to = 20,
resolution = 1,
width = 10,
length = 400,
showvalue = 0,
orient = "horizontal",
relief = "flat",
command = self.set_pwm_width)
self.rate_scale.pack()
def set_pwm_width(self, rate):
pulse_width = self.rate_scale.get()
self.config(text = "PWM-LED: {0}".format(pulse_width))
self.queue.put(pulse_width)
def release(self):
self.pwm_led.stop()
self.parent.destroy()
def main(queue):
queue = queue.Queue()
root = tk.Tk()
root.title("PWM-LED")
root.resizable(0, 0)
with PwmLed(queue) as pwm_led:
pwm_led_ui = PwmLedUI(root, queue, pwm_led)
pwm_led_ui.pack(expand=tk.YES, padx = 10, pady = 10)
root.protocol("WM_DELETE_WINDOW",pwm_led_ui.release)
root.mainloop()
if __name__ == '__main__':
main(queue)