QT. USB Custom HID. Библиотека Libusb.

Всем доброго дня! Сегодня мы будем писать свою собственную программу для хоста, которая будет осуществлять прием и передачу данных от устройства, для которого мы уже реализовали класс USB Custom HID. А произошло это в двух предыдущих статьях, посвященных работе с USB HID — раз и два 😉

USB HID

В качестве среды разработки я буду использовать мой любимый QT Creator, а для работы с USB предлагаю использовать специально для этого созданную библиотеку Libusb. Собственно, начать думаю стоит с ее установки.

1. Скачиваем архив с библиотекой с официального сайта Libusb.
2. Теперь необходимо скопировать ряд файлов, которые можно найти в распакованном архиве в папке bin. Файл libusb0.dll копируем в папку C:\Windows\System32, а файл libusb0.sys  — в C:\Windows\System32\drivers. На этом операции с файловой системой заканчиваются и мы переходим к шагу 3 😉
3. На каждое устройство, которое планируется использовать необходимо установить так называемый фильтр. Делается это следующим образом. Все в той же папке bin находим файл install-filter-win.exe и запускаем. В появившемся окне выбираем Install a device filter и нажимаем Next. Сразу же открывается новое окно, в котором мы видим список подключенных устройств. Нам необходимо найти наш девайс и выбрать именно его:

QT и Libusb

Жмем на Install и все — фильтр успешно установлен 😉

4. Создаем где-нибудь директорию для хранения файлов библиотеки — я к примеру создал папку прямо на диске C:/ и назвал ее вполне тривиально — libusb 😉 В эту папку из скачанного архива переносим папки include и lib.

С установкой библиотеки на этом заканчиваем и переходим к созданию нового проекта  в QT Creator. Библиотеки то готовы к использованию, но их необходимо подключить непосредственно к нашему проекту, для этого в файле .pro добавляем строки следующего содержания:

LIBS +=          C:/libusb/lib/gcc/libusb.a
INCLUDEPATH +=   C:/libusb/include/
DEPENDPATH +=    C:/libusb/include/
PRE_TARGETDEPS+= C:/libusb/lib/gcc/libusb.a

А в файле MainWindow.h добавляем:

#include "C:\libusb\include/lusb0_usb.h"

Сразу же определим переменную:

usb_dev_handle *USB_Device = NULL;

Для работы с USB-устройством нам по сути требуется научиться делать три вещи — обеспечивать подключение/открытие устройства с нужными значениями PID/VID, принимать данные и передавать данные. Вот именно в такой последовательности и будем разбираться. Начинаем с того, что определяем все подключенные к шине устройства:

usb_init();
  usb_find_busses();
  usb_find_devices();

Перебираем все устройства в поисках того, которое нам нужно. А нужен нам девайс со значениями PID/VID — 0x0483/0x5750:

struct usb_bus *usbBus;
  struct usb_device *usbDevice;
 
  for (usbBus = usb_get_busses(); usbBus; usbBus = usbBus->next)
  {
      for (usbDevice = usbBus->devices; usbDevice; usbDevice = usbDevice->next)
      {
          if ((usbDevice->descriptor.idVendor == 0x0483) && (usbDevice->descriptor.idProduct == 0x5750))
          {
              USB_Device = usb_open(usbDevice);
          }
      }
  }

Открыв устройство выполняем настройку:

usb_set_configuration(dev, 1);
usb_claim_interface(dev, 0);

Для примера делаем все максимально просто, в реальном проекте как минимум стоит проверить какой код возвращают все эти функции для того, чтобы детектировать возможные ошибки при подключении устройства. В качестве примера при приеме и передаче данных мы как раз-таки произведем анализ на ошибки и выведем их код при помощи qDebug().

А тем временем переходим к приему данных ) А делается это очень просто:

int ret=0;
char usbData[64];
 
ret = usb_bulk_read(USB_Device, 0x81, usbData, 4, 2000);
 
if (ret < 0)
{
    qDebug() << "No received data!" << ret;
}
else
{
   qDebug() << "Received data" << usbData;
}

