前言
很久没开发蓝牙相关的项目,找到一块Noridc蓝牙kit板,正好配合6轴来做一个拟合姿态的无线鼠标
如有异议,欢迎指正

方案实现
使用了6轴传感器和Nordic kit开发板,6轴获取姿态换算获得欧拉角,然后对应的角度拟合到HID鼠标描述符的XY轴移动改变量,通过蓝牙传给到PC端,用于控制鼠标指针动作


HID基本概念
HID(The Human Interface Device)人机交互设备,定义了在人机接口设备中的协议、特征和使用规范,蓝牙HID协议以USB HID协议规范为基础 https://www.usb.org/hid

HID角色
HID device:HID 设备,PC主机
HID Host:HID 主机,鼠标、键盘
设备描述符结构
HID的定义在接口描述符中,HID接口描述包含了报告描述符和物理描述符


HID report
HID Device支持三种报告

Input report(输入报告): hid device发送给 hid host的封包;例如鼠标将移动和点击事件信息发送给PC端
Output report(输出报告):hid host发送给 hid device的封包;例如PC端发送数据来控制键盘灯
Feature report(特征报告):传输双向数据
HID逻辑链路
HID Control:通道主要用于传输控制封包,这个通道传输的封包为同步封包(synchronous reports),L2CAP的psm为0x0011
HID Interrupt:通道传输的封包不需要确认,所有称为异步封包(asynchronous reports),L2CAP的psm为0x0013

HID Report Models报告模式
两种协议

boot protocol:boot protocl不解析报告描述符,设备支持该协议,在PC系统处于BIOS时就可以识别设备
report protocol:需要解析报告描述符,PC系统启动完成后进行枚举描述符
蓝牙HID
工程实例
使用nordic52832 Kit开发板,SDK为当前最新版本nRF5_SDK_17.0.2_d674dde,工程目录examples\ble_peripheral\ble_app_hids_mouse,协议栈使用S132

广播报文
广播报文包含服务Serivice UUIDs为 0x1812,指示为HID服务,Apperance外观(Type 0x19)为962(960~968),标识HID子类型为鼠标Mouse


代码分析
主函数
主函数初始化按键与协议栈,在循环中加入6轴的拟合数据后发送鼠标移动量到PC端

int main(void)
{bool erase_bonds;// Initialize.log_init();timers_init(); //初始化定时模块buttons_leds_init(&erase_bonds); //按键led初始化,注册事件回调power_management_init();电源管理/*
* 下面是对蓝牙相关配置的初始化,一般不需要修改
*/  ble_stack_init();//协议栈初始化scheduler_init();//调度器gap_params_init();//gap初始化gatt_init();//gatt初始化advertising_init();//广播初始化services_init();//服务初始化sensor_simulator_init();//模拟电池conn_params_init();//连接参数初始化peer_manager_init();//配对绑定初始化// Start execution.NRF_LOG_INFO("HID Mouse example started.");timers_start();// 开启定时器//erase_bonds = true;//擦除绑定advertising_start(erase_bonds);//广播开启后,蓝牙内部协议栈的调度就开始跑起来了Lsm6dlSensor_Init();//6轴初始化// Enter main loop.for (;;){x_mov = 0, y_mov = 0;Lsm6dl_Read(&x_mov, &y_mov);//获取移动量if (m_conn_handle != BLE_CONN_HANDLE_INVALID){            if(x_mov != 0 || y_mov != 0)mouse_movement_send(x_mov, y_mov);//发送}idle_state_handle();//等待事件}
}

服务初始化

static void services_init(void)
{qwr_init();//dis_init();//初始化设备信息服务(产品信息)bas_init();//注册电池服务hids_init();//hid服务初始化
}

报告描述符
HID设备是通过报告(report)来传输数据的,包含有输入报告(Input)和输入(Output)报告


