Всем доброго дня! Сегодня мы будем писать свою собственную программу для хоста, которая будет осуществлять прием и передачу данных от устройства, для которого мы уже реализовали класс 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. Сразу же открывается новое окно, в котором мы видим список подключенных устройств. Нам необходимо найти наш девайс и выбрать именно его:
Жмем на 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 снова!
Спасибо огромное за цикл статей! Очень интересно, доступно и полезно! Буду внедрять в свой основной проект!
Спасибо за отзыв! Рад, что понравилось!
За проделанную работу огромное спасибо! Будем пробовать.
А в следующих статьях было бы здорово увидеть работу с ethernet на базе W5100 или DP83848 Ethernet Board ( думаю не одному мне будет интересно )
Кстати, возможно будет в ближайшее время Ethernet, по-моему как раз-таки DP83848, но точно не помню какая там микросхема физического уровня стоит )
Самая адекватная информация. спасибо.
"1. Скачиваем архив с библиотекой с официального сайта Libusb."
а подскажи пожалуйста более конкретную информацию, где это взять.
http://www.libusb.org/ - вот официальный сайт, а вообще можно из любого источника скачать, особой разницы не будет.
Пока ещё не знаток в 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 - это косячит движок кода на сайте, который периодически подменяет правильные символы (в частности знаки ">" и "<"). Сейчас я все поправлю в коде.
1
Теперь проект собирается, но в поле ошибок при сборке выходит следующее
Запускается 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);
Aveal, заработало. Контакт МК и ПК установлен, благодарю за статьи.
Отлично!
А как быть в случае передачи с МК на ПК если я беру данные например с АЦП, там ведь тип не char получается... На ПК соответственно вижу разнообразные символы вместо цифр?
На ПК через qDebug() видишь символы? Тогда выводи так:
qDebug() << (unsigned char)variable; Можно объявить массив unsigned char и в него считывать данные.
Тут при работе с STM32 случилась неприятность. Вдруг в диспетчере задач и вкладки Устройства HID пропал мой STM32. Я переустановил драйвера, по новой настроил фильтра, всё вроде ок. Но проект на Qt с USB не запускается, сообщение Qt Программа неожиданно завершилась и ..........exe завершился крахом. В диспетчере МК отображается с вопросиком, в его свойствах написано "Запуск этого устройства невозможен. (Код 10)" и "Найдена лишняя конечная коллекция, или конечные коллекции не обнаружены.".
Использую Windows 10, перед этим ничего не менял и не трогал, просто вдруг перестало работать.
Еще странно после установки драйверов libUSB (я кстати для их установки использовал exe файл котрый шел в архиве, как и раньше) МК попадал не в список HID, а в диспетчере создалась отельная вкладка "STM32 Custom Human interface" и фильтр её не видел. Далее я нажимал по этой вкладке-> обновить драйвер автоматически, вроде бы начиналась загрузка и установка но заканчивалось с ошибкой что то вроде Windows удалось обнаружить драйвера но при их установке возникла ошибка. И далее МК уже перемещался в HID устройства, после чего я мог внести его в фильтр.
Подскажите? Возможно бывало такое?
У меня вроде бы такого не возникало...
Несколько раз переустановил драйвера и пока работает.
Ещё один вопросик, а на какой максимальной скоростью можно передавать данные, я так понимаю если передавать пакетами по 64 байта и каждый кадр будет передаваться с интервалом 1 мс то получается 64 КБ, правильно, реальна такая скорость достижима?
Надо проверять на практике...
Приветствую!
Хотелось бы уточнить тип проекта для Кьюта - он обычный, Win32 прога или консольный? Автору пожелание - если возможно, выложить в архиве этот проект, легче разбираться, заодно и ошибки в проекте в новых версиях Кьюта переловить и предостеречь других новичков...
К сожалению, проект не сохранился (
Проект - Qt Widget - GUI приложение Qt.
Но можно и консольное сделать и любое другое.
Здравствуйте! Не подскажете, как была исправлена ошибка, про которую писал LEV 27.04.2016 в 21:15, что проект собирается, но выходит в крах? У меня сейчас то же самое, не могу понять, где и что не так...
Обычно выходит "программа неожиданно завершилась" если идет обращение к несущестующему элементу массива, например.
А можно как-то без стороннего ПО для установки фильтра? (чтобы наша программа делала все сама, юзеру только запустить и подключить)
Я, если честно, такой проблемой не озадачивался, не могу ничего подсказать )
Привет и огромное спасибо за столь доходчивое пояснение о создании HID,воткнулся в проблему отсутствия файлов из статьи в новой версии LIBUSB 1.0.24
Благодарю за отзыв!
Я особо больше не пользовался этой библиотекой, по новым версиям не подскажу.
Та же проблема была. Использовал 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;
}
Привет. Возможно ли чтение данных только с USB-накопителя?