Програмування USB-пристроїв у Delphi, Delphi, компоненти Delphi, вихідні джерела Delphi

Програмування USB-пристроїв у Delphi

Що таке HID-пристрій

Властивості пристрою HID

Організація обміну даними між HID-пристроєм та комп'ютером

Щоб описати взаємодію HID-пристрою з комп'ютером, використаємо термін «хост». У цьому випадку під ним розуміється керуючий пристрій у загальній фізичній архітектурі взаємодії USB-протоколом. Так, усі порти в комп'ютері – хости. До них можна підключати різні USB-пристрої (флешки, миші, веб-камери, фотоапарати та ін.), які не мають хоста. Хост забезпечує виявлення, підключення, відключення, конфігурування пристроїв, а також збирання статистики та керування енергоспоживанням.

HID-пристрій може встановити частоту опитування, під час якого з'ясовується наявність у ньому будь-яких нових даних. Значить, навіть на такому низькому рівні програміст може довіритись системі, оскільки частота опитування та інші параметри обміну даними повинні бути заздалегідь задані у програмі контролера HID-пристрою. Цим протоколом HID відрізняється від загального опису USB 1.1 або USB 2.0, в якому немає жорстких вимог до організації протоколу. Однак при специфічних завданнях, що вимагають підвищеного рівня безпеки, може виявитися досить складно позбавитися циклічних опитувань, коли постійно передаються майже одні й ті самі блоки даних.

Особливості програмування HID-пристроїв

HID пристрої мають спеціальні дескриптори. Коли хост визначить, що пристрій належить до класу HID, він передає керування ним відповідного драйвера. Передбачається, що подальший обмін даними ведеться під його керівництвом.

Програмування пристроїв HID на «верхньому рівні»

Для роботи з HID-пристроєм у середовищі Delphi for win32 застосовуєтьсякомпонент TJvHidDeviceController, що є зручним глобальним менеджером для доступу до HID-пристроїв. А вже на його базі можна отримати об'єктний екземпляр для роботи з конкретним пристроєм.

Основні властивості та події компоненту TJvHidDeviceController

Розглянемо компонент TJvHidDeviceController докладніше. Подія OnArrival спрацьовує на вступ (підключення) до системи HID-пристрою, доступ до пристрою надається в обробнику цієї події через екземпляр класу TJvHidDevice. Проста подія OnDeviceChange реагує на зміну стану пристрою, тільки сигналізує про зміни в системі. Подія OnDeviceData спрацьовує при надходженні даних від одного з пристроїв HID і передає обробникові наступне: HidDev: TJvHidDevice; - Пристрій, від якого були отримані дані;

Подія OnDeviceDataError повідомляє про помилку передачі даних, передаючи процедуру обробки параметри HidDev: TJvHidDevice; - HID-пристрій та Error: DWORD; - код помилки. Подія OnDeviceUnplug повідомляє про виймання пристрою зі списку встановлених у системі. Типи обробників подій на Plug та Unplug однакові (у вихідному тексті: TJvHidUnplugEvent = TJvHidPlugEvent). Обробник передає об'єкт класу TJvHidDevice, що відповідає HID-пристрою.

Для послідовного перерахування наявних у системі HID-пристроїв на виклик методу Enumerate призначено подію OnEnumerate, тобто в обробнику події знайдені пристрої послідовно передаються у вигляді об'єктів. Ця подія примусово ініціюється методом Enumerate, що використовується для «проведення» наявних пристроїв HID через обробник, наприклад при ревізії стану пристроїв HID з ініціативи хоста (комп'ютера).

Подія OnRemoval спрацьовує нафізичне вилучення пристрою з системи і має той самий тип обробника TJvHidUnplugEvent, що й OnDeviceUnplug. Функція CountByProductName видає кількість пристроїв, що задовольняють вказаний в аргументі імені продукту, а CountByVendorName - вказаний в аргументі імені виробника.

Основні властивості та події класу TJvHidDevice

Клас TJvHidDevice — віртуальне подання окремого HID-пристрою. Новий об'єкт цього класу можна отримати, як було сказано, з події OnArrival або OnEnumerate. Функціонал класів TJvHidDeviceController і TJvHidDevice частково дублюється, оскільки в першому з них інтегровані загальний інструментарій для роботи з набором наявних в системі пристроїв HID і механізм доступу до одного з них.

Пристрій можна однозначно ідентифікувати за властивостями SerialNumber, ProductName та VendorName. Щоб отримати відомості про надходження даних із застосуванням такого об'єкта, можна скористатися подією OnData. Відсилання даних ведеться через метод WriteFile (у строгому сенсі через функцію). WriteFile – це оболонка системної функції WriteFile (kernel32).

