Стаття Досвід застосування Human Interface Driver (HID) при розробці USB пристрою на базі PIC16С745

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

1. Постановка задачі

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

2. Вибір моделі мікроконтролера та способу передачі даних

Для створення системи було обрано мікроконтролер PIC16C745, т.к. він володіє інтерфейсом USB, має вбудований 8-бітний АЦП, програмне забезпечення для роботи з шиною USB і безкоштовне середовище розробки. Вибір способу передачі даних після вибору мікроконтролера був очевидним. Компанія Microchip надає приклад прошивки мікроконтролера, в якому емульується USB миша з інтерфейсом HID. Завдання розробки системи передачі та прийому даних у мікроконтролері зводиться до правильної конфігурації дескриптора HID пристрою та використання процедур запису та читання наданих у ПЗ для роботи з USB. Завдання програми - проводити пошук пристрою на USB шині, отримувати доступ до нього, працювати з ним і потім правильно закривати.

3. ПЗ мікроконтролера

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

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

3.2. Взаємодія з USB

При підключенні пристрою до USB шини відбувається процес ініціалізації пристрою, тобто. його скидання та отримання про нього інформації. Всі ці функції реалізовані ПЗ для взаємодії з USB. Для того, щоб пристрій правильно визначався операційною системою, потрібно правильно налаштувати дескриптори самого пристрою, його інтерфейсів та дескриптори HID. Розглянемо це питання детальніше.

Дескриптори в ПЗ взаємодії з USB від Microchip є послідовними рядками коду, що повертають будь-яке значення. Коли відбувається запит на будь-який дескриптор, відбувається послідовний виклик цих стік, запис повернутих значень в буфер і подальша передача в комп'ютер. Інформація про те, як це відбувається, можна отримати з вихідних текстів програми.

3.3.1. Device Descriptor

Device Descriptor описує пристрій загалом, у ньому вказується версія протоколу USB, IDвиробника пристрою та самого пристрою, максимальний розмір пакета, який може відправити або прийняти пристрій.

3.3.2. Config Descriptor

Описує конфігурацію пристрою, кількість кінцевих точок та їх призначення.

3.3.3. Report Descriptor

Цей дескриптор описує формат повідомлення HID пристрою та те, як пристрій буде представлено на панелі диспетчера пристроїв Windows. При цій конфігурації пристрій може приймати та надсилати пакети довжиною 8 байт і подається як пристрій ручного введення ReportDescriptor

retlw 0x06 retlw 0x00; page (vendor defined) retlw 0xFF retlw 0x09 retlw 0x01 ; usage (vendor defined 1)

retlw 0xA1 retlw 0x01; collection (application)

retlw 0x19 retlw 0x01 ;usage (vendor usage) retlw 0x29 retlw 0x08 ;usage (vendor usage ) retlw 0x15 retlw 0x00 retlw 0x26 <4 4>retlw 0x00

retlw 0x75 retlw 0x08; report size (8) retlw 0x95 retlw 0x08; report count (2)

retlw 0x81 retlw 0x02; input (2 position bytes X & Y)

