2.4.6. 三模鼠标应用

2.4.6.1. 概述

  • 支持标准的蓝牙BLE鼠标模式,蓝牙鼠标支持 windows 系统,mac 系统、安卓系统、ios 系统连接。

  • 支持2.4g + dongle鼠标1k回报率模式

  • 支持USB有线直连1k回报率模式

  • 默认不开低功耗

2.4.6.2. 工程配置

  • 使用快捷键Alt + g 打开app_config.h,配置CONFIG_APP_MOUSE_DUAL使能

//app case 选择,只选1,要配置对应的board_config.h
#define CONFIG_APP_KEYBOARD                 0//hid按键 ,default case
#define CONFIG_APP_KEYFOB                   0//自拍器,
#define CONFIG_APP_MOUSE_DUAL               1//三模鼠标(ble&2.4g&usb) 需搭配CONFIG_BOARD_AW313A_MOUSE板级
#define CONFIG_APP_KEYPAGE                  0//翻页器
#define CONFIG_APP_REMOTE_CONTROL           0//遥控器,需搭配CONFIG_BOARD_AW31A_RC板级
#define CONFIG_APP_IDLE                     0//IDLE
  • 使用快捷键Alt + g 打开board_config.h,板级选择CONFIG_BOARD_AW313A_MOUSE

#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H

// #define CONFIG_BOARD_AW31N_DEMO
// #define CONFIG_BOARD_AW31A_RC
#define CONFIG_BOARD_AW313A_MOUSE

#include "board_aw31n_demo_cfg.h"
#include "board_aw312a_rc_cfg.h"
#include "board_aw313a_mouse_cfg.h"

#endif

以下外设配置默认适配SF03_AW313A_V1.0鼠标原理图

apps/demo/hid/board/bd47/board_aw313a_mouse_cfg.h

  • key IO配置,根据鼠标的硬件原理图来配置按键

//*********************************************************************************//
//                                 adkey 配置                                      //
//*********************************************************************************//
#define KEY_AD_EN				            ENABLE           //<AD按键使能
#define AD_KEY_IO		                    IO_PORTA_01       // 可用的IO见adc_ch_io_table

//*********************************************************************************//
//                                 iokey 配置                                      //
//*********************************************************************************//
#define KEY_IO_EN         	                ENABLE                  //<IO按键使能
#define MOUSE_KEY_SCAN_MODE                 ENABLE_THIS_MOUDLE

#define KEY_LK_VAL          BIT(0)   //1
#define KEY_RK_VAL          BIT(1)   //2
#define KEY_HK_VAL          BIT(2)   //4
#define KEY_FB_VAL          BIT(3)   //8
#define KEY_BB_VAL          BIT(4)  //16
#define KEY_CPI_VAL         BIT(5)  //32

#define KEY_LK_RK_VAL       (KEY_LK_VAL | KEY_RK_VAL)             //3
#define KEY_LK_HK_VAL       (KEY_LK_VAL | KEY_HK_VAL)             //5
#define KEY_RK_HK_VAL       (KEY_RK_VAL | KEY_HK_VAL)             //6
#define KEY_LK_RK_HK_VAL    (KEY_LK_VAL | KEY_RK_VAL | KEY_HK_VAL)//7

#define TCFG_IOKEY_MOUSE_LK_PORT		            IO_PORTA_08
#define TCFG_IOKEY_MOUSE_RK_PORT		            IO_PORTA_10
#define TCFG_IOKEY_MOUSE_HK_PORT		            IO_PORTA_09
#define TCFG_IOKEY_MOUSE_CPI_PORT		            IO_PORTA_11
#define TCFG_IOKEY_MOUSE_SWITCH_PORT		        IO_PORTA_00

  • 传感器 io配置

//*********************************************************************************//
//                                  optical mouse sensor配置                       //
//*********************************************************************************//
#define TCFG_OMSENSOR_ENABLE

#define TCFG_HAL3205_EN                           DISABLE
#define TCFG_HAL3212_EN                           ENABLE

#define TCFG_OPTICAL_SENSOR_SCLK_PORT             IO_PORTA_04
#define TCFG_OPTICAL_SENSOR_DATA_PORT             IO_PORTA_05
#define TCFG_OPTICAL_SENSOR_INT_PORT              IO_PORTA_02
  • 鼠标滚轮器配置

//*********************************************************************************//
//                                  code switch配置                                //
//*********************************************************************************//
#define TCFG_CODE_SWITCH_ENABLE                   ENABLE_THIS_MOUDLE
#define TCFG_CODE_SWITCH_A_PHASE_PORT             IO_PORTA_07
#define TCFG_CODE_SWITCH_B_PHASE_PORT             IO_PORTA_06

apps/demo/hid/board/bd47/board_aw313a_mouse_global_build_cfg.h

  • 默认打开USB模式,可以通过开关这个宏的方式来控制USB模式

#define HAS_USB_EN                              1

