2.4.4. 三模鼠标应用

2.4.4.1. 概述

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

  • 支持2.4g + dongle鼠标133回报率模式(高回报率暂未支持)

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

2.4.4.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_AW3XX_MOUSE板级,可配置单模ble
#define CONFIG_APP_MOUSE_LOW_LATENCY        0//AW33N暂不支持, 待迭代. 低延时从机,高回报率测试,需搭配CONFIG_BOARD_AW313A_MOUSE板级
#define CONFIG_APP_KEYPAGE                  0//翻页器
#define CONFIG_APP_REMOTE_CONTROL           0//AW33N暂不支持, 待迭代. 遥控器,需搭配CONFIG_BOARD_AW31A_RC板级
  • 使用快捷键Alt + g 打开board_config.h,板级选择CONFIG_BOARD_AW33N_MOUSE

#ifndef BOARD_CONFIG_H
#define BOARD_CONFIG_H

//#define CONFIG_BOARD_AW33N_DEMO
 #define CONFIG_BOARD_AW33N_MOUSE
// #define CONFIG_BOARD_AW33N_MOUSE_M143
// #define CONFIG_BOARD_AW33N_RC

#include "board_aw33n_demo_cfg.h"
#include "board_aw33n_mouse_cfg.h"
#include "board_aw33n_mouse_m143_cfg.h"
#include "board_aw33n_rc_cfg.h"

#endif

apps/demo/hid/board/bd57/board_aw33n_mouse_cfg.h

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

//*********************************************************************************//
//                                 adkey 配置                                      //
//*********************************************************************************//
#define KEY_AD_EN				            ENABLE           //<AD按键使能
#define AD_KEY_IO		                    IO_PORTA_09       // 可用的IO见adc_ch_io_table
#define EXTERN_R_UP                         0                 // 220 -> 22k,外挂上拉电阻,0使用内部上拉,内部上拉为10k

#if EXTERN_R_UP
#define R_UP                                EXTERN_R_UP
#else
#define R_UP                                100               // 内部上拉为10K,有20%误差
#endif

#define ADC10_33                            (0x3ffL)

// 根据具体电路配置
#define ADC10_30   (ADC10_33 * 1000 / (1000 + R_UP)) //100K
#define ADC10_27   (ADC10_33 * 50   / (50  + R_UP))  //5K
#define ADC10_23   (ADC10_33 * 0    / (0  + R_UP))   //0
#define ADC10_20   (0)
#define ADC10_17   (0)
#define ADC10_13   (0)  //NULL
#define ADC10_10   (0)  //NULL
#define ADC10_07   (0)  //NULL
#define ADC10_04   (0)  //NULL
#define ADC10_00   (0)  //NULL

#define AD_NOKEY        ((ADC10_33 + ADC10_30) / 2)
#define ADKEY1_0		((ADC10_30 + ADC10_27) / 2)
#define ADKEY1_1		((ADC10_04 + ADC10_00) / 2)
#define ADKEY1_2		((ADC10_23 + ADC10_20) / 2)
#define ADKEY1_3		((ADC10_20 + ADC10_17) / 2)
#define ADKEY1_4		((ADC10_17 + ADC10_13) / 2)
#define ADKEY1_5		((ADC10_13 + ADC10_10) / 2)
#define ADKEY1_6		((ADC10_10 + ADC10_07) / 2)
#define ADKEY1_7		((ADC10_07 + ADC10_04) / 2)
#define ADKEY1_8		((ADC10_27 + ADC10_23) / 2)

#define TCFG_ADKEY_VALUE0                   0
#define TCFG_ADKEY_VALUE1                   1
#define TCFG_ADKEY_VALUE2                   2
#define TCFG_ADKEY_VALUE3                   3
#define TCFG_ADKEY_VALUE4                   4
#define TCFG_ADKEY_VALUE5                   5
#define TCFG_ADKEY_VALUE6                   6
#define TCFG_ADKEY_VALUE7                   7
#define TCFG_ADKEY_VALUE8                   8
#define TCFG_ADKEY_VALUE9                   9
#define KEY_CPI_VAL                         0

