Funpack3-1:基于 xG24-EK2703A 开发套件实现蓝牙键盘鼠标复合设备
该项目使用了xG24-EK2703A 的蓝牙功能,实现了蓝牙键盘鼠标复合设备的设计,它的主要功能为:可以模拟鼠标滚轮向上或向下滚动,以及通过长按两个按键输入 EETREE.CN 字符串。
标签
嵌入式系统
Funpack活动
开发板
topgear
更新2024-03-05
115

项目描述

本项目基于 xG24-EK2703A 开发套件实现一个蓝牙鼠标/键盘的复合设备,开发套件上有两只按钮,分别短按两只按钮后模拟鼠标滚轮向上翻页和向下翻页一行的功能,同时长按两只按钮两秒后模拟键盘依次发送字符 EETREE.CN

本项目开发过程中使用 Simplicity Studio 开发环境,并基于官方的蓝牙 HID 键盘示例程序进行修改。

硬件资源

xG24-EK2703A 开发套件

EFR32xG24 Explorer 套件是一个基于 EFR32MG24 片上系统的小封装开发和评估平台。EFR32xG24 Explorer 套件专注于快速原型化和概念创建 2.4 GHz 无线协议的 IoT 应用程序,包括蓝牙 LE、蓝牙网状网络、Zigbee、Thread 和 Matter。

EFR32MG24B210F1536IM48

  • 高性能 2.4 GHz 无线电
  • 78 MHz 32-位 ARM® Cortex®-M33
  • 1536 kB 闪存和 256 kB RAM

板级外设

  • 两个 LED 和两个按钮
  • 重置按钮
  • 板载 SEGGER J-Link 调试器
  • 虚拟 COM 端口

设计思路

  • 蓝牙 GATT 配置
    配置 HID(Human Interface Device)服务描述键盘和鼠标的输入功能,并确保在蓝牙连接上正确识别为键盘和鼠标复合设备。
  • 配置 Button/LED 等硬件资源,用于实现逻辑控制
  • 键盘功能实现
    检测到两个按钮同时被按压时,开启一个两秒钟的单次计时器,如果计时期间保持按压状态,那么两秒计时结束后,在回调函数中记录键盘事件,并发送 evt_system_external_signal 事件给蓝牙协议栈,在蓝牙事件处理函数中,发送 EETREE.CN 对应的蓝牙 HID 报文。如果不能保持按压状态,则立即停止计时器。
  • 鼠标功能实现
    检测到任意一个按钮被按压时,分别记录鼠标滚轮向上翻页和向下翻页事件,同时发送 evt_system_external_signal 事件给蓝牙协议栈,在蓝牙事件处理函数中,分别发送鼠标滚轮向上翻页和向下翻页一行的 HID 报文。

软件流程

image.png

开发环境

Simplicity Studio Version 5

Silicon Labs Gecko SDK

安装 gecko-sdk 有两种方式:

  • 使用 Simplicity Studio 自带的 Installation Manager 安装

    安装过程中需要调用 git 克隆 gecko-sdk 仓库,我尝试了两三次,大概在安装进度 80% 时中断了,最后放弃使用这种方法。

  • 从 github 下载 SDK 发布版本,然后在 Simplicity Studio -> Preferences -> Simplicity Studio -> SDKs 窗口中手动添加 SDK 路径

    这里我下载的是 v4.4.1 版本https://github.com/SiliconLabs/gecko_sdk/releases/download/v4.4.1/gecko-sdk.zip

    image.png

Silicon Labs bluetooth applications

  • 下载地址

    git clone https://github.com/SiliconLabs/bluetooth_applications.git

  • 添加仓库
    在 Simplicity Studio -> Preferences -> Simplicity Studio -> External Repos 窗口中添加仓库路径
    • image.png

工程开发

导入 Bluetooth - HID Keyboard 工程

  • 连接板卡,在 Debug Adapters 窗口可以看到板卡信息
    • image.png
  • 点击板卡,IDE 右侧会出现详细信息,点击 EXAMPLE PROJECTS & DEMOS 标签页,然后搜索 HID,然后创建 Bluetooth - HID Keyboard 工程
    • image.png
  • 选择工程保存路径,这里我选择拷贝源文件到工程路径
    • image.png

配置蓝牙 GATT Profile

