Solution: Multi Model Mouse¶
重要
此例程仅存在于特殊版本的SDK中,如有需要请联系Panchip。
1 功能概述¶
此sample为pan108xxb5
(64pin芯片)在实体鼠标板下的应用
具体支持的feature如下:
通用功能:
光电传感:通过sensor进行鼠标基础坐标获取,可以通过按键进行切换Sensor DPI切换(PAW3325 800-1600(默认)-2400-3200-6400-12000)
QDEC滚轮模块:支持去抖的正交解码器,反馈鼠标滚轮变化情况
按键模块:
a. 基础按键左中右,中键长按1s切换2.4G上报率(1000-500-250-125),左中右长按进行强制对码
b. 底部具有模式切换键
LED模块:反馈对码情况(2.4G未对码/强制对码快闪黄灯,已存储配对信息慢闪黄灯,已连接RB紫色灯呼吸)
电量检测:ADC采集电量信息,通过串口反馈电量情况
供电及模式切换:
a. 电池供电:底部按键可以切换2.4G模式与蓝牙模式,开关拨到中间中间为断电状态,USB接入PC时为优先级最高的USB模式
b. USB仅供电:底部按键可以切换2.4G模式与蓝牙模式,中间为保电状态
c. USB插入PC:USB接入PC时为优先级最高的USB模式
2.4G模式(PRF增强型模式)
跳频:在信号质量不好(连续
FREQ_HOP_NOACK_THREHOLD
=15个ack未收到)/对码前在8个频点进行跳频对码:上电跳频找到dongle端的频点后,通信互发对端的MAC地址后2字节,之后切换到私有地址进行通信
重传:丢包时会进行重传,以最快速度进行重传直到收到对端回复
ACK:可解析的ACK,具有演示代码
性能:默认上报率1000的情况下,近距离稳定在990包以上,8-9米稳定在960包以上;近距离可以切换上报率进行测试(可选)
测试模式(可选):宏控制自动画圈;关闭PWM灯测试功耗;设置功率测试;ACK测试
BLE模式
蓝牙白名单,分时连接
配对,保存
连接,重连功能:
OTA(未使能):与mcuboot配合通过NRF工具进行升级
性能:133HZ上报率
性能:兼容性
USB模式
性能:USB2.0最高速率1000hz
电脑的休眠唤醒(未做)
升级:与mcuboot配合通过USB工具进行升级
EMI测试
2 环境要求¶
board:
pan108xxb5
(芯片型号)鼠标板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 电池供电模式¶
电池上电,鼠标端默认中键关闭状态
鼠标端开关拨到左边为2.4G模式,黄色灯快闪,插入接收器至PC,黄色灯变为紫色呼吸灯,可以测试鼠标基础功能,最大上报率近距离达到1000,远距离8-9m达到900+,通过按DPI键切换DPI,通过长按中键测试上报率
鼠标端开关拨到右边为蓝牙模式,浅蓝色灯效,可以在开启蓝牙的PC端搜索到名为
Pan_Mouse
的蓝牙设备,连接后进行控制,最大可以达到133hz左右拔出dongle,鼠标端通过拨动模式切换键再次进入2.4G模式,黄色灯慢闪,插入dongle变为呼吸灯,鼠标功能恢复
鼠标端通过拨动模式切换键再次进入BLE模式,等待蓝牙重新连接至PC,鼠标功能恢复
不拔出dongle,鼠标端再次通过拨动模式切换键进入2.4G模式,黄色灯慢闪,随后变为呼吸灯,鼠标功能恢复
长按左中右3键,黄色灯快闪,进入强制对码模式,此时重新复位dongle,鼠标对码成功呼吸灯启动,鼠标功能恢复
4.2 USB仅供电模式¶
USB供电与鼠标供电区别在于模式切换键在中间档位不断电,灯的状态暂时未固定,验证非断电模式切换正常即可
模式切换键在中间状态下,插入USB供电,参考电池步骤5.6,测试2.4G与蓝牙下鼠标功能恢复
4.3 USB模式(USB插入PC)¶
在以上任何情况下(电池供电下任何模式或者关闭/电池未供电),插入USB,鼠标变为USB功能,深蓝色灯效,拔出USB退出到插入之前的状态
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格式结构体¶
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;
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;
};
6 补充说明¶
补充说明当前功耗测试情况,支持中遇到的问题(供参考)及已知仍可能存在的问题
6.1 功耗说明¶
功耗测试首先进行不开启休眠唤醒的测试,但需要关闭PWM灯进行测试
CONFIG_LED_THREAD_ENABLE=n
之后进行休眠唤醒电流测试,生成表格记录如下
设备 |
LED灯(clk off) |
ADC配置 |
apb div |
上报率 |
一级休眠mA |
二级休眠mA |
二级休眠uA QDEC |
电流(静止)mA |
电流(移动)mA |
---|---|---|---|---|---|---|---|---|---|
鼠标X |
OFF |
50ms,空闲关闭 |
4 |
1000 |
3.33 |
0.03 |
39.5 |
7.05 |
11.39-11.58 |
鼠标X |
OFF |
50ms,空闲关闭 |
4 |
500 |
3.18 |
0.03 |
44.1 |
5.09 |
9.45-9.70 |
鼠标X |
OFF |
50ms,空闲关闭 |
4 |
250 |
3.10 |
0.04 |
48.6 |
4.01 |
8.48-8.52 |
鼠标X |
OFF |
50ms,空闲关闭 |
4 |
125 |
3.06 |
0.04 |
48.6 |
3.53 |
7.97-7.98 |
6.2 已知问题(待测试)¶
No |
已知问题 |
目前状态 |
---|---|---|
1 |
qdec唤醒电脑时需要3格滑动 |
通过QDEC引脚IO唤醒模拟,第一格滑动触发唤醒鼠标,第二格触发RESUME信号,第三格发送QDEC数据唤醒电脑 |
2 |
传感器SAW3325 |
可能存在硬件损坏偶尔读出为0;DPI切换可能不生效 |
3 |
蓝牙上报率 |
蓝牙133Hz上报率实际效果待优化 |
7 RAM/Flash资源使用情况¶
FLASH部分包含蓝牙controller固定资源120K,不包含初始位置开始的60K MCUBOOT程序
Memory region Used Size Region Size %age Used
FLASH: 120736 B 256 KB 46.06%%
SRAM: 42120 B 50 KB 82.27%