bSize(bit0-bit1):用来标识后面所带的数据字节数,00 - 11指代0~4字节(0、1、2、4)
bType(bit2-bit3):标识条目类型,0-主条目、1-全局条目、2-局部条目、3-保留
主条目:分为Input(输入)、Output(输出)、Feature(特性)、Collection(集合)、End Collection(结束集合)
全局条目:选择用途页,定义数据域长度、数量、报告ID,对后面出现的主条目都有效
局部条目:局部有效,遇到主条目后失效,定义控制的特性,如最大最小值
bTag(bit4-bit7):标识条目功能,具体可以查看HID协议
查看hids_init函数
数组rep_map_data为HID的报告描述符,按域划分,用来描述报告的结构与通信数据中具体的作用;代码中主要描述了鼠标的按键与滚轮数据、鼠标移动组织结构,还描述了播放音乐的高级按键

static void hids_init(void)
{ret_code_t                err_code;ble_hids_init_t           hids_init_obj;ble_hids_inp_rep_init_t * p_input_report;uint8_t                   hid_info_flags;static ble_hids_inp_rep_init_t inp_rep_array[INPUT_REPORT_COUNT];static uint8_t rep_map_data[] = //HID 报告描述{0x05, 0x01, // Usage Page (Generic Desktop)0x09, 0x02, // Usage (Mouse)鼠标0xA1, 0x01, // Collection (Application)指示下面对mouse解释// Report ID 1: Mouse buttons + scroll/pan 按键滚轮0x85, 0x01,       // Report Id 1 id = 1 报告Id0x09, 0x01,       // Usage (Pointer) 指针形式0xA1, 0x00,       // Collection (Physical) 指针定义0x95, 0x05,       // Report Count (3)0x75, 0x01,       // Report Size (1)0x05, 0x09,       // Usage Page (Buttons) //接下来定义按键0x19, 0x01,       // Usage Minimum (01) button1 左键0x29, 0x05,       // Usage Maximum (05) button50x15, 0x00,       // Logical Minimum (0) 键值00x25, 0x01,       // Logical Maximum (1) 键值10x81, 0x02,       // Input (Data, Variable, Absolute)0x95, 0x01,       // Report Count (1)0x75, 0x03,       // Report Size (3)0x81, 0x01,       // Input (Constant) for padding0x75, 0x08,       // Report Size (8)0x95, 0x01,       // Report Count (1)0x05, 0x01,       // Usage Page (Generic Desktop)0x09, 0x38,       // Usage (Wheel) 0x15, 0x81,       // Logical Minimum (-127)0x25, 0x7F,       // Logical Maximum (127)0x81, 0x06,       // Input (Data, Variable, Relative)0x05, 0x0C,       // Usage Page (Consumer)0x0A, 0x38, 0x02, // Usage (AC Pan)0x95, 0x01,       // Report Count (1)0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0xC0,             // End Collection (Physical)// Report ID 2: Mouse motion 移动0x85, 0x02,       // Report Id 20x09, 0x01,       // Usage (Pointer)0xA1, 0x00,       // Collection (Physical)0x75, 0x0C,       // Report Size (12)0x95, 0x02,       // Report Count (2)0x05, 0x01,       // Usage Page (Generic Desktop)0x09, 0x30,       // Usage (X)0x09, 0x31,       // Usage (Y)0x16, 0x01, 0xF8, // Logical maximum (2047)0x26, 0xFF, 0x07, // Logical minimum (-2047)0x81, 0x06,       // Input (Data, Variable, Relative)0xC0,             // End Collection (Physical)0xC0,             // End Collection (Application)// Report ID 3: Advanced buttons 高级按键0x05, 0x0C,       // Usage Page (Consumer) 0x09, 0x01,       // Usage (Consumer Control)0xA1, 0x01,       // Collection (Application)0x85, 0x03,       // Report Id (3)0x15, 0x00,       // Logical minimum (0)0x25, 0x01,       // Logical maximum (1)0x75, 0x01,       // Report Size (1)0x95, 0x01,       // Report Count (1)0x09, 0xCD,       // Usage (Play/Pause)播放暂停0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0x0A, 0x83, 0x01, // Usage (AL Consumer Control Configuration)0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0x09, 0xB5,       // Usage (Scan Next Track)下一首0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0x09, 0xB6,       // Usage (Scan Previous Track)//上一首0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0x09, 0xEA,       // Usage (Volume Down)音量-0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0x09, 0xE9,       // Usage (Volume Up)音量+0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0x0A, 0x25, 0x02, // Usage (AC Forward)应用控制0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0x0A, 0x24, 0x02, // Usage (AC Back)返回键0x81, 0x06,       // Input (Data,Value,Relative,Bit Field)0xC0              // End Collection};memset(inp_rep_array, 0, sizeof(inp_rep_array));// Initialize HID Service.p_input_report                      = &inp_rep_array[INPUT_REP_BUTTONS_INDEX];//按键输入报告p_input_report->max_len             = INPUT_REP_BUTTONS_LEN;p_input_report->rep_ref.report_id   = INPUT_REP_REF_BUTTONS_ID;p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;p_input_report->sec.cccd_wr = SEC_JUST_WORKS;p_input_report->sec.wr      = SEC_JUST_WORKS;p_input_report->sec.rd      = SEC_JUST_WORKS;p_input_report                      = &inp_rep_array[INPUT_REP_MOVEMENT_INDEX];//移动输入报告p_input_report->max_len             = INPUT_REP_MOVEMENT_LEN;p_input_report->rep_ref.report_id   = INPUT_REP_REF_MOVEMENT_ID;p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;p_input_report->sec.cccd_wr = SEC_JUST_WORKS;p_input_report->sec.wr      = SEC_JUST_WORKS;p_input_report->sec.rd      = SEC_JUST_WORKS;p_input_report                      = &inp_rep_array[INPUT_REP_MPLAYER_INDEX];//播放输入报告p_input_report->max_len             = INPUT_REP_MEDIA_PLAYER_LEN;p_input_report->rep_ref.report_id   = INPUT_REP_REF_MPLAYER_ID;p_input_report->rep_ref.report_type = BLE_HIDS_REP_TYPE_INPUT;p_input_report->sec.cccd_wr = SEC_JUST_WORKS;p_input_report->sec.wr      = SEC_JUST_WORKS;p_input_report->sec.rd      = SEC_JUST_WORKS;hid_info_flags = HID_INFO_FLAG_REMOTE_WAKE_MSK | HID_INFO_FLAG_NORMALLY_CONNECTABLE_MSK;memset(&hids_init_obj, 0, sizeof(hids_init_obj));hids_init_obj.evt_handler                    = on_hids_evt;hids_init_obj.error_handler                  = service_error_handler;hids_init_obj.is_kb                          = false; //禁用键盘hids_init_obj.is_mouse                       = true; //使能鼠标hids_init_obj.inp_rep_count                  = INPUT_REPORT_COUNT;//3个输入hids_init_obj.p_inp_rep_array                = inp_rep_array;//报告描述符hids_init_obj.outp_rep_count                 = 0;//无输出报告hids_init_obj.p_outp_rep_array               = NULL;hids_init_obj.feature_rep_count              = 0;//无特征报告hids_init_obj.p_feature_rep_array            = NULL;//hids_init_obj.rep_map.data_len               = sizeof(rep_map_data);hids_init_obj.rep_map.p_data                 = rep_map_data;hids_init_obj.hid_information.bcd_hid        = BASE_USB_HID_SPEC_VERSION;hids_init_obj.hid_information.b_country_code = 0;hids_init_obj.hid_information.flags          = hid_info_flags;hids_init_obj.included_services_count        = 0;hids_init_obj.p_included_services_array      = NULL;hids_init_obj.rep_map.rd_sec         = SEC_JUST_WORKS;hids_init_obj.hid_information.rd_sec = SEC_JUST_WORKS;hids_init_obj.boot_mouse_inp_rep_sec.cccd_wr = SEC_JUST_WORKS;hids_init_obj.boot_mouse_inp_rep_sec.wr      = SEC_JUST_WORKS;hids_init_obj.boot_mouse_inp_rep_sec.rd      = SEC_JUST_WORKS;hids_init_obj.protocol_mode_rd_sec = SEC_JUST_WORKS;hids_init_obj.protocol_mode_wr_sec = SEC_JUST_WORKS;hids_init_obj.ctrl_point_wr_sec    = SEC_JUST_WORKS;err_code = ble_hids_init(&m_hids, &hids_init_obj);//添加HID服务 0x1812APP_ERROR_CHECK(err_code);
}
————————————————
版权声明:本文为CSDN博主「玉怀一捧雪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/libin55/article/details/115309215

