基于RP2350B核心板实现体感鼠标与演示控制器
任务介绍
本项目旨在基于Raspberry Pi Pico 2(RP2350B)核心板开发一款体感鼠标与演示控制器,通过加速度计检测用户的手部动作,实现鼠标的移动、点击和滚轮控制功能,同时配备数码管显示和LED状态指示,适用于教学演示和实际应用场景。
主要功能
- 体感鼠标控制:通过三轴加速度计检测倾斜角度,控制鼠标光标移动
- 鼠标按键功能:支持左键、右键、中键和滚轮向上滚动
- 状态指示:使用WS2812B LED显示工作状态(粉色常亮/暗红色常亮)
- 数码管显示:使用74HC595驱动4位数码管显示状态信息
- 休眠唤醒:20秒无操作自动休眠,按键或晃动唤醒
- 数据滤波:采用卡尔曼滤波算法处理加速度计数据,提高稳定性
实物展示



硬件介绍
主控芯片
- 型号:Raspberry Pi Pico 2(RP2350B)
- 架构:ARM Cortex-M33双核,主频150MHz
- 特点:支持USB 2.0、PIO、DMA等外设
传感器模块
- 加速度计:KXTJ3-1057
- 量程:±2g
- 输出频率:50Hz
- 接口:I2C
- 功能:检测三轴加速度,实现体感控制
- 数码管驱动:74HC595
- 功能:串行转并行输出
- 用途:驱动4位共阴极数码管
- 接口:SPI
- LED指示:WS2812B
- 类型:RGB LED
- 控制方式:单线串行数据
- 用途:显示工作状态
按键输入
- DIP开关:4位拨码开关+4个按钮
- 功能:选择显示数值和触发鼠标按键
- 状态检测:ADC读取电压值判断按键状态
软件介绍
开发环境
- IDE:VS Code
- 插件:Raspberry Pi Pico
- 编译器:ARM GCC
- 构建系统:CMake
- SDK:Pico SDK 2.0.0
核心库
- TinyUSB库
- 功能:实现USB HID协议
- 支持设备枚举、报告描述符、数据传输
- 配置:tusb_config.h自定义配置
- Pico SDK
- 硬件抽象层:GPIO、I2C、DMA、PIO
- 标准库:stdio、math、sync
- 自定义驱动
ws2812b.c/h:WS2812B LED驱动(bit-bang实现)kalman.c/h:卡尔曼滤波算法hc595.c/h:74HC595数码管驱动kxtj3.c/h:KXTJ3加速度计驱动
主要功能模块
1. USB HID鼠标模块
- 设备描述符:实现标准HID鼠标描述符
- 报告格式:6字节报告(按钮、X、Y、滚轮、保留)
- 回调函数:tud_hid_set_report_cb、tud_hid_get_report_cb
2. 加速度计处理模块
// 原始数据读取
kxtj3_1057_read_accel(&accel_data);
// 卡尔曼滤波
kalman3d_update(&accel_data);
// 姿态解算
float total_g = sqrt(x*x + y*y + z*z);
float angle_x = atan2(x, z) * 180 / PI;
float angle_y = atan2(y, z) * 180 / PI;
// 非线性映射
float speed = pow(abs(angle) / 45.0f, 1.5f) * 127.0f;
3. 鼠标移动控制
- 方向判断:根据倾斜角度确定移动方向
- 速度控制:采用非线性映射,小角度慢速,大角度快速
- 精度优化:累积小数部分,精确到0.01
- 平滑移动:合成速度阈值判断,实现任意方向平滑移动
4. 休眠唤醒模块
// 休眠检测
if (current_ms - last_activity_ms > SLEEP_TIMEOUT_MS) {
is_sleeping = true;
ws2812b_off(); // 关闭LED
}
// 唤醒检测
if (has_button_press || total_g > WAKE_G_THRESHOLD) {
is_sleeping = false;
ws2812b_blue(); // 恢复
}
5. LED状态控制
- 正常工作:粉色常亮
- 休眠状态:暗红色常亮
- 信号优化:强制复位机制,避免信号堆积
设计思路
采用模块化开发思路,分别编写调试各模块驱动,确认功能运行正常后依次集成到主项目中
硬件架构

数据流向
- 加速度计 → 卡尔曼滤波 → 姿态解算 → 鼠标移动
- DIP开关 → 按键检测 → 鼠标按键/数码管显示
- 系统状态 → LED控制 → 状态指示
- 休眠检测 → 唤醒检测 → 状态切换
使用的算法
1. 卡尔曼滤波
用于降低加速度计噪声,提高数据稳定性:
// 预测
x_pred = x_prev + v * dt;
P_pred = P_prev + Q;
// 更新
K = P_pred / (P_pred + R);
x = x_pred + K * (measurement - x_pred);
P = (1 - K) * P_pred;
2. 非线性速度映射
实现倾斜角度到鼠标速度的非线性映射:
// 小角度:增长缓慢
// 大角度:增长明显
float speed = pow(abs(angle) / 45.0f, 1.5f) * 127.0f;
3. 姿态解算
通过合成速度阈值判断,避免锯齿状移动:
float speed = sqrt(dx*dx + dy*dy);
if (speed > MOVEMENT_THRESHOLD) {
// 发送移动报告
tud_hid_mouse_report(..., dx, dy, ...);
}
程序设计框图


