2009-06-29 9 views
4

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:

  1. Allocation: allouer une libusb_transfer (Ceci est self.transfer)
  2. Remplissage: remplir l'instance libusb_transfer avec des informations sur le transfert vous souhaitez effectuer (libusb_fill_bulk_transfer)
  3. présentation: demander libusb de soumettre le transfert (libusb_submit_transfer)
  4. 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)
  5. 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 

Répondre

1

Ok Je ne sais pas si je vous comprends bien. Vous avez un appareil avec écran LCD, vous avez un firmware pour gérer les demandes USB. Du côté du PC, vous utilisez PyUSB qui enveloppe libUsb. Quelques suggestions si vous expérimentez des problèmes de vitesse, essayez de limiter les données que vous transférez. Ne transférez pas les données brutes entières, seulement les pixels qui ont changé. Deuxièmement, avez-vous mesuré la vitesse des transferts en utilisant un logiciel USB d'évaluation, si vous n'avez pas d'argent pour l'analyseur usb hardvare, essayez peut-être la version du logiciel. Je n'ai jamais utilisé ce genre d'analyseurs, mais je pense que les données fournies par eux ne sont pas très fiables. Troisièmement, voir ce que l'appareil est en train de faire, peut-être que c'est le goulot d'étranglement de vos transferts de données.

Je n'ai pas beaucoup de temps aujourd'hui pour répondre exactement à votre question, je vais y revenir plus tard. Je regarde ce fil depuis un certain temps, et il y a un silence de mort autour de cela, alors j'ai essayé d'épargner du temps et de regarder plus en profondeur. Encore peu de temps aujourd'hui peut-être plus tard aujourd'hui. Malheureusement, je ne suis pas un expert en python, mais je connais quelques trucs sur C, C++, Windows et surtout USB. Mais je pense que cela peut être problème d'appareil LCD, qu'utilisez-vous, Parce que si les transferts fonctionne bien, et les données ont été récupérées par l'appareil, il indique que c'est un problème de périphérique.

J'ai regardé votre code un peu, pourriez-vous faire quelques tests, en envoyant seulement 1 octet, 8 octets, et le transfert de longueur d'octet de taille de point final. Et voir à quoi cela ressemble sur USB mon?

La taille du point d'extrémité correspond à la taille du tampon Hardvare utilisé par le contrôleur PICO LCD USB. Je ne suis pas sûr de ce que c'est pour votre mais je devine que lorsque vous envoyez ENdpoint taille message prochain masage devrait être 0 octets de longueur. Peut-être qu'il y a le problème. En ce qui concerne le test, je suppose que vous avez vu les données que vous avez programmé pour envoyer. La deuxième chose pourrait être que les données sont écrasées, ou pas assez rapidement. Dire écrasé je veux dire LCD ne pouvait pas voir la fin des données, et mélanger un transfert avec un autre. Je ne suis pas sûr de ce que l'USB mon est capable de montrer, mais selon le standard USB après le paquet de taille de point de terminaison, il devrait y avoir 0 paquets de données envoyées, montrant que c'est la fin du transfert.

+0

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

+0

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

+0

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