2.4.6.3. 模式选择

Note

三模鼠标默认有三种模式可以选择,其中ble和2.4g在sdk中通过拨码滑动来控制选择,usb模式则是在ble&2.4g工作时插入自动转换成usb模式,具体逻辑代码如下:

/*************************************************************************************************/
/*!
 *  \brief      mouse软件上电检测,ble&2.4g模式切换
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
static void mouse_power_mode_check(void)
{
#ifdef TCFG_IOKEY_MOUSE_SWITCH_PORT
    uint8_t delay_10ms_cnt = 0;
    uint32_t port_max_value = 1023;
    // 根据具体电路分压
    uint32_t port_side1_value = 1023 / 2;
    uint32_t port_size2_value = 1023 * 3 / 4;
    uint32_t vol_sum = 0; // 用于累加电压值

    while (1) {
        wdt_clear();
        delay_10ms(1);
        uint32_t vol_value = adc_get_value_blocking(adc_io2ch(TCFG_IOKEY_MOUSE_SWITCH_PORT));
        log_info("vol_value is %d", vol_value);

        // filter error value
        if (vol_value == port_max_value) {
            continue;
        }

        log_info("+");
        vol_sum += vol_value; // 累加电压值
        delay_10ms_cnt++;

        if (delay_10ms_cnt >= 10) {
            uint32_t vol_avg = vol_sum / 10; // 计算平均值
            log_info("Average vol_value is %d", vol_avg);

            if (vol_avg > port_side1_value) {
                if (vol_avg < port_size2_value) {
                    mouse_switch_start_mode = HID_MODE_BLE;
                } else {
                    mouse_switch_start_mode = HID_MODE_24G;
                }
                return;
            } else {
                log_info("-");
                log_info("switch enter softpoweroff\n");
                p33_io_wakeup_edge(TCFG_IOKEY_MOUSE_SWITCH_PORT, RISING_EDGE);
                app_power_set_soft_poweroff(NULL);
            }
        }
    }
#endif
}

Note

这段代码会在初始化时检测拨码io的ad值从而判断选择哪种模式(ble或2.4g),通过软件检测的方式来实现,用户在做切换模式设计时也可以设计其他电路来实现模式的切换

Note

而USB则是通过插入检测来实现模式转换,在ble&2.4g都能进行插入检测

/*************************************************************************************************/
/*!
 *  \brief     usb初始化,OTG检测
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
void mouse_usb_start()
{
    usb_hid_mouse_set_report_map(mouse_report_map, sizeof(mouse_report_map));
    const struct otg_dev_data otg_data = {
        .usb_dev_en = TCFG_OTG_USB_DEV_EN,
        .slave_online_cnt = TCFG_OTG_SLAVE_ONLINE_CNT,
        .slave_offline_cnt = TCFG_OTG_SLAVE_OFFLINE_CNT,
        .host_online_cnt = TCFG_OTG_HOST_ONLINE_CNT,
        .host_offline_cnt = TCFG_OTG_HOST_OFFLINE_CNT,
        .detect_mode = TCFG_OTG_MODE,
        .detect_time_interval = TCFG_OTG_DET_INTERVAL,
        .usb_otg_sof_check_init = usb_otg_sof_check_init,
    };
    usb_otg_init(NULL, (void *)&otg_data);
    usb_pc_in_handler_register(mouse_usb_in_hook);
}

Note

插入usb检测后,将会调用usb插入回调,关闭蓝牙,转化为USB模式

/*************************************************************************************************/
/*!
 *  \brief     usb in 回调函数
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
void mouse_usb_in_hook()
{
    // 关闭蓝牙模块
    ble_module_enable(0);
    mouse_info_t *mouse_info = mouse_get_status_info();
    mouse_info->mouse_hid_mode = HID_MODE_USB;
    // 开启usb定时器发数
    if (mouse_info->mouse_gtimer_tid) {
        gptimer_set_timer_period(mouse_info->mouse_gtimer_tid, MOUSE_USB_GTIMER_INIT_TIME);
    } else {
        mouse_send_data_timer_init(MOUSE_USB_GTIMER_INIT_TIME);
    }
}

Warning

值得注意的是,sdk未实现USB拔出检测,如果要从USB模式转成ble或者是2.4g,需要拔出USB线重新拨码上电实现

2.4.6.4. BLE模式运行逻辑

image-20240708134907999

  • 配对与连接

启动配对模式:按下鼠标上的配对按钮,鼠标进入配对模式。选择ble模式后,上电默认会开启广播,如果是非测试模式并且没有绑定设备时关闭广播,这时候需要手动打开广播

apps/demo/hid/examples/mouse_dual/app_mouse_dual.c

static void mouse_select_btmode(uint8_t mode)
{
    if (mode != HID_MODE_INIT) {
        if (mouse_info.mouse_hid_mode == mode) {
            return;
        }
        mouse_info.mouse_hid_mode = mode;
    }

    log_info("###### %s: %d,%d\n", __FUNCTION__, mode, mouse_info.mouse_hid_mode);

    if (mouse_info.mouse_hid_mode == HID_MODE_BLE || mouse_info.mouse_hid_mode == HID_MODE_24G) {

        if (mouse_info.mouse_hid_mode == HID_MODE_BLE) {
            log_info("---------app select ble--------\n");
            rf_set_24g_hackable_coded(0);
        } else {
            log_info("---------app select 24g--------\n");
            log_info("set_24g_code: %04x", CFG_RF_24G_CODE_ID);
            rf_set_24g_hackable_coded(CFG_RF_24G_CODE_ID);
        }

        if (!STACK_MODULES_IS_SUPPORT(BT_BTSTACK_LE) || !BT_MODULES_IS_SUPPORT(BT_MODULE_LE)) {
            log_info("not surpport ble,make sure config !!!\n");
            ASSERT(0);
        }
        // 非测试模式并且没有绑定设备时关闭广播
#if (TEST_MOUSE_SIMULATION_ENABLE == 0)
        if (!le_hogp_get_is_paired()) {
            ble_module_enable(0);
        }
#endif

    }

    mouse_vm_deal(1);
}

左右键长按2s打开广播,值得注意的是这个操作会将配对信息清除,电脑或手机如果还存在配对信息,将无法根据mac地址自动回连

apps/demo/hid/examples/mouse_dual/app_mouse_dual.c

// left+right键, 长按数秒开广播
#if (DOUBLE_KEY_HOLD_PAIR)
        if (KEY_LK_RK_VAL == event->u.key.value && event_type == KEY_EVENT_LONG) {
            log_info("adv start:key3 hold:%d", mouse_double_key_long_cnt);
            if (++mouse_double_key_long_cnt >= DOUBLE_KEY_HOLD_CNT) {
                if (!ble_hid_is_connected()) {
                    le_hogp_set_pair_allow();
                    ble_module_enable(1);
                }

                mouse_double_key_long_cnt = 0;
            }
            return;
        } else {
            mouse_double_key_long_cnt = 0;
        }
#endif
  • 搜索设备:计算机搜索附近的BLE设备,并找到鼠标。

  • 配对与连接:计算机与鼠标交换密钥,建立安全连接。配对成功后,鼠标与计算机之间可以传输HID报文。

  • BLE HID协议

BLE HID协议是专门为低功耗设备设计的,允许鼠标、键盘等设备通过BLE与计算机进行通信。BLE HID设备使用标准的HID描述符来定义其功能和报告格式。

apps/demo/hid/examples/mouse_dual/app_mouse_dual.h

static const uint8_t mouse_report_map[] = {
    0x05, 0x01,                    // Usage Page (Generic Desktop Ctrls) - 定义了后续字段的用途范畴,这里是通用桌面控制。
    0x09, 0x02,                    // Usage (Mouse) - 指定当前设备为鼠标。
    0xA1, 0x01,                    // Collection (Application) - 开始应用层集合,标志着一个应用集合的开始。
    0x85, MOUSE_SEND_DATA_REPORT_ID,                    //   Report ID (1) - 报告ID为1。
    0x09, 0x01,                    //   Usage (Pointer) - 指明是一个指针设备(在这里,是个鼠标)。
    0xA1, 0x00,                    //   Collection (Physical) - 开始物理集合,表示这是个物理设备。
    // Buttons (5 buttons)
    0x95, 0x05,                    //     Report Count (5) - 报告包含5项数据。
    0x75, 0x01,                    //     Report Size (1) - 每项数据大小为1位。
    0x05, 0x09,                    //     Usage Page (Button) - 用途页面为按钮。
    0x19, 0x01,                    //     Usage Minimum (0x01) - 按钮的最小值为1(第一个按钮)。
    0x29, 0x05,                    //     Usage Maximum (0x05) - 按钮的最大值为5(第五个按钮)。
    0x15, 0x00,                    //     Logical Minimum (0) - 数据的逻辑最小值为0。
    0x25, 0x01,                    //     Logical Maximum (1) - 数据的逻辑最大值为1。
    0x81, 0x02,                    //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 输入类型,代表了按钮的状态。
    // Padding for the buttons - 3 bits
    0x95, 0x01,                    //     Report Count (1) - 一项填充数据。
    0x75, 0x03,                    //     Report Size (3) - 填充大小为3位,用于对齐字节。
    0x81, 0x01,                    //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 输入类型为常量,用于占位不使用。
    // Wheel (8 bits)
    0x75, 0x08,                    //     Report Size (8) - 滚轮数据的大小为8位。
    0x95, 0x01,                    //     Report Count (1) - 一项数据。
    0x05, 0x01,                    //     Usage Page (Generic Desktop Ctrls) - 同上。
    0x09, 0x38,                    //     Usage (Wheel) - 滚轮。
    0x15, 0x81,                    //     Logical Minimum (-127) - 滚轮数据的逻辑最小值为-127。
    0x25, 0x7F,                    //     Logical Maximum (127) - 滚轮数据的逻辑最大值为127。
    0x81, 0x06,                    //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 输入类型,代表了滚轮的滚动。
    // X and Y Axis (12 bits each)
    0x75, 0x0C,                    //     Report Size (12) - 每项数据的大小为12位。
    0x95, 0x02,                    //     Report Count (2) - 两项数据(X轴和Y轴)。
    0x05, 0x01,                    //     Usage Page (Generic Desktop Ctrls) - 同上,指定用途页面为通用桌面控制。
    0x09, 0x30,                    //     Usage (X) - X轴。
    0x09, 0x31,                    //     Usage (Y) - Y轴。
    0x16, 0x01, 0xF8,              //     Logical Minimum (-2047) - X和Y轴数据的逻辑最小值为-2047。
    0x26, 0xFF, 0x07,              //     Logical Maximum (2047) - X和Y轴数据的逻辑最大值为2047。
    0x81, 0x06,                    //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 输入类型,代表了轴的移动。
    0xC0,                          //   End Collection - 结束当前集合。
    0xC0,                           // End Collection - 结束最外层的集合。
// Volume and media control report
    0x05, 0x0C, // Usage Page (Consumer Devices)
    0x09, 0x01, // Usage (Consumer Control)
    0xA1, 0x01, // Collection (Application)
    0x85, CUSTOM_SEND_DATA_REPORT_ID, // Report ID (2)
    0x09, 0xE9, // Usage (Volume Up)
    0x09, 0xEA, // Usage (Volume Down)
    0x09, 0xCD, // Usage (Play/Pause)
    0x09, 0xE2, // Usage (Mute)
    0x09, 0xB6, // Usage (Scan Next Track)
    0x09, 0xB5, // Usage (Scan Previous Track)
    0x15, 0x00, // Logical Minimum (0)
    0x25, 0x01, // Logical Maximum (1)
    0x75, 0x01, // Report Size (1)
    0x95, 0x06, // Report Count (6)
    0x81, 0x02, // Input (Data,Var,Abs)
    // Padding for alignment
    0x95, 0x01, // Report Count (1)
    0x75, 0x02, // Report Size (2)
    0x81, 0x01, // Input (Const,Array,Abs)
    0xC0 // End Collection
};

这个HID报文定义了一个BLE鼠标的描述符,它包括了鼠标的主要功能,如按键、滚轮、X轴和Y轴的移动。该描述符还定义了音量和媒体控制功能。以下是每一部分的详细解释:

顶层集合

0x05, 0x01,                    // Usage Page (Generic Desktop Ctrls) - 定义了后续字段的用途范畴,这里是通用桌面控制。
0x09, 0x02,                    // Usage (Mouse) - 指定当前设备为鼠标。
0xA1, 0x01,                    // Collection (Application) - 开始应用层集合,标志着一个应用集合的开始。
0x85, MOUSE_SEND_DATA_REPORT_ID, // Report ID (1) - 报告ID为1。

这部分代码定义了一个使用页面为“通用桌面控制”的鼠标设备,并且开始一个应用层集合,报告ID为1。

指针设备(物理集合)

0x09, 0x01,                    // Usage (Pointer) - 指明是一个指针设备(在这里,是个鼠标)。
0xA1, 0x00,                    // Collection (Physical) - 开始物理集合,表示这是个物理设备。

这一部分定义了一个指针设备,并开始一个物理集合。

按钮

0x95, 0x05,                    // Report Count (5) - 报告包含5项数据。
0x75, 0x01,                    // Report Size (1) - 每项数据大小为1位。
0x05, 0x09,                    // Usage Page (Button) - 用途页面为按钮。
0x19, 0x01,                    // Usage Minimum (0x01) - 按钮的最小值为1(第一个按钮)。
0x29, 0x05,                    // Usage Maximum (0x05) - 按钮的最大值为5(第五个按钮)。
0x15, 0x00,                    // Logical Minimum (0) - 数据的逻辑最小值为0。
0x25, 0x01,                    // Logical Maximum (1) - 数据的逻辑最大值为1。
0x81, 0x02,                    // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) - 输入类型,代表了按钮的状态。

这里定义了5个按钮,每个按钮的数据大小为1位。它们的逻辑值范围为0到1,表示按钮按下和释放状态。

填充位

0x95, 0x01,                    // Report Count (1) - 一项填充数据。
0x75, 0x03,                    // Report Size (3) - 填充大小为3位,用于对齐字节。
0x81, 0x01,                    // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) - 输入类型为常量,用于占位不使用。

这里添加了3位填充位,以对齐字节。

滚轮

0x75, 0x08,                    // Report Size (8) - 滚轮数据的大小为8位。
0x95, 0x01,                    // Report Count (1) - 一项数据。
0x05, 0x01,                    // Usage Page (Generic Desktop Ctrls) - 同上。
0x09, 0x38,                    // Usage (Wheel) - 滚轮。
0x15, 0x81,                    // Logical Minimum (-127) - 滚轮数据的逻辑最小值为-127。
0x25, 0x7F,                    // Logical Maximum (127) - 滚轮数据的逻辑最大值为127。
0x81, 0x06,                    // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 输入类型,代表了滚轮的滚动。

这里定义了滚轮数据,数据大小为8位,范围从-127到127,表示滚轮的滚动量。

X轴和Y轴

0x75, 0x0C,                    // Report Size (12) - 每项数据的大小为12位。
0x95, 0x02,                    // Report Count (2) - 两项数据(X轴和Y轴)。
0x05, 0x01,                    // Usage Page (Generic Desktop Ctrls) - 同上,指定用途页面为通用桌面控制。
0x09, 0x30,                    // Usage (X) - X轴。
0x09, 0x31,                    // Usage (Y) - Y轴。
0x16, 0x01, 0xF8,              // Logical Minimum (-2047) - X和Y轴数据的逻辑最小值为-2047。
0x26, 0xFF, 0x07,              // Logical Maximum (2047) - X和Y轴数据的逻辑最大值为2047。
0x81, 0x06,                    // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 输入类型,代表了轴的移动。

这里定义了X轴和Y轴的数据,每个轴的数据大小为12位,范围从-2047到2047,表示鼠标的移动量。

结束集合

0xC0,                          // End Collection - 结束当前集合。
0xC0,                          // End Collection - 结束最外层的集合。

结束物理集合和应用集合。

音量和媒体控制报文

顶层集合

0x05, 0x0C, // Usage Page (Consumer Devices)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, CUSTOM_SEND_DATA_REPORT_ID, // Report ID (2)

定义了一个使用页面为“消费者设备”的集合,报告ID为2。

控制按钮

0x09, 0xE9, // Usage (Volume Up)
0x09, 0xEA, // Usage (Volume Down)
0x09, 0xCD, // Usage (Play/Pause)
0x09, 0xE2, // Usage (Mute)
0x09, 0xB6, // Usage (Scan Next Track)
0x09, 0xB5, // Usage (Scan Previous Track)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x06, // Report Count (6)
0x81, 0x02, // Input (Data,Var,Abs)

定义了6个控制按钮,数据大小为1位,逻辑值范围为0到1,表示按钮的状态。

填充位

0x95, 0x01, // Report Count (1)
0x75, 0x02, // Report Size (2)
0x81, 0x01, // Input (Const,Array,Abs)

添加了2位填充位,以对齐字节。

结束集合

0xC0 // End Collection

结束应用集合。

这整个HID报文描述符定义了鼠标的基本功能(按键、滚轮、X轴和Y轴)以及音量和媒体控制功能,通过这些报文,鼠标可以向计算机报告其状态和用户的操作。

  • 数据采集与处理

鼠标内部的传感器检测到用户的操作,如移动和点击。传感器将这些操作转换为数字信号。微处理器读取传感器数据,确定光标的移动量和按键状态。

Note

使用定时器去实现采集数据,并发送

/*************************************************************************************************/
/*!
 *  \brief     普通BLE && USB鼠标发数函数,定时器调用
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
static void mouse_timer_handler(u32 tid, void *private_data)
{
    if (!mouse_info.mouse_is_paired && mouse_info.mouse_hid_mode == HID_MODE_BLE) {
        return;
    }

    mouse_data_send(NULL, 0, false);
}

/*************************************************************************************************/
/*!
 *  \brief      BLE模式 mouse发送定时器,使用gtimer
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
void mouse_send_data_timer_init(uint32_t resolu_us)
{
    const struct gptimer_config mouse_timer_config = {
        .timer.period_us = resolu_us,  //us
        .irq_cb = mouse_timer_handler, //设置中断回调函数
        .irq_priority = 4,             //中断优先级
        .mode = GPTIMER_MODE_TIMER,    //设置工作模式
        .private_data = NULL,
    };

    mouse_info.mouse_gtimer_tid = gptimer_init(MOUSE_GTIMER_ID, &mouse_timer_config);
    gptimer_start(mouse_info.mouse_gtimer_tid);
    log_info("mouse gtimer_config tid = %d\n", mouse_info.mouse_gtimer_tid);
}

Note

在更新请求连接间隔参数中去更新定时器的间隔


    case BLE_ST_CONNECTION_UPDATE_OK:
#if TEST_MOUSE_SIMULATION_ENABLE
        // 测试模式不能立即发数,否则会报超时断开
        sys_timeout_add((void *)1, mouse_set_is_paired, 500);
#else
        if (!le_hogp_get_is_paired()) {
            mouse_set_is_paired(1);
        }
#endif
        // update gptimer period from connect inteval
        log_info("BLE_ST_CONNECTION_UPDATE_OK, INTERVAL:%d\n", value);
        if (mouse_info.mouse_hid_mode == HID_MODE_BLE) {
            gptimer_set_timer_period(mouse_info.mouse_gtimer_tid, value * 1000);
        }
        break;

  • 生成HID报文

微处理器将传感器数据转换为HID报文。HID报文包含以下信息:

  • 光标移动:表示光标在X和Y轴上的移动量。

  • 按键状态:表示鼠标按键的按下或释放状态。

  • 滚轮滚动:表示鼠标滚轮的滚动量。

  • 发送HID报文来实现对电脑或者手机的控制

/*************************************************************************************************/
/*!
 *  \brief     鼠标发数函数,定时器调用
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
static void mouse_data_send(void *priv_hw, uint8_t hw_state, bool is_24g)
{
#if TEST_MOUSE_SIMULATION_ENABLE
    mouse_send_data_test();
#else
#ifdef TCFG_OMSENSOR_ENABLE
    optical_mouse_read_sensor_handler_high(&mouse_send_packet, &mouse_flag);
#endif
#endif
    // 取数
    if (!(mouse_flag.sensor_send_flag && mouse_flag.wheel_send_flag && mouse_flag.button_send_flag)) {
        mouse_reset_cnt++;
        uint32_t mouse_reset_cnt_max;
        if (mouse_info.mouse_hid_mode == HID_MODE_BLE) {
            mouse_reset_cnt_max = MOUSE_7MS_CLEAR_WDT_CNT_MAX;
        } else {
            mouse_reset_cnt_max = MOUSE_1MS_CLEAR_WDT_CNT_MAX;
        }
        if (mouse_reset_cnt > mouse_reset_cnt_max) {
#if (TCFG_HID_AUTO_SHUTDOWN_TIME)
            sys_timer_modify(mouse_info.mouse_auto_shutdown_timer, TCFG_HID_AUTO_SHUTDOWN_TIME * 1000);
#endif
            // 避免看门狗超时
            wdt_clear();
            mouse_reset_cnt = 0;
        }

        if (is_24g) {
            hw_send_packet_fast(HID_REPORT_ID_01_SEND_HANDLE, priv_hw, (uint8_t *)&mouse_send_packet, sizeof(mouse_packet_data_t), 27, hw_state);
        } else {
            if (mouse_info.mouse_hid_mode == HID_MODE_BLE) {
                ble_hid_data_send(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet));
            }
#if SWITCH_MODE_USB_ENABLE
            else if (mouse_info.mouse_hid_mode == HID_MODE_USB) {
                mouse_usb_data_send(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet));
            }
#endif
        }

        // 清除鼠标信息
        if (mouse_flag.button_send_flag == 0) {
            mouse_flag.button_send_flag = 1;
        }

        if (mouse_flag.wheel_send_flag == 0) {
            mouse_flag.wheel_send_flag = 1;
            mouse_send_packet.wheel = 0;
        }

        if (mouse_flag.sensor_send_flag == 0) {
            mouse_flag.sensor_send_flag = 1;
            memset((void *)&mouse_send_packet.xymovement, 0, sizeof(mouse_send_packet.xymovement));
        }
    } else {
        // 未取到按键/传感器数据,过滤
        return;
    }
}

2.4.6.5. 2.4G模式运行逻辑

image-20240708114934650

上图展示了一个BLE无线鼠标系统的详细架构。以下是对图中各部分的详细解释:

  1. MOUSE SENSOR(鼠标传感器)

  • 功能:检测鼠标的移动和按键操作,将这些物理操作转换为数字信号。

  • 通信接口:使用SPI(串行外设接口)与鼠标主控芯片通信。

  1. 鼠标(BLE从机)

  • 功能:接收来自鼠标传感器的数据,通过BLE(2.4GHz频段)发送HID(Human Interface Device)报文。

  • 通信接口

    • SPI:与鼠标传感器通信,接收传感器数据。

    • BLE(2.4GHz):无线发送HID报文到BLE主机。

  1. BLE主机(USB接收器)

  • 功能:作为BLE主机设备,接收来自BLE从机(鼠标)的HID报文,并通过USB接口将这些报文传输到计算机。

  • 通信接口

    • BLE(2.4GHz):无线接收来自鼠标的HID报文。

    • USB:有线传输数据到计算机。

  1. 带USB接口设备(计算机)

  • 功能:接收来自BLE主机(USB接收器)的HID报文,并通过操作系统的HID驱动程序处理这些报文,将其转换为用户的操作(如光标移动、点击等)。

  • 通信接口:通过USB接口与BLE主机通信。

  1. 工作流程

  2. 用户操作:用户移动鼠标或点击按钮。

  3. 传感器检测:鼠标传感器检测到用户的操作,并通过SPI接口将数据传输到鼠标主控芯片。

  4. BLE从机发送数据:鼠标主控芯片将接收到的数据转换为HID报文,通过BLE(2.4GHz频段)发送到BLE主机(USB接收器)。

  5. BLE主机接收数据:USB接收器接收到来自鼠标的HID报文,并通过USB接口将这些数据传输到计算机。

  6. 计算机处理数据:计算机通过USB接口接收HID报文,并由操作系统的HID驱动程序处理这些数据,实现用户的操作。

Note

和ble模式不同的是,2.4模式的定时数据发送不是通过硬件定时器实现的,而是通过蓝牙1ms的起中断去实现的

#if CONFIG_BLE_CONNECT_SLOT
/*************************************************************************************************/
/*!
 *  \brief      1. 基带接口,用于高回报率鼠标,需搭配aw31 dongle使用;
                2. 底层有事件触发后回调直接来上层拿数据发送,函数名不可修改,无需调用;
                3. 不可做耗时操作(需要在us级);
                4. 仅在2.4g模式使用;
 *
 *  \param      [in] NUll
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
void ble_event_irq_hook(void *priv_hw, void *link, uint8_t hw_state, uint8_t flag)
{
    if (HID_MODE_BLE == mouse_info.mouse_hid_mode || mouse_info.mouse_is_paired == 0) {
        return;
    }

    // 等待压包完成后再发送下一包
    if (flag == 1) {
        return;
    }

    mouse_data_send(priv_hw, hw_state, true);
}
#endif

最终使用hw_send_packet_fast这个发数接口去实现发数
 if (is_24g) {
            hw_send_packet_fast(HID_REPORT_ID_01_SEND_HANDLE, priv_hw, (uint8_t *)&mouse_send_packet, sizeof(mouse_packet_data_t), 27, hw_state);
        } else {
            if (mouse_info.mouse_hid_mode == HID_MODE_BLE) {
                ble_hid_data_send(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet));
            }
#if SWITCH_MODE_USB_ENABLE
            else if (mouse_info.mouse_hid_mode == HID_MODE_USB) {
                mouse_usb_data_send(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet));
            }
#endif
        }

2.4.6.6. USB模式运行逻辑

image-20240708114622436

上图展示了一个通过USB直接连接的有线鼠标系统。以下是对图中各部分的详细解释:

有线鼠标

  • 功能:这是一个有线鼠标,通过USB接口与计算机连接,负责检测用户的操作(移动、点击等),并通过HID(Human Interface Device)协议将这些操作转换为数据报文。

  • 通信:鼠标通过USB电缆直接与计算机进行通信。

带USB接口设备(计算机)

  • 功能:这是计算机或任何带有USB接口的设备,接收来自有线鼠标的HID报文,并通过操作系统的HID驱动程序处理这些报文,将其转换为用户的操作(如光标移动、点击等)。

工作流程

  1. 用户操作:用户移动鼠标或点击按钮。

  2. 有线鼠标发送数据:鼠标的传感器检测到用户的操作,并将数据转换为HID报文,通过USB电缆发送到计算机。

  3. 计算机处理数据:计算机通过USB接口接收HID报文,并由操作系统的HID驱动程序处理这些数据,实现用户的操作。

Note

USB模式的工作原理比较简单,直接使用一个1ms的硬件定时器去采集发数

void mouse_usb_in_hook()
{
    // 关闭蓝牙模块
    ble_module_enable(0);
    mouse_info_t *mouse_info = mouse_get_status_info();
    mouse_info->mouse_hid_mode = HID_MODE_USB;
    // 开启usb定时器发数
    if (mouse_info->mouse_gtimer_tid) {
        gptimer_set_timer_period(mouse_info->mouse_gtimer_tid, MOUSE_USB_GTIMER_INIT_TIME);
    } else {
        mouse_send_data_timer_init(MOUSE_USB_GTIMER_INIT_TIME);
    }
}

2.4.6.7. 休眠逻辑

Note

目前三模鼠标的睡眠采用的是设置无操作定时软关机时间,默认是1min

apps/demo/hid/include/app config.h

#if CONFIG_APP_MOUSE_DUAL
#define CONFIG_BLE_CONNECT_SLOT            1 //BLE高回报率设置, 支持私有协议
#define TCFG_HID_AUTO_SHUTDOWN_TIME       (1 * 60)      //HID无操作自动关机(单位:秒)
#else
#define CONFIG_BLE_CONNECT_SLOT            0 //BLE高回报率设置, 支持私有协议
#define TCFG_HID_AUTO_SHUTDOWN_TIME       (0 * 60)      //HID无操作自动关机(单位:秒)
#endif

Note

在发数函数中滑动操作大约2s重置睡眠定时器,有按键和滚轮操作也重置定时器

apps/demo/hid/examples/mouse dual/app mouse dual.c

if (mouse_info.mouse_hid_mode == HID_MODE_BLE) {
            mouse_reset_cnt_max = MOUSE_7MS_CLEAR_WDT_CNT_MAX;
        } else {
            mouse_reset_cnt_max = MOUSE_1MS_CLEAR_WDT_CNT_MAX;
        }
        if (mouse_reset_cnt > mouse_reset_cnt_max) {
#if (TCFG_HID_AUTO_SHUTDOWN_TIME)
            sys_timer_modify(mouse_info.mouse_auto_shutdown_timer, TCFG_HID_AUTO_SHUTDOWN_TIME * 1000);
#endif
            // 避免看门狗超时
            wdt_clear();
            mouse_reset_cnt = 0;
        }
.....
#if (TCFG_HID_AUTO_SHUTDOWN_TIME)
    //重置无操作定时计数
    if (event->type != SYS_DEVICE_EVENT || DEVICE_EVENT_FROM_POWER != (int)event->arg) { //过滤电源消息
        sys_timer_modify(mouse_info.mouse_auto_shutdown_timer, TCFG_HID_AUTO_SHUTDOWN_TIME * 1000);
    }
#endif

2.4.6.8. 测试发数逻辑

Note

鼠标应用中添加了模拟sensor发数的逻辑,当滑动鼠标回报率未能成功到达预期时,可以用来测试是传感器数据获取流程出现问题,还是BLE主从通信是否存在问题,会默认控制鼠标画正方形

apps/demo/hid/examples/mouse single/app_mouse_single.h

// 模拟sensor发数使能
#define TEST_MOUSE_SIMULATION_ENABLE        0

apps/demo/hid/examples/mouse_dual/app_mouse_dual.c

#if TEST_MOUSE_SIMULATION_ENABLE
    mouse_send_data_test();
#else
#ifdef TCFG_OMSENSOR_ENABLE
    optical_mouse_read_sensor_handler_high(&mouse_send_packet, &mouse_flag);
#endif
#endif
....
//=====================================MOUSE TEST==================================================//
#if TEST_MOUSE_SIMULATION_ENABLE
/*************************************************************************************************/
/*!
 *  \brief  mouse测试函数
 *
 *  \param
 *
 *  \return
 *
 *  \note   持续控制鼠标画一个矩形
 */
