Senden einer USB-Massenübertragungsanforderung (UWP-App)
In diesem Thema erfahren Sie mehr über eine USB-Massenübertragung und wie Sie eine Übertragungsanforderung von Ihrer UWP-App initiieren, die mit einem USB-Gerät kommuniziert.
USB-Geräte mit voller Geschwindigkeit, Hochgeschwindigkeits- und SuperSpeed-Geräte können Massenendpunkte unterstützen. Diese Endpunkte werden zum Übertragen großer Datenmengen verwendet, z. B. zum Übertragen von Daten auf oder von einem USB-Flashlaufwerk. Massenübertragungen sind zuverlässig, da sie die Fehlererkennung ermöglichen und eine begrenzte Anzahl von Wiederholungen umfassen, um sicherzustellen, dass die Daten vom Host oder dem Gerät empfangen werden. Massenübertragungen werden für Daten verwendet, die nicht zeitkritisch sind. Daten werden nur übertragen, wenn auf dem Bus nicht verwendete Bandbreite verfügbar ist. Wenn der Bus also mit anderen Übertragungen beschäftigt ist, können Massendaten unbegrenzt warten.
Massenendpunkte sind unidirektional, und in einer Übertragung können Daten entweder in IN- oder OUT-Richtung übertragen werden. Um das Lesen und Schreiben von Massendaten zu unterstützen, muss das Gerät Bulk-IN- und Bulk OUT-Endpunkte unterstützen. Der Bulk IN-Endpunkt wird verwendet, um Daten vom Gerät an den Host zu lesen, und der Bulk OUT-Endpunkt wird verwendet, um Daten vom Host an das Gerät zu senden.
Um eine Massenübertragungsanforderung zu initiieren, muss Ihre App über einen Verweis auf die Pipe verfügen, die einen Endpunkt darstellt. Eine Pipe ist ein Kommunikationskanal, der vom Gerätetreiber geöffnet wird, wenn das Gerät konfiguriert wird. Für die App ist eine Pipe eine logische Darstellung eines Endpunkts. Zum Lesen von Daten aus dem Endpunkt ruft die App Daten aus der zugeordneten Bulk-IN-Pipe ab. Zum Schreiben von Daten an den Endpunkt sendet die App Daten an die Bulk-OUT-Pipe. Verwenden Sie für Massenlese- und Schreibpipes die Klassen UsbBulkInPipe und UsbBulkOutPipe .
Ihre App kann auch das Verhalten der Pipe ändern, indem sie bestimmte Richtlinienflags festlegt. Für eine Leseanforderung können Sie beispielsweise ein Flag festlegen, das eine Verzögerungsbedingung für die Pipe automatisch löscht. Informationen zu diesen Flags finden Sie unter UsbReadOptions und UsbWriteOptions.
Vorbereitung
- Sie müssen das Gerät geöffnet und das UsbDevice-Objekt abgerufen haben. Weitere Informationen finden Sie unter Herstellen einer Verbindung mit einem USB-Gerät (UWP-App).
- Sie können den vollständigen Code in diesem Thema im CustomUsbDeviceAccess-Beispiel Scenario4_BulkPipes Dateien sehen.
Schritt 1: Abrufen des Massenpipeeobjekts
Um eine Übertragungsanforderung zu initiieren, müssen Sie einen Verweis auf das Massenpipeobjekt (UsbBulkOutPipe oder UsbBulkInPipe) abrufen. Sie können Pipes abrufen, indem Sie alle Einstellungen aller Schnittstellen auflisten. Für Datenübertragungen dürfen Sie jedoch nur Pipes einer aktiven Einstellung verwenden. Wenn das Pipeobjekt NULL ist, wenn sich der zugeordnete Endpunkt nicht in der aktiven Einstellung befindet.
Zweck | Verwenden Sie diesen Eigenschaftswert |
---|---|
Senden Sie Daten an eine Massenpipeline, und rufen Sie einen Verweis auf UsbBulkOutPipe ab. | UsbDevice.DefaultInterface.BulkOutPipes[n], wenn Ihre Gerätekonfiguration eine USB-Schnittstelle verfügbar macht. UsbDevice.Configuration.UsbInterfaces[m]. BulkOutPipes[n] zum Auflisten von Bulk OUT-Rohren in mehreren schnittstellen, die vom Gerät unterstützt werden. UsbInterface.InterfaceSettings[m]. BulkOutEndpoints[n]. Pipe zum Auflisten von Bulk OUT-Rohren, die durch Einstellungen in einer Schnittstelle definiert werden. UsbEndpointDescriptor.AsBulkOutEndpointDescriptor.Pipe zum Abrufen des Pipeobjekts aus dem Endpunktdeskriptor für den Massen-OUT-Endpunkt. |
Empfangen von Daten aus einer Massenpipeline können Sie das UsbBulkInPipe-Objekt abrufen. | UsbDevice.DefaultInterface.BulkInPipes[n], wenn Ihre Gerätekonfiguration eine USB-Schnittstelle verfügbar macht. UsbDevice.Configuration.UsbInterfaces[m]. BulkInPipes[n] zum Auflisten von Bulk-IN-Rohren in mehreren schnittstellen, die vom Gerät unterstützt werden. UsbInterface.InterfaceSettings[m]. BulkInEndpoints[n]. Pipe zum Auflisten von Massen-IN-Rohren, die durch Einstellungen in einer Schnittstelle definiert sind. UsbEndpointDescriptor.AsBulkInEndpointDescriptor.Pipe zum Abrufen des Pipeobjekts aus dem Endpunktdeskriptor für den Massen-IN-Endpunkt. |
Hinweis
Sollte in der aktiven Einstellung sein oder erfordert eine NULL-Überprüfung.
Schritt 2: Konfigurieren der Massenpipeline (Optional)
Sie können das Verhalten des Lese- oder Schreibvorgangs ändern, indem Sie bestimmte Flags für die abgerufene Massenpipe festlegen.
Legen Sie zum Lesen vom Gerät die UsbBulkInPipe.ReadOptions-Eigenschaft auf einen der in UsbReadOptions definierten Werte fest. Legen Sie im Fall des Schreibens die UsbBulkOutPipe.WriteOptions-Eigenschaft auf einen der in UsbWriteOptions definierten Werte fest.
Zweck | Dieses Flag festlegen |
---|---|
Automatisches Löschen einer Fehlerbedingung auf dem Endpunkt, ohne den Datenfluss zu beenden | AutoClearStall Weitere Informationen finden Sie unter Löschen von Bedingungen für den Stillstand. Dieses Flag gilt für Lese- und Schreibübertragungen. |
Senden Sie mehrere Leseanforderungen mit maximaler Effizienz. Steigern Sie die Leistung, indem Sie die Fehlerüberprüfung umgehen. | OverrideAutomaticBufferManagement Eine Datenanforderung kann in eine oder mehrere Übertragungen unterteilt werden, wobei jede Übertragung eine bestimmte Anzahl von Bytes enthält, die als maximale Übertragungsgröße bezeichnet wird. Bei mehreren Übertragungen kann es aufgrund der fehlerbasierten Überprüfung durch den Treiber zu einer Verzögerung bei zwei Übertragungen kommen. Dieses Flag umgeht diese Fehlerüberprüfung. Verwenden Sie die UsbBulkInPipe.MaxTransferSizeBytes-Eigenschaft , um die maximale Übertragungsgröße abzurufen. Wenn Ihre Anforderungsgröße UsbBulkInPipe.MaxTransferSizeBytes ist, müssen Sie dieses Flag festlegen. Wichtig: Wenn Sie dieses Flag festlegen, müssen Sie Daten in Vielfachen der maximalen Paketgröße der Pipe anfordern. Diese Informationen werden im Endpunktdeskriptor gespeichert. Die Größe hängt von der Busgeschwindigkeit des Geräts ab. Für volle Geschwindigkeit, hohe Geschwindigkeit und SuperSpeed; die maximalen Paketgrößen sind 64, 512 bzw. 1024 Bytes. Verwenden Sie zum Abrufen dieses Werts die UsbBulkInPipe.EndpointDescriptor.MaxPacketSize-Eigenschaft . Dieses Flag gilt nur für Leseübertragungen. |
Beenden einer Schreibanforderung mit einem Paket der Länge null | ShortPacketTerminate Sendet ein Paket der Länge null, um das Ende einer OUT-Übertragung anzugeben. Dieses Flag gilt nur für Schreibübertragungen. |
Deaktivieren des Lesens kurzer Pakete (kleiner als die maximale Paketgröße, die vom Endpunkt unterstützt wird) | IgnoreShortPacket Wenn das Gerät Bytes sendet, die kleiner als die maximale Paketgröße sind, empfängt die App diese standardmäßig. Wenn Sie keine kurzen Pakete empfangen möchten, legen Sie dieses Flag fest. Dieses Flag gilt nur für Leseübertragungen. |
Schritt 3: Einrichten des Datenstroms
Wenn Massendaten vom Gerät gesendet werden, werden die Daten wie im Eingabestream in der Massenpipe empfangen. Hier sind die Schritte zum Abrufen des Eingabedatenstroms:
- Rufen Sie einen Verweis auf den Eingabedatenstrom ab, indem Sie die UsbBulkInPipe.InputStream-Eigenschaft abrufen.
- Erstellen Sie ein DataReader-Objekt , indem Sie den Eingabedatenstrom im DataReader-Konstruktor angeben.
Um Daten auf das Gerät zu schreiben, muss die App in einen Ausgabedatenstrom in der Massenpipe schreiben. Hier sind die Schritte zum Vorbereiten des Ausgabestreams:
- Rufen Sie einen Verweis auf den Ausgabestream ab, indem Sie die UsbBulkOutPipe.OutputStream-Eigenschaft abrufen.
- Erstellen Sie ein DataWriter-Objekt , indem Sie den Ausgabestream im DataWriter-Konstruktor angeben.
- Füllen Sie den Dem Ausgabestream zugeordneten Datenpuffer auf.
- Schreiben Sie abhängig vom Datentyp Übertragungsdaten in den Ausgabestream, indem Sie DataWriter-Methoden wie WriteBytes aufrufen.
Schritt 4: Starten eines asynchronen Übertragungsvorgangs
Massenübertragungen werden über asynchrone Vorgänge initiiert.
Um Massendaten zu lesen, starten Sie einen asynchronen Lesevorgang, indem Sie DataReader.LoadAsync aufrufen.
Um Massendaten zu schreiben, starten Sie einen asynchronen Schreibvorgang, indem Sie DataWriter.StoreAsync aufrufen.
Schritt 5: Abrufen der Ergebnisse des Leseübertragungsvorgangs
Nachdem der asynchrone Datenvorgang abgeschlossen ist, können Sie die Anzahl der vom Aufgabenobjekt gelesenen oder geschriebenen Bytes abrufen. Rufen Sie für einen Lesevorgang DataReader-Methoden wie ReadBytes auf, um Daten aus dem Eingabedatenstrom zu lesen.
Löschen von Stallbedingungen
Manchmal treten bei der App möglicherweise Fehler bei Datenübertragungen auf. Eine fehlgeschlagene Übertragung kann auf eine Verzögerungsbedingung für den Endpunkt zurückzuführen sein. Solange der Endpunkt ins Stocken geraten ist, können Keine Daten in ihn geschrieben oder daraus gelesen werden. Um mit der Datenübertragung fortzufahren, muss die App die Stallbedingung auf der zugehörigen Pipe löschen.
Ihre App kann die Pipe so konfigurieren, dass Stillstandsbedingungen automatisch gelöscht werden, wenn sie auftreten. Legen Sie hierzu die UsbBulkInPipe.ReadOptions-Eigenschaft auf UsbReadOptions.AutoClearStall oder UsbBulkOutPipe.WriteOptions-Eigenschaft auf UsbWriteOptions.AutoClearStall fest. Bei dieser automatischen Konfiguration treten bei der App keine Fehlerhaften Übertragungen auf, und die Datenübertragung ist nahtlos.
Rufen Sie usbBulkInPipe.ClearStallAsync für eine Bulk-IN-Pipe auf, um eine Sperrbedingung manuell zu löschen. Rufen Sie UsbBulkOutPipe.ClearStallAsync für eine Bulk-OUT-Pipe auf.
Hinweis
Eine Stockbedingung gibt keinen leeren Endpunkt an. Wenn keine Daten im Endpunkt vorhanden sind, wird die Übertragung abgeschlossen, aber die Länge beträgt 0 Bytes.
Für Lesevorgänge müssen Sie möglicherweise ausstehende Daten in der Pipe löschen, bevor Sie eine neue Übertragungsanforderung starten. Rufen Sie hierzu die UsbBulkInPipe.FlushBuffer-Methode auf.
Beispiel für USB-Massenübertragungscode
In diesem Codebeispiel wird gezeigt, wie sie in eine Massenpipe schreiben. Im Beispiel werden Daten an die erste Bulk-OUT-Pipe auf der Standardschnittstelle gesendet. Es konfiguriert die Pipe so, dass am Ende der Übertragung ein Paket der Länge Null gesendet wird. Wenn die Übertragung abgeschlossen ist, wird die Anzahl der Bytes angezeigt.
private async void BulkWrite()
{
String dataBuffer = "Hello World!";
UInt32 bytesWritten = 0;
UsbBulkOutPipe writePipe = usbDevice.DefaultInterface.BulkOutPipes[0];
writePipe.WriteOptions |= UsbWriteOptions.ShortPacketTerminate;
var stream = writePipe.OutputStream;
DataWriter writer = new DataWriter(stream);
writer.WriteString(dataBuffer);
try
{
bytesWritten = await writer.StoreAsync();
}
catch (Exception exception)
{
ShowStatus(exception.Message.ToString());
}
finally
{
ShowStatus("Data written: " + bytesWritten + " bytes.");
}
}
In diesem Codebeispiel wird gezeigt, wie aus einer Massenpipe gelesen wird. Im Beispiel werden Daten aus der ersten Massen-IN-Pipe auf der Standardschnittstelle abgerufen. Es konfiguriert die Pipe für maximale Effizienz und empfängt Daten in Blöcken mit maximaler Paketgröße. Wenn die Übertragung abgeschlossen ist, wird die Anzahl der Bytes angezeigt.
private async void BulkRead()
{
UInt32 bytesRead = 0;
UsbBulkInPipe readPipe = usbDevice.DefaultInterface.BulkInPipes[0];
// Warning: Setting IgnoreShortPacket causes LoadAsync to block until you receive a number of packets >= readPipe.EndpointDescriptor.MaxPacketSize.
// Remove the following line if you want to see messages that are less than the max transfer size, for example if you are communicating with a USBTMC device.
readPipe.ReadOptions |= UsbReadOptions.IgnoreShortPacket;
var stream = readPipe.InputStream;
DataReader reader = new DataReader(stream);
try
{
bytesRead = await reader.LoadAsync(readPipe.EndpointDescriptor.MaxPacketSize);
}
catch (Exception exception)
{
ShowStatus(exception.Message.ToString());
}
finally
{
ShowStatus("Number of bytes: " + bytesRead);
IBuffer buffer = reader.ReadBuffer(bytesRead);
using (var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer))
{
dataReader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
ShowData(dataReader.ReadString(buffer.Length));
}
}
}