项目难点
1. USB HID设备枚举失败
问题:学习了TinyUSB库的使用方法,单独编写模块时能跑通,但是集成到主项目时一直报错,显示USB描述符请求失败
原因分析:
- 了解了USB通信原理后,发现主项目的轮询延迟过高,干扰了USB通信,导致电脑端枚举失败
解决方案:
- 优化了各模块的运行延时,避免阻塞USB通信,最终能正常运行
2.WS2812B的控制
问题:一开始使用PIO进行通信,但是对PIO编程不熟悉,反复调试多次无法点亮LED
原因分析:PIO时序控制较为严格,需要协调系统时钟
解决方案:后续采用bit-bang方式,直接控制GPIO按照通信时序进行输出,成功点灯
3. WS2812B LED信号堆积
问题:长时间运行后LED颜色异常(红蓝混色、高亮白色等)
原因分析:
- WS2812B内部数据寄存器未及时清空
- 频繁刷新导致时序错乱
- 复位时间不足
解决方案:
// 添加强制复位函数
static void send_pixel_force(uint32_t grb) {
// 不检查缓存,强制发送
...
}
// 切换颜色前先关闭
void ws2812b_blue(void) {
send_pixel_force(0); // 强制关闭
busy_wait_us(500); // 延长复位
ws2812b_set(0, 0, BRIGHTNESS);
}
// 关闭时发送多次0
void ws2812b_off(void) {
for (int i = 0; i < 3; i++) {
send_pixel_force(0);
busy_wait_us(300);
}
}
4. 体感鼠标控制精度
问题:鼠标移动出现锯齿状轨迹,无法平滑斜线移动
原因分析:
- X/Y轴独立判断,导致方向切换不连续
- 阈值设置不当,小抖动也触发移动
- 缺少姿态解算,无法精确控制方向
解决方案:
// 1. 姿态解算
float angle_x = atan2(accel_data.x, accel_data.z) * 180 / PI;
float angle_y = atan2(accel_data.y, accel_data.z) * 180 / PI;
// 2. 合成速度判断
float speed = sqrt(dx*dx + dy*dy);
if (speed > MOVEMENT_THRESHOLD) {
// 发送移动
}
5. 加速度计噪声干扰
问题:原始数据波动大,导致鼠标抖动
解决方案:
- 实现卡尔曼滤波算法
- 调整滤波参数(Q、R、P)
- 增加移动阈值,过滤小抖动
6. ADC检测
问题:编写拨码开关的ADC时,发现电压不稳定,每次上电测得的初始电压都不同
解决方案:在初始化时读取电压,测量偏差值,利用差值对后续测得的电压值进行补正,解决了这一问题
心得体会
1. 硬件调试的重要性
本项目初期遇到USB枚举失败问题,通过仔细阅读RP2350B数据手册,发现需要清除USB PHY隔离位。这让我深刻体会到硬件调试中查阅官方文档的重要性,不能仅凭经验猜测。
2. 协议栈的复杂性
USB HID协议涉及设备描述符、配置描述符、报告描述符等多个层次,任何一个环节出错都会导致枚举失败。通过参考官方示例和逐步排查,最终成功实现。这让我认识到模块化设计和分层调试的重要性。
3. 信号时序的精确性
WS2812B采用单线串行通信,时序要求非常严格(ns级别)。初期出现颜色异常问题,通过增加nop数量、延长复位时间、添加强制复位机制解决。这让我理解到实时系统中时序控制的挑战。
4. 算法优化的价值
从简单的线性映射到非线性映射,从独立轴控制到合成速度判断,每一步优化都显著提升了用户体验。特别是卡尔曼滤波的应用,让体感控制更加稳定。这让我认识到算法优化对嵌入式系统性能提升的重要性。
5. 状态机设计的优势
使用状态机管理LED状态和休眠逻辑,使代码结构清晰、易于维护。状态切换时添加强制复位机制,解决了信号堆积问题。这让我体会到状态驱动设计在复杂系统中的价值。
6. 调试工具的使用
通过printf输出调试信息、使用逻辑分析仪观察信号、逐步注释代码排查问题,这些调试方法帮助我快速定位问题。这让我认识到良好的调试习惯对开发效率的提升。
7. 持续改进的态度
项目从最初的简单实现,到后来添加卡尔曼滤波、优化移动算法、修复LED问题,每一步都是持续改进的结果。这让我认识到迭代开发和持续优化的重要性。
总结
本项目成功实现了基于RP2350B的体感鼠标控制器,涵盖了硬件驱动、USB通信、传感器处理、状态管理等多个方面。通过解决USB枚举、LED信号、体感精度等技术难点,积累了丰富的嵌入式开发经验。未来可以进一步优化功耗、增加更多手势识别功能,提升用户体验。