Mises à jour:USB - synchronisation vs vs async semi-async
J'ai écrit une version asynchrone C et il fonctionne comme il se doit.
Il s'avère que le problème de vitesse était dû à GIL de Python. Il existe une méthode pour affiner son comportement. sys.setcheckinterval (intervalle)
La définition de l'intervalle sur zéro (la valeur par défaut est 100) résout le problème de vitesse lente. Maintenant tout ce qui reste est de comprendre ce qui cause l'autre problème (tous les pixels ne sont pas remplis). Celui-ci n'a aucun sens. usbmon montre toutes les communications en cours. La messagerie de débogage de libusb ne montre rien d'extraordinaire. Je suppose que je dois prendre la sortie d'usbmon et comparer la synchronisation vs async. Les données affichées par usbmon semblent correctes en un coup d'œil (le premier octet devrait être 0x96 ou 0x95). Comme indiqué ci-dessous dans la question originale, S. Lott, c'est pour un contrôleur LCD USB. Il existe trois versions différentes de drv_send, qui est la méthode des points de terminaison sortants. J'ai expliqué les différences ci-dessous. Peut-être que cela aidera si je décris les opérations USB asynchrones. Notez que les opérations USB synchrones fonctionnent de la même manière, c'est juste que c'est fait de manière synchrone.
Nous pouvons voir E/S asynchrones en 5 étapes:
- Allocation: allouer une libusb_transfer (Ceci est self.transfer)
- Remplissage: remplir l'instance libusb_transfer avec des informations sur le transfert vous souhaitez effectuer (libusb_fill_bulk_transfer)
- présentation: demander libusb de soumettre le transfert (libusb_submit_transfer)
- manutention d'achèvement: examiner les résultats de transfert dans la structure de libusb_transfer (libusb_handle_events et libusb_handle_events_ délai d'attente)
- désallocation: nettoyer les ressources (Non indiqué ci-dessous)
question d'origine:
J'ai trois versions différentes. L'un est entièrement synchrone, l'autre semi-asynchrone, et le dernier est entièrement asynchrone. Les différences sont que synchrone remplit entièrement l'écran LCD que je contrôle avec les pixels attendus, et c'est vraiment rapide. La version semi-asynchrone ne remplit qu'une partie de l'affichage, mais elle est toujours très rapide. La version asynchrone est vraiment lente et ne remplit qu'une partie de l'affichage. Je suis déconcerté pourquoi les pixels ne sont pas entièrement remplis, et pourquoi la version asynchrone est vraiment lente. Des indices?
est ici la version complète synchrone:
def drv_send(self, data):
if not self.Connected():
return
self.drv_locked = True
buffer = ''
for c in data:
buffer = buffer + chr(c)
length = len(buffer)
out_buffer = cast(buffer, POINTER(c_ubyte))
libusb_fill_bulk_transfer(self.transfer, self.handle, LIBUSB_ENDPOINT_OUT + 1, out_buffer, length, self.cb_send_transfer, None, 0)
lib.libusb_submit_transfer(self.transfer)
while self.drv_locked:
r = lib.libusb_handle_events(None)
if r < 0:
if r == LIBUSB_ERROR_INTERRUPTED:
continue
lib.libusb_cancel_transfer(transfer)
while self.drv_locked:
if lib.libusb_handle_events(None) < 0:
break
self.count += 1
Voici la version semi-asynchrone:
def drv_send(self, data):
if not self.Connected():
return
def f(d):
self.drv_locked = True
buffer = ''
for c in data:
buffer = buffer + chr(c)
length = len(buffer)
out_buffer = cast(buffer, POINTER(c_ubyte))
libusb_fill_bulk_transfer(self.transfer, self.handle, LIBUSB_ENDPOINT_OUT + 1, out_buffer, length, self.cb_send_transfer, None, 0)
lib.libusb_submit_transfer(self.transfer)
while self.drv_locked:
r = lib.libusb_handle_events(None)
if r < 0:
if r == LIBUSB_ERROR_INTERRUPTED:
continue
lib.libusb_cancel_transfer(transfer)
while self.drv_locked:
if lib.libusb_handle_events(None) < 0:
break
self.count += 1
self.command_queue.put(Command(f, data))
Voici la version totalement asynchrone. device_poll est dans un thread par lui-même.
def device_poll(self):
while self.Connected():
tv = TIMEVAL(1, 0)
r = lib.libusb_handle_events_timeout(None, byref(tv))
if r < 0:
break
def drv_send(self, data):
if not self.Connected():
return
def f(d):
self.drv_locked = True
buffer = ''
for c in data:
buffer = buffer + chr(c)
length = len(buffer)
out_buffer = cast(buffer, POINTER(c_ubyte))
libusb_fill_bulk_transfer(self.transfer, self.handle, LIBUSB_ENDPOINT_OUT + 1, out_buffer, length, self.cb_send_transfer, None, 0)
lib.libusb_submit_transfer(self.transfer)
self.count += 1
self.command_queue.put(Command(f, data))
Et voici où la file d'attente est vidée. C'est le rappel pour un timeout de gobject.
def command_worker(self):
if self.drv_locked: # or time.time() - self.command_time < self.command_rate:
return True
try:
tmp = self.command_queue.get_nowait()
except Queue.Empty:
return True
tmp.func(*tmp.args)
self.command_time = time.time()
return True
Voici le rappel du transfert. Il change juste l'état verrouillé à false, indiquant que l'opération est terminée.
def cb_send_transfer(self, transfer):
if transfer[0].status.value != LIBUSB_TRANSFER_COMPLETED:
error("%s: transfer status %d" % (self.name, transfer.status))
print "cb_send_transfer", self.count
self.drv_locked = False
Eh bien, je n'utilise pas pyusb. Je suis en train d'encapsuler libusb-1.0 par le biais de ctypes Python car pyusb est basé sur libusb-0.1 qui ne supporte que les transferts synchrones. Deuxièmement, j'ai réglé le problème de la vitesse, mais en disant que je commence à me demander si cet autre problème n'est pas lié à la vitesse. Troisièmement, j'utilise usbmon pour analyser les transferts. Je ne suis pas sûr de savoir comment analyser la vitesse. Quoi qu'il en soit, bon de savoir que quelqu'un a finalement essayé de répondre à cela. :) – Scott
Eh bien, j'ai écrit une version C asynchrone comme suggéré par un libusb dev. Après quelques cheveux tirant je l'ai eu pour bien fonctionner. C'est un problème vraiment étrange. usbmon montre que les transferts sont en cours. Le rappel est appelé. Je ne suis pas sûr de ce que je peux faire d'autre à ce stade. – Scott
Je ne suis pas sûr de ce que vous entendez par "taille de point final". J'ai fait 1 et 8 octets, et ils sont tous les deux corrects. Les deux ont commencé avec 0x96, le transfert de 8 octets suivi de 7 zéros. – Scott