Compartir a través de


Cómo enviar una solicitud de transferencia masiva USB (aplicación para UWP)

En este tema, obtendrá información sobre una transferencia masiva USB y cómo iniciar una solicitud de transferencia desde la aplicación para UWP que se comunica con un dispositivo USB.

Los dispositivos USB de velocidad completa, alta velocidad y SuperSpeed pueden admitir puntos de conexión masivos. Estos puntos de conexión se usan para transferir grandes cantidades de datos, como transferir datos a o desde una unidad flash USB. Las transferencias masivas son confiables porque permiten la detección de errores e implican un número limitado de reintentos para asegurarse de que el host o el dispositivo reciben los datos. Las transferencias masivas se usan para los datos que no son críticos para el tiempo. Los datos solo se transfieren cuando hay ancho de banda no utilizado disponible en el bus. Por lo tanto, cuando el autobús está ocupado con otras transferencias, los datos masivos pueden esperar indefinidamente.

Los puntos de conexión masivos son unidireccionales y, en una transferencia, los datos se pueden transferir en una dirección IN o OUT. Para admitir la lectura y escritura de datos masivos, el dispositivo debe admitir puntos de conexión IN y OUT masivos. El punto de conexión BULK IN se usa para leer datos del dispositivo al host y el punto de conexión OUT masivo se usa para enviar datos del host al dispositivo.

Para iniciar una solicitud de transferencia masiva, la aplicación debe tener una referencia a la canalización que representa un punto de conexión. Una canalización es un canal de comunicación abierto por el controlador de dispositivo cuando se configura el dispositivo. Para la aplicación, una canalización es una representación lógica de un punto de conexión. Para leer datos del punto de conexión, la aplicación obtiene datos de la canalización masiva de IN asociada. Para escribir datos en el punto de conexión, la aplicación envía datos a la canalización de salida masiva. Para canalizaciones de lectura y escritura masivas, use las clases UsbBulkInPipe y UsbBulkOutPipe .

La aplicación también puede modificar el comportamiento de la canalización estableciendo determinadas marcas de directiva. Por ejemplo, para una solicitud de lectura, puede establecer una marca que borra automáticamente una condición de detendado en la tubería. Para obtener información sobre esas marcas, consulte UsbReadOptions y UsbWriteOptions.

Antes de empezar

Paso 1: Obtener el objeto de canalización masiva

