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模式运行逻辑
配对与连接
启动配对模式:按下鼠标上的配对按钮,鼠标进入配对模式。选择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模式运行逻辑
上图展示了一个BLE无线鼠标系统的详细架构。以下是对图中各部分的详细解释:
MOUSE SENSOR(鼠标传感器)
功能:检测鼠标的移动和按键操作,将这些物理操作转换为数字信号。
通信接口:使用SPI(串行外设接口)与鼠标主控芯片通信。
鼠标(BLE从机)
功能:接收来自鼠标传感器的数据,通过BLE(2.4GHz频段)发送HID(Human Interface Device)报文。
通信接口
SPI:与鼠标传感器通信,接收传感器数据。
BLE(2.4GHz):无线发送HID报文到BLE主机。
BLE主机(USB接收器)
功能:作为BLE主机设备,接收来自BLE从机(鼠标)的HID报文,并通过USB接口将这些报文传输到计算机。
通信接口
BLE(2.4GHz):无线接收来自鼠标的HID报文。
USB:有线传输数据到计算机。
带USB接口设备(计算机)
功能:接收来自BLE主机(USB接收器)的HID报文,并通过操作系统的HID驱动程序处理这些报文,将其转换为用户的操作(如光标移动、点击等)。
通信接口:通过USB接口与BLE主机通信。
工作流程
用户操作:用户移动鼠标或点击按钮。
传感器检测:鼠标传感器检测到用户的操作,并通过SPI接口将数据传输到鼠标主控芯片。
BLE从机发送数据:鼠标主控芯片将接收到的数据转换为HID报文,通过BLE(2.4GHz频段)发送到BLE主机(USB接收器)。
BLE主机接收数据:USB接收器接收到来自鼠标的HID报文,并通过USB接口将这些数据传输到计算机。
计算机处理数据:计算机通过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模式运行逻辑
上图展示了一个通过USB直接连接的有线鼠标系统。以下是对图中各部分的详细解释:
有线鼠标
功能:这是一个有线鼠标,通过USB接口与计算机连接,负责检测用户的操作(移动、点击等),并通过HID(Human Interface Device)协议将这些操作转换为数据报文。
通信:鼠标通过USB电缆直接与计算机进行通信。
带USB接口设备(计算机)
功能:这是计算机或任何带有USB接口的设备,接收来自有线鼠标的HID报文,并通过操作系统的HID驱动程序处理这些报文,将其转换为用户的操作(如光标移动、点击等)。
工作流程
用户操作:用户移动鼠标或点击按钮。
有线鼠标发送数据:鼠标的传感器检测到用户的操作,并将数据转换为HID报文,通过USB电缆发送到计算机。
计算机处理数据:计算机通过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) |
行为正常 |