РадиоКот :: Управление USB-устройствами под Windows
Например TDA7294

РадиоКот >Статьи >

Теги статьи: Добавить тег

Управление USB-устройствами под Windows

Автор: Serg1987, axentfly@yandex.ru
Опубликовано 16.12.2016
Создано при помощи КотоРед.

Написать данный мануал меня сподвигло желание помочь окружающим в вопросе написания программ под Windows для управления USB устройствами, т.к. в сети на этот счет очень мало сколь более менее толковой информации. "Мало??" - удивятся некоторые. Да, мало.. Нет, есть конечно инструкции по написанию софта с использованием всяких внешних библиотек, типа libusb и т.д. Когда передо мной стала задача написания софта под Windows, я тоже смотрел в сторону данных библиотек. Но меня жутко не устраивал тот факт, что при переносе программы на другой компьютер также необходимо было и перекачивать прилагающуюся usb-либу. Многие согласятся, что это весьма неудобно, особенно, если свой продукт вы собираетесь куда-то продвигать. Поэтому было принято решение - сделать всё средствами Windows. Тем более поставляющиеся драйвера и ресурсы от Майкрософта позволяют программистам делать ПО для USB устройств без всяких там libusb или ещё черт знает каких рудиментов. И такие программы без проблем запустятся на других машинах. Кроме установщика ничего копировать не придется. Что ж.. 

Для начала.. Статья ориентирована на более продвинутых пользователей, нежели чем на начинающих. В статье отсылок к коду будет мало. Для этого смотрите сам проект. Софт писался для устройства USB класса Custom-HID.  Оговорюсь об используемом средстве разработки. У меня это C++ Builder Embarcaderro XE7. В Visual Studio настройка проекта и т.п. может быть немножко по другому. Но общий принцип не меняется - и там и там код по сути одинаков. ОС - Windows 7. Проект я прикрепил в архиве. Напоминаю, данный проект работает только в C++ Builder XE7 и выше. Для того, чтобы открыть в более старых версиях, нужно будет выкинуть из кода инициализации некоторые инклуды и описатели (реализация C++ Builder старых и новых версий отличается). В старых версиях проще создать свой проект и скопировать туда приложенный в моем проекте код.

Это было предисловие. Поехали..

Для начала, для того чтобы программировать под Windows для USB вам нужно скачать пакет для разработчиков от Майрософта Windows WDK по ссылке https://www.microsoft.com/en-us/download/details.aspx?id=11800

Качаем и устанавливаем. Пути оставляем по умолчанию. После этого открываем наш Builder. Заходим в свойства Tools->Options и далее в Environment Options->C++ Options->Paths And Directories.. ищем строку System Include Path.. и добавляем следующие пути для хидер-файлов из WDK. У меня пути для h-файлов такие:

C:WinDDK7600.16385.1incddk
C:WinDDK7600.16385.1incapi

Потом добавляем путь для библиотек lib. Всё тоже самое, но на этот раз ищем строку System Library Path.. В данном блоке должны быть прописаны пути:

C:WindowsSystem32
C:WindowsSysWOW64 (нужно также добавить, если у вас ОС 64 бит)

Готово. Переходим непосредственно к программе. В софте имеются 3 кнопки (Connect, Disconnect и Check). Нас интересует прежде всего волшебная кнопка Connect, а именно тот код, который она выполняет. Итак.. Открываем файл Unit2.cpp и смотрим Инициализация USB и поиск устройства осуществляется с помощью функций Windows, подробное описание которых приведено на сайте MS. Все базовые функции для работы с HID на уровне Windows и указатели на них объявляются в начале программы.

Код инициализации (и поиска нашего устройства) - это функция enumd()

Сразу вызываем:

hDev = SetupDiGetClassDevs(&hguid,NULL,0,DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);

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

Далее получаем размеры структур инфы об устройствах и интерфейсе:

dInf.cbSize = sizeof(SP_DEVINFO_DATA);
dIntDat.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);

А дальше в хитроумной цикле while поочередно получаем инфу о каждом USB-устройстве, пока те не закончатся)) Из этого перебора читаем данные (VID и PID или ещё чего), с и ищем нужное нам устройство (сравниваем полученные данные с теми, что нам нужны . -> функция connecthid()). 

В функции connecthid мы поочередно пытаемся открыть каждое обнаруженное системой устройство. Многие знающие заметят, что тут используются функции, знакомые некоторым по работе с COM-портом. Те же CreateFile и CloseFile. Да, USB устройство открывается как объект файла. А также как и для COM-порта для USB-девайса создаются потоки записи и чтения.

Функция getinfo сравнивает значение VID и PID текущего устройства с искомым. Если такового нет, то ненужное устройство отключается функцией disconhid(), если мы вдруг с ним соединились (не всякое HID устройство можно открыть методом CreateFile). Если устройство успешно найдено, то индикатор на форме меняет цвет с красного на зеленый, а надпись Off внутри него на On. Искомые VID и PID (а также я добавил строку продукта) определены в переменной:

AnsiString productdata[3] = {"1155","22350","I2Cdial"};

VID и PID тут представлены в десятичном, а не hex-виде.

Это краткий мануал для тех, кто хочет писать ПО для своих девайсов на более низком и качественном уровне, без всяких рудиментарных дополнительных библиотек. Как я и говорил, статья рассчитана на более продвинутых пользователей, но если что - вопросы можете задавать. 

Ссылка для скачки самого проекта - внизу.

Исправлено и дополнено (06.07.2017): Друзья, нюанс в данном коде был найден давно. Но я решил дополнить статью сейчас, чтобы некоторые не наступили на те грабли, что и я. Вопрос касается самого порядка инициализации девайса. В проекте инициализация происходит так:

-получаем путь каждого устройства, после чего пытаемся открыть (CreateFile) данное устройство. -После этого считываем методом GetAttribute его VID и PID.                                                                   -И если устройство не то, то закрываем его (CloseFile).

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

1) Находим устройство, получаем его путь в строке. Данная строка в проекте передается как аргумент в функцию connecthid

2) Найдя путь, вычленяем из строки пути искомые VID и PID. Работаем чисто со строкой, девайс не открываем! Для того чтобы понять, какое положение значения VID и PID занимают в строке, советую вывести её куда-либо (какой-нибудь MEMO или StringList). Копируем полученные значения VID и PID в переменные и сравниваем с исходными. Здесь вся работа целиком со строками.

3) Если по пункту 2. VID и PID совпали с искомыми то только сейчас! мы вызываем функцию CreateFile и открываем наше устройство!

Наслаждаемся))


Файлы:
USB project


Все вопросы в Форум.




Как вам эта статья?

Заработало ли это устройство у вас?

9 4 20