当前文档版本为 v1.0.1,您可以访问当前页面的 开发中 版本以获取最近可能的更新。

Solution: Multi Model Mouse 4K

重要

此例程仅存在于特殊版本的SDK中,如有需要请联系Panchip。

1 功能概述

此sample为pan108xxb5(64pin芯片)或者pan108xxa3(48pin芯片)在实体鼠标板下的应用

基于1k鼠标,4k鼠标提高了TRX转换时间,去除了部分线程操作等延时操作,应用层使用高速的底层接口以加快收发包速度,并对payload长度有一定限制

需要配合4k Dongle使用,4K Dongle端包含了一颗高速USB芯片(CH32V30x)

具体支持的feature如下:

  • 通用功能:

    1. 光电传感:通过sensor进行鼠标基础坐标获取,可以通过按键进行切换Sensor DPI切换(PAW3325 800-1600(默认)-2400-3200-6400-12000)

    2. QDEC滚轮模块:支持去抖的正交解码器,反馈鼠标滚轮变化情况

    3. 按键模块:

      a. 基础按键左中右,中键长按1s切换2.4G上报率(4000-3000-2000-1000-500-250-125),左中右长按进行强制对码

      b. 底部具有模式切换键

    4. LED模块:反馈对码情况(2.4G未对码/强制对码快闪黄灯,已存储配对信息慢闪黄灯,已连接RB紫色灯呼吸)

    5. 电量检测:ADC采集电量信息,通过串口反馈电量情况

    6. 供电及模式切换:

      ​ a. 电池供电:底部按键可以切换2.4G模式与蓝牙模式,开关拨到中间中间为断电状态,USB接入PC时为优先级最高的USB模式

      ​ b. USB仅供电:底部按键可以切换2.4G模式与蓝牙模式,中间为保电状态

      ​ c. USB插入PC:USB接入PC时为优先级最高的USB模式

  • 2.4G模式(PRF增强型模式)

    1. 跳频:在信号质量不好(连续FREQ_HOP_NOACK_THREHOLD=30个ack未收到)/对码前在8个频点进行跳频

    2. 对码:上电跳频找到dongle端的频点后,通信互发对端的MAC地址后2字节,之后切换到私有地址进行通信

    3. 重传:丢包时会进行重传,以最快速度进行重传直到收到对端回复

    4. ACK:可解析的ACK,具有演示代码

    5. 4k性能:默认上报率4100的情况下,近距离稳定在3900包以上,6米稳定在3800包以上;近距离可以切换上报率进行测试(可选)

    6. 测试模式(可选):宏控制自动画圈;关闭PWM灯测试功耗;设置功率测试;ACK测试

  • BLE模式

    1. 蓝牙白名单,分时连接

    2. 配对,保存

    3. 连接,重连功能:

    4. OTA(未使能):与mcuboot配合通过NRF工具进行升级

    5. 性能:133HZ上报率

    6. 性能:兼容性

  • USB模式

    1. 性能:USB2.0最高速率1000hz

    2. 电脑的休眠唤醒(未做)

    3. 升级:与mcuboot配合通过USB工具进行升级

  • EMI测试

