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

Solution: Multi Model Mouse

重要

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

1 功能概述

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

具体支持的feature如下:

  • 通用功能:

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

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

    3. 按键模块:

      a. 基础按键左中右,中键长按1s切换2.4G上报率(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=15个ack未收到)/对码前在8个频点进行跳频

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

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

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

    5. 性能:默认上报率1000的情况下,近距离稳定在990包以上,8-9米稳定在960包以上;近距离可以切换上报率进行测试(可选)

    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/06_dev_tools/pan108x_toolbox_intro.html)

  • 鼠标测试工具:MouseTest.exe

3 编译和烧录

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

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

4 演示说明

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

4.1 电池供电模式

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

  2. 鼠标端开关拨到左边为2.4G模式,黄色灯快闪,插入接收器至PC,黄色灯变为紫色呼吸灯,可以测试鼠标基础功能,最大上报率近距离达到1000,远距离8-9m达到900+,通过按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退出到插入之前的状态

4.4 测试模式

4.4.1 自动画圈

#define MOUSE_AUTO_CIRCLE               1

4.4.2 开启低功耗(休眠唤醒)

CONFIG_PM_MOUSE=y

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)等待相应的信号量,以此通过控制信号量控制各个线程的调度关系

架构中应用层主要包含

  • 8个线程

    • 电量检测

    • 组包

    • 灯控

    • 低功耗

    • 跳频

    • 对码

    • RF发包

    • USB发包

  • 3个中断

    • TIMER中断

    • USB中断

    • PRF中断

  • 2个重要接口

    • BLE接口(Zephyr API):FRAME组包后通过调用蓝牙接口进入HOST层进行发包

    • USB EP3接口(VENDOR DFU):通过USB中断进入进行DFU升级,EMI测试

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

根据线程定义,定义了如下几个线程

5.2.1 BATTERY线程

蓝牙线程,主要处理蓝牙的初始化,广播开启关闭,及蓝牙获取组包数据,

#define BATTERY_THREAD_PRIORITY                 7
#define BATTERY_THREAD_STACKSIZE                512
K_THREAD_DEFINE(battery, BATTERY_THREAD_STACKSIZE, thread_battery, NULL, NULL, NULL,
		BATTERY_THREAD_PRIORITY, 0, 0);

5.2.2 FRAME线程

组包线程,以timer为调度周期(给出信号量)(以上报率1000为例,调度周期1ms)

主要处理组包有关逻辑(ringbuffer的填入),组合按键检测(基础按键组包,DPI模式切换,上报率切换,低电量ADC检测)

#define FRAME_PACK_THREAD_PRIORITY                      2
#define FRAME_PACK_THREAD_STACKSIZE                     2048

K_THREAD_DEFINE(frame_pack, FRAME_PACK_THREAD_STACKSIZE, thread_frame_pack, NULL, NULL, NULL,
		FRAME_PACK_THREAD_PRIORITY, 0, 0);

5.2.3 LED线程

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

#define LED_THREAD_PRIORITY             8
#define LED_THREAD_STACKSIZE            512

K_THREAD_DEFINE(led, LED_THREAD_STACKSIZE, thread_led, NULL, NULL, NULL,
		LED_THREAD_PRIORITY, 0, 0);

5.2.4 PM线程

低功耗休眠唤醒线程,控制低功耗休眠唤醒的逻辑

#define LOWPOWER_THREAD_PRIORITY                4
#define LOWPOWER_THREAD_STACKSIZE               256

K_THREAD_DEFINE(lowpower, LOWPOWER_THREAD_STACKSIZE, thread_lowpower, NULL, NULL, NULL,
		LOWPOWER_THREAD_PRIORITY, 0, 0);

5.2.5 FREQ_HOP线程

跳频线程,初始化对码线程会触发进入,在收到ACK少的时候会触发,通过信号量与PRF线程存在互斥关系

#define FREQ_HOP_THREAD_PRIORITY                2
#define FREQ_HOP_THREAD_STACKSIZE               512

K_THREAD_DEFINE(freq_hop, FREQ_HOP_THREAD_STACKSIZE, thread_freq_hop, NULL, NULL, NULL,
		FREQ_HOP_THREAD_PRIORITY, 0, 0);

5.2.6 PRF_PAIR线程

配对线程,强制对码/初始化会进入

#define PAIR_THREAD_PRIORITY            2
#define PAIR_THREAD_STACKSIZE           512

K_THREAD_DEFINE(pair, PAIR_THREAD_STACKSIZE, thread_pair, NULL, NULL, NULL,
		PAIR_THREAD_PRIORITY, 0, 0);

5.2.7 PRF线程

2.4G主线程,会进行重传处理,在重传条件下收到ACK数量低于阈值进入跳频线程

#define PRF_THREAD_PRIORITY             1
#define PRF_THREAD_STACKSIZE            512