/*************************************************************************************************/
static void mouse_send_data_test(void)
{
    static int16_t deltaX, deltaY;
    static uint8_t dcount = 100;

    dcount++;
    if (dcount >= 100) {
        dcount = 0;
        if ((deltaX == 0) && (deltaY == 4)) {
            deltaX = 4;
            deltaY = 0;
        } else if ((deltaX == 4) && (deltaY == 0)) {
            deltaX = 0;
            deltaY = -4;
        } else if ((deltaX == 0) && (deltaY == -4)) {
            deltaX = -4;
            deltaY = 0;
        } else if ((deltaX == -4) && (deltaY == 0)) {
            deltaX = 0;
            deltaY = 4;
        } else {
            deltaX = 4;
            deltaY = 0;
        }
    }
    mouse_optical_sensor_event(0, deltaX, deltaY);
}
//=====================================MOUSE TEST==================================================//
#endif

2.4.6.9. case默认应用资源(默认使用SF03_AW313A_V1.0鼠标)

Note

demo默认占用外设资源如下,具体可以查看 apps/demo/hid/board/bd47/board_aw313a_mouse_cfg.h

  • I/O口占用情况

PA口 I/O

默认占用情况

PA3

默认打印UART0引脚

PA8

鼠标左键

PA10

鼠标右键

