1. 设备描述符修改
设备描述符主要修改的是PID、VID、设备发现版本号以及字符串描述。
static const u8 sDeviceDescriptor[] = { //<Device DescriptorUSB_DT_DEVICE_SIZE, // bLength: Size of descriptorUSB_DT_DEVICE, // bDescriptorType: Device
#if defined(FUSB_MODE) && FUSB_MODE0x10, 0x01, // bcdUSB: USB 1.1
#elif defined(FUSB_MODE) && (FUSB_MODE ==0 )0x00, 0x02, // bcdUSB: USB 2.0
#else
#error "USB_SPEED_MODE not defined"
#endif0x00, // bDeviceClass: none0x00, // bDeviceSubClass: none0x00, // bDeviceProtocol: noneEP0_SETUP_LEN,//EP0_LEN, // bMaxPacketSize0: 8/64 bytes'Y', 'D', // idVendor: 59 44 - YD,电脑上小端格式显示为0x4459'Z', 'L', // idProduct: 5a 4c - ZL,电脑上小端格式显示为0x4c5a0x00, 0x01, // bcdDevice: version 1.00x01, // iManufacturer: Index to string descriptor that contains the string <Your Name> in Unicode0x02, // iProduct: Index to string descriptor that contains the string <Your Product Name> in Unicode0x00, // iSerialNumber: none//usb设备序列号0x01 // bNumConfigurations: 1
};
iManufacturer和iProduct如果不想用可以全部填0x00,这样主机不会去请求字符串描述符。
- Manufacturer string
static const u8 product_string[] = {38, //该描述符的长度为38字节,全部长度,含长度和类型字段0x03, //字符串描述符的类型编码为0x03'U', 0x00,'S', 0x00,'B', 0x00,' ', 0x00,'B', 0x00,'L', 0x00,'E', 0x00,'_', 0x00,'N', 0x00,'F', 0x00,'C', 0x00,' ', 0x00,'W', 0x00,'r', 0x00,'i', 0x00,'t', 0x00,'e', 0x00,'r', 0x00,
};
- iProduc string
static const u8 MANUFACTURE_STR[] = {38, //该描述符的长度为38字节,全部长度,含长度和类型字段0x03, //字符串描述符的类型编码为0x030x59, 0x00, //Y0x75, 0x00, //u0x61, 0x00, //a0x6e, 0x00, //n0x44, 0x00, //D'o', 0x00, //o'u', 0x00, //u0x20, 0x00, //0x54, 0x00, //T0x65, 0x00, //e0x63, 0x00, //c0x68, 0x00, //h0x6e, 0x00, //n0x6f, 0x00, //o0x6c, 0x00, //l0x6f, 0x00, //o0x67, 0x00, //g0x79, 0x00, //y
};
2. 配置描述符修改
配置描述符保留原样,不做修改。当然,也可以自己修改是否支持唤醒和供电电流最大限值等。
static const u8 sConfigDescriptor[] = { //<Config Descriptor
//ConfiguraTIonUSB_DT_CONFIG_SIZE, //bLengthUSB_DT_CONFIG, //DescriptorType : ConfigDescriptor0, 0, //TotalLength0,//bNumInterfaces: 在set_descriptor函数里面计算0x01, //bConfigurationValue - ID of this configuration0x00, //Unused
#if USB_ROOT2 || USB_SUSPEND_RESUME || USB_SUSPEND_RESUME_SYSTEM_NO_SLEEP0xA0, //Attributes:Bus Power remotewakeup
#else0x80, //Attributes:Bus Power
#endif50, //MaxPower * 2ma
};
3. 接口描述符、HID类描述符、端点描述符修改
- 接口描述符里设置接口编号、端点数量和USB设备类别为HID类。
- HID类描述符主要设置了HID协议版本号,以及下级描述符得数量为1个,类型为报表描述符、报表描述符的长度值
- 端点描述符描述了一个输入端点0x84,一个输出端点0x04。输入端点用于设备向主机上传数据,输出端点用于主机向设备下传数据。传输方式均设置为中断传输。注意,如果不配置输出端点,会默认走控制传输通道默认端点0。
接口描述符、HID类描述符、端点描述符一般是与配置描述符一起被主机请求的。
static const u8 sHIDDescriptor[] = {
//HID//InterfaceDeszcriptor:USB_DT_INTERFACE_SIZE, // LengthUSB_DT_INTERFACE, // DescriptorType,接口描述符0x00, // bInterface number,接口编号为00x00, // AlternateSetting,无备用接口描述符0x02, // NumEndpoint,端点2个USB_CLASS_HID, // Class = Human Interface Device0x00, // Subclass, 0 No subclass, 1 Boot Interface subclass0x00, // Procotol, 0 None, 1 Keyboard, 2 Mouse0x00, // Interface Name,字符串描述符里无接口说明//HIDDescriptor:0x09, // bLengthUSB_HID_DT_HID, // bDescriptorType, HID Descriptor0x10, 0x01, // bcdHID, HID Class Specification release NO.HID协议版本V1.10x00, // bCuntryCode, Country localization (=none)0x01, // bNumDescriptors, Number of descriptors to follow//下一级描述符数量0x22, // bDescriptorType, Report Desc. 0x22, Physical Desc. 0x23//下一级描述符类型:报表描述符sizeof(sHIDReportDesc), 0, // wDescriptorLength//下一级的报表描述符长度//EndpointDescriptor:USB_DT_ENDPOINT_SIZE, // bLength,长度:9bytesUSB_DT_ENDPOINT, // bDescriptorType, Type,类型:端点描述符USB_DIR_IN | CUSTOM_HID_EP_IN, // bEndpointAddress,设备上传端点地址,0x84USB_ENDPOINT_XFER_INT, // Interrupt,传输类型:中断LOBYTE(MAXP_SIZE_CUSTOM_HIDIN), HIBYTE(MAXP_SIZE_CUSTOM_HIDIN),// Maximum packet size,端点一次最大传输字节数:64bytes0x01, // bInterval, for high speed 2^(n-1) * 125us, for full/low speed n * 1ms,主机1ms从端点取一次数据//Endpoint Descriptor:USB_DT_ENDPOINT_SIZE, // bLengthUSB_DT_ENDPOINT, // bDescriptorType, Type,类型:端点描述符CUSTOM_HID_EP_OUT, // bEndpointAddress,主机下传端点地址,0x04USB_ENDPOINT_XFER_INT, // Interrupt,传输类型:中断LOBYTE(MAXP_SIZE_CUSTOM_HIDOUT), HIBYTE(MAXP_SIZE_CUSTOM_HIDOUT),// Maximum packet size,,端点一次最大传输字节数:64bytes0x01, // bInterval, for high speed 2^(n-1) * 125us, for full/low speed n * 1ms,主机向端点写入数据间隔为1ms
};
4. 报表描述符修改
对HID设备来说,报表描述符是最关键的。它定义了数据格式和意义,主机必须遵守报表的规则,下发数据给设备和解析设备上传的数据。
这里简单写了一个自定义的报表描述符,主要功能是进行数据通讯。因此LOGICAL_MINIMUM是0,LOGICAL_MAXIMUM是255。
- LOGICAL_MAXIMUM
注意LOGICAL_MAXIMUM不能写作0x25, 0xFF,必须写成0x26, 0xFF, 0x00。
因为最高位是表示符号,如果用0x25, 0xFF,那是一个负数了。为了保证为正数,大于127(0x7F)的数,必须进位,255(0xFF)应该表示为0x00FF。因为多加了1个字节,所以0x25要改成0x26。
-
REPORT_SIZE
设为0x08,即一个字节。 -
REPORT_COUNT
设为0x20,即规定一个完整的报表应传输32bytes。如果不足这个数量,数据留在驱动层不会报到应用层。实际测试HID调试助手是把不足的字节自动补0了,凑齐了再发送。REPORT_COUNT具体数值可以根据实际应用需求来设置。
//自定义hid报表描述符----custom hid
static const u8 sHIDReportDesc[] = {0x06, 0x00, 0xFF, //USAGE_PAGE (Vendor Defined Page 1)0x09, 0x00, //USAGE (Undefined)0xA1, 0x01, //COLLECTION (Application)//开一个集合0x09, 0x00, //USAGE (Undefined)0x15, 0x00, //LOGICAL_MINIMUM (0)0x26, 0xFF, 0x00, //LOGICAL_MAXIMUM (255)0x75, 0x08, //REPORT_SIZE (8)0x95, 0x20, //REPORT_COUNT (32)//一次报表32个字节,如果端点传输不满32bytes,不会向应用层报告0x81, 0x06, //INPUT (Data,Var,Rel)0x09, 0x00, //USAGE (Undefined)0x91, 0x06, //OUTPUT (Data,Var,Rel)0xC0 //END_COLLECTION//集合关闭
};
5. 实现自己的hid接收回调函数
在\apps\common\device\usb\device\task_pc.c文件里,实现自己的HID接收回调功能函数。
下面的代码例子,只是简单的对hid收到的数据进行了串口输出打印,然后将接收数据按位取反,然后重新发回主机。
#if TCFG_USB_CUSTOM_HID_ENABLE
static void custom_hid_rx_handler(void *priv, u8 *buf, u32 len)
{printf("%s,%d,\n", __func__, __LINE__);put_buf(buf, len);//串口打印hid收到的数据for(int i=0;i<len;i++){buf[i]=(unsigned char)~buf[i];//对数据按位取反}custom_hid_tx_data(0, buf, len);//将收到的数据填入上传端点,让主机取走
}
#endif
以上修改完成以后需要屏蔽一个USB升级函数,主要是给dongle例程使用的。如果不屏蔽,编译会报错,因为我们并没有链接dongle相关的.c和.h文件。编译器会报告找不到dongle_send_data_to_pc_3(data, len)函数。
static int update_send_user_data_do(void *priv, void *data, u16 len)
{
//#if TCFG_USB_CUSTOM_HID_ENABLE
// //-------------------!!!!!!!!!!考虑关闭RCSP_BTMATE_EN使能编译报错
// extern void dongle_send_data_to_pc_3(u8 * data, u16 len);
// dongle_send_data_to_pc_3(data, len);
//#endifreturn 0;
}
6. 添加void usb_start()函数到app start程序里
- void usb_start()函数如下:
void usb_start()
{//......此处省略1万字,,,,,无关代码省略.......//#ifdef USB_DEVICE_CLASS_CONFIGg_printf("USB_DEVICE_CLASS_CONFIG:%x", USB_DEVICE_CLASS_CONFIG);usb_device_mode(usbfd, USB_DEVICE_CLASS_CONFIG);//根据设定的类别,初始化USB
#endif//......此处省略1万字,,,,,无关代码省略.......//#if TCFG_USB_CUSTOM_HID_ENABLEcustom_hid_set_rx_hook(NULL, custom_hid_rx_handler);//设置USB中断接收回调函数printf("custom_hid rx_hook\n");
#endif
}
- 添加位置如下:
我这里用的spp_ble例程,直接加到末尾即可。
/*************************************************************************************************/
/*!* \brief app start** \param [in]** \return** \note*/
/*************************************************************************************************/
static void spple_app_start()
{log_info("=======================================");log_info("-----------spp_and_le demo-------------");log_info("=======================================");log_info("app_file: %s", __FILE__);if (enter_btstack_num == 0) {enter_btstack_num = 1;clk_set("sys", BT_NORMAL_HZ);//有蓝牙
#if (TCFG_USER_EDR_ENABLE || TCFG_USER_BLE_ENABLE)u32 sys_clk = clk_get("sys");bt_pll_para(TCFG_CLOCK_OSC_HZ, sys_clk, 0, 0);#if TCFG_USER_EDR_ENABLEbtstack_edr_start_before_init(NULL, 0);
#if DOUBLE_BT_SAME_MAC//手机自带搜索界面,默认搜索到EDR__change_hci_class_type(BD_CLASS_TRANSFER_HEALTH);//
#endif
#endif#if TCFG_USER_BLE_ENABLEbtstack_ble_start_before_init(&trans_data_ble_config, 0);
#endifbtstack_init();#else
//no bt,to for testsys_timer_add(NULL, spple_timer_handle_test, 1000);
#endif}/* 按键消息使能 */sys_key_event_enable();#if (TCFG_PC_ENABLE)extern void usb_start();extern void usb_hid_set_repport_map(const u8 * map, int size);//配置选择上报PC的描述符log_info("my usb_hid_set_repport_map.");extern const u8 sHIDReportDesc_custom[];extern int HID_REPORTDESC_LEN;usb_hid_set_repport_map(sHIDReportDesc_custom, HID_REPORTDESC_LEN);usb_start();//添加USB初始化函数#endif
}
7. 测试实例
- 通过计算机设备管理器查看人机接口设备变化
USB插入前:
USB插入后:多了一个标准的供应商自定义HID设备。
- 通过计算机设备管理器查看自定义设备的硬件ID
可以点击查看详细信息,看到VID和PDI是刚才代码里设备描述符设置的,VID: 0x4459,PID: 0x4C5A。其中REV_0100是设备描述符里自己定义的设备发行版本号BCD格式,V1.0。
- 使用USB HID调试助手进行数据通讯测试
使用USB HID调试助手测试,配置好VID、PID和接口、端点地址后,发送32bytes数据,可以看到接收区域收到取反的32bytes数据。这是在回调函数里进行简单取反测试通讯功能的。
- 使用SSCOM串口助手查看USB主机发来的数据
hid接收回调函数里进行了串口打印输出。
- 使用USB BusHound软件查看收发数据流