K_THREAD_DEFINE(prf, PRF_THREAD_STACKSIZE, thread_prf, NULL, NULL, NULL,
		PRF_THREAD_PRIORITY, 0, 0);

5.2.8 USB线程

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

#define USB_THREAD_PRIORITY                     2
#define USB_THREAD_STACKSIZE            512

K_THREAD_DEFINE(usb, USB_THREAD_STACKSIZE, thread_usb, NULL, NULL, NULL,
		USB_THREAD_PRIORITY, 0, 0);

5.3 RF中断说明

5.3.1 RF中断

鼠标端为PRF TX端,增强型模式会在TX后自动转入RX, 中断中主要处理信号量sem_prf_isr的给出及ack_lost_cnt的计数

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.1.8 休眠唤醒低功耗状态
enum mouse_low_power_stat_t {
	active_stat,
	prf_off_stat,
	deep_sleep_v1_stat,
	deep_sleep_v2_stat,
	standby_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

3/14B

2B

0x550f71

固定的

增强型的字段

跳频对码包/鼠标数据包

16bit crc

其中鼠标Payload为用户可以修改的数据段,Payload中又包含了3个字节的固定数据,其中3个字节的固定数据格式如下

Type

Addr

1B

2B

包的类型:对码跳频包/鼠标包

己端MAC地址后两个字节

Type定义如下

#define PRF_PKT_TYPE_PAIR                          (1 << 0)
#define PRF_PKT_TYPE_FREQ_HOP                      (1 << 1)
#define PRF_PKT_TYPE_MOUSE_DATA                    (1 << 2)

跳频对码包:

header: PRF_PKT_TYPE_FREQ_HOP | PRF_PKT_TYPE_PAIR

跳频包:

header: PRF_PKT_TYPE_FREQ_HOP

数据包:

header: PRF_PKT_TYPE_MOUSE_DATA

另外包含11个字节应用层数据(ringbuf管理的数据结构)

header

key_value

x_value

y_value

roll_value

sequence

rate_pkt_index

reserved

1B

1B

2B

2B

1B

1B

1B

1B

包头

按键

delta X

delta Y

滚轮数据

序列号

上报率

保留扩展位

struct pkt_detect_t {
	int8_t header;
	uint8_t key_value;
	int16_t x_value;
	int16_t y_value;
	int16_t roll_value;
	uint8_t sequence;
	uint8_t rate_pkt_index;
	int8_t reserved;
} __packed;

header数据为0x55为特殊idle数据,非0x55为鼠标数据

用户可以自定义header并自定义后10B数据格式,在dongle端根据header数据进行数据解析

鼠标接受的2.4G ACK数据帧格式如下

前导码

接入地址

signal

payload

crc

3B

5B

10bit

xB

2B

0x550f71

固定的

增强

ACK数据(长度自定义,默认数据长度为0)

16bit crc

用户可以根据需求扩展使用ACK数据

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 2.4G主要流程图
image

鼠标2.4G流程图

5.5.1.3 USB主要流程图
image

USB主要流程图

5.5.1.4 其他线程流程图
image

其他线程流程图

5.5.2 2.4G对码重传跳频

5.5.2.1 对码逻辑
image

对码逻辑

5.5.2.2 重传逻辑
image

重传逻辑

5.5.2.3 跳频逻辑
image

跳频逻辑

6 补充说明

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

6.1 功耗说明

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

CONFIG_LED_THREAD_ENABLE=n

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

2.4G模式功耗如下:

设备

LED灯(clk off)

ADC配置

apb div

上报率

一级休眠mA

二级休眠uA

电流(静止)mA

电流(移动)mA

自动画圈mA

鼠标D

OFF

50ms,空闲关闭

4

1000

3.03

105.6

6.52

11.74-11.95

6.38-6.59

鼠标D

OFF

50ms,空闲关闭

4

500

2.89

105.8

4.69

9.82-9.97

4.56

鼠标D

OFF

50ms,空闲关闭

4

250

2.81

105.7

3.70

8.84-8.99

3.65

鼠标D

OFF

50ms,空闲关闭

4

125

2.77

105.2

3.21

8.31-8.32

3.21

蓝牙模式功耗如下:

设备

LED灯(clk off)

ADC配置

apb div

上报率

二级休眠uA

电流(静止)mA

电流(移动)mA

自动画圈mA

鼠标D

OFF

50ms,空闲关闭

4

133

110.3

4.67

9.91-10.01

4.63

6.2 已知问题

No

已知问题

目前状态

1

休眠模式下切换模式过快需要复位

由于鼠标处于休眠状态,切换模式本应该经过断电状态,但如果切换过快,休眠状态放电不充分,芯片不能完全断电复位,仍处于休眠状态,此时唤醒可以恢复对应模式的正常运行状态

7 RAM/Flash资源使用情况

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

Memory region         Used Size  Region Size  %age Used
FLASH:      129612 B       256 KB     49.44%
SRAM:        43848 B        50 KB     85.64%