按键回调
kit板上带有四个按键,例程中是用来做XY轴的鼠标移动行为,这里我改成了鼠标按键的功能

新增发送按键功能

void mouse_button_send(int8_t click, int8_t wheel, int8_t pan)
{ret_code_t err_code;uint8_t buffer[INPUT_REP_BUTTONS_LEN];APP_ERROR_CHECK_BOOL(INPUT_REP_BUTTONS_LEN == 3);buffer[0] = click;buffer[1] = wheel;buffer[2] = wheel;err_code = ble_hids_inp_rep_send(&m_hids,INPUT_REP_BUTTONS_INDEX,INPUT_REP_BUTTONS_LEN,buffer,m_conn_handle);if ((err_code != NRF_SUCCESS) &&(err_code != NRF_ERROR_INVALID_STATE) &&(err_code != NRF_ERROR_RESOURCES) &&(err_code != NRF_ERROR_BUSY) &&(err_code != BLE_ERROR_GATTS_SYS_ATTR_MISSING)){APP_ERROR_HANDLER(err_code);}
}

按键事件回调

static void bsp_event_handler(bsp_event_t event)
{ret_code_t err_code;switch (event){case BSP_EVENT_SLEEP:sleep_mode_enter();break;case BSP_EVENT_DISCONNECT:err_code = sd_ble_gap_disconnect(m_conn_handle,BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);if (err_code != NRF_ERROR_INVALID_STATE){APP_ERROR_CHECK(err_code);}break;case BSP_EVENT_WHITELIST_OFF:if (m_conn_handle == BLE_CONN_HANDLE_INVALID){err_code = ble_advertising_restart_without_whitelist(&m_advertising);if (err_code != NRF_ERROR_INVALID_STATE){APP_ERROR_CHECK(err_code);}}break;case BSP_EVENT_KEY_0:if (m_conn_handle != BLE_CONN_HANDLE_INVALID){mouse_button_send(1, 0, 0);//左键  //mouse_movement_send(-MOVEMENT_SPEED, 0);}break;case BSP_EVENT_KEY_1:if (m_conn_handle != BLE_CONN_HANDLE_INVALID){mouse_button_send(2, 0, 0);//右键   //mouse_movement_send(0, -MOVEMENT_SPEED);}break;case BSP_EVENT_KEY_2:if (m_conn_handle != BLE_CONN_HANDLE_INVALID){mouse_button_send(4, 0, 0);//中键    mouse_movement_send(MOVEMENT_SPEED, 0);}break;case BSP_EVENT_KEY_3:if (m_conn_handle != BLE_CONN_HANDLE_INVALID){mouse_button_send(0, 0, 0);//取消 mouse_movement_send(0, MOVEMENT_SPEED);}break;default:break;}
}

总结
PC端绑定设备后会在设备管理器中出现Sim_Mouse的鼠标设备,此时已经实现了鼠标的功能。BLE工程代码并未做太多的深入,这个后续有时间在进行解读,蓝牙HID协议以USB HID协议规范为基础,新增功能可以在报告描述中添加,具体完整版本可以阅读USB HID规范

————————————————
版权声明:本文为CSDN博主「玉怀一捧雪」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/libin55/article/details/115309215

基于Nordic52832的六轴HID蓝牙鼠标开发相关推荐

  1. 基于Nordic52832的六轴HID蓝牙鼠标开发(上)

    前言 很久没开发蓝牙相关的项目,找到一块Noridc蓝牙kit板,正好配合6轴来做一个拟合姿态的无线鼠标 如有异议,欢迎指正 方案实现 使用了6轴传感器和Nordic kit开发板,6轴获取姿态换算获 ...

  2. 基于MATLAB的关节型六轴机械臂轨迹规划仿真(2021实测完整代码)

    我是一个目录 基于MATLAB的关节型六轴机械臂轨迹规划仿真 1 实验目的 2 实验内容 2.1标准D-H参数法 2.2实验中使用的Matlab函数 3实验结果 4 全部代码 基于MATLAB的关节型 ...

  3. 基于模型设计(MBD)工程方法开发六轴机械臂系统实战

    本课程采用基于模型设计(MBD)的工程开发方法,实现对六轴机械臂系统的开发,包括六轴机械臂的知识和控制器的软硬件实现,有助于设计串联.并联的机械臂.康复医疗机器人等其它机电设备的电控系统.目前,这种世 ...

  4. C语言 | 基于MPU605(六轴传感器)的I2C实现LCD1602显示(代码类)

    博主github:https://github.com/MichaelBeechan 博主CSDN:https://blog.csdn.net/u011344545 基于MPU605(六轴传感器)的I ...

  5. 用imspost制作catia后处理_基于IMS POST五轴海德汉系统后处理的开发

    摘要:为发挥五轴数控加工高速.高效.高精的加工优势,本文以HEIDENHAIN数控系统为例,基于IMSPOST开发了五轴HEIDENHAIN系统专用后处理.经实际验证,所生成的加工代码符合五轴数控加工 ...

  6. 基于蚁群算法的六轴机械臂路径规划(运动学模型建立)

    机器人运动学模型的建立 1 D-H参数法建立坐标系 2 机器人运动学分析 2.1 运动学正解 2.2 运动学逆解 3 机器人的轨迹仿真 1 D-H参数法建立坐标系 代码: clear; clc; %建 ...

  7. 基于战舰V3的MPU6050六轴陀螺仪实验

    基于战舰V3的MPU6050六轴陀螺仪实验 陀螺仪的分类 3轴传感器指的是3轴的加速度,根据这个加速度我们解算出XY两轴的角度. 6轴传感器指的是3轴的加速度和3轴角速度,根据这两个数据我们解算出XY ...

  8. 【机械】基于简化几何解法的六轴机械臂位置规划附matlab代码

    1 内容介绍 基于简化几何解法的六轴机械臂位置规划附matlab代码 2 部分代码 clc; clear; %载入数据 importfile('shuiping.mat'); theta_shuipi ...

  9. 将CAD模型通过SolidWorks导出机器人URDF文件(基于innfos六轴机械臂)

    将CAD模型通过SolidWorks导出机器人URDF文件(基于innfos六轴机械臂) 前言 也是初次接触这方面的学习,看了许多的资料,其中古月老师的有个课程挺好的,有机械臂和小车的,我们实验室有机 ...

最新文章

  1. gatb_core_components
  2. android-menudrawer 和SlidingMenu 用法
  3. python kafka offset自动提交_Spring-Kafka —— 实现批量消费和手动提交offset
  4. java商城项目性能调优_javaWEB项目性能调优
  5. mysql的代码大全_MySql数目字函数大全
  6. 手误【删库】 == 跑路,不存在的 ——删瓦辛格
  7. ZOJ 3962:Seven Segment Display(思维)
  8. (三)HTTP再邂逅--熟悉HTTP协议结构和通讯原理
  9. 银行交易系统 TiDB 在线缩容迁移
  10. (转载)Linux编程获取本机IP地址的三种方法
  11. CyclicBarrier-同步辅助类
  12. 第三:GitHub的使用(超详细)
  13. fiddler如何显示IP地址栏?
  14. spring学习--AOP-面向切面编程(一)
  15. 从 0 开始机器学习 - 机器学习系统的设计与误差分析
  16. 【使用工具和软件汇总】
  17. ActivityMQ
  18. 2022华为杯研究生数学建模竞赛E题思路解析
  19. 软件需求分析模板2020-11-04
  20. xu2w显示屏软件下载_LED BEST(LED显示屏控制软件) V2.8 官方版

热门文章

  1. 使用Xcode的memory graph查找内存泄漏
  2. python实现树莓派监控_树莓派上安装pyaudio 及 对声音实时监控
  3. 怎样删除计算机硬盘记录,有什么方法能删除电脑里使用过的移动硬盘的记录
  4. 基于matlab 非局部均值(NLM)滤波图像去噪
  5. C语言递归之苹果分盘问题
  6. 几分钟黑掉阿里,被马云500万年薪收编的黑客,现在混得咋样了?
  7. mathtype向上取整函数
  8. 什么力量推动了互联网的进化
  9. DAVIS346动态视觉传感器
  10. 菜孔孔学python--集合