2 环境要求

  • board: pan108xxb5或者pan108xxa3(芯片型号)鼠标板

  • uart (option): overlay中默认P24显示串口log

  • USB升级工具(USB升级工具获取:https://docs.panchip.com/pan1080dk-doc/latest/docs-zdk/06_dev_tools/pan108x_toolbox_intro.html)

  • 4K鼠标测试工具:05_TOOLS\dev-tools\MouseSupportTool\Polling Rate Tester App_v1.02.00.exe

3 编译和烧录

例程位置:zephyr\samples_panchip\solutions\multimode_mouse_4K

使用 ZAL 工具可以对其进行编译、烧录、打开 VS Code 调试等操作。关于 ZAL 工具的详细介绍请参考:Zephyr APP Launcher 工具介绍

4 演示说明

芯片全部擦除还原默认状态,准备好烧录multimode_mouse_dongle_4K的接收器

4.1 电池供电模式

  1. 电池上电,鼠标端默认中键关闭状态

  2. 鼠标端开关拨到左边为2.4G模式,黄色灯快闪,插入接收器至PC,黄色灯变为紫色呼吸灯,可以测试鼠标基础功能,最大上报率近距离达到4000,远距离6m达到3800+,通过按DPI键切换DPI,通过长按中键测试上报率

  3. 鼠标端开关拨到右边为蓝牙模式,浅蓝色灯效,可以在开启蓝牙的PC端搜索到名为Pan_Mouse的蓝牙设备,连接后进行控制,最大可以达到133hz左右

  4. 拔出dongle,鼠标端通过拨动模式切换键再次进入2.4G模式,黄色灯慢闪,插入dongle变为呼吸灯,鼠标功能恢复

  5. 鼠标端通过拨动模式切换键再次进入BLE模式,等待蓝牙重新连接至PC,鼠标功能恢复

  6. 不拔出dongle,鼠标端再次通过拨动模式切换键进入2.4G模式,黄色灯慢闪,随后变为呼吸灯,鼠标功能恢复

  7. 长按左中右3键,黄色灯快闪,进入强制对码模式,此时重新复位dongle,鼠标对码成功呼吸灯启动,鼠标功能恢复

4.2 USB仅供电模式

​ USB供电与鼠标供电区别在于模式切换键在中间档位不断电,灯的状态暂时未固定,验证非断电模式切换正常即可

​ 模式切换键在中间状态下,插入USB供电,参考电池步骤5.6,测试2.4G与蓝牙下鼠标功能恢复

4.3 USB模式(USB插入PC)

​ 在以上任何情况下(电池供电下任何模式或者关闭/电池未供电),插入USB,鼠标变为USB功能,深蓝色灯效,拔出USB退出到插入之前的状态

​ USB模式下,上报率不支持1000以上,因此1000以上的上报率默认为1000上报率

4.4 测试模式

4.4.1 组合按键测试

按键线程每50ms扫描一次键值

#define KEY_SCAN_INTERVAL_MS                    50

void key_analysis(void)
{
	uint8_t key_value = 0;

	key_get_value(&key_value);
	#if EVB64_TEST
	evb_test_key();
	#endif
	if (mouse_work_mode == mouse_prf_mode) {
		combo_key_detect(key_value);
		unpair_key_detect(key_value);
		report_rate_key_detect(key_value);
	} else if (mouse_work_mode == mouse_usb_mode) {
		combo_key_detect(key_value);
		report_rate_key_detect(key_value);
	} else if (mouse_work_mode == mouse_ble_mode) {
		combo_key_detect(key_value);
		unpair_key_detect(key_value);
		ble_multi_dev_detect(key_value);
	}
	dpi_key_detect(key_value);
	long_press_key_detect(key_value);
}

void thread_key(void *dummy1, void *dummy2, void *dummy3)
{
	printk("T key %d\n", KEY_THREAD_PRIORITY);
	while (1) {
		k_msleep(KEY_SCAN_INTERVAL_MS);
		key_analysis();
	}
}

组合按键说明

按键

检测时间
0代表短按

2.4G模式

USB模式

蓝牙模式

DPI

0

切换传感器灵敏度

切换传感器灵敏度

切换传感器灵敏度

DPI + L

0

Reboot

Reboot

Reboot

DPI + M

0

显示电压

更新USB VID,PID

显示电压

DPI + R

0

擦除配对 + Reboot

擦除配对 + Reboot

M

1

切换上报率

切换上报率

L + M + R

3

擦除配对 + Reboot

不重启重新配对

M + R

3

自动画圈

自动画圈

自动画圈

R + 4

3

关闭灯效

关闭灯效

关闭灯效

R + 5

3

切换彩虹灯效

空闲进入Deepsleep
会影响灯效

4 + 5

3

键鼠套件模式下
切换键盘

L

0

键鼠套件模式下
模拟上报键盘数据1

5

0

正常模式下
正常键值
键鼠套件模式下
上报键值A

正常键值

正常键值

底部按键

0

切换蓝牙多设备

4.4.2 ACK回包统计

默认开启了回包打印1s统计,方便记录实际的成功上报率

4.4.3 EMI测试(RF测试模式)

环境要求:

  • PAN1080 鼠标板

  • USB TYPE-C线

  • PAN1080 ToolBox下载

测试说明:

  • TX模式,如下图所示:

    image

    RF TEST TX模式

    发包数必须设为0,当前不支持发包数的设置

  • RX模式,如下图所示:

    image

    RF TEST RX模式

  • TX单载波模式,如下图所示:

    image

    RF TEST 单载波模式

5 开发说明

5.1 架构说明

multi_model_mouse基于zephyr架构,进行多线程编程,线程静态初始化后,根据优先级进行先后初始化,之后各个线程运行至while(1)等待相应的信号量,以此通过控制信号量控制各个线程的调度关系

架构中应用层主要包含

  • 6个线程

    • 电量检测(包含WDT喂狗)

    • 灯控线程

    • USB发包(根据信号量,取出缓存包发送)

    • 蓝牙线程(监测蓝牙状态)

    • EMI线程(根据emi flag处理定时发包等逻辑)

    • 按键检测(实际产品中可以移除)

  • 3个中断

    • TIMER1中断(1ms刷新灯效)

    • USB中断

    • PRF中断

  • 3个重要接口

    • BLE接口(Zephyr API):FRAME组包后通过调用蓝牙接口进入HOST层进行发包,非阻塞接口,会为ble host填充最多6包数据

      /** @brief Notify attribute value change no wait.
       *
       *  Send notification of attribute value change, if connection is NULL notify
       *  all peer that have notification enabled via CCC otherwise do a direct
       *  notification only the given connection.
       *
       *  The attribute object on the parameters can be the so called Characteristic
       *  Declaration, which is usually declared with BT_GATT_CHARACTERISTIC followed
       *  by BT_GATT_CCC, or the Characteristic Value Declaration which is
       *  automatically created after the Characteristic Declaration when using
       *  BT_GATT_CHARACTERISTIC.
       *
       *  @param conn Connection object.
       *  @param attr Characteristic or Characteristic Value attribute.
       *  @param data Pointer to Attribute data.
       *  @param len Attribute value length.
       *
       *  @return 0 in case of success or negative value in case of error.
       */
      static inline int bt_gatt_notify_no_wait(struct bt_conn *conn,
      				 const struct bt_gatt_attr *attr,
      				 const void *data, uint16_t len)
      
    • USB EP3接口(VENDOR DFU):通过USB中断进入进行DFU升级,EMI测试

      void usb_vendor_ep3_out(void)
      
    • USB/蓝牙模式组包逻辑

      __ramfunc void report_pkt_detect(void)
      

5.2 线程说明

线程定义方式如下,选用静态线程的方式,由于包含多种reboot操作,实际单模式生效可对其他无用线程进行中止操作,线程之间通过信号量进行切换或者挂起

/**
 * @brief Statically define and initialize a thread.
 *
 * The thread may be scheduled for immediate execution or a delayed start.
 *
 * Thread options are architecture-specific, and can include K_ESSENTIAL,
 * K_FP_REGS, and K_SSE_REGS. Multiple options may be specified by separating
 * them using "|" (the logical OR operator).
 *
 * The ID of the thread can be accessed using:
 *
 * @code extern const k_tid_t <name>; @endcode
 *
 * @param name Name of the thread.
 * @param stack_size Stack size in bytes.
 * @param entry Thread entry function.
 * @param p1 1st entry point parameter.
 * @param p2 2nd entry point parameter.
 * @param p3 3rd entry point parameter.
 * @param prio Thread priority.
 * @param options Thread options.
 * @param delay Scheduling delay (in milliseconds), zero for no delay.
 *
 *
 * @internal It has been observed that the x86 compiler by default aligns
 * these _static_thread_data structures to 32-byte boundaries, thereby
 * wasting space. To work around this, force a 4-byte alignment.
 *
 */
#define K_THREAD_DEFINE(name, stack_size,                                \
			entry, p1, p2, p3,                               \
			prio, options, delay)                            \
	K_THREAD_STACK_DEFINE(_k_thread_stack_##name, stack_size);	 \
	struct k_thread _k_thread_obj_##name;				 \
	STRUCT_SECTION_ITERABLE(_static_thread_data, _k_thread_data_##name) = \
		Z_THREAD_INITIALIZER(&_k_thread_obj_##name,		 \
				    _k_thread_stack_##name, stack_size,  \
				entry, p1, p2, p3, prio, options, delay, \
				NULL, name);				 	 \
	const k_tid_t name = (k_tid_t)&_k_thread_obj_##name

根据线程定义,定义了如下几个线程,并在common.h统一管理线程优先级及栈大小

#define BATTERY_THREAD_PRIORITY                     7
#define BATTERY_THREAD_STACKSIZE                    392

#define FRAME_PACK_THREAD_PRIORITY                  2
#define FRAME_PACK_THREAD_STACKSIZE                 656

#define FREQ_HOP_THREAD_PRIORITY                    2
#define FREQ_HOP_THREAD_STACKSIZE                   480

#define PRF_THREAD_PRIORITY                         1
#define PRF_THREAD_STACKSIZE                        512

#define USB_THREAD_PRIORITY                         2
#define USB_THREAD_STACKSIZE                        376

#define BLE_MONITOR_THREAD_PRIORITY                 3
#define BLE_MONITOR_THREAD_STACKSIZE                432

#define EMI_THREAD_PRIORITY                         2
#define EMI_THREAD_STACKSIZE                        368

#define KEY_THREAD_PRIORITY                         8
#define KEY_THREAD_STACKSIZE                        800

5.2.1 BATTERY线程

电量监测线程,ADC_SCAN_INTERVAL_MS = 100 ms的间隔进行电量采集,并同时进行WDT喂狗,监测到低电量会切换灯效,蓝牙模式会准备电量数据,在不移动鼠标时进行上报

5.2.2 LED线程

灯控线程,通过信号量控制主要灯的状态

5.2.3 USB线程

USB线程,USB插入PC时进入,获取组包并且上报

5.2.4 BLE监测线程

监测蓝牙连接状态并处理进入关灯切换空闲deepsleep并更新latency相关逻辑

5.2.5 EMI线程

通过TIMER0触发EMI测试中发包相关逻辑

5.2.6 KEY线程

按键检测线程,相对独立,实际应用中可以移除

5.3 RF中断说明

5.3.1 RF中断

鼠标端为PRF TX端,增强型模式会在TX后自动转入RX

中断中处理跳频相关逻辑

5.3.2 TIMER中断

TIMER中断产生多线程的控制信号量,并且根据不同上报率切换timer中断间隔

5.4 主要数据结构说明

5.4.1 枚举状态

5.4.1.1 配对状态
enum prf_pair_stat_t {
	prf_pair_start,
	prf_pair_comm,
	prf_pair_addr,
	prf_pair_end,
	prf_paired_private,
	prf_paired_public,
};
5.4.1.2.连接状态
enum ble_connect_stat_t {
	ble_disconnect_stat,
	ble_connect_stat,
};
5.4.1.3 工作模式
enum mouse_work_mode_t {
	mouse_null_mode,
	mouse_usb_mode,
	mouse_prf_mode,
	mouse_ble_mode,
};
5.4.1.4 2.4G RF状态
enum prf_trx_stat_t {
	prf_idle_stat,
	prf_tx_done_stat,
	prf_rx_done_stat,
	prf_rx_timeout_stat,
	prf_rx_crc_err_stat,
	prf_rx_pid_err_stat,
};
5.4.1.5 跳频状态
enum prf_freq_hop_stat_t {
	freq_hop_disconnect_stat,
	freq_hop_connecting_stat,
	freq_hop_done_stat,
};
5.4.1.6 USB状态
enum usb_plug_mode_t {
	usb_plug_in,
	usb_plug_out,
};
5.4.1.7 灯状态
enum mouse_led_stat_t {
	led_unpair_stat,
	led_paired_stat,
	led_low_batt_stat,
	led_prf_connected,
	led_key_stat,
};

5.4.2 全局结构

5.4.2.1 组包ring_buf(zephyr)
/**
 * @brief A structure to represent a ring buffer
 */
struct ring_buf {
	uint32_t head;	 /**< Index in buf for the head element */
	uint32_t tail;	 /**< Index in buf for the tail element */
	union ring_buf_misc {
		struct ring_buf_misc_item_mode {
			uint32_t dropped_put_count; /**< Running tally of the
						     * number of failed put
						     * attempts.
						     */
		} item_mode;
		struct ring_buf_misc_byte_mode {
			uint32_t tmp_tail;
			uint32_t tmp_head;
		} byte_mode;
	} misc;
	uint32_t size;   /**< Size of buf in 32-bit chunks */

	union ring_buf_buffer {
		uint32_t *buf32; /**< Memory region for stored entries */
		uint8_t *buf8;
	} buf;
	uint32_t mask;   /**< Modulo mask if size is a power of 2 */

	struct k_spinlock lock;
};
5.4.2.2 传感器结构体
struct sensor_data_t {
	uint8_t motion;
	uint8_t observation;
	uint8_t x_delta_l;
	uint8_t x_delta_h;
	uint8_t y_delta_l;
	uint8_t y_delta_h;
};
5.4.2.3 packet格式结构体

鼠标发送的2.4G帧格式如下

前导码

接入地址

signal

payload

crc

3B

5B

10bit

10B

2B

0x550f71

固定的

增强型的字段

鼠标数据包

16bit crc

其中鼠标Payload为用户可以修改的数据段,Payload 10B格式如下

Type

sequence num

reserved

key

x_value

y_value

roll_value

1B

1B

1B

1B

2B

2B

2B

鼠标包 0x40

己端MAC地址后两个字节

预留位

按键值

传感器x变化量

传感器y变化量

滚轮数据

4k鼠标长时间解析超过2B的ACK情况下不能保证上报率,但支持配合dongle进行固定头为0x5a上报长包ACK,最多测试35B数据的解析测试,长包ACK由dongle端CH32 USB OUT端点获取,鼠标端会打印显示数据

5.4.2.4 收包计数结构体
struct prf_pkt_cnt_t {
	uint32_t tx_cnt;
	uint32_t rx_cnt;
	uint8_t prf_repeat_cnt;
};
5.4.2.5 配对信息结构体
struct pair_ctrl_t {
	enum prf_pair_stat_t prf_pair_stat;
	uint32_t prf_pair_timeout;
	uint8_t pair_own_addr[2];
	uint8_t pair_peer_addr[2];
	bool paired_flag;
};

5.5 主要逻辑图示

5.5.1 鼠标端多线程流程图

5.5.1.1 模式检测及中断、低功耗接口流程图
image

鼠标主框架流程图

5.5.1.2 其他线程流程图
image

其他线程流程图

5.5.2 2.4G对码重传跳频

5.5.2.1 对码逻辑
image

对码逻辑

5.5.2.2 重传逻辑
image

重传逻辑

5.5.2.3 跳频逻辑

跳频基础逻辑同1k鼠标,鼠标端跳频逻辑阈值增加,30包未收到后进行跳频,配合dongle端timeout 10ms逻辑

/* mouse */
#define ACK_HOP_CNT          30
/* dongle */
panchip_prf_rx_timeout(10000); /*rx lost cnt hop 10ms*/
image

跳频逻辑

6 补充说明

补充说明当前功耗测试情况,支持中遇到的问题(供参考)及已知仍可能存在的问题

6.1 功耗说明

功耗测试首先进行不开启休眠唤醒的测试,但需要关闭PWM灯进行测试

CONFIG_LED_THREAD_ENABLE=n

之后进行休眠唤醒电流测试。

2.4G模式功耗如下

设备

LED灯(clk off)

ADC配置

apb div

上报率

状态1-电流(移动)(mA)

状态2-关闭rF (mA)

状态3-deepsleep,关灯 (mA)

状态4-deepsleep+传感器关闭 uA

自动画圈mA

鼠标D

OFF

50ms,空闲关闭

4

4000

16.37

4.61

0.12

35

10.31

鼠标D

OFF

50ms,空闲关闭

4

3000

13.81

4.56

0.12

35

8.74

鼠标D

OFF

50ms,空闲关闭

4

2000

12.28

4.51

0.11

35

7.28

鼠标D

OFF

50ms,空闲关闭

4

1000

10.86

4.46

0.11

35

5.87

鼠标D

OFF

50ms,空闲关闭

4

500

9.99

4.44

0.11

35

5.17

鼠标D

OFF

50ms,空闲关闭

4

250

9.6

4.43

0.11

35

4.82

鼠标D

OFF

50ms,空闲关闭

4

125

9.38

4.41

0.11

35

4.65

蓝牙模式功耗如下:

设备

LED灯(clk off)

ADC配置

apb div

上报率

状态1-电流(移动)(mA)

状态2-关灯,idle deepsleep (uA)

状态3-deepsleep(uA)

状态4-deepsleep+传感器关闭 uA

自动画圈mA

鼠标D

OFF

50ms,空闲关闭

4

133

9.73

573

56.6

35

5.07

6.2 待完善内容

​ 硬件就绪后,可以对Sensor 3395进行适配

7 RAM/Flash资源使用情况

FLASH部分包含蓝牙controller固定资源120K,不包含初始位置开始的60K MCUBOOT程序

默认开启了调试log(flash占用11K左右),并且需要CONFIG_SPEED_OPTIMIZATIONS=y及中断ramfunc处理,会消耗一定资源

ramfunc可以后续进行一部分裁剪优化(参考1k)

Memory region

Used Size

Region Size

%age Used

FLASH

142636 B

152 KB

91.64%

SRAM

45656 B

50 KB

89.17%