Отправка запроса на массовую передачу USB (приложение UWP)

В этом разделе вы узнаете о массовой передаче USB и о том, как инициировать запрос на передачу из приложения UWP, которое взаимодействует с USB-устройством.

Usb full speed, high speed и SuperSpeed устройства могут поддерживать массовые конечные точки. Эти конечные точки используются для передачи больших объемов данных, таких как передача данных на USB-накопитель или из нее. Массовая передача надежна, так как они позволяют обнаруживать ошибки и включают ограниченное количество повторных попыток, чтобы убедиться, что данные получены узлом или устройством. Массовые передачи используются для данных, которые не являются критически важными. Данные передаются только в том случае, если на шине доступна неиспользуемая пропускная способность. Поэтому, когда автобус занят другими передачами, объемные данные могут ждать сколько угодно долго.

Каналы обмена данными являются однонаправленными и в одной передаче данные могут передаваться в направлении IN или OUT. Для поддержки чтения и записи пакетных данных устройство должно поддерживать конечные точки пакетной передачи IN и OUT. Конечная точка bulk IN используется для чтения данных с устройства на хост, а конечная точка bulk OUT — для отправки данных с хоста на устройство.

Чтобы инициировать массовый запрос на передачу, приложение должно иметь ссылку на канал , представляющий конечную точку. Канал связи — это канал связи, открытый драйвером устройства при настройке устройства. Для приложения канал является логическим представлением конечной точки. Чтобы считывать данные из конечной точки, приложение получает данные из связанного потокового канала типа bulk IN. Чтобы записать данные в конечную точку, приложение отправляет данные в канал bulk OUT. Для каналов массового чтения и записи используйте классы UsbBulkInPipe и UsbBulkOutPipe.

Приложение также может изменить поведение канала, задав определенные флаги политики. Например, для запроса на чтение можно задать флаг, который автоматически очищает состояние блокировки канала. Для получения информации об этих флагах см. UsbReadOptions и UsbWriteOptions.

Перед началом работы

Шаг 1. Получение объекта трубопровода.

