文章目录
- ble hid 学习笔记
- HID报告描述符与BLE HID profile之间关系
- 1、HID报告描述符
- 富芮坤fr8016 设计Joystick例子
- 1、描述X轴Y轴Z轴
- 2、描述按钮
- 3、Joystick 报告描述符
- 4、程序修改
- 5、win10测试
- 6、android 测试
- 总结
ble hid 学习笔记
HID报告描述符与BLE HID profile之间关系
说起BLE HID(Human Interface Device)设备,就不得不提USB HID。我的理解HID 读写报告属于传输层,所以BLE和USB在关于HID协议的规定都是一样的,运输层的通信不关心底层是通过蓝牙传还是USB传输。能研究BLE HID的朋友一定明白BLE通信是通过读写GATT属性表中的特征值实现的,同样HID是基于BLE实现的,所以HID协议中的读写报告,是基于BLE属性表中特征值实现的。蓝牙联盟已经替大家想好了,推出了HID profile规范,这个规范规定了BLE HID 模型是啥样的,大家都执行这个标准就可以互通往来了。
1、HID报告描述符
HID类设备定义连接https://usb.org/sites/default/files/hid1_11.pdf
一个报告以说USAGE_PAGE和USAGE开头 COLLECTION Application 和END_COLLECTION相当于一个大括号里面的内容是对 USAGE进行解释。 一个集合分配是一个报告ID,一个报告描述符内可以有多个集合。也就是可以有多个报告ID。每个集合内最多有3种报告。3种报告对应着input、 output、 feature三种报告。比如下图例子。最先出现的是input 其次是 output 所以这个集合有2个报告,报告顺序是input ,output 。依此类推,整理出整个报告描述符中的3种报告的顺序和数量与GATT HID profile 的report 特征值的一一对应关系。
0x05, 0x01, // Usage Pg (Generic Desktop)0x09, 0x06, // Usage (Keyboard)0xA1, 0x01, // Collection: (Application)
// 0x85, 0x03, // Report Id (3 for keyboard)//0x05, 0x07, // Usage Pg (Key Codes)0x19, 0xE0, // Usage Min (224)0x29, 0xE7, // Usage Max (231)0x15, 0x00, // Log Min (0)0x25, 0x01, // Log Max (1)//// Modifier byte0x75, 0x01, // Report Size (1) 1 bit * 80x95, 0x08, // Report Count (8)0x81, 0x02, // Input: (Data, Variable, Absolute)// Reserved byte0x95, 0x01, // Report Count (1)0x75, 0x08, // Report Size (8)0x81, 0x01, // Input: (Constant)//LED repor0x95, 0x05, //Report Count (5)0x75, 0x01, //Report Size (1)0x05, 0x08, //Usage Pg (LEDs )0x19, 0x01, //Usage Min0x29, 0x05, //Usage Max0x91, 0x02, //Output (Data, Variable, Absolute)//3 bit reserved0x95, 0x01, //Report Count (1)0x75, 0x03, //Report Size (3)0x91, 0x01, //Output (Constant)// Key arrays (6 bytes)// this is key array,support simultaneously pressing 6keys report,// from report_buf[3]~report_buf[3]0x95, 0x06, // Report Count (6)0x75, 0x08, // Report Size (8)0x15, 0x00, // Log Min (0)0x25, 0xE7, // Log Max (237)0x05, 0x07, // Usage Pg (Key Codes) , here is the key page,look usb hid key define0x19, 0x00, // Usage Min (0)0x29, 0xE7, // Usage Max (237)0x81, 0x00, // Input: (Data, Array)0xC0, // End Collection
富芮坤fr8016 设计Joystick例子
使用富芮坤FR8016H 蓝牙5,0芯片,做一个蓝牙Joystick,Joystick有3个轴分别是X、Y、Z3个轴,加上8个按钮。每个轴对应一个字节,一个按钮对应一个比特所以8个按钮对应1个字节。所以输入报告需要4个字节。
数据结构如下所示:
1、描述X轴Y轴Z轴
在报告描述符中,一个项目必须包含三个字段, 定义3轴:
//(Generic Desktop) 详细解释上摇杆上的3个轴属于桌面设备0x09, 0x30, // USAGE (X) 定义一个X轴0x09, 0x31, // USAGE (Y) 定义一个Y轴0x09, 0x32, // USAGE (Z) 定义一个Z轴
每个轴的逻辑范围 最小-127 最大12
0x15, 0x81, // LOGICAL_MINIMUM (-127) 对3个轴进行规范 逻辑最小值为-1270x25, 0x7f, // LOGICAL_MAXIMUM (127) 对3个轴进行规范 逻辑最大值为127
用3个字节表示3个轴
//报告大小为8bit 有符号 对应-127 到1270x75, 0x08, // REPORT_SIZE (8)0x95, 0x03, // REPORT_COUNT (3) 3个轴总共有3个字节
发送这3个轴数据到电脑:
3个轴总共有3个字节0x81, 0x02, // INPUT (Data,Var,Abs) 报告为输入,变量,值,绝对值。
3个轴最终描述
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 详细解释上摇杆上的3个轴属于桌面设备0x09, 0x30, // USAGE (X) 定义一个X轴0x09, 0x31, // USAGE (Y) 定义一个Y轴0x09, 0x32, // USAGE (Z) 定义一个Z轴0x15, 0x81, // LOGICAL_MINIMUM (-127) 对3个轴进行规范 逻辑最小值为-1270x25, 0x7f, // LOGICAL_MAXIMUM (127) 对3个轴进行规范 逻辑最大值为127//报告大小为8bit 有符号 对应-127 到1270x75, 0x08, // REPORT_SIZE (8)0x95, 0x03, // REPORT_COUNT (3) 3个轴总共有3个字节0x81, 0x02, // INPUT (Data,Var,Abs) 报告为输入,变量,值,绝对值。
2、描述按钮
定义8个按钮
0x05, 0x09, // USAGE_PAGE (Button) 详细解释 摇杆上有按钮 0x19, 0x01, // USAGE_MINIMUM (Button 1) 定义最小按钮 0x29, 0x08, // USAGE_MAXIMUM (Button 8) 定义最大按钮
每个按钮的状态有2种 0 和1
// 对8个按钮进行规范 逻辑最小值为00x25, 0x01, // LOGICAL_MAXIMUM (1) 对8个按钮进行规范 逻辑最大值为10x95, 0x08, // REPORT_COUNT (8) 8个按钮总共有1个字节 最小按钮到最大按钮对应低到高bit位
用1个字节表示8个按钮
0x75, 0x01, // REPORT_SIZE (1) 8个按钮总共1个字节0x81, 0x02, // INPUT
最为输入报告发送到电脑上
0x81, 0x02, // INPUT (Data,Var,Abs) 报告为输入,变量,值,绝对值。
最终的按钮表示方式
0x05, 0x09, // USAGE_PAGE (Button) 详细解释 摇杆上有按钮 0x19, 0x01, // USAGE_MINIMUM (Button 1) 定义最小按钮 0x29, 0x08, // USAGE_MAXIMUM (Button 8) 定义最大按钮 0x15, 0x00, // LOGICAL_MINIMUM (0) 对8个按钮进行规范 逻辑最小值为00x25, 0x01, // LOGICAL_MAXIMUM (1) 对8个按钮进行规范 逻辑最大值为10x95, 0x08, // REPORT_COUNT (8) 8个按钮总共有1个字节 最小按钮到最大按钮对应低到高bit位0x75, 0x01, // REPORT_SIZE (1) 8个按钮总共1个字节0x81, 0x02, // INPUT (Data,Var,Abs) 报告为输入,变量,值,绝对值。
3个轴和8个按钮合并一起
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 详细解释上摇杆上的3个轴属于桌面设备0x09, 0x30, // USAGE (X) 定义一个X轴0x09, 0x31, // USAGE (Y) 定义一个Y轴0x09, 0x32, // USAGE (Z) 定义一个Z轴0x15, 0x81, // LOGICAL_MINIMUM (-127) 对3个轴进行规范 逻辑最小值为-1270x25, 0x7f, // LOGICAL_MAXIMUM (127) 对3个轴进行规范 逻辑最大值为127//报告大小为8bit 有符号 对应-127 到1270x75, 0x08, // REPORT_SIZE (8)0x95, 0x03, // REPORT_COUNT (3) 3个轴总共有3个字节0x81, 0x02, // INPUT (Data,Var,Abs) 报告为输入,变量,值,绝对值。0x05, 0x09, // USAGE_PAGE (Button) 详细解释 摇杆上有按钮 0x19, 0x01, // USAGE_MINIMUM (Button 1) 定义最小按钮 0x29, 0x08, // USAGE_MAXIMUM (Button 8) 定义最大按钮 0x15, 0x00, // LOGICAL_MINIMUM (0) 对8个按钮进行规范 逻辑最小值为00x25, 0x01, // LOGICAL_MAXIMUM (1) 对8个按钮进行规范 逻辑最大值为10x95, 0x08, // REPORT_COUNT (8) 8个按钮总共有1个字节 最小按钮到最大按钮对应低到高bit位0x75, 0x01, // REPORT_SIZE (1) 8个按钮总共1个字节0x81, 0x02, // INPUT (Data,Var,Abs) 报告为输入,变量,值,绝对值。
要注意的是即使按照上面的写法电脑还是解析不出来,还要给报告描述符加上个壳用了标记自己是桌面摇杆设备。(TI 可以不写报告ID也能识别到,富芮坤 的SDK 不写报告ID 电脑识别不到)
USAGE_PAGE (Generic Desktop)
USAGE (Joystick)
COLLECTION (Application)``Report Id (1) ``COLLECTION (Physical)//上面的报告描述写到这里 ``END COLLECTION
END COLLECTION
3、Joystick 报告描述符
按照上诉要求整理,最后的报告描述符如下所示:
/******************************* HID Report Map characteristic defination */
static const uint8_t hid_report_map[] =
{0x05, 0x01, // USAGE_PAGE (Generic Desktop) 开始声明当前设备为 桌面设备0x09, 0x04, // USAGE (Joystick) 指明作为一个摇杆设备//集合相当于括号,必须要以COLLECTION开始 END_COLLECTION结束0xa1, 0x01, // COLLECTION (Application)0x85, 0x01, // Report Id (1) 报告ID为1 规范上不写也可以但是实测富芮坤的SDK 不加电脑识别不到0xa1, 0x00, // COLLECTION (Physical)0x05, 0x01, // USAGE_PAGE (Generic Desktop) 详细解释上摇杆上的3个轴属于桌面设备0x09, 0x30, // USAGE (X) 定义一个X轴0x09, 0x31, // USAGE (Y) 定义一个Y轴0x09, 0x32, // USAGE (Z) 定义一个Z轴0x15, 0x81, // LOGICAL_MINIMUM (-127) 对3个轴进行规范 逻辑最小值为-1270x25, 0x7f, // LOGICAL_MAXIMUM (127) 对3个轴进行规范 逻辑最大值为127//报告大小为8bit 有符号 对应-127 到1270x75, 0x08, // REPORT_SIZE (8)0x95, 0x03, // REPORT_COUNT (3) 3个轴总共有3个字节0x81, 0x02, // INPUT (Data,Var,Abs) 报告为输入,变量,值,绝对值。0x05, 0x09, // USAGE_PAGE (Button) 详细解释 摇杆上有按钮 0x19, 0x01, // USAGE_MINIMUM (Button 1) 定义最小按钮 0x29, 0x08, // USAGE_MAXIMUM (Button 8) 定义最大按钮 0x15, 0x00, // LOGICAL_MINIMUM (0) 对8个按钮进行规范 逻辑最小值为00x25, 0x01, // LOGICAL_MAXIMUM (1) 对8个按钮进行规范 逻辑最大值为10x95, 0x08, // REPORT_COUNT (8) 8个按钮总共有1个字节 最小按钮到最大按钮对应低到高bit位0x75, 0x01, // REPORT_SIZE (1) 8个按钮总共1个字节0x81, 0x02, // INPUT (Data,Var,Abs) 报告为输入,变量,值,绝对值。0xc0, // END_COLLECTION //关集合,跟上面Physical的对应0xc0 // END_COLLECTION //关集合,跟上面Application的对应};
4、程序修改
富芮坤提供了 ble_hid_kbd_mice demo 在此基础上修改蓝牙的报告描述符,报告特征值数量,即可完整通信。
按照开头的分析方法,分析Joystick报告描述符可知,Joystick需要一个input 报告 这个报告向主机发送4个字节的数据。
所以先报告的数量改成 1
// Number of HID reports defined in the service
#define HID_NUM_REPORTS 1
因为只有一个报告 所以BLE HID profile中多余的报告屏蔽掉,保留一个报告特征值,用于传输HID input 报告。
// HID Feature Report No 0[HID_FEATURE_DECL_IDX] = { { UUID_SIZE_2, UUID16_ARR(GATT_CHARACTER_UUID) },GATT_PROP_READ, 0,NULL,},[HID_FEATURE_IDX] = { { UUID_SIZE_2, UUID16_ARR(REPORT_UUID) },GATT_PROP_READ | GATT_PROP_NOTI,60,NULL,},[HID_REPORT_REF_FEATURE_IDX] = { { UUID_SIZE_2, UUID16_ARR(GATT_REPORT_REF_UUID) },GATT_PROP_READ,sizeof(hid_report_ref_t), NULL,},[HID_FEATURE_CCCD_IDX] = { { UUID_SIZE_2, UUID16_ARR(GATT_CLIENT_CHAR_CFG_UUID) },GATT_PROP_READ | GATT_PROP_WRITE, 0, NULL,},
再修改hid_gatt_add_service中的hid 报告信息数组,因为只有一个输入报告,所以只写一个报告即可。
/********************************************************************** @fn hid_gatt_add_service** @brief Simple Profile add GATT service function.* 添加GATT service到ATT的数据库里面。** @param None.*** @return None.*/
void hid_gatt_add_service(void)
{gatt_service_t hid_profie_svc;hid_profie_svc.p_att_tb = hid_profile_att_table;hid_profie_svc.att_nb = HID_ATT_NB;hid_profie_svc.gatt_msg_handler = hid_gatt_msg_handler;hid_rpt_info[0].report_id = 1; //refer to report map, this is Joystick input.hid_rpt_info[0].report_type = HID_REPORT_TYPE_INPUT; //att_table, perm must be GATT_PROP_READ | GATT_PROP_NOTIhid_svc_id = gatt_add_service(&hid_profie_svc);
}
最后是发送报告,这里测试发现如果发送报告的数据特别频繁容易导致发送出错,所以这里 程序修改成,当3个轴数据 按钮 和之前不一样则更新数据。
adc_enable(NULL, NULL, 0);adc_get_result(ADC_TRANS_SOURCE_PAD, 0x01, &result);myJoystick.axis_x = 255.00*result/1023;adc_get_result(ADC_TRANS_SOURCE_PAD, 0x02, &result);myJoystick.axis_y = 255.00*result/1023;adc_get_result(ADC_TRANS_SOURCE_PAD, 0x04, &result);myJoystick.axis_z = 255.00*result/1023;key[0] = myJoystick.axis_x ; // key[1] = myJoystick.axis_y ; // key[2] = myJoystick.axis_z ; // 1if(oldMyJoystick.axis_x != myJoystick.axis_x||oldMyJoystick.axis_y != myJoystick.axis_y||oldMyJoystick.axis_z != myJoystick.axis_z){hid_gatt_report_notify(slave_link_conidx,HID_KEYBOARD_RPT_ID,key,HID_KEYBOARD_IN_RPT_LEN);}oldMyJoystick.axis_x = myJoystick.axis_x ;oldMyJoystick.axis_y = myJoystick.axis_y ;oldMyJoystick.axis_z = myJoystick.axis_z ;
5、win10测试
按照上述修改将程序下载到芯片中,电脑连接蓝牙,如下图所示。
只有当摇杆出现在鼠标,键盘和笔这一栏时,说明设备连接成功可以发送数据。
电脑端可以用win10 自带的驱动对摇杆进行测试,打开控制面板下的设备和打印机,如果写的没有问题,就会在设备栏出现个手柄。
在手柄上右键游戏控制器设置点击属性
当出现下面界面时,就可以按手柄上的按钮和3个轴,界面就会显示对于的值。
如果觉得自带的软件麻烦可以用 joystick tester软件也可以测试。
6、android 测试
手机上安装 Game Pad Test 手机上只能识别到2个轴不知道啥原因
总结
设计 BLE HID 设备的前提一定要搞懂 BLE特征值和HID报告描述符之间的关系,以及报告描述符中有多少个报告每个报告有多少个字节,如果这些关系弄明白了 做BLE HID 设备将轻车熟路了。