Всем привет!), сегодня речь пойдет об USB HID (основа Keil\ARM\Boards\…\…\HID). USB HID (human interface device) class — класс устройств USB для взаимодействия с человеком. Этот класс включает в себя такие устройства как клавиатура, мышь, игровой контроллер. Класс USB HID определен в нескольких документах, предоставляемых USB Implementers Forum, в частности, рабочей группой по работе с устройствами. Иногда всё-таки приходит время перехода с виртуальных com портов (сюда же CDC), на чистый usb протокол, и над протокол HID (пользовательский протокол). USB HID характеризуется типом передачи, существует 4 типа передачи:
1. Передача по управлению (USB_ENDPOINT_TYPE_CONTROL 0x00).
2. Передача по прерыванию (USB_ENDPOINT_TYPE_INTERRUPT 0x03).
3. Передачи массивами (USB_ENDPOINT_TYPE_BULK 0x02).
4. Изохронная передача данных (USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01).
И конечными точками (USB_ENDPOINT_IN, USB_ENDPOINT_OUT). Конечная точка это канал для работы с пользовательским интерфейсом. Например у SAM3U4C у него аж 6 штук. Причем каждая конечная точка отличается размером буфера и количеством банков.
Самое главное это правильное описание дескриптора пакета передачи данных, для начала укажем сколько байт мы хотим передать, принять или тоже самое для особенных пакетов данных.
usbdesc.c
#define HID_INPUT_REPORT_BYTES 64 /* size of report in Bytes */ #define HID_OUTPUT_REPORT_BYTES 64 /* size of report in Bytes */ #define HID_FEATURE_REPORT_BYTES 64 /* size of report in Bytes */
Далее необходима описать сам USB дескриптор.
const U8 HID_ReportDescriptor[] = { HID_UsagePageVendor( 0x00 ), HID_Usage ( 0x01 ), HID_Collection ( HID_Application ), HID_LogicalMin ( 0 ), /* value range: 0 - 0xFF */ HID_LogicalMaxS ( 0xFF ), HID_ReportSize ( 8 ), /* 8 bits */ HID_ReportCount ( HID_INPUT_REPORT_BYTES ), HID_Usage ( 0x01 ), HID_Input ( HID_Data | HID_Variable | HID_Absolute ), HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ), HID_Usage ( 0x01 ), HID_Output ( HID_Data | HID_Variable | HID_Absolute ), HID_ReportCount ( HID_FEATURE_REPORT_BYTES ), HID_Usage ( 0x01 ), HID_Feature ( HID_Data | HID_Variable | HID_Absolute ), HID_EndCollection, };
Или можно еще так, делал для себя, когда hid-овские полтергейсты не довали мне с ним нормально обмениваться данными.
const U8 HID_ReportDescriptor[] = { //................................. 0x06, 0xFF, 0xFF, // 04|2 , Usage Page (vendor defined?) 0x09, 0x01, // 08|1 , Usage (vendor defined 0xA1, 0x01, // A0|1 , Collection (Application) //.................................. // IN report 0x09, 0x02, // 08|1 , Usage (vendor defined) 0x09, 0x03, // 08|1 , Usage (vendor defined) 0x15, 0x00, // 14|1 , Logical Minimum(0 for signed byte?) 0x26, 0xFF, 0x00, // 24|1 , Logical Maximum(255 for signed byte?) 0x75, 0x08, // 74|1 , Report Size(8) = field size in bits = 1 byte // 94|1 , ReportCount(size) = repeat count of previous item 0x95, HID_INPUT_REPORT_BYTES, 0x81, 0x02, // 80|1 , IN report (Data,Variable, Absolute) //.................................... // OUT report 0x09, 0x04, // 08|1 , Usage (vendor defined) 0x09, 0x05, // 08|1 , Usage (vendor defined) 0x15, 0x00, // 14|1 , Logical Minimum(0 for signed byte?) 0x26, 0xFF, 0x00, // 24|1 , Logical Maximum(255 for signed byte?) 0x75, 0x08, // 74|1 , Report Size(8) = field size in bits = 1 byte // 94|1 , ReportCount(size) = repeat count of previous item 0x95, HID_OUTPUT_REPORT_BYTES, 0x91, 0x02, // 90|1 , OUT report (Data,Variable, Absolute) // Feature report 0x09, 0x06, // 08|1 , Usage (vendor defined) 0x09, 0x07, // 08|1 , Usage (vendor defined) 0x15, 0x00, // 14|1 , LogicalMinimum(0 for signed byte) 0x26, 0xFF, 0x00, // 24|1 , Logical Maximum(255 for signed byte) 0x75, 0x08, // 74|1 , Report Size(8) =field size in bits = 1 byte 0x95, HID_FEATURE_REPORT_BYTES, // 94|x , ReportCount in byte 0xB1, 0x02, // B0|1 , Feature report 0xC0 // C0|0 , End Collection };
Далее делаем USB конфигурацию.
/* Configuration 1 */ USB_CONFIGUARTION_DESC_SIZE, /* bLength */ USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL( /* wTotalLength */ USB_CONFIGUARTION_DESC_SIZE + USB_INTERFACE_DESC_SIZE + HID_DESC_SIZE + USB_ENDPOINT_DESC_SIZE*2 //добавляем один USB_ENDPOINT_DESC_SIZE для отправки данных на ПК ), 0x01, /* bNumInterfaces */ 0x01, /* bConfigurationValue: 0x01 is used to select this configuration */ 0x00, /* iConfiguration: no string to describe this configuration */ // USB_CONFIG_BUS_POWERED /*|*/ /* bmAttributes */ USB_CONFIG_SELF_POWERED /*USB_CONFIG_REMOTE_WAKEUP*/, USB_CONFIG_POWER_MA(100), /* bMaxPower, device power consumption is 100 mA */ /* Interface 0, Alternate Setting 0, HID Class */ USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x00, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x02, /* bNumEndpoints */ ------ количество конечных точек USB_DEVICE_CLASS_HUMAN_INTERFACE, /* bInterfaceClass */ HID_SUBCLASS_NONE, /* bInterfaceSubClass */ HID_PROTOCOL_NONE, /* bInterfaceProtocol */ 0x04, /* iInterface */ /* HID Class Descriptor */ /* HID_DESC_OFFSET = 0x0012 */ HID_DESC_SIZE, /* bLength */ HID_HID_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL(0x0100), /* 1.00 */ /* bcdHID */ 0x00, /* bCountryCode */ 0x01, /* bNumDescriptors */ HID_REPORT_DESCRIPTOR_TYPE, /* bDescriptorType */ WBVAL(HID_REPORT_DESC_SIZE), /* wDescriptorLength */ /* Endpoint, HID Interrupt In */ USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_IN(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ WBVAL(64), /* wMaxPacketSize */ //РАЗМЕР 64 БАЙТА 1,//0x08, /* 32ms */ /* bInterval */ /* on High_speed 2^(bInterval-1) */ //------ ставим время 1с. //------ дописываем пару строк для OUT отправки данных USB_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(2), /* bEndpointAddress */ USB_ENDPOINT_TYPE_INTERRUPT, /* bmAttributes */ WBVAL(64), /* wMaxPacketSize */ 1,//0x09, /* 32ms */ /* bInterval */ /* on High_speed 2^(bInterval-1) */ /* Terminator */ 0 /* bLength */ };
Чтобы не запутаться в IN и OUT, есть аналогия с ПК, IN – входные данные в ПК, OUT – выходные данные из ПК. Сейчас необходимо описать работу конечных точек. Конечную точку номер 1, я буду использовать для записи данных в ПК, конечную точку 2 будем использовать для чтения данных из ПК.
usbuser.c
#if USB_CONFIGURE_EVENT void USB_Configure_Event (void) { if (USB_Configuration) { USB_WriteEP(USB_ENDPOINT_IN(1), HIDVariablesIN, sizeof(HIDVariablesIN)); } } #endif void USB_EndPoint1 (U32 event) { switch (event) { case USB_EVT_IN: USB_WriteEP(USB_ENDPOINT_IN(1), HIDVariablesIN, sizeof(HIDVariablesIN)); break; } } void USB_EndPoint2 (U32 event) { U32 size_OUT; switch (event) { case USB_EVT_OUT: size_OUT = USB_ReadEP(USB_ENDPOINT_OUT(2), HIDVariablesOUT); memcpy(HIDVariablesIN, HIDVariablesOUT, size_OUT); } }
Наша программа будет принимать данные с ПК и отправлять через USB HID их назад. Далее скачиваем кросс платформенную либу для USB HID (https://github.com/signal11/hidapi) , и меняем исходник.
#include <stdio.h> #include <wchar.h> #include <string.h> #include <stdlib.h> #include "hidapi.h" // Headers needed for sleeping. #ifdef _WIN32 #include <windows.h> #else #include <unistd.h> #endif #define VID 0xC251 #define PID 0x2301 int main(int argc, char* argv[]) { FILE *fp; fp = fopen("report.txt", "w"); int res; unsigned char buf[64]; #define MAX_STR 255 wchar_t wstr[MAX_STR]; hid_device *handle; int i; #ifdef WIN32 UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); #endif struct hid_device_info *devs, *cur_dev; if (hid_init()) return -1; devs = hid_enumerate(0x0, 0x0); cur_dev = devs; while (cur_dev) { printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number); printf("\n"); printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string); printf(" Product: %ls\n", cur_dev->product_string); printf(" Release: %hx\n", cur_dev->release_number); printf(" Interface: %d\n", cur_dev->interface_number); printf("\n"); cur_dev = cur_dev->next; } hid_free_enumeration(devs); // Set up the command buffer. memset(buf,0x00,sizeof(buf)); buf[0] = 0x01; buf[1] = 0x82; // Open the device using the VID, PID, // and optionally the Serial number. ////handle = hid_open(0x4d8, 0x3f, L"12345"); handle = hid_open(VID, PID, NULL); if (!handle) { printf("unable to open device\n"); return 1; } // Read the Manufacturer String //wstr[0] = 0x0000; res = hid_get_manufacturer_string(handle, wstr, MAX_STR); if (res < 0) printf("Unable to read manufacturer string\n"); printf("Manufacturer String: %ls\n", wstr); // Read the Product String wstr[0] = 0x0000; res = hid_get_product_string(handle, wstr, MAX_STR); if (res < 0) printf("Unable to read product string\n"); printf("Product String: %ls\n", wstr); // Read the Serial Number String wstr[0] = 0x0000; res = hid_get_serial_number_string(handle, wstr, MAX_STR); if (res < 0) printf("Unable to read serial number string\n"); printf("Serial Number String: (%d) %ls", wstr[0], wstr); printf("\n"); // Read Indexed String 1 wstr[0] = 0x0000; res = hid_get_indexed_string(handle, 1, wstr, MAX_STR); if (res < 0) printf("Unable to read indexed string 1\n"); printf("Indexed String 1: %ls\n", wstr); // Set the hid_read() function to be non-blocking. hid_set_nonblocking(handle, 0); // Read requested state. hid_read() has been set to be // non-blocking by the call to hid_set_nonblocking() above. // This loop demonstrates the non-blocking nature of hid_read(). res = 0; int t; int inc_array = 0; char array_char[5][64] = { " mcu.by_1", " mcu.by_2", " mcu.by_3", " mcu.by_4", " mcu.by_5", }; int k; for(t=0; t<1000; t++) { memcpy(buf, array_char[inc_array], sizeof(array_char[inc_array])); for (k = 0; k < 3; k++) { res = hid_write(handle, buf, sizeof(buf)); #ifdef WIN32 Sleep(1); #else usleep(1*1000); #endif } fprintf(fp,"IN: "" ");printf("IN: "" "); printf("%s\n",buf); fprintf(fp,"%s\n",buf); if (inc_array < 4) inc_array++; else inc_array = 0; unsigned char outbuf[64] = {0}; for (k = 0; k < 3; k++) { res = hid_read(handle, outbuf, 64); #ifdef WIN32 Sleep(1); #else usleep(1*1000); #endif } fprintf(fp,"OUT: "" ");printf("OUT: "" "); for (i = 0; i < res; i++){ printf("%c", outbuf[i]); fprintf(fp,"%c", outbuf[i]); } printf(" ""\n");fprintf(fp," ""\n"); } fclose(fp); hid_close(handle); hid_exit(); #ifdef WIN32 system("pause"); #endif return 0; }
Я делаю трижды read и write, только для примера.
Итого:
Всем пока!) продолжим следующий раз.