#define TCFG_ADC_VTEMP_CH_EN                ENABLE //温度采集功能

//*********************************************************************************//
//                                 iokey 配置                                      //
//*********************************************************************************//
#if LOW_CONNECT_INTERVAL_TEST
#define KEY_IO_EN         	                DISABLE                  //<IO按键使能
#else
#define KEY_IO_EN         	                ENABLE                  //<IO按键使能
#endif
#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_04
#define TCFG_IOKEY_MOUSE_RK_PORT		            IO_PORTA_06
#define TCFG_IOKEY_MOUSE_HK_PORT		            IO_PORTA_05
// #define TCFG_IOKEY_MOUSE_CPI_PORT		            IO_PORTA_11
#if (LOW_CONNECT_INTERVAL_TEST == 0)
#define TCFG_POWER_CHECK_MODE_ENABLE                0//ENABLE
// #define TCFG_IOKEY_MOUSE_SWITCH_PORT		        IO_PORTA_00
#define TCFG_MOUSE_DET_S1_SWITCH_PORT		        IO_PORTB_01
#define TCFG_MOUSE_DET_S2_SWITCH_PORT		        IO_PORTA_13
#endif

  • 传感器 io配置

//*********************************************************************************//
//                                  optical mouse sensor配置                       //
//*********************************************************************************//
#if (LOW_CONNECT_INTERVAL_TEST == 0)
#define TCFG_OMSENSOR_ENABLE
#endif

#define TCFG_S202_EN                              DISABLE
#define TCFG_HAL3205_EN                           DISABLE
#define TCFG_HAL3212_EN                           ENABLE

#define TCFG_OPTICAL_SENSOR_SCLK_PORT             IO_PORTA_15
#define TCFG_OPTICAL_SENSOR_DATA_PORT             IO_PORTA_14
#define TCFG_OPTICAL_SENSOR_INT_PORT              IO_PORTB_01
  • 鼠标滚轮器配置

//*********************************************************************************//
//                                  code switch配置                                //
//*********************************************************************************//
#if LOW_CONNECT_INTERVAL_TEST
#define TCFG_CODE_SWITCH_ENABLE                   DISABLE_THIS_MOUDLE
#else
#define TCFG_CODE_SWITCH_ENABLE                   ENABLE_THIS_MOUDLE
#endif
#define TCFG_CODE_SWITCH_A_PHASE_PORT             IO_PORTA_03
#define TCFG_CODE_SWITCH_B_PHASE_PORT             IO_PORTA_02

apps/demo/hid/board/bd57/board_aw33n_mouse_global_build_cfg.h

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

#define HAS_USB_EN                              1

2.4.4.3. 模式选择

Note

三模鼠标默认有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

#if TCFG_POWER_CHECK_MODE_ENABLE
    gpio_set_mode(IO_PORT_SPILT(TCFG_MOUSE_DET_S1_SWITCH_PORT), PORT_INPUT_PULLUP_200K);
    gpio_set_mode(IO_PORT_SPILT(TCFG_MOUSE_DET_S2_SWITCH_PORT), PORT_INPUT_PULLUP_200K);

    if (!gpio_read(TCFG_MOUSE_DET_S1_SWITCH_PORT)) {
        mouse_switch_start_mode = HID_MODE_BLE;
    } else if (!gpio_read(TCFG_MOUSE_DET_S2_SWITCH_PORT)) {
        mouse_switch_start_mode = HID_MODE_24G;
    } else {
        mouse_switch_start_mode = HID_MODE_USB;
    }
    log_info("mode = %d", mouse_switch_start_mode);
#endif

}

Note

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

2.4.4.4. BLE模式运行逻辑