Все сводится к вызову одной функции) ее аргументы:

  • объект USB_Device,
  • 0x81 — это адрес конечной точки нашего устройства (помните, мы определяли все это при создании дескрипторов),
  • usbData — массив для сохранения принятых данных
  • количество принимаемых байт
  • таймаут

С приемом и инициализацией разобрались, на очереди передача данных. Выглядит все это безобразие следующим образом:

usbData[0] = 'S';
usbData[1] = 'n';
usbData[2] = 'd';
usbData[3] = '\0';
 
ret = usb_bulk_write(USB_Device, 0x01, usbData, 4, 500);
 
if (ret < 0)
{
    qDebug() << "No sended data!" << ret;
}
else
{
    qDebug() << "Sended data" << usbData;
}

Здесь все практически так же, как и в случае приема данных, только не упускаем из виду, что адрес конечной точки стал другим. Что абсолютно логично, ведь для приема и передачи у нас используются разные endpoint’ы.

Теперь можно без проблем написать свое маленькое приложение, которое будет, к примеру, по нажатию кнопки выполнять отправку данных. А наша статья на этом подходит к концу, даже больше того, подходит к концу «трехстатейный» мини-курс посвященный работе с USB HID 😉 Надеюсь информация окажется полезной и интересной! Следите за обновлениями и новыми статьями, ждем вас на сайте MicroTechnics снова =)

STM32Cube. USB Custom HID. Прием и передача данных.

Понравилась статья? Поделись с друзьями!

