USB Verständnisproblem



  • Hallo zusammen,

    ich verwende zu Ansteuerung eines HIDevice die Windows DDK hid.lib Kommuniktaion mit dem Gerät klappt auch wunderbar. Da ich bei USB ja nicht direkt Nachrichten vom Device an den Host schicken kann , sondern pollen muss, steht ich jetzt bei folgender Frage vor einem Problem.

    Ich würde gerne ein Funktion realisieren, die eine Variable, die auf dem USB Gerät ist ausliest.

    bool LED1 = usbinterface.readVariable ( USB_LED_1_ON );
    

    so hätte ich das gerne.

    im hintergrund müsste ich doch dann ein writeFile senden mit den sachen, die er mir schicken soll und dann kommt mit einem der nächsten inputreports das ergebnis zurück. Oder verstehe ich das falsch? aber woher weiss ich, welches report, dass richtige ist, da kommen ja jede x ms welche an.

    Vielleicht kann mitr ja jemand helfen.

    Grüße Nils



  • Das ist ein Missverständnis, Du mußt gar nichts pollen. Das Pollen erledigt der Host (lies: der Treiber) von ganz allein. Wie häufig gepollt wird, bestimmt der Endpoint-Descriptor über bInterval. Der Host versucht zwar per SET_IDLE das Gerät etwas weniger mitteilungsfreudig zu machen, aber ich unterstelle Dir, daß Du diesen Request schlicht STALLst.

    Das heißt in Kürze: Das Gerät sendet in regelmäßigen Abständen seine Reports an den Host, so oder so. Diese Reports kannst Du ganz einfach per ReadFile abholen. Holst Du nicht oft genug ab, gehen eben die ältesten Reports verloren.

    Wie viele Reports der Host für Dich vorhält, kannst Du mit HidD_SetNumInputBuffers steuern. Wenn Du die Anzahl auf 1 verkürzt, bekommst Du garantiert immer den frischesten Report von ReadFile geliefert.



  • Ok, dann hab ich das falsch verstanden oder besser ich wusste nicht wirklich was von dieser Zwischenschicht.

    Ich spiel mal mit HidD_SetNumInputBuffers rum, wenn ich es aber auf 1 setze, kann es dann nicht sein, dass ich z.B. einen Report verliere, der mir sagt, das gerade ein Knopf auf meinem USB Gerät gedrückt wurde?

    Müsste ich das dann so ungefähr machen:

    //begin pseudocode
    
    void leseVariable ( BYTE varNummer )
    {
        write ( ..., ANFRAGE_ZUM_LESEN_DER_VARIABLE, ... );
        BYTE* readData;
        read( readData );
    
        BYTE result = lese_werte_aus_erstem_report ( readData );
        verarbeite_den_rest_der_reports( readData );
    
        return result;
    }
    
    //end pseudocode
    

    oder wie würdet ihr das angehen? den ersten report kann ich ja auch nicht nur auslesen, da es ja sein kann, dass in der zwischenzeit schon wieder einer nach kam. also muss ich alle nach der bestimmten signatur untersuchen?



  • Nils_Langner schrieb:

    da es ja sein kann, dass in der zwischenzeit schon wieder einer nach kam.

    Absolut richtig.

    Nils_Langner schrieb:

    also muss ich alle nach der bestimmten signatur untersuchen?

    Soetwas gibt es nicht.

    Also: Wenn Du Dir einen Report besorgst, bekommst Du den aktuellen Zustand geliefert. Wenn Dir jetzt der Report 10 Millisekunden zuvor flöten geht und dort ein Taster gedrückt war, bekommst Du das eben nicht mit; da hättest Du den Taster eben 10 Millisekunden länger drücken müssen. Da sehe ich aber absolut kein Problem.

    Wenn Du auf kleinste Änderungen reagieren möchtest, solltest Du ReadFile in einem extra Thread aufrufen. Dort vergleichst Du dann den aktuellen Report mit dem vorherigen und kannst bei Bedarf Events triggern. Wenn der Thread sonst nichts macht, ist das schnell genug, auch wenn Windows kein RTOS ist.

    Und wenn Dich der aktuellen Zustand sowieso nicht interessiert, solltest Du Dein Gerät ändern. Wenn keine Änderung passiert ist, antwortest Du eben mit einem NACK und nicht mit Daten. Daten schiebst Du erst dann wieder in die FIFOs, wenn sich auch wirklich etwas geändert hat.



  • so habe mir jetzt zwei Funktionen gebastelt: die erste Sendet eine Bytefolge an das USB Device (getVariable), daraufhin schickt das Device ne Nachricht zurück, die versuche ich dann mit processData abzufangen und auszugeben.

    void usbInterface_LPC2148::getVariable ( BYTE name )
    {	
    	BYTE* datagram = new BYTE[Capabilities.OutputReportByteLength];
    	sprintf( (char*)datagram, "%csRI %c ", '\0', name );
    	usbInterface::write( datagram );
    	processData( );
    }
    
    void usbInterface_LPC2148::processData( )
    {	
    	BYTE* readData = new BYTE[Capabilities.InputReportByteLength]; 
    	unsigned long bytesRead = 0;
    
    	*readData = 0;
    	bytesRead = usbInterface::read( readData );
    
    	if ( readData[1] == 1 )
    	{			
    		switch ( readData[2] )
    		{
    			case 1:	
    				printf( "   Die Wert der Variable %i ist %i \n", readData[3], readData[4] );
    				break;
    		}
    	}
    }
    

    Leider funktioniert der Code nur ab und zu. Also ab und zu imer Sinne von: Das printf wird nicht immer ausgegeben wenn es sollte. Ich könnte mir vorstellen, dass die Verarbeitung im Programm zu lange dauert, so dass ein InputReport übersprungen wurde.

    Den Tipp mit dem setNumInputBuffers habe ich ausprobiert, erstmal wollte ich über getNumInputBuffers die momentane Größe auslesen, nur leider bekomme ich immer false als Antwort (im msdn wird leider nicht drauf eingegangen woran es liegen könnte).
    Oder muss man vielleicht erstmal einen setzen, bevor man ihn lesen kann?

    Grüße Nils



  • Nils_Langner schrieb:

    void usbInterface_LPC2148::getVariable ( BYTE name )
    {    
        BYTE* datagram = new BYTE[Capabilities.OutputReportByteLength];
        sprintf( (char*)datagram, "%csRI %c ", '\0', name );
        usbInterface::write( datagram );
        processData( );
    }
    

    Das geht so nicht! Capabilities.OutputReportByteLength ist in Deinem Fall 2. Ich bin mir aber sicher, daß sprintf etwas mehr als zwei Bytes in diesen Buffer schreibt.

    Nils_Langner schrieb:

    .
    .
    .
    if ( readData[1] == 1 )
    .
    .
    .
    

    Auch das ist, mit Verlaub, Unfug. Dieses Byte enthält in Deinem Fall die Informationen zu den Tastern. Du wertest den Report damit also nur aus, wenn S2 gedrückt und S3/S4 gelöst sind. Das das nicht gewünscht ist, sagst Du ja selber.

    Und sage mir jetzt nicht, daß Du den Report nach Deiner Anfrage mit anderen Daten befüllst. Das geht so nicht. Die Daten im Report haben immer die selbe Bedeutung, ansonsten ist eine Zuordung nicht sicher möglich. Wenn Du über den selben Endpunkt etwas anderes senden willst, mußt Du schon mit mehreren Reports arbeiten (die Reports in COLLECTIONs zusammenfassen und IDs verteilen, Firmware anpassen (es wird ein zusätzliches Byte übertragen)).



  • hmmm ... mein problem ist, ich muss ein Protokoll über USB tunneln. dieses kann methoden aufrufen, events registrieren und variablen schreiben/lesen. Das Problem ist, das ich Hunderte von Methoden habe.

    Schreibe ich jetzt also für jede Methode einen Report oder einen für Methodenaufrufe, einen für Variablen setzen und einen für Events registrieren?

    Die Größe der Reports hatte ich angepasst, deswegen stimmt das mit der 2 nicht.

    Das ich mit meinem Versuch das Protokoll zu tunneln gegen die USB Richlinien verstoße wusste ich nicht, werde mal versuchen mich in "mehrere InputReports" einzulesen.



  • Es ist gar nicht so einfach, Dir gezielt zu helfen, da die Informationen nur kleckerweise kommen.

    Du solltest jedenfalls nur einen Methoden-Report erstellen. Das Gerät erkennt dann Anhand des Identifiers, ob Du LEDs togglen oder eine Methode aufrufen willst. Umgekehrt machst Du's genauso. Du hast Deinen einen Report für die Taster und den ADC, einen für die Quittung des Methoden-Aufrufs und die Rückgabewerte und einen weiteren für Events. Diesmal ist es der Host, der Anhand des Report-Identifiers die Daten sinvoll zuordnen kann.

    BTW: Wie ich in meinem vorherigen Post angedeutet habe, mußt Du die Firmware anpassen. Wenn Du nicht mit IDs hantierst, wird ein Output-Report AS-IS an das Gerät übertragen. Mit IDs ist das anders, da wird der Identifier im ersten Byte mit über die Strippe geschickt. Bei der Auswertung rutscht also alles ein Byte nach hinten.

    Ebenfalls mußt Du das beim Senden berücksichtigen. Bislang schickst Du den Input-Report so auf die Reise. Mit IDs darfst Du das ersyte Byte nicht vergessen!



  • OK schonmal vielen dank, das alles nur so kleckerweise kommt liegt daran, dass ich mein problem vielleicht an ner anderen stelle vermute, als es in wirklichkeit ist.

    ich war ja schon froh, dass ich daten übertragen kann, die dann vom device interpretiert werden können. eigentlich wollte ich wirklich nur einen tunnel für dieses protokoll basteln. da das protokoll für rs232 entwickelt wurde, gibt es da keine unterscheidung der reports o.Ä. deswegen hatte ich gedacht, dass es hier auch über eine "Leitung" gehen könnte.


Anmelden zum Antworten