image-20240708134907999

  • 配对与连接

启动配对模式:左键+中键+右键长按3s进入配对模式。选择ble模式后,上电默认会开启广播,广播30s没有连接进入软关机

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

void mouse_select_btmode(uint8_t mode)
{
    if (mode == HID_MODE_USB) {
        mouse_info.mouse_hid_mode = mode;
        if (pair_timer_id) {
            sys_timeout_del(pair_timer_id);
            pair_timer_id = 0;
        }
        return;
    }

    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

        ble_module_enable(1);
    }

    mouse_vm_deal(1);
}

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

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

        // 中键+左键+右键, 长按数秒开广播
#if (DOUBLE_KEY_HOLD_PAIR)
        if (KEY_LK_RK_HK_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) {
                ble_module_enable(0);
                sys_timeout_add(NULL, mouse_bt_pair_mode_handle, WAIT_DISCONN_TIME_MS); //确保断开连接
                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, 0x10,                    //     Report Size (16) - 每项数据的大小为16位。
    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, 0x00, 0x80,              //     Logical Minimum (-32768) - X和Y轴数据的逻辑最小值为-32768。
    0x26, 0xFF, 0x7F,              //     Logical Maximum (32767) - X和Y轴数据的逻辑最大值为32767。
    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, 0x10,                    //     Report Size (16) - 每项数据的大小为16位。
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, 0x00, 0x80,              //     Logical Minimum (-32768) - X和Y轴数据的逻辑最小值为-32768。
0x26, 0xFF, 0x7F,              //     Logical Maximum (32767) - X和Y轴数据的逻辑最大值为32767。
0x81, 0x06,                    // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) - 输入类型,代表了轴的移动。

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

结束集合

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

ble模式通过蓝牙事件中断发数

//蓝牙EVT中断回调,注意代码执行时间要断
LP_RUNCODE_AT(.lp_running.app.text)
void conn_evt_probe_handle_hook(u16 conn_handle, u16 event_cnt)
{
//保证每次连接事件推送一次发包操作
    if (!mouse_info.mouse_is_paired) {
        return;
    }
    if (!mouse_sleep_exit_send_flag) {
        if (!mouse_conn_send_flag) {
            bt_event_update_to_user(NULL, SYS_BT_EVENT_TYPE_CON_STATUS, BT_STATUS_CONN_EVTT_PROBE, 0);
        }
    } else {
        if (!(mouse_flag.sensor_send_flag && mouse_flag.wheel_send_flag && mouse_flag.button_send_flag)) {
            bt_event_update_to_user(NULL, SYS_BT_EVENT_TYPE_CON_STATUS, BT_STATUS_CONN_SEND_DATA, 0);
        }
        mouse_sleep_exit_send_flag = 0;
    }

}
  • 生成HID报文

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

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

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

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

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

/*************************************************************************************************/
/*!
 *  \brief     鼠标发数函数,定时器调用
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
static void mouse_data_send(void *priv_hw, uint8_t hw_state)
{
    /* 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 (mouse_info.mouse_hid_mode == HID_MODE_24G && SWITCH_MODE_24G_ENABLE) {
#if 1
#if CONFIG_BLE_CONNECT_SLOT
        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
//直通接口
        ble_hid_data_send_hw_fast(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet));
        putchar('@');
        /* ble_hid_data_send(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet)); */
#endif
#else //如需进行长包短包间隔测试,可以运行下面发包逻辑
        static u32 cnt = 0;
        cnt++;
        if (cnt % 1000 == 0) {
            /* log_info("hw_send_packet_fast[cnt:%d len:%d]", cnt, sizeof(mouse_packet_data_t)); */
            /* put_buf((uint8_t *)&mouse_send_packet, sizeof(mouse_packet_data_t)); */
            static u8 data[64] = {0};
            memset(data, ++data[0], sizeof(data));
            //测试发BLE长包
            hw_send_packet_fast(HID_REPORT_ID_01_SEND_HANDLE, priv_hw, data, sizeof(data), PACKET_DATE_LEN, hw_state);
        } else {
            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);
        }
