mikrocontroller.net

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


Autor: Stevo (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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;
}

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Benjamin S. (recycler)
Datum:

Bewertung
0 lesenswert
nicht 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:
    usb_bulk_setup_async(this->fd_dev, &context0, EP_IN);
    usb_submit_async(context0, this->buf, sizeof(this->buf));
#include "allmyincludes_usb.h"


struct usb_bus *bus;
struct usb_device *dev;


function open()
 {
  for(bus = usb_get_busses(); bus; bus = bus->next)
   {
    for(dev = bus->devices; dev; dev = dev->next)
     {
      printf("USB device found [%s][%ld]: %s %d\r\n", bus->dirname, bus->location, dev->filename, dev->devnum);
   }
   }

  printf("Enumeration of Devices done\r\n");

  for(bus = usb_get_busses(); bus; bus = bus->next)
   {
    for(dev = bus->devices; dev; dev = dev->next)
     {
      if(dev->descriptor.idVendor == MY_VID && dev->descriptor.idProduct == MY_PID)
       {
        printf("\r\nDevice found\r\n");
      return usb_open(dev);
       }
     }
   }
  return NULL;
}

int main()
 {

  usb_init();     /* initialize the library */
  usb_find_busses();   /* find all busses */
  usb_find_devices(); /* find all connected devices */





  if(!(fd_dev = open()))
   {
    printf("Error: device not found!\r\n");
    return(false);
   }

  if(usb_set_configuration(fd_dev, 1) < 0)
   {
    printf("Error: setting config 0 failed\n");
    printf("Error: %s\r\n", usb_strerror());
    usb_close(this->fd_dev);
    return(false);
   }

  if(usb_claim_interface(fd_dev, 0) < 0)
    {
    }


// als thread ausführen


  while(this->isrunning)
   {
    usb_bulk_setup_async(this->fd_dev, &context0, EP_IN);
    usb_submit_async(context0, this->buf, sizeof(this->buf));

    bytesread = 0;

    bytesread = usb_reap_async(context0, 500);

    if(bytesread < 0)
     {
      if(bytesread != -116) // nothing read
       {
        printf("code: %d error:%s\n", bytesread, usb_strerror());
       }
     }
    else
     {
      printf("%d bytes read\n\r", bytesread);
     }
   }
 }


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!

Autor: Stevo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

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

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Benjamin S. (recycler)
Datum:

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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.