【免费下载链接】OpenSplitDeck
A steam deck-like controller made from a mix of off the shelf parts and 3d printed parts with custom hardware and firmware
OpenSplitDeck 是一款自研的 Steam Deck 风格分体式无线手柄,用 nRF52840 微控制器 + 3D 打印外壳 + 定制固件打造。这篇文章面向新手,完整拆解它的电源管理设计:休眠与唤醒流程、电池电压检测原理、外设关断清单,以及长按关机/恢复出厂的按键组合机制。
一、为什么无线手柄要重视电源管理?
OpenSplitDeck 的两只手柄半部是电池供电,通过磁吸 pogo pin 充电,用 Enhanced ShockBurst(ESB)无线协议把输入数据发给 USB Dongle。
对这种设备来说,功耗直接决定续航。固件在 firmware/controller/ 目录下的 Zephyr 工程里,专门写了一个电源管理驱动:
- 电源管理核心:firmware/controller/src/power_mgmt_driver.c
- 对外接口定义:firmware/controller/src/power_mgmt_driver.h
- 电池电压检测:firmware/controller/src/analog_driver.c
- 外设关断/唤醒实现:firmware/controller/src/main.c
整套设计围绕一个状态机展开,共 4 种电源状态(见 power_mgmt_driver.h):
| 状态 | 含义 |
|---|---|
POWER_STATE_ACTIVE | 正常运行 |
POWER_STATE_ENTERING_SLEEP | 正在进入休眠 |
POWER_STATE_SLEEPING | 深度休眠中 |
POWER_STATE_WAKING_UP | 正在唤醒 |
二、休眠触发:自动休眠 + 按键组合手动休眠
2.1 自动休眠(5 分钟无操作)
power_mgmt_process()在主循环中被周期调用,逻辑非常直白:如果开启了自动休眠且当前未休眠,就比较"距上次用户活动的时间"和超时阈值,超了就调用power_mgmt_enter_sleep()。
默认配置(见 power_mgmt_driver.c):
- 自动休眠超时:
300000ms(5 分钟) - 关机组合键长按:
3000ms - 恢复出厂组合键长按:
5000ms
只要用户动了摇杆、按了按键,调用power_mgmt_reset_auto_sleep_timer()刷新计时,休眠就不会误触发。
💡 小知识:当前主程序初始化时把自动休眠设为关闭状态(
.auto_sleep_enabled = false),改为手动组合键触发休眠,避免玩游戏时误休眠,见 main.c。
2.2 手动休眠:三连震后进入 System OFF
手动休眠的入口在 main.c:长按休眠组合键后,固件会先驱动 DRV2605 马达做三次短震作为"我要睡了"的确认反馈,然后调用power_mgmt_enter_sleep()。
三、深度休眠核心:nRF52840 System OFF + GPIO 唤醒
这是整个电源管理里最"硬核"的一步。power_mgmt_enter_sleep()的流程(见 power_mgmt_driver.c):
- 保存系统状态:调用注册的保存回调,把配置写入存储(
power_mgmt_save_state); - 关断所有外设:调用注册的关断回调(下一节详述);
- 配置唤醒源:把唤醒按键(MODE 键)对应的 GPIO 配置为 nRF52840 的"Sense 引脚"——
nrf_gpio_cfg_sense_input(),上拉 + 低电平唤醒,这样芯片在 System OFF 模式下依然能被这个引脚唤醒; - 进入 System OFF:调用 Zephyr 的
sys_poweroff(),芯片掉进 nRF52840 的System OFF 模式——这是最低功耗状态,CPU、外设全部掉电,唤醒方式就是芯片复位重启,固件从头跑一遍main(),再走状态恢复流程。
注意一个细节:因为唤醒等于"复位重启",所以唤醒后不是"接着跑",而是重新启动 + 恢复存储里的标定和偏好数据(
controller_storage_load_calibration等)。这也是为什么休眠前必须先保存状态。
唤醒按键的回调power_mgmt_wake_button_callback还带了一个100ms 防抖动过滤(power_mgmt_driver.c),防止机械按键抖动造成误判。
四、外设关断清单:休眠前都关掉了什么?
shutdown_all_peripherals()在 main.c 中实现,堪称一份"省电操作清单":
| 外设 | 关断动作 |
|---|---|
| 状态 LED | 熄灭 |
| SSD1306 OLED 屏幕 | 先Display OFF(0xAE),再关闭电荷泵(0x8D 0x10)——电荷泵是 OLED 的隐藏耗电大户 |
| 轨迹板 / 显示线程 | k_thread_suspend()挂起 |
| DRV2605 震动马达 | 进入 Standby 待机模式 |
| LSM6DS3 IMU(陀螺仪/加速度计) | 进入 power-down 模式 |
| ESB 无线通信 | esb_comm_enter_sleep()彻底禁用(见 esb_comm_driver.c) |
| ADC | PM_DEVICE_ACTION_SUSPEND挂起 |
| I2C0 / I2C1 总线 | 双双挂起,连总线上电逻辑都暂停 |
唤醒侧的wakeup_all_peripherals()则是严格镜像操作(main.c):
- 先恢复 I2C 总线并等 100ms 稳定;
- 恢复轨迹板/显示线程、重启 ESB 通信;
- 从存储恢复标定数据、按键绑定、用户偏好;
- 恢复显示、ADC;
- DRV2605 唤醒后连震两下,给玩家"手柄已复活"的触觉确认;
- IMU 重新上线。
这套"关断 → 保存 → 复位重启 → 镜像恢复"的设计,是嵌入式低功耗开发的典型范式,值得借鉴。
五、电池电压检测:分压 + 按需采样
5.1 硬件链路
电池电压不能直接喂给 ADC(量程只有 3.6V 以内),所以板子上有一组1MΩ + 510kΩ 分压电阻,分压后的信号接到 nRF52840 的P0.31(AIN7)引脚,ADC 通道配置在 boards/seeed_xiao_ble.overlay:
channel@3 { reg = <3>; zephyr,gain = "ADC_GAIN_1_6"; zephyr,reference = "ADC_REF_INTERNAL"; zephyr,input-positive = <NRF_SAADC_AIN7>; /* P0.31/Battery */ };5.2 一个省到骨子里的小设计
分压电阻是常通电的话,电池会一直漏电流。所以 OpenSplitDeck 用 P0.14 这个 GPIO 去控制分压支路的通断(低电平导通),采样时才把"电流汇"打开。初始化时程序用了三层兜底策略配置该引脚(见 main.c):
- Open-drain 输出(首选,标准电流汇);
- 普通输出 + 下拉;
- 直接操作原始 GPIO 寄存器。
最后还会读回引脚电平自检,确认分压真的通了,否则打印警告。
5.3 电压换算
analog_driver_get_battery_voltage()(analog_driver.c)把 12 位 ADC 原始值换算回真实电池电压:
- 引脚电压 =
(raw / 4095) × 3.6V(1/6 增益 + 0.6V 内部基准); - 再乘上分压比
× 2.96(即 1M + 510k 的反算系数); - 输出单位 mV。
主程序每10 秒读一次电池电压打到日志里(main.c),并在状态屏幕上展示电量数值。"低频采样 + 按需开分压"这套组合拳,就是小电流设备省电池的标准做法。
六、长按组合键:关机与恢复出厂
power_mgmt_check_button_combo()(power_mgmt_driver.c)实现了一个非阻塞的组合键检测器,支持两种组合:
BUTTON_COMBO_SHUTDOWN:双键同按3 秒→ 触发关机;BUTTON_COMBO_FACTORY_RESET:双键同按5 秒→ 触发恢复出厂(回调清配置)。
设计上有两个新手容易忽略的细节:
- 检测是轮询式非阻塞的,主循环随时可以查,不卡 UI;
- 松开任意一个键立即取消计时(打印
Shutdown combo cancelled),不会误触发。
在 main.c 中,这两个组合平时是被禁用的,防止游戏过程中误触——需要主动开启才会生效。
七、电源管理统计:给自己留个"体检数据"
驱动内置了一个统计结构power_mgmt_stats_t(power_mgmt_driver.h),记录:休眠/唤醒次数、累计休眠时长、上次休眠时长、恢复出厂次数、关机组合触发次数、上次唤醒是否来自按键。
调试时想确认"到底睡了多久、醒了没",用power_mgmt_get_stats()一问便知——这也是排查功耗 bug 最快的工具。
八、小结
OpenSplitDeck 的电源管理虽然代码量不大,但麻雀虽小五脏俱全:
- 休眠:自动 5 分钟超时 + 手动组合键,最低功耗落到 nRF52840 System OFF;
- 唤醒:GPIO Sense 引脚低电平唤醒 → 复位重启 → 镜像恢复外设;
- 外设关断:屏幕电荷泵、无线芯片、IMU、马达、ADC、I2C 一个不落全关;
- 电池检测:分压电阻 + GPIO 开关 + 10 秒低频采样,把"测电压"这件事的漏电也省掉了。
如果你也在做电池供电的手柄、键盘类开源硬件,这套"回调注册 + 状态机 + 关断清单"的结构非常值得参考,可以直接对照 firmware/controller/ 源码阅读。
【免费下载链接】OpenSplitDeck
A steam deck-like controller made from a mix of off the shelf parts and 3d printed parts with custom hardware and firmware
相关推荐
KOReader终极电源管理指南:嵌入式设备休眠唤醒全解析
KOReader终极电源管理指南:嵌入式设备休眠唤醒全解析 KOReader作为一款跨平台的电子书阅读器,其强大的 电源管理 功能让用户在Kindle、Kobo
嵌入式桌面应用Zephyr RTOS电源状态管理:系统休眠与唤醒机制
Zephyr RTOS电源状态管理:系统休眠与唤醒机制 在嵌入式系统开发中,电源管理直接影响设备续航能力和运行效率。Zephyr RTOS(实时操作系统)作为新
操作系统嵌入式嵌入式OS物联网内核驱动系统编程FreeRTOS低功耗设计:任务休眠与外设电源管理优化
FreeRTOS低功耗设计:任务休眠与外设电源管理优化 在嵌入式系统开发中,低功耗设计是延长设备续航的核心需求。FreeRTOS作为实时操作系统(RTOS),提
操作系统嵌入式物联网RTOS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考