在工程目录 config/btconf 中,双击 gatt_configuration.btconf 打开 Bluetooth GATT Configurator 界面,可以配置不同的 Service。

Generic Access Service

在 Device Name Characteristic 页面,修改 device_name 为 "EETREE HID",如下图:

image.png

Human Interface Device Service
Report Map 简述

Report Map 是一种用于描述蓝牙设备的数据格式的标准。它定义了蓝牙HID(Human Interface Device)Profile中的数据报告的结构,包括键盘、鼠标、游戏手柄等输入设备通过蓝牙传输数据时使用的报告格式。通过使用ReportMap,蓝牙设备制造商能够遵循统一的数据格式标准,从而提高设备的兼容性,使得这些设备可以与各种不同的蓝牙主机设备进行通信。本项目需要实现键盘加鼠标的功能,所以需要键盘和鼠标对应的 Report Map。

键盘 Report Map
0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x06,        // Usage (Keyboard)
0xA1, 0x01,        // Collection (Application)
0x85, 0x01,        //   Report ID (1)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0xE0,        //   Usage Minimum (0xE0)
0x29, 0xE7,        //   Usage Maximum (0xE7)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x01,        //   Logical Maximum (1)
0x75, 0x01,        //   Report Size (1)
0x95, 0x08,        //   Report Count (8)
0x81, 0x02,        //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //   Report Count (1)
0x75, 0x08,        //   Report Size (8)
0x81, 0x01,        //   Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x06,        //   Report Count (6)
0x75, 0x08,        //   Report Size (8)
0x15, 0x00,        //   Logical Minimum (0)
0x25, 0x65,        //   Logical Maximum (101)
0x05, 0x07,        //   Usage Page (Kbrd/Keypad)
0x19, 0x00,        //   Usage Minimum (0x00)
0x29, 0x65,        //   Usage Maximum (0x65)
0x81, 0x00,        //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              // End Collection
鼠标 Report Map
0x05, 0x01,        // Usage Page (Generic Desktop Ctrls)
0x09, 0x02,        // Usage (Mouse)
0xA1, 0x01,        // Collection (Application)
0x85, 0x02,        //   Report ID (2)
0x75, 0x01,        //   Report Size (1)
0x95, 0x08,        //   Report Count (8)
0x09, 0x01,        //   Usage (Pointer)
0xA1, 0x00,        //   Collection (Physical)
0x05, 0x09,        //     Usage Page (Button)
0x19, 0x01,        //     Usage Minimum (0x01)
0x29, 0x03,        //     Usage Maximum (0x03)
0x15, 0x00,        //     Logical Minimum (0)
0x25, 0x01,        //     Logical Maximum (1)
0x95, 0x03,        //     Report Count (3)
0x75, 0x01,        //     Report Size (1)
0x81, 0x02,        //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01,        //     Report Count (1)
0x75, 0x05,        //     Report Size (5)
0x81, 0x03,        //     Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01,        //     Usage Page (Generic Desktop Ctrls)
0x09, 0x30,        //     Usage (X)
0x09, 0x31,        //     Usage (Y)
0x09, 0x38,        //     Usage (Wheel)
0x15, 0x81,        //     Logical Minimum (-127)
0x75, 0x08,        //     Report Size (8)
0x95, 0x03,        //     Report Count (3)
0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0,              //   End Collection
0xC0,              // End Collection
GATT configurator 更新 Report Map

将键盘和鼠标的 Report Map 以16进制字符串的形式组合在一起,然后在 Report Map Characteristic 页面,修改 report_map,如下图:

image.png
修改完毕后保存 gatt_configuration.btconf 文件,IDE 会自动在 autogen 目录生成 gatt_db.c,里面包含 bluetooth gatt configuration。

软件组件配置

按键配置

在导入Bluetooth-Hid-Keyboard 工程时,Simplicity Studio 自动添加了 btn0 的实例,可以按照下图手动添加 btn1 实例。添加完成后,IDE 根据板卡型号自动生成了 btn1 实例相关的代码。

image.png

image.png

LED 配置

双击打开 bluetooth_hid_keyboard.slcp,然后在 SOFTWARE COMPONENTS 页面,依次点击 Platform -> Driver -> LED -> Simple LED,然后点击 Install,在弹出窗口中分别添加 led0 和 led1 实例。同样,IDE 根据板卡型号自动生成了 led0 和 led1 实例相关的代码。