PA9

鼠标中键

PA0

模式切换IO

PA11

CPI切换键

PA1

ADKEY

  • 硬件定时器占用情况

定时器

默认占用情况

TIMER0

给ble或者USB模式定时采集数据发数

TIMER1

暂无使用

TIMER2

暂无使用

TIMER3

暂无使用

  • 串口占用情况

串口

默认占用情况

UART0

SDK打印

UART1

暂无使用

UART2

暂无使用

  • USB暂用情况

USB

默认占用情况

USB0

默认使用USB模式发数

2.4.6.10. case默认应用功能现象

操作行为

现象

进入配对模式后,使用bd47 dongle(2.4g)/电脑(ble)连接进行绑定操作,name为AW31N_HID

能够成功绑定连接

左键+右键长按2s(不存在绑定信息时),进入配对模式

能够成功绑定连接

拨码切换模式(ble,2.4g,有绑定进入广播回连,没绑定则不广播) 1. 上档为ble模式 2. 下档为24g模式 3. 中间档为软关机

操作正常

在开机情况下插上usb,关闭蓝牙,(ble或2.4g)转为USB模式

操作正常

左,中、右键

操作正常

DPI切换键

操作正常

滑轮

操作正常

侧键1

控制电脑音乐播放和暂停

侧键2

控制电脑音乐禁音

所有模式1min无鼠标操作进入睡眠,按键唤醒

行为正常

2.4g模式时时dongle进入suspend,主机主动断开,进入睡眠

行为正常

移动顺滑

行为正常

鼠标移动回包率(usb和2.4g最高稳定在900+,ble最高133)

行为正常