1. 设备描述符修改
设备描述符主要修改的是PID、VID、设备发现版本号以及字符串描述。
static const u8 sDeviceDescriptor[] = { //<Device Descriptor
USB_DT_DEVICE_SIZE, // bLength: Size of descriptor
USB_DT_DEVICE, // bDescriptorType: Device
#if defined(FUSB_MODE) && FUSB_MODE
0x10, 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"
#endif
0x00, // bDeviceClass: none
0x00, // bDeviceSubClass: none
0x00, // bDeviceProtocol: none
EP0_SETUP_LEN,//EP0_LEN, // bMaxPacketSize0: 8/64 bytes
'Y', 'D', // idVendor: 59 44 - YD,电脑上小端格式显示为0x4459
'Z', 'L', // idProduct: 5a 4c - ZL,电脑上小端格式显示为0x4c5a
0x00, 0x01, // bcdDevice: version 1.0
0x01, // iManufacturer: Index to string descriptor that contains the string <Your Name> in Unicode
0x02, // iProduct: Index to string descriptor that contains the string <Your Product Name> in Unicode
0x00, // 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, //字符串描述符的类型编码为0x03
0x59, 0x00, //Y
0x75, 0x00, //u
0x61, 0x00, //a
0x6e, 0x00, //n
0x44, 0x00, //D
'o', 0x00, //o
'u', 0x00, //u
0x20, 0x00, //
0x54, 0x00, //T
0x65, 0x00, //e
0x63, 0x00, //c
0x68, 0x00, //h
0x6e, 0x00, //n
0x6f, 0x00, //o
0x6c, 0x00, //l
0x6f, 0x00, //o
0x67, 0x00, //g
0x79, 0x00, //y
};
2. 配置描述符修改
配置描述符保留原样,不做修改。当然,也可以自己修改是否支持唤醒和供电电流最大限值等。
static const u8 sConfigDescriptor[] = { //<Config Descriptor
//ConfiguraTIon
USB_DT_CONFIG_SIZE, //bLength
USB_DT_CONFIG, //DescriptorType : ConfigDescriptor
0, 0, //TotalLength
0,//bNumInterfaces: 在set_descriptor函数里面计算
0x01, //bConfigurationValue - ID of this configuration
0x00, //Unused
#if USB_ROOT2 || USB_SUSPEND_RESUME || USB_SUSPEND_RESUME_SYSTEM_NO_SLEEP
0xA0, //Attributes:Bus Power remotewakeup
#else
0x80, //Attributes:Bus Power
#endif
50, //MaxPower * 2ma
};
3. 接口描述符、HID类描述符、端点描述符修改
- 接口描述符里设置接口编号、端点数量和USB设备类别为HID类。
- HID类描述符主要设置了HID协议版本号,以及下级描述符得数量为1个,类型为报表描述符、报表描述符的长度值
- 端点描述符描述了一个输入端点0x84,一个输出端点0x04。输入端点用于设备向主机上传数据,输出端点用于主机向设备下传数据。传输方式均设置为中断传输。注意,如果不配置输出端点,会默认走控制传输通道默认端点0。
接口描述符、HID类描述符、端点描述符一般是与配置描述符一起被主机请求的。
static const u8 sHIDDescriptor[] = {
//HID
//InterfaceDeszcriptor:
USB_DT_INTERFACE_SIZE, // Length
USB_DT_INTERFACE, // DescriptorType,接口描述符
0x00, // bInterface number,接口编号为0
0x00, // AlternateSetting,无备用接口描述符
0x02, // NumEndpoint,端点2个
USB_CLASS_HID, // Class = Human Interface Device
0x00, // Subclass, 0 No subclass, 1 Boot Interface subclass
0x00, // Procotol, 0 None, 1 Keyboard, 2 Mouse
0x00, // Interface Name,字符串描述符里无接口说明
//HIDDescriptor:
0x09, // bLength
USB_HID_DT_HID, // bDescriptorType, HID Descriptor
0x10, 0x01, // bcdHID, HID Class Specification release NO.HID协议版本V1.1
0x00, // 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,长度:9bytes
USB_DT_ENDPOINT, // bDescriptorType, Type,类型:端点描述符
USB_DIR_IN | CUSTOM_HID_EP_IN, // bEndpointAddress,设备上传端点地址,0x84
USB_ENDPOINT_XFER_INT, // Interrupt,传输类型:中断
LOBYTE(MAXP_SIZE_CUSTOM_HIDIN), HIBYTE(MAXP_SIZE_CUSTOM_HIDIN),// Maximum packet size,端点一次最大传输字节数:64bytes
0x01, // bInterval, for high speed 2^(n-1) * 125us, for full/low speed n * 1ms,主机1ms从端点取一次数据
//Endpoint Descriptor:
USB_DT_ENDPOINT_SIZE, // bLength
USB_DT_ENDPOINT, // bDescriptorType, Type,类型:端点描述符
CUSTOM_HID_EP_OUT, // bEndpointAddress,主机下传端点地址,0x04
USB_ENDPOINT_XFER_INT, // Interrupt,传输类型:中断
LOBYTE(MAXP_SIZE_CUSTOM_HIDOUT), HIBYTE(MAXP_SIZE_CUSTOM_HIDOUT),// Maximum packet size,,端点一次最大传输字节数:64bytes
0x01, // 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);
//#endif
return 0;
}
6. 添加void usb_start()函数到app start程序里
- void usb_start()函数如下:
void usb_start()
{
//......此处省略1万字,,,,,无关代码省略.......//
#ifdef USB_DEVICE_CLASS_CONFIG
g_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_ENABLE
custom_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_ENABLE
btstack_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_ENABLE
btstack_ble_start_before_init(&trans_data_ble_config, 0);
#endif
btstack_init();
#else
//no bt,to for test
sys_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软件查看收发数据流