image.png

TIMER 配置

同样在 SOFTWARE COMPONENTS 页面,依次点击 Application -> Utility -> Timer,然后点击 Install,在弹出窗口中分别添加 led0 和 led1 实例。同样,IDE 会自动添加 。

image.png

代码实现

按键状态 handler
void sl_button_on_change(const sl_button_t *handle)
{
if (&sl_button_btn0 == handle) {
if (sl_button_get_state(handle) == SL_SIMPLE_BUTTON_PRESSED) {
app_log("Button-0 pushed - callback\r\n");
btn_status |= 1;
sl_led_turn_on(&sl_led_led0);
} else {
app_log("Button-0 released - callback \r\n");
btn_status &= ~1;
sl_led_turn_off(&sl_led_led0);
}
}

if (&sl_button_btn1 == handle) {
if (sl_button_get_state(handle) == SL_SIMPLE_BUTTON_PRESSED) {
app_log("Button-1 pushed - callback\r\n");
btn_status |= 2;
sl_led_turn_on(&sl_led_led1);
} else {
app_log("Button-1 released - callback \r\n");
btn_status &= ~2;
sl_led_turn_off(&sl_led_led1);
}
}
}
按键状态逻辑处理
SL_WEAK void app_process_action(void)
{
/////////////////////////////////////////////////////////////////////////////
// Put your additional application code here! //
// This is called infinitely. //
// Do not call blocking functions from here! //
/////////////////////////////////////////////////////////////////////////////

if (btn_status == 1) {
app_timer_stop(&btn_timer);
sl_bt_external_signal(1);
} else if (btn_status == 2) {
app_timer_stop(&btn_timer);
sl_bt_external_signal(1);
} else if (btn_status == 3) {
if (btn_timer_status == 0) {
app_timer_start(&btn_timer, 2000, btn_timer_cb, NULL, 0);
btn_timer_status = 1;
}
} else {
app_timer_stop(&btn_timer);
btn_timer_status = 0;
}
}
蓝牙协议栈事件处理
  • 键盘报文 ID 为 1,占 1 字节,数据长度为 8 字节,总报文长度为 9 字节
  • 鼠标报文 ID 为 2,占 1 字节,数据长度为 4 字节,总报文长度为 4 字节
    通过蓝牙发送报文时,一定不能搞错这个长度,本人在调试期间因为搞错长度浪费了很多时间。
    case  sl_bt_evt_system_external_signal_id:
if (notification_enabled == 1) {
memset(input_report_data, 0, sizeof(input_report_data));

switch (btn_status) {
case 1: // scroll up
input_report_data[0] = 0x02;
input_report_data[4] = 0x01;
sc = sl_bt_gatt_server_notify_all(gattdb_report,
5, input_report_data);
app_assert_status(sc);
app_log("Mouse report was sent\r\n");
break;
case 2: // scroll down
input_report_data[0] = 0x02;
input_report_data[4] = 0xff;
sc = sl_bt_gatt_server_notify_all(gattdb_report,
5, input_report_data);
app_assert_status(sc);
app_log("Mouse report was sent\r\n");
break;
case 3:
for (uint8_t i = 0; i < sizeof(eetree_key_array); i++) {
input_report_data[0] = 0x01;
input_report_data[1] = (eetree_key_array[i] != 0x37) ? CAPSLOCK_KEY_ON : CAPSLOCK_KEY_OFF;
input_report_data[3] = eetree_key_array[i];
sc = sl_bt_gatt_server_notify_all(gattdb_report,
9, input_report_data);
app_assert_status(sc);

input_report_data[0] = 0x01;
input_report_data[1] = CAPSLOCK_KEY_OFF;
input_report_data[3] = 0x00;
sc = sl_bt_gatt_server_notify_all(gattdb_report,
9, input_report_data);
app_assert_status(sc);
}
app_log("Key report was sent\r\n");
break;
default:
break;
}

功能展示

蓝牙连接设备

  • 搜索设备
    • image.png
  • 连接成功
    • image.png

键盘鼠标演示

见视频

参考资料链接

附件下载
bluetooth_hid_keyboard.zip
项目工程
团队介绍
在职工程师
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号