Чтобы инициировать запрос на передачу, необходимо получить ссылку на объект массового канала (UsbBulkOutPipe или UsbBulkInPipe. Пайпы можно получить, перечислив все параметры всех интерфейсов. Однако для передачи данных необходимо использовать только каналы с активными настройками. Если объект канала имеет значение NULL, это значит, что связанная конечная точка не находится в активном параметре.

Если вы хотите... Используйте это значение свойства
Отправьте данные в массовый канал передачи данных, получите указатель на UsbBulkOutPipe. UsbDevice.DefaultInterface.BulkOutPipes[n], если конфигурация устройства предоставляет один USB-интерфейс.

UsbDevice.Configuration.UsbInterfaces[m].BulkOutPipes[n] для перечисления каналов Bulk OUT в нескольких интерфейсах, поддерживаемых устройством.

UsbInterface.InterfaceSettings[m].BulkOutEndpoints[n].Канал для перечисления массовых выходных каналов, определенных параметрами в интерфейсе.

UsbEndpointDescriptor.AsBulkOutEndpointDescriptor.Pipe для получения объекта канала из дескриптора конечной точки для конечной точки bulk OUT.
Для получения данных из массового канала используйте объект UsbBulkInPipe . UsbDevice.DefaultInterface.BulkInPipes[n], если конфигурация устройства предоставляет один USB-интерфейс.

UsbDevice.Configuration.UsbInterfaces[m].BulkInPipes[n] для перечисления каналов пакетной передачи данных IN в нескольких интерфейсах, поддерживаемых устройством.

UsbInterface.InterfaceSettings[m].BulkInEndpoints[n].Pipe для перечисления каналов Bulk IN, определенных параметрами в интерфейсе.

UsbEndpointDescriptor.AsBulkInEndpointDescriptor.Pipe для получения объекта канала из дескриптора конечной точки для Bulk IN.

Примечание.

Должен находиться в активном состоянии или требуется проверка на null.

Шаг 2. Настройка магистрального трубопровода (опционально)

Вы можете изменить поведение операции чтения или записи, установив определенные флаги на полученном канале передачи данных.

Для чтения с устройства задайте свойству UsbBulkInPipe.ReadOptions одно из значений, определенных в UsbReadOptions. В случае записи задайте для свойства UsbBulkOutPipe.WriteOptions значение одного из значений, определенных в UsbWriteOptions.

Если вы хотите... Установка этого флага
Автоматическое очистка любого условия ошибки в конечной точке без остановки потока данных AutoClearStall

Дополнительные сведения см. в разделе Очистка условий заклинивания.

Этот флаг применяется как к передаче чтения, так и записи.
Отправляйте несколько запросов на чтение с максимальной эффективностью. Повышение производительности путем обхода проверки ошибок. OverrideAutomaticBufferManagement

Запрос данных можно разделить на одну или несколько передач, где каждая передача содержит определенное количество байтов, называемых максимальным размером передачи. Для нескольких передач может возникнуть задержка при очередности двух передач, вызванная проверкой ошибок, выполняемой драйвером. Этот флаг обходит эту проверку ошибок. Чтобы получить максимальный размер передачи, используйте свойство UsbBulkInPipe.MaxTransferSizeBytes. Если размер запроса — UsbBulkInPipe.MaxTransferSizeBytes, необходимо задать этот флаг.

Важно: Если этот флаг установлен, вы должны запросить данные в размерах, кратных максимальному размеру пакета канала. Эти сведения хранятся в дескрипторе конечной точки. Размер зависит от скорости шины устройства. Для полной скорости, высокой скорости и SuperSpeed; Максимальный размер пакета — 64, 512 и 1024 байт соответственно. Чтобы получить это значение, используйте свойство UsbBulkInPipe.EndpointDescriptor.MaxPacketSize.

Этот флаг применяется только к операциям чтения.
Завершение запроса записи с помощью пакета нулевой длины ShortPacketTerminate

Отправляет пакет нулевой длины, чтобы указать конец передачи OUT.

Этот флаг применяется только к передаче записей.
Отключение чтения коротких пакетов (меньше максимального размера пакетов, поддерживаемых конечной точкой) IgnoreShortPacket

По умолчанию, если устройство отправляет байты меньше максимального размера пакета, приложение получает их. Если вы не хотите получать короткие пакеты, установите этот флаг.

Этот флаг применяется только к операциям чтения.

Шаг 3. Настройка потока данных

При отправке массовых данных устройством данные получаются как входной поток на массовом канале. Ниже приведены шаги по получению входного потока:

  1. Получите ссылку на входной поток, получив свойство UsbBulkInPipe.InputStream.
  2. Создайте объектDataReader, указав входной поток в конструкторе DataReader.

Чтобы записать данные на устройство, приложение должно записать в выходной поток на массовом канале. Ниже приведены шаги по подготовке выходного потока:

  1. Получите ссылку на выходной поток, получив свойство UsbBulkOutPipe.OutputStream.
  2. Создайте объект DataWriter, указав выходной поток в конструкторе DataWriter.
  3. Заполните буфер данных, связанный с выходным потоком.
  4. В зависимости от типа данных записывайте данные для передачи в выходной поток, вызывая методы DataWriter, такие как WriteBytes.

Шаг 4. Запуск асинхронной операции передачи

Передачи данных в большом объеме осуществляются с помощью асинхронных операций.

Чтобы считывать массовые данные, запустите асинхронную операцию чтения, вызвав DataReader.LoadAsync.

Чтобы записать массовые данные, запустите асинхронную операцию записи, вызвав DataWriter.StoreAsync.

Шаг 5. Получение результатов операции передачи данных для чтения

После завершения асинхронной операции данных можно получить количество байтов, считываемых или записанных из объекта задачи. Для операции чтения вызовите методы DataReader, например ReadBytes, для чтения данных из входного потока.

Устранение условий зависания

Иногда приложение может испытывать сбой передачи данных. Сбой передачи может быть вызван состоянием остановки на конечной точке. Пока конечная точка остановилась, данные не могут быть записаны в нее или считываться из нее. Чтобы продолжить передачу данных, приложение должно очистить состояние остановки в ассоциированном канале.

Ваше приложение может настроить поток для автоматического снятия условий застоя при их возникновении. Для этого задайте для свойства UsbBulkInPipe.ReadOptions значение значение UsbReadOptions.AutoClearStall или свойству UsbBulkOutPipe.WriteOptions значение UsbWriteOptions.AutoClearStall. При этой автоматической настройке приложение избегает неудачных передач, и процесс передачи данных проходит гладко.

Чтобы очистить состояние блокировки вручную, воспользуйтесь UsbBulkInPipe.ClearStallAsync для массового конвейера IN; воспользуйтесь UsbBulkOutPipe.ClearStallAsync для массового конвейера OUT.

Примечание.

Условие остановки не указывает на пустую конечную точку. Если в конечной точке передачи данных нет данных, передача завершается, но длина равна нулю байт.

Для операций чтения может потребоваться очистить ожидающие данные в канале до начала нового запроса на передачу. Для этого вызовите метод UsbBulkInPipe.FlushBuffer.

Пример кода для пакетной передачи данных по USB

В этом примере кода показано, как записать в массовый канал. В этом примере данные отправляются в первый канал bulk OUT в интерфейсе по умолчанию. Он настраивает канал для отправки пакета нулевой длины в конце передачи. После завершения передачи отображается число байтов.

    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 в интерфейсе по умолчанию. Он настраивает канал для достижения максимальной эффективности и получает данные в блоках, соответствующих максимальному размеру пакета. После завершения передачи отображается число байтов.

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