基于富芮坤fr8016 蓝牙5.0 芯片设计的BLE HID Joystick 游戏摇杆设备

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 byte
    0x75, 0x01,     // Report Size (1)   1 bit * 8
    0x95, 0x08,     // Report Count (8)
    0x81, 0x02,     // Input: (Data, Variable, Absolute)

    // Reserved byte
    0x95, 0x01, // Report Count (1)
    0x75, 0x08, // Report Size (8)
    0x81, 0x01, // Input: (Constant)

    //LED repor
    0x95, 0x05,    //Report Count (5)
    0x75, 0x01,    //Report Size (1)
    0x05, 0x08,    //Usage Pg (LEDs )
    0x19, 0x01,    //Usage Min
    0x29, 0x05,    //Usage Max
    0x91, 0x02,    //Output (Data, Variable, Absolute)
    //3 bit reserved
    0x95, 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 define
    0x19, 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个轴进行规范 逻辑最小值为-127
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)   对3个轴进行规范 逻辑最大值为127

用3个字节表示3个轴

	//报告大小为8bit 有符号 对应-127 到127
    0x75, 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个轴进行规范 逻辑最小值为-127
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)   对3个轴进行规范 逻辑最大值为127
	
	//报告大小为8bit 有符号 对应-127 到127
    0x75, 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个按钮进行规范 逻辑最小值为0
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1) 对8个按钮进行规范 逻辑最大值为1
    0x95, 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个按钮进行规范 逻辑最小值为0
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1) 对8个按钮进行规范 逻辑最大值为1
    0x95, 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个轴进行规范 逻辑最小值为-127
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)   对3个轴进行规范 逻辑最大值为127
	
	//报告大小为8bit 有符号 对应-127 到127
    0x75, 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个按钮进行规范 逻辑最小值为0
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1) 对8个按钮进行规范 逻辑最大值为1
    0x95, 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个轴进行规范 逻辑最小值为-127
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)   对3个轴进行规范 逻辑最大值为127
	
	//报告大小为8bit 有符号 对应-127 到127
    0x75, 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个按钮进行规范 逻辑最小值为0
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1) 对8个按钮进行规范 逻辑最大值为1
    0x95, 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_NOTI



    hid_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 ;   			 //   1

		if(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 设备将轻车熟路了。

结语

有朋友反馈,找不到我,今打油诗一首

加流一壶真千金
微云澹日映寒流
信意麾毫无点误
KING_SONGING
唱奇腾怪可删修
歌咏康衢了此生
的然民仰如父母
国家涵养自建隆
王俭归来幕府非

有朋自远发来,不亦说乎!

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值