Щоб проконтролювати факт вилучення пристрою, слід присвоїти свій обробник події OnUnplug. Перед початком обміну даними з HID-пристроєм потрібно переконатися у можливості такого обміну за допомогою HasReadWriteAccess. У цьому класі виникнення помилки обміну даними навіть є окрема подія OnDataError.

Метод ScanDevices (листинг 1) призначений для ініціювання процесу пошуку в системі необхідного пристрою HID. Більшість коду, за винятком виклику методу Enumerate, необов'язкова і забезпечує гнучкість програми, наприклад, для того, щоб до цієї ж тестової програми можна було додати можливістьроботи з інтерфейсу, відмінному від HID. Метод AddError виводить у вікно налагоджувальну інформацію в процесі роботи програми.

Лістинг 1. Ініціювання диска пошуку пристрою

У лістингу 2 наведено обробник події OnEnumerate для пошуку необхідного зовнішнього пристрою. Для простоти вважатимемо, що програма може працювати лише з одним пристроєм потрібного їй типу.

Лістинг 2. Обробник пошуку

Перш ніж розглядати подальшу реалізацію проекту, слід трохи розповісти про прийнятий формат обміну даними верхнього рівня, тобто про структуру, покликану бути посередником між методами прийому-передачі даних та конкретним розв'язуваним прикладним завданням.

Справа в тому, що тут розробнику надається можливість реалізувати свої творчі здібності. Точніше, розробникам, бо процес створення нового протоколу дуже часто буває двостороннім, і при цьому першу скрипку грає той, кому важче реалізовувати алгоритм обміну. Загалом, хоч би яким був протокол обміну, завжди приємно робити кожну програмну сутність максимально наочною та самодостатньою, хай навіть на шкоду деяким загальноприйнятим традиціям.

Рядковий тип описаних членів запису дозволяє вільно та наочно маніпулювати даними у форматі HEX-рядка. І що найприємніше, формат описаного запису ідеологічно стоїть десь посередині між його безпосереднім призначенням та різними формами її подання (INI, HEX, XML тощо).

Листинг 3. Приклад структури відсилання даних для опису алгоритмів обміну даними із зовнішнім пристроєм.

// структура відповідає протоколу

// верхнього рівня, // обумовленому для специфічного пристрою. TedCommand = RECORD Caption: String; // Мітка команди INDEX:Integer; // індекс команди Enabled: Boolean; // Прапор придатності команди WT: Cardinal; // час обробки edClass: String [2] ;// HBX-рядок класу команди edCode:String[2];// HEX-рядок коду команди edLength:String[2]; // Довжина edParam: String; // HEX-CTpoкa (параметр відсилання) edAnswerdta:String;// HEX-CTpoкa // отриманих даних IsAnswerdta:Boolean; // прапор нових даних ERROR:String; // поле для останньої помилки WORKED: Boolean; //Прапор обробки команди end;

Виконання команди, тобто відсилання даних пристрій, реалізовано із застосуванням відсилання пакетів даних довжиною 8 байт (листинг 4). Ця довжина є не єдиним рішенням, такий вибір продиктований вимогами протоколу верхнього рівня і в кожному конкретному випадку може бути іншим. Це, як то кажуть, справа смаку. Дивний прапор IsUSBMode у методі ExecuteCommand (листинг 5 на «Світ ПК-диску») залишений як нагадування про те, що замість роботи з USB нам може знадобитися використовувати COM-порт або інший інтерфейс. На початку відсилається групи даних пристрій передається синхросерія довільно вибраного формату (наприклад, 3E3E3E2B), повідомляє пристрою, що у нього на вході цілком легальні дані. Нагадаю, що в даному випадку йдеться не стільки про HID, скільки про специфічний протокол верхнього рівня, ідеологічно відірваний від «заліза» і призначений для вирішення особливих прикладних завдань.

Листинг 4. Відсилання даних

В обробнику GetDataExecutor отриманих від пристрою даних (пакет по 8 байт) використано спеціально створену подію OnNewInputData для передачі первинно оброблених даних на подальшу обробку, причому із зазначенням їх старого та нового значень (листинг 6 на «Світ ПК-диску»). ТакимТаким чином, події надходження необроблених даних та вказівку на подальшу обробку розв'язуються, дозволяючи додавати якийсь специфічний алгоритм попередження на ранньому етапі помилкової, повторної або непотрібної вхідної інформації.

Представлені приклади роботи з HID-пристроєм ілюструють загальну ідею статті - відносну простоту програмування нестандартних HID-пристроїв засобами Delphi.