Top.Mail.Ru

Библиотека Libusb для работы с USB Custom HID.

Всем доброго дня! Сегодня мы будем писать свою собственную программу для хоста, которая будет осуществлять прием и передачу данных от устройства, для которого мы уже реализовали класс USB Custom 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);

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

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

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 снова!

Подписаться
Уведомить о
guest

31 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Alexey
Alexey
9 лет назад

Спасибо огромное за цикл статей! Очень интересно, доступно и полезно! Буду внедрять в свой основной проект!

Паша
Паша
9 лет назад

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

Ed
Ed
8 лет назад

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

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

LEV
LEV
8 лет назад

Пока ещё не знаток в 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)

LEV
LEV
Ответ на комментарий  Aveal
8 лет назад

1

LEV
LEV
Ответ на комментарий  Aveal
8 лет назад

Теперь проект собирается, но в поле ошибок при сборке выходит следующее
Запускается 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 завершился крахом

LEV
LEV
Ответ на комментарий  LEV
8 лет назад

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

LEV
LEV
Ответ на комментарий  LEV
8 лет назад

Если я закомментирую от 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);

LEV
LEV
Ответ на комментарий  Aveal
8 лет назад

Aveal, заработало. Контакт МК и ПК установлен, благодарю за статьи.

LEV
LEV
8 лет назад

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

LEV
LEV
8 лет назад

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

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

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

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

LEV
LEV
Ответ на комментарий  Aveal
8 лет назад

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

Евгений
Евгений
8 лет назад

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

Евгений
Евгений
8 лет назад

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

Кайл
Кайл
8 лет назад

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

Алексей
Алексей
3 лет назад

Привет и огромное спасибо за столь доходчивое пояснение о создании HID,воткнулся в проблему отсутствия файлов из статьи в новой версии LIBUSB 1.0.24

Андрей
Андрей
Ответ на комментарий  Алексей
3 месяцев назад

Та же проблема была. Использовал LibUsbDotNet. Там хоть примеры есть.

public static UsbDevice MyUsbDevice;
    public static UsbDeviceFinder MyUsbFinder = new UsbDeviceFinder(0x483, 0x5750);

//Обработка открытия формы
public void Form1_Load(object sender, EventArgs e)
    {   
      MyUsbDevice = UsbDevice.OpenUsbDevice(MyUsbFinder);
      if (MyUsbDevice != null)
      {
        label2.Text = " подключено !";
        IUsbDevice wholeUsbDevice = MyUsbDevice as IUsbDevice;

        wholeUsbDevice.SetConfiguration(1);

        // Claim interface #0.
        wholeUsbDevice.ClaimInterface(0);      
      }
      else label2.Text = " не найдено !";
    }

/* При нажатии кнопки отправляет массив на МК. МК в ответ (если первый элемент массива 5) также отправляет массив символов, строка из которых выводится в окно */
    private void button1_Click(object sender, EventArgs e)
    {
      var writeEndpoint = MyUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);      
      var readEndpoint = MyUsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);
      var buffer = new byte[64];
      buffer[0] = 5;
      buffer[1] = 1;
      buffer[2] = 0x23;
      buffer[3] = 0x23;
      
      writeEndpoint.Write(buffer, 3000, out var bytesWritten);

      var readBuffer = new byte[64];

      //Read some data
      readEndpoint.Read(readBuffer, 3000, out var readBytes);
      string s = Encoding.Default.GetString(readBuffer);
      label1.Text = "значение" + s;
    }

jeyms
jeyms
2 лет назад

Привет. Возможно ли чтение данных только с USB-накопителя?

31
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x