retlw 0x19 retlw 0x01 ; input (2 position bytes X & Y; retlw 0xC0 ; end collection end_ReportDescriptor

3.4. Загальний порядок роботи програми

При включенні починає виконуватись основна програма мікроконтролера. Вона викликає функцію ініціалізації USB і чекає, поки пройде ініціалізація. Вся робота з USB відбувається за переривання. Переривання від шини USB обробляється, а потім виконується будь-яка дія (видача дескриптора, просто прийом або передача пакета та ін.). При ініціалізації пристрою він передає дескриптори.комп'ютера і, якщо все вдало (якщо вони побудовані правильно), пристрій з'являється в списку пристроїв Windows. З цього моменту програма користувача може почати з ним працювати.

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

Визначення змінних виглядають так:

bool CDevice::FindTheHID() //Використання серії API Calls до find a HID with matching Vendor and Product ID. H > Attributes; SP_DEVICE_INTERFACE_DATA devInfoData; bool LastDevice = FALSE; int MemberIndex = 0; bool MyDeviceDetected = FALSE; LONG Result;

//Це є vendor and product IDs to look for.

const unsigned int Vendor > const unsigned int Product > Length = 0; detailData = NULL; DeviceHandle=NULL;

/* API функція: HidD_GetHidGuid Get GUID для всіх систем HIDs. Відновити: GUID в HidGuid. */

H > /* API функція: SetupDiGetClassDevs Оберти: натисніть на клавіатуру, щоб дізнатися про set set all installed devices. Requires: the GUID повернуто до GetHidGuid. */ hDevInfo=SetupDiGetClassDevs (&HidGuid, NULL, NULL, DIGCF_PRESENTDIGCF_INTERFACEDEVICE); devInfoData.cbSize = sizeof(devInfoData);

//Step throughдоступні пристрої, які шукають той, який ми хочемо. //Безуспішно припинити виявлення потрібного пристрою або перевірити всі доступні пристрої. Індекс члена = 0; Останній пристрій = FALSE;

do MyDeviceDetected=FALSE; /* Функція API: SetupDiEnumDeviceInterfaces Після повернення MyDeviceInterfaceData містить опис структури SP_DEVICE_INTERFACE_DATA для виявленого пристрою. Потрібно: DeviceInfoSet повертається в SetupDiGetClassDevs. HidGuid повернуто в GetHidGuid. Індекс для визначення пристрою. */

if (Result != 0) //Пристрій було виявлено, тому отримайте більше інформації про нього.

/* Функція API: SetupDiGetDeviceInterfaceDetail Повертає: структуру SP_DEVICE_INTERFACE_DETAIL_DATA що містить інформацію про пристрій. Щоб отримати інформацію, викличте цю функцію двічі. Перший час повертає розмір структури в довжині. Другий раз повертає покажчик на дані в DeviceInfoSet. Потрібно: Набір даних DeviceInfoSet, повернутий SetupDiGetClassDevs Структура SP_DEVICE_INTERFACE_DATA, повернута SetupDiEnumDeviceInterfaces. Останній параметр є додатковим покажчиком на структуру SP_DEV_INFO_DATA. Ця програма не отримує та не використовує структуру. Якщо ви отримуєте структуру, установіть MyDeviceInfoData.cbSize = довжину MyDeviceInfoData. і передати адресу структури. */ //Отримати значення довжини. //Виклик повернеться з помилкою "замалий буфер", яку можна проігнорувати. Результат = SetupDiGetDeviceInterfaceDetail (hDevInfo, &devInfoData, NULL, 0, &Length, NULL);

//Виділити пам'ять для структури hDevInfo, використовуючи повернену довжину. detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(Length); //Установіть cbSize у структурі detailData. detailData -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

// Викличте функцію знову, цього разу передаючи їй повернутий розмір буфера . Результат = SetupDiGetDeviceInterfaceDetail (hDevInfo, &devInfoData, detailData, Length, &Required, NULL);

//Відкрити маркер пристрою.

/* Функція API: CreateFile Повертає: дескриптор, який дозволяє читати та записувати на пристрій . Вимагає: DevicePath у структурі detailData поверненої SetupDiGetDeviceInterfaceDetail. */

DeviceHandle=CreateFile (detailData->DevicePath, GENERIC_READGENERIC_WRITE, FILE_SHARE_READFILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

/* Функція API: HidD_GetAttributes Запитує інформацію з пристрою. Потрібно: дескриптор, який повертає CreateFile. Повертає: структура HIDD_ATTRIBUTES, що містить ідентифікатор постачальника, ідентифікатор продукту та номер версії продукту. Використовуйте цю інформацію, щоб вирішити, чи виявлений пристрій це той, який ми шукаємо. */

//Установіть для розміру кількість байтів у структурі. Атрибути.Розмір = sizeof(Атрибути);

Результат = HidD_GetAttributes (DeviceHandle, &Атрибути); // DisplayLastError("HidD_GetAttributes: "); //Це потрібний пристрій? MyDeviceDetected = FALSE;

if (Attributes.Vendor > if (Attributes.Product > //Ідентифікатори продукту та постачальника збігаються. GetDeviceCapabilities(); PrepareForOverlappedTransfer(); MyDeviceDetected = TRUE; > //якщо (Attributes.Product > else //Ідентифікатор продукту не збігається. CloseHandle(DeviceHandle); > //if (Attributes.Vendor > else / /Ідентифікатор постачальника не збігається. CloseHandle(DeviceHandle);

//Звільнити пам'ять, яку використовує структура detailData (більше не потрібна). вільно (detailData); > //якщо (Результат != 0)

else //SetupDiEnumDeviceInterfaces повернув 0, тому більше немає пристроїв для перевірки. LastDevice=TRUE;

//Якщо ми ще не знайшли пристрій і не спробували всі доступні пристрої, //спробуйте наступний. MemberIndex = MemberIndex + 1;

> //виконати while ((LastDevice == FALSE) && (MyDeviceDetected == FALSE)); SetupDiDestroyDeviceInfoList(hDevInfo); повернути MyDeviceDetected; >

vo > //Отримайте інший дескриптор пристрою для перекритих ReadFiles. ReadHandle=CreateFile (detailData->DevicePath, GENERIC_READGENERIC_WRITE, FILE_SHARE_READFILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

//Отримати об'єкт події для перекритої структури.

/*Функція API: CreateEvent Потрібно: Атрибути безпеки або Null Скидання вручну (true). Використовуйте ResetEvent, щоб установити стан об’єкта події на безсигнальний. Початковий стан (true = signaled) Назва об’єкта події (необов’язково) Повертає: дескриптор об’єкта події */

if (hEventObject == 0) hEventObject = CreateEvent (NULL, TRUE, TRUE, "");

//Встановити елементи структури, що перекривається. H > H > H > > >

DWORD WINAPI ReadThread() Результат DWORD; DWORD BytesWritten = 0; /* Тут ініціалізація приладу та занесення інформації в OutputReport */ /* Кстати, значення Capabilities.OutputReportByteLength і Capabilities.InputReportByteLength задаються в пристрій HID дескриптора */ Result= WriteFile (ReadHandle, OutputReport, Capabilities.OutputReportByteLength, &NumberOfBytesRead,(LPOVERLAPPED) &HIDOverlapped); Result = WaitForSingleObject(hEventObject, 1000); if (Result!=0) Result=GetLastError(); > while (Поки кипить робота) Result = ReadFile (ReadHandle, InputReport, Capabilities.InputReportByteLength, &NumberOfBytesRead, (LPOVERLAPPED) &H > >Result = WaitForSingleObject(hEventObject, 1000); switch (Result) case WAIT_OBJECT_0: case 1: DataReaded=TRUE; те, що прийшло в інпутрепорті*/ break; > case WAIT_TIMEOUT: break; >

default: break; > > ResetEvent(hEventObject); > /* Тут код завершення роботи*/ WriteFile (ReadHandle, OutputReport, Capabilities.OutputReportByteLength, &NumberOfBytesRead, (LPOVERLAPPED) &HIDOverlapped); Result = WaitForSingleObject(hEventObject, 1000); if (Result!=0) Result=GetLastError(); > return 0; >

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