QT. USB Custom HID. Библиотека Libusb.: 27 комментариев
  1. Спасибо огромное за цикл статей! Очень интересно, доступно и полезно! Буду внедрять в свой основной проект!

  2. За проделанную работу огромное спасибо! Будем пробовать.
    А в следующих статьях было бы здорово увидеть работу с ethernet на базе W5100 или DP83848 Ethernet Board ( думаю не одному мне будет интересно )

    • Кстати, возможно будет в ближайшее время Ethernet, по-моему как раз-таки DP83848, но точно не помню какая там микросхема физического уровня стоит )

  3. Самая адекватная информация. спасибо.

    «1. Скачиваем архив с библиотекой с официального сайта Libusb.»
    а подскажи пожалуйста более конкретную информацию, где это взять.

  4. Пока ещё не знаток в C++ и Qt, но связать МК и компьютером хочется) и есть непонятности по статье:

    for (usbBus = usb_get_busses(); usbBus; usbBus = usbBus->next)
    {
    for (usbDevice = usbBus->devices; usbDevice; usbDevice = usbDevice->next)
    {
    if ((usbDevice->descriptor.idVendor == 0x0483) && (usbDevice->descriptor.idProduct == 0x5750))
    {
    USB_Device = usb_open(usbDevice);
    }
    }
    }

    for (usbBus = usb_get_busses(); usbBus; usbBus = usbBus->next)

    Не понятно что такое “next” и почему в цикле он идёт 4-м параметром. При компиляции Qt выдает три ошибки на этой строке
    F:\Qt\Project\USB_Lib01\USB\main.cpp:29: ошибка: expected ‘)’ before ‘;’ token
    for (usbBus = usb_get_busses(); usbBus; usbBus = usbBus->next)
    и
    F:\Qt\Project\USB_Lib01\USB\main.cpp:29: ошибка: ‘next’ was not declared in this scope
    for (usbBus = usb_get_busses(); usbBus; usbBus = usbBus->next)

    Также непонятна переменная “gt” что за тип и откуда она взялась. При сборке проекта выдается ошибка F:\Qt\Project\USB_Lib01\USB\main.cpp:29: ошибка: ‘next’ was not declared in this scope
    for (usbBus = usb_get_busses(); usbBus; usbBus = usbBus->next)

    • Я приношу свои извинения… gt — это косячит движок кода на сайте, который периодически подменяет правильные символы (в частности знаки «>» и «<"). Сейчас я все поправлю в коде.

      • Теперь проект собирается, но в поле ошибок при сборке выходит следующее
        Запускается F:\Qt\Project\Qt_OTHER\Express\build-USB_02-Desktop_Qt_5_6_0_MinGW_32bit-Debug\debug\USB_02.exe…
        Программа неожиданно завершилась.
        F:\Qt\Project\Qt_OTHER\Express\build-USB_02-Desktop_Qt_5_6_0_MinGW_32bit-Debug\debug\USB_02.exe завершился крахом

        • int main(int argc, char *argv[])
          {
          usb_dev_handle *USB_Device = NULL;
          QApplication app(argc, argv);
          MainWindow w;
          usb_init();
          usb_find_busses();
          usb_find_devices();

          struct usb_bus *usbBus;
          struct usb_device *usbDevice;

          for (usbBus = usb_get_busses(); usbBus; usbBus = usbBus->next)
          {
          for (usbDevice = usbBus->devices; usbDevice; usbDevice = usbDevice->next)
          {
          if ((usbDevice->descriptor.idVendor == 0x0743) && (usbDevice->descriptor.idProduct == 0x5750))
          {
          USB_Device = usb_open(usbDevice);
          }
          }
          }

          w.show();

          return app.exec();

          • Если я закомментирую от usb_init(); до w.show(); то программа запускается (пустое окно).

            В строках usb_set_configuration(dev, 1);
            usb_claim_interface(dev, 0);

            поменял dev на USB_Device, без этого ошибка — F:\Qt\Project\Qt_OTHER\Express\USB_02\main.cpp:45: ошибка: ‘dev’ was not declared in this scope
            usb_set_configuration(dev, 1);

  5. А как быть в случае передачи с МК на ПК если я беру данные например с АЦП, там ведь тип не char получается… На ПК соответственно вижу разнообразные символы вместо цифр?

    • На ПК через qDebug() видишь символы? Тогда выводи так:
      qDebug() << (unsigned char)variable; Можно объявить массив unsigned char и в него считывать данные.

  6. Тут при работе с STM32 случилась неприятность. Вдруг в диспетчере задач и вкладки Устройства HID пропал мой STM32. Я переустановил драйвера, по новой настроил фильтра, всё вроде ок. Но проект на Qt с USB не запускается, сообщение Qt Программа неожиданно завершилась и ……….exe завершился крахом. В диспетчере МК отображается с вопросиком, в его свойствах написано «Запуск этого устройства невозможен. (Код 10)» и «Найдена лишняя конечная коллекция, или конечные коллекции не обнаружены.».

    Использую Windows 10, перед этим ничего не менял и не трогал, просто вдруг перестало работать.

    Еще странно после установки драйверов libUSB (я кстати для их установки использовал exe файл котрый шел в архиве, как и раньше) МК попадал не в список HID, а в диспетчере создалась отельная вкладка «STM32 Custom Human interface» и фильтр её не видел. Далее я нажимал по этой вкладке-> обновить драйвер автоматически, вроде бы начиналась загрузка и установка но заканчивалось с ошибкой что то вроде Windows удалось обнаружить драйвера но при их установке возникла ошибка. И далее МК уже перемещался в HID устройства, после чего я мог внести его в фильтр.

    Подскажите? Возможно бывало такое?

      • Несколько раз переустановил драйвера и пока работает.
        Ещё один вопросик, а на какой максимальной скоростью можно передавать данные, я так понимаю если передавать пакетами по 64 байта и каждый кадр будет передаваться с интервалом 1 мс то получается 64 КБ, правильно, реальна такая скорость достижима?

  7. Приветствую!
    Хотелось бы уточнить тип проекта для Кьюта — он обычный, Win32 прога или консольный? Автору пожелание — если возможно, выложить в архиве этот проект, легче разбираться, заодно и ошибки в проекте в новых версиях Кьюта переловить и предостеречь других новичков…

  8. Здравствуйте! Не подскажете, как была исправлена ошибка, про которую писал LEV 27.04.2016 в 21:15, что проект собирается, но выходит в крах? У меня сейчас то же самое, не могу понять, где и что не так…

    • Обычно выходит «программа неожиданно завершилась» если идет обращение к несущестующему элементу массива, например.

  9. А можно как-то без стороннего ПО для установки фильтра? (чтобы наша программа делала все сама, юзеру только запустить и подключить)

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *