Всем привет!), сегодня речь пойдет об 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, только для примера.
Итого:

Всем пока!) продолжим следующий раз.
