Forum: Mikrocontroller und Digitale Elektronik Libusb usb_bulk_read "echtzeitfähig"?


von Stevo (Gast)


Lesenswert?

Hallo,

ich versuche mit der usb_bulk_read Funktion von libusb Daten auf meinen 
Rechner von einem USB-Controller zu übertragen.
Der Transfer funktioniert zwar, jedoch scheint es so, als ob die 
Usb_bulk_read funktion nicht wieder schnell genug aufgerufen wird, da 
Daten (welche kontinulierlich vom Controller versendet werden) zwischen 
dem mehrmaligen aufrufen verloren gehen.(also ein Neuaufruf immer wenn 
das Buffer-Array voll ist).
Macht man das Array jedoch "unendlich " groß gehen während dem Lesen der 
64Byte Blöcke vom Controller keine Daten verloren - an Bandbreite kann 
es also nicht mangeln.

Daher meine Frage: Ist diese Funktion überhaupt geeignet um eine quasi 
Echtzeitkommunikation zu ermöglichen (der Endpoint sollte alle 128µs 
voll sein).
Falls nicht, gibt es andere freie Bibliotheken?



Anbei der Quellcode (Ein Thread holt die Daten, der andere Schreibt sie 
in ein Datei.

//SOURCE CODE


#include <time.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <windows.h>
#include "usb.h"




#define AVR_VID 0x03eb
#define AVR_PID 0x2022
#define EP_IN 0x81   //in Firmware definiert
#define EP_OUT 0x02 //in Firmware definiert

#define BUF_SIZE 64*16
#define BUF_IN_SIZE 64*8

FILE *datei;
static  char wert[BUF_SIZE];
static  char *pread=wert;
static  char *pwrite=wert;
static  char *start=wert;
 int byteread=0;
 int bytewritten=0;
 int durch1=0;
 float durch2=0;
int w1=0;
clock_t clock();
long t1=clock();
long t2=clock();
long t3=clock();

static int count=0;


DWORD thread0id=0;
DWORD thread1id=1;

static HANDLE  hThread[2];

CRITICAL_SECTION cs;



usb_dev_handle *open_dev(void)
{
  struct usb_bus *bus;
  struct usb_device *dev;

  for(bus = usb_get_busses(); bus; bus = bus->next)   //alle vorhandenn 
Busse durchgehen...
    {
      for(dev = bus->devices; dev; dev = dev->next)   //alle Geräte 
durchgehen...
        {
          if(dev->descriptor.idVendor == AVR_VID      //überprüfe 
aktuelles Gerät unserer VID/PID entspricht
             && dev->descriptor.idProduct == AVR_PID)
            {
              return usb_open(dev);                   //Gerät öffnen 
falls erfolgreich
            }
        }
    }
  return NULL;
}




DWORD WINAPI readthread(LPVOID lpParam)
{
     int i;
  usb_dev_handle *dev = NULL;
  usb_init();
  usb_find_busses();
  usb_find_devices();




  if(!(dev = open_dev()))                   //überprüfen ob Gerät 
gefunden wurde

      {printf(">> AVR USB KEY nicht gefunden\n\n");
      system("PAUSE");
      return 0;
      }
    else {printf(">> AVR USB KEY gefunden\n\n");}



  if(usb_set_configuration(dev, 1) < 0)  //1. Konfiguration auswählen 
(gibt nur 0 bei erfolg zurück, sonst <0,  hier nur eine Konfiguration
    {
      printf(">> error: Konfiguration konnte nicht geladen werden\n\n");
      usb_close(dev);
      system("PAUSE");
      return 0;
    }

  if(usb_claim_interface(dev, 0) < 0)
    {
      printf(">> error: Interface 0 anfordern fehlgeschlagen\n\n");
      usb_close(dev);
      system("PAUSE");
      return 0;
    }


while(1)
{

       durch1++;
       int byte_empfangen;
       char buffer_in[BUF_IN_SIZE];
       usb_bulk_read(dev, EP_IN, buffer_in, sizeof(buffer_in),20);

      if ((byte_empfangen)!= sizeof(buffer_in))
         {
          printf(">> error: bulk read timeout\n Error: returned: 
%i\n",byte_empfangen);
          system("PAUSE");
          return 0;
         }
*/

EnterCriticalSection(&cs);
         memcpy(pread,buffer_in,BUF_IN_SIZE);
LeaveCriticalSection(&cs);
        free(buffer_in);
         if ((pread-start) >= BUF_SIZE)
         {
EnterCriticalSection(&cs);
         pread=start; // Falls Buffer voll -> von Anfang an beschreiben
LeaveCriticalSection(&cs);
         }
         else
         {
EnterCriticalSection(&cs);
          pread+=BUF_IN_SIZE; //Lesezeiger verschieben
LeaveCriticalSection(&cs);
          byteread+=BUF_IN_SIZE;
         }

         if ( (pread+64)==pwrite )
         {    printf("Buffer überschrieben! !");
         }


 }
 return 1;
}


DWORD WINAPI writethread(LPVOID lpParam)
{


     int i=0;
     int tmp,tmp2;
while(1)
  {
durch2++;


      if(pread!=pwrite)
        {
         if ( (pwrite-wert)>=BUF_SIZE)
              {
EnterCriticalSection(&cs);
               pwrite=start;
LeaveCriticalSection(&cs);
              }
         else
         { //Anschlüsse auf Board verkehrt herum -> Wert drehen
                        tmp=0x00;
                        tmp|=0x80&( (*pwrite)<<7);
                      tmp|=0x40&( (*pwrite)<<5);
                      tmp|=0x20&( (*pwrite)<<3);
                      tmp|=0x10&( (*pwrite)<<1);
                      tmp|=0x08&( (*pwrite)>>1);
                      tmp|=0x04&( (*pwrite)>>3);
                      tmp|=0x02&( (*pwrite)>>5);
                      tmp|=0x01&( (*pwrite)>>7);
EnterCriticalSection(&cs);
                        pwrite++;
LeaveCriticalSection(&cs);
                        tmp|=(*pwrite<<8);
                        if(i>=64) {i=0;}

                    // 
fprintf(datei,"%i:\t%i\t%i\n",i,tmp,tmp-tmp2-1);
                      // fprintf(datei,"%i\n",tmp);

                        i++;
                        bytewritten+=2;
EnterCriticalSection(&cs);
                        pwrite++;
LeaveCriticalSection(&cs);

                       fprintf(datei,"%i",tmp);
                                               tmp2=tmp;
          }


       }

}

  return 0;

}



int main(void)
{

datei=fopen("daten.txt","w+");
if (!InitializeCriticalSectionAndSpinCount(&cs,0x80000400) ) return 0;
hThread[0]=CreateThread(NULL,0,readthread,0,0,&thread0id);
hThread[1]=CreateThread(NULL,0,writethread,0,0,&thread1id);
SetThreadPriority(hThread[0], 31) 
;
WaitForMultipleObjects(2,hThread,TRUE,2000);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
DeleteCriticalSection(&cs);
if (fclose(datei)!=0) printf("Fehler fclose()");






  return 0;
}

von Christian R. (supachris)


Lesenswert?

USB ist nicht echtzeitfähig, da ist es die LibUSB und alle anderen 
Treiber natürlich auch nicht. Wieso geht da was verloren? Wo ist denn 
die Fluss-Steuerung auf der Controllerseite? Die muss doch warten, bis 
der Puffer wieder gefüllt werden kann. Wir verwenden ja hier den FX2 von 
Cypress in unseren Designs, hohe Geschwindigkeiten und niedrige Latenzen 
sind nur mit großem Puffer vor dem USB Controller zu schaffen. Wir 
kommen auf kontinuierliche 40MB/s, mit 256kByte FIFO auf der 
Controllerseite und dem Anfordern großer Blöcke. Wenn du immer nur so 
kleine Stücke anforderst, erreichst du nix, der Kernel ist dann viel zu 
sehr mit dem Anfragen beschäftigt.

von Benjamin S. (recycler)


Lesenswert?

Hi,
ich hab mir deinen Code nicht ganz durchgelesen, aber ich weiß was du 
willst. Es gibt halbdokumentierte Funktionen, die das Lesen übernehemen.

es sind die Funktionen:
1
    usb_bulk_setup_async(this->fd_dev, &context0, EP_IN);
2
    usb_submit_async(context0, this->buf, sizeof(this->buf));
1
#include "allmyincludes_usb.h"
2
3
4
struct usb_bus *bus;
5
struct usb_device *dev;
6
7
8
function open()
9
 {
10
  for(bus = usb_get_busses(); bus; bus = bus->next)
11
   {
12
    for(dev = bus->devices; dev; dev = dev->next)
13
     {
14
      printf("USB device found [%s][%ld]: %s %d\r\n", bus->dirname, bus->location, dev->filename, dev->devnum);
15
   }
16
   }
17
18
  printf("Enumeration of Devices done\r\n");
19
20
  for(bus = usb_get_busses(); bus; bus = bus->next)
21
   {
22
    for(dev = bus->devices; dev; dev = dev->next)
23
     {
24
      if(dev->descriptor.idVendor == MY_VID && dev->descriptor.idProduct == MY_PID)
25
       {
26
        printf("\r\nDevice found\r\n");
27
      return usb_open(dev);
28
       }
29
     }
30
   }
31
  return NULL;
32
}
33
34
int main()
35
 {
36
37
  usb_init();     /* initialize the library */
38
  usb_find_busses();   /* find all busses */
39
  usb_find_devices(); /* find all connected devices */
40
41
42
43
44
45
  if(!(fd_dev = open()))
46
   {
47
    printf("Error: device not found!\r\n");
48
    return(false);
49
   }
50
51
  if(usb_set_configuration(fd_dev, 1) < 0)
52
   {
53
    printf("Error: setting config 0 failed\n");
54
    printf("Error: %s\r\n", usb_strerror());
55
    usb_close(this->fd_dev);
56
    return(false);
57
   }
58
59
  if(usb_claim_interface(fd_dev, 0) < 0)
60
    {
61
    }
62
63
64
// als thread ausführen
65
66
67
  while(this->isrunning)
68
   {
69
    usb_bulk_setup_async(this->fd_dev, &context0, EP_IN);
70
    usb_submit_async(context0, this->buf, sizeof(this->buf));
71
72
    bytesread = 0;
73
74
    bytesread = usb_reap_async(context0, 500);
75
76
    if(bytesread < 0)
77
     {
78
      if(bytesread != -116) // nothing read
79
       {
80
        printf("code: %d error:%s\n", bytesread, usb_strerror());
81
       }
82
     }
83
    else
84
     {
85
      printf("%d bytes read\n\r", bytesread);
86
     }
87
   }
88
 }

Im Thread kannst du die Daten dann weiterschicken. Habe ich für meine 
Diplomarbeit verwendet. Wenn dann melde dich.

Gruß Benjamin

PS.: Der Code ist zusammenkopiert, alle Variablen müssen noch deklariert 
werden und es kann sein, dass C++ Code drinnen ist!

von Stevo (Gast)


Lesenswert?

Hallo,

danke für die Antworten. Ich habe nun die undokumentierten Funktionen 
eingebau... Leider hat sich an dem Problem nichts geändert :(

von Christian R. (supachris)


Lesenswert?

Naja, ob du nun synchron oder asynchron liest, ändert nix an der 
Tatsache, dass du auf Controller-Seite eine Fluss-Steuerung benötigst, 
wenn keine Daten verloren gehen dürfen. Du musst abfragen, ob der Buffer 
geleert wurde, und kannst erst dann neue Daten schreiben. Wenn du eine 
konstante Datenrate benötigst, musst du eventuell mal auf isochronen 
Datentransfer gehen, da wird die Bandbreite garantiert, allerdings gibts 
keine Wiederholungen, falls wirklich mal was verloren geht. Konstante 
Datenrate bei BULK geht nur mit riesigen Puffern, denn BULK ist am 
niedrigsten priorisiert, das findet nur statt, wenn sonst nix auf dem 
USB los ist. Wir haben Messungen gemacht, und herausgefunden, dass 
Windows XP auf einem aktuellen Rechner bis zu 30ms lang einfach mal gar 
nix von USB abholt....diese Zeit musst du durch Puffer überbrücken 
können.

von Benjamin S. (recycler)


Lesenswert?

Wie groß ist dein Puffer auf der Clientseite also dem Slave. Der sollte 
so groß sein, wie der am Host.

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
Noch kein Account? Hier anmelden.