#endif
    } else if (mouse_info.mouse_hid_mode == HID_MODE_BLE && SWITCH_MODE_BLE_ENABLE) {
//直通接口
        ble_hid_data_send_hw_fast(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet));
        putchar('@');
        /* ble_hid_data_send(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet)); */
    } else if (mouse_info.mouse_hid_mode == HID_MODE_USB && SWITCH_MODE_USB_ENABLE) {
        mouse_usb_data_send(MOUSE_SEND_DATA_REPORT_ID, (uint8_t *)&mouse_send_packet, sizeof(mouse_send_packet));
    }

    // 清除鼠标信息
    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));
    }
    mouse_conn_send_flag = 0;
}

2.4.4.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

2.4g模式和ble模式一样在蓝牙事件中断实现发数,2.4g暂未实现高回报率

2.4.4.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的硬件定时器去采集发数

/*************************************************************************************************/
/*!
 *  \brief     usb in 回调函数
 *
 *  \param      [in]
 *
 *  \return
 *
 *  \note
 */
/*************************************************************************************************/
void mouse_usb_in_hook()
{
#if SWITCH_MODE_BLE_ENABLE || SWITCH_MODE_24G_ENABLE
    // 关闭蓝牙模块
    ble_module_enable(0);
#endif
    mouse_info_t *mouse_info = mouse_get_status_info();
    mouse_select_btmode(HID_MODE_USB);
    /* mouse_info->mouse_hid_mode = HID_MODE_USB; */
#if TCFG_LED_ENABLE
    mouse_led_operate(MOUSE_LED_USB);
#endif
    // 开启usb定时器发数
    if (mouse_info->mouse_check_mode_timer) {
        sys_s_hi_timer_modify(mouse_info->mouse_check_mode_timer, 20);
    }
    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.4.7. 休眠逻辑

Note

睡眠可设置无操作定时软关机时间

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

#define CONFIG_BLE_CONNECT_SLOT            0 //BLE高回报率设置, 支持私有协议,AW33三模鼠标暂不支持高回报率
#define TCFG_HID_AUTO_SHUTDOWN_TIME       (0 * 60)      //HID无操作自动关机(单位:秒)

Note

有按键和滚轮操作或者蓝牙事件都会重置定时器

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

#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.4.8. 测试发数逻辑

Note

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

apps/demo/hid/examples/mouse_dual/app_mouse_dual.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
....
//=====================================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.4.9. case默认应用资源

Note

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

  • I/O口占用情况

PA口 I/O

默认占用情况

PA11

默认打印UART0引脚

PA4

鼠标左键

PA6

鼠标右键

PA5

鼠标中键

PA9

ADKEY

  • 硬件定时器占用情况

定时器

默认占用情况

TIMER0

暂无使用

TIMER1

USB模式定时采集数据发数

TIMER2

暂无使用

TIMER3

暂无使用

  • 串口占用情况

串口

默认占用情况

UART0

SDK打印

UART1

暂无使用

UART2

暂无使用

  • USB暂用情况

USB

默认占用情况

USB0

默认使用USB模式发数

2.4.4.10. case默认应用功能现象

操作行为

现象

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

能够成功绑定连接

左键+中键+右键长按3s进入配对模式

能够成功绑定连接

拨码切换模式 1. 上档为ble模式 2. 下档为24g模式 3. 中间档为关机或UBS模式

操作正常

拨码开关在中间档同时插上usb,关闭蓝牙,(ble或2.4g)转为USB模式

操作正常

左,中、右键

操作正常

DPI切换键

操作正常

滑轮

操作正常

侧键1

操作正常

侧键2

操作正常

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

行为正常

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

行为正常

移动顺滑

行为正常

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

行为正常