Para iniciar una solicitud de transferencia, debe obtener una referencia al objeto de canalización masiva (UsbBulkOutPipe o UsbBulkInPipe. Puede obtener canalizaciones mediante la enumeración de todas las configuraciones de todas las interfaces. Sin embargo, para las transferencias de datos solo debe usar canalizaciones de una configuración activa. Si el objeto de canalización es null si el punto de conexión asociado no está en la configuración activa.

Si desea... Usar este valor de propiedad
Envíe datos a una canalización masiva y obtenga una referencia a UsbBulkOutPipe. UsbDevice.DefaultInterface.BulkOutPipes[n] si la configuración del dispositivo expone una interfaz USB.

UsbDevice.Configuration.UsbInterfaces[m]. BulkOutPipes[n] para enumerar canalizaciones OUT masivas en varias interfaces compatibles con el dispositivo.

UsbInterface.InterfaceSettings[m]. BulkOutEndpoints[n]. Canalización para enumerar canalizaciones OUT masivas definidas por la configuración de una interfaz.

UsbEndpointDescriptor.AsBulkOutEndpointDescriptor.Pipe para obtener el objeto de canalización desde el descriptor de punto de conexión para el punto de conexión out masivo.
Recibir datos de una canalización masiva, puede obtener el objeto UsbBulkInPipe . UsbDevice.DefaultInterface.BulkInPipes[n] si la configuración del dispositivo expone una interfaz USB.

UsbDevice.Configuration.UsbInterfaces[m]. BulkInPipes[n] para enumerar canalizaciones IN masivas en varias interfaces compatibles con el dispositivo.

UsbInterface.InterfaceSettings[m]. BulkInEndpoints[n]. Canalización para enumerar canalizaciones IN masivas definidas por la configuración en una interfaz.

UsbEndpointDescriptor.AsBulkInEndpointDescriptor.Pipe para obtener el objeto de canalización desde el descriptor de punto de conexión para el punto de conexión IN masivo.

Nota:

Debe estar en la configuración activa o requiere una comprobación nula.

Paso 2: Configurar la canalización masiva (opcional)

Puede modificar el comportamiento de la operación de lectura o escritura estableciendo determinadas marcas en la canalización masiva recuperada.

Para leer desde el dispositivo, establezca la propiedad UsbBulkInPipe.ReadOptions en uno de los valores definidos en UsbReadOptions. En el caso de escribir, establezca la propiedad UsbBulkOutPipe.WriteOptions en uno de los valores definidos en UsbWriteOptions.

Si desea... Establecer esta marca
Borrar automáticamente cualquier condición de error en el punto de conexión sin detener el flujo de datos AutoClearStall

Para obtener más información, consulte Borrar condiciones de detención.

Esta marca se aplica a las transferencias de lectura y escritura.
Enviar varias solicitudes de lectura con máxima eficacia. Mejora del rendimiento omitiendo la comprobación de errores. OverrideAutomaticBufferManagement

Una solicitud de datos se puede dividir en una o varias transferencias, donde cada transferencia contiene un número determinado de bytes denominado tamaño máximo de transferencia. En el caso de varias transferencias, puede haber retraso en la puesta en cola de dos transferencias debido a la comprobación de errores realizada por el controlador. Esta marca omite esa comprobación de errores. Para obtener el tamaño máximo de transferencia, utilice la propiedad UsbBulkInPipe.MaxTransferSizeBytes . Si el tamaño de la solicitud es UsbBulkInPipe.MaxTransferSizeBytes, debe establecer esta marca.

Importante: Si establece esta marca, debe solicitar datos en múltiplos del tamaño máximo de paquete de la canalización. Esa información se almacena en el descriptor de punto de conexión. El tamaño depende de la velocidad del bus del dispositivo. Para velocidad completa, alta velocidad y SuperSpeed; los tamaños máximos de paquete son 64, 512 y 1024 bytes respectivamente. Para obtener ese valor, utilice la propiedad UsbBulkInPipe.EndpointDescriptor.MaxPacketSize .

Esta marca solo se aplica a las transferencias de lectura.
Finalizar una solicitud de escritura con un paquete de longitud cero ShortPacketTerminate

Envía un paquete de longitud cero para indicar el final de una transferencia OUT.

Esta marca solo se aplica a las transferencias de escritura.
Deshabilitar la lectura de paquetes cortos (menor que el tamaño máximo de paquete admitido por el punto de conexión) IgnoreShortPacket

De forma predeterminada, si el dispositivo envía bytes inferiores al tamaño máximo de paquete, la aplicación las recibe. Si no desea recibir paquetes cortos, establezca esta marca.

Esta marca solo se aplica a las transferencias de lectura.

Paso 3: Configuración del flujo de datos

Cuando el dispositivo envía datos masivos, los datos se reciben como en el flujo de entrada de la canalización masiva. Estos son los pasos para obtener el flujo de entrada:

  1. Obtenga una referencia al flujo de entrada obteniendo la propiedad UsbBulkInPipe.InputStream .
  2. Cree un objeto DataReader especificando el flujo de entrada en el constructor DataReader.

Para escribir datos en el dispositivo, la aplicación debe escribir en un flujo de salida en la canalización masiva. Estos son los pasos para preparar el flujo de salida:

  1. Obtenga una referencia al flujo de salida obteniendo la propiedad UsbBulkOutPipe.OutputStream .
  2. Cree un objeto DataWriter especificando el flujo de salida en el constructor DataWriter .
  3. Rellene el búfer de datos asociado al flujo de salida.
  4. Dependiendo del tipo de datos, escriba datos transferidos al flujo de salida llamando a métodos DataWriter, como WriteBytes.

Paso 4: Iniciar una operación de transferencia asincrónica

Las transferencias masivas se inician a través de operaciones asincrónicas.

Para leer datos masivos, inicie una operación de lectura asincrónica llamando a DataReader.LoadAsync.

Para escribir datos masivos, inicie una operación de escritura asincrónica llamando a DataWriter.StoreAsync.

Paso 5: Obtener los resultados de la operación de transferencia de lectura

Una vez completada la operación de datos asincrónica, puede obtener el número de bytes leídos o escritos desde el objeto de tarea. Para una operación de lectura, llame a los métodos DataReader, como ReadBytes, para leer datos del flujo de entrada.

Borrar condiciones de despeje

En ocasiones, la aplicación podría experimentar transferencias de datos con errores. Una transferencia con errores puede deberse a una condición de detenida en el punto de conexión. Siempre que el punto de conexión esté detenido, los datos no se pueden escribir ni leer desde él. Para continuar con las transferencias de datos, la aplicación debe borrar la condición de bloqueo en la canalización asociada.

La aplicación puede configurar la canalización para borrar automáticamente las condiciones de parada, cuando se producen. Para ello, establezca la propiedad UsbBulkInPipe.ReadOptions en UsbReadOptions.AutoClearStall o UsbBulkOutPipe.WriteOptions en UsbWriteOptions.AutoClearStall. Con esa configuración automática, la aplicación no experimenta transferencias con errores y la experiencia de transferencia de datos es perfecta.

Para borrar manualmente una condición de parada, llame a UsbBulkInPipe.ClearStallAsync para una canalización IN masiva; llame a UsbBulkOutPipe.ClearStallAsync para una canalización OUT masiva.

Nota:

Una condición de parada no indica un punto de conexión vacío. Si no hay datos en el punto de conexión, la transferencia se completa, pero la longitud es cero bytes.

En el caso de las operaciones de lectura, es posible que tenga que borrar los datos pendientes en la canalización antes de iniciar una nueva solicitud de transferencia. Para ello, llame al método UsbBulkInPipe.FlushBuffer .

Ejemplo de código de transferencia masiva USB

En este ejemplo de código se muestra cómo escribir en una canalización masiva. El ejemplo envía datos a la primera canalización OUT masiva en la interfaz predeterminada. Configura la canalización para enviar un paquete de longitud cero al final de la transferencia. Una vez completada la transferencia, se muestra el número de bytes.

    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.");
        }
    }

En este ejemplo de código se muestra cómo leer desde una canalización masiva. En el ejemplo se recuperan datos de la primera canalización IN masiva en la interfaz predeterminada. Configura la canalización para lograr la máxima eficacia y recibe datos en fragmentos de tamaño máximo de paquete. Una vez completada la transferencia, se muestra el número de bytes.

    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));
            }
        }
    }