QMK启用摇杆和鼠标按键功能
虽然选择了触摸屏,我仍选择为机械键盘嵌入摇杆模块,这本质上是对"操作连续性"的执着。
值得深思的是,本次开发过程中借助DeepSeek的代码生成与逻辑推理,其展现的能力已然颠覆传统编程范式,需求描述可自动转化为功能实现,算法优化能自主完成多目标博弈,这昭示着技术生产关系的根本性变革。
技术演进正在重构价值坐标系,边缘计算设备通过蒸馏更好的模型实现端侧智能,使AI能力呈指数级渗透产业格局,算力资本形成的新型生产资料,正在重塑技术话语权分配机制,工程师的核心竞争力将从代码实现转向需求抽象、系统架构与伦理把控在这场人机协同的认知革命,真正的危机并非技术替代,而是思维范式的停滞。
当AI解构了执行层的技术壁垒,人类智慧的战场必将向更高维度迁移——那些涉及跨领域创新、价值判断与复杂系统设计的领域,正是技术人亟待开垦的新边疆,但愿技术人可以在这样的狭缝中获得存在的意义。
言归正传还是回到我们的QMK键盘增加摇杆功能,首先要了解一下QMK 生成键盘的整体文件结构:
qmk_firmware/keyboards/demo_keyboard/
├── config.h
├── keymaps/
│ └── default/
│ ├── keymap.c
├── rules.mk
└── keyboard.json
keyboard.json功能:定义键盘的硬件配置、布局、功能和元数据。示例
rules.mk功能:定义编译选项和功能开关。
config.h功能:定义键盘的硬件配置和宏。
keymaps/default/keymap.c功能:定义默认键位布局,开启自定义功能。
所以增加摇杆和鼠标就需要在这些文件里面进行修改
在rules.mk中启用摇杆和鼠标按键功能
POINTING_DEVICE_ENABLE = yes
POINTING_DEVICE_DRIVER = analog_joystick
MOUSEKEY_ENABLE = yes
# DEBUG_ENABLE = yes
# CONSOLE_ENABLE = yes # 启用调试输出
在config.h中添加摇杆和鼠标键的读取对应端口
/*
Copyright 2025 <JohnsonLv>This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/#pragma once#define ANALOG_JOYSTICK_X_AXIS_PIN GP26
#define ANALOG_JOYSTICK_Y_AXIS_PIN GP27
#define MOUSE_BTN1_PIN GP15
在keymap.c中添加,保留以前的键盘键的映射,然后添加一些关于摇杆的函数
/// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later#pragma once#include QMK_KEYBOARD_H
#include "pointing_device.h"
#include "print.h"
#include "timer.h" // 用于 timer_read32 和 timer_elapsed32// 定义摇杆的灵敏度
#define JOYSTICK_SENSITIVITY 1static bool debounce = false;
static uint32_t debounce_timer = 0;// 初始化独立按键的 GPIO
void keyboard_post_init_user(void) {setPinInputHigh(MOUSE_BTN1_PIN); // 设置为输入模式,启用内部上拉电阻
}// 新增参数定义(需根据实际需求调整)
#define MAX_CURSOR_SPEED 10 // 最大光标速度
#define SPEED_REGULATOR 3 // 速度调节系数// 非线性映射函数(基于 IBM 专利逻辑)
void joystick_ibm_algorithm(int16_t x, int16_t y, int16_t* x_out, int16_t* y_out) {static int16_t z_prev = 0; // 静态变量保存上一次的 z 值// --- 核心算法逻辑 ---// 1. 计算近似平方根的 z 值int16_t ax = abs(x);int16_t ay = abs(y);int16_t z = ax + ay - ((2 * (ax < ay ? ax : ay)) / 3);// 2. 动态调整光标移动if (z > 4) {// 计算动态变化的 zi 值(包含释放补偿)int16_t zi = (z - z_prev) * 6 + z;// 计算最终坐标(避免除以零)int16_t x_calc = (zi == 0) ? 0 : (x * z * MAX_CURSOR_SPEED) / (zi * SPEED_REGULATOR);int16_t y_calc = (zi == 0) ? 0 : (y * z * MAX_CURSOR_SPEED) / (zi * SPEED_REGULATOR);*x_out = x_calc;*y_out = y_calc;} else {*x_out = 0;*y_out = 0;}// 3. 保存当前 z 值供下次使用z_prev = z;
}// 处理独立按键和摇杆的函数
void my_process_joystick(void) {// 获取摇杆的 X/Y 轴值int16_t x_raw= joystick_state.axes[0];int16_t y_raw= joystick_state.axes[1];// 创建鼠标报告report_mouse_t mouse_report = {0};// 应用 IBM 算法int16_t x_mapped, y_mapped;joystick_ibm_algorithm(x_raw, y_raw, &x_mapped, &y_mapped);// 检测独立按键状态(按下时为低电平)bool btn_state = !readPin(MOUSE_BTN1_PIN); // 按下时为 true// 消抖逻辑if (btn_state && !debounce) {debounce = true;debounce_timer = timer_read32();mouse_report.buttons |= KC_BTN1; // 触发左键} else if (!btn_state && debounce) {if (timer_elapsed32(debounce_timer) > 5) { // 消抖时间 5msdebounce = false;mouse_report.buttons &= ~KC_BTN1; // 释放左键}}// 发送鼠标报告pointing_device_set_report(mouse_report);pointing_device_send();
}// 键盘矩阵扫描后的钩子函数
void matrix_scan_user(void) {my_process_joystick();
}// 键盘布局定义(无需为独立按键分配矩阵键位)
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {[0] = LAYOUT_numpad_4x4(KC_1, KC_2, KC_3, KC_4,KC_5, KC_6, KC_7, KC_8,KC_9, KC_0, KC_A, KC_B,KC_C, KC_D, KC_BTN2, KC_BTN3 // 保持矩阵中的键位不变)
};
在上面的这段算法中借用了 https://patents.google.com/patent/US5570111A, IBM的一个专利技术
以下是这段基于 IBM 专利的摇杆算法的逐层解析,我将用 「物理直觉 → 数学实现 → 代码表达」 的逻辑链解释其精妙之处:
1. 近似矢量长度(模拟平方根)物理需求:
需要计算摇杆偏移的「矢量长度」,但避免耗时的真实平方根运算。数学实现:
通过线性组合近似替代 x 2 + y 2 \sqrt{x² + y²} x2+y2,专利给出的公式: z = ∣ x ∣ + ∣ y ∣ − ( 2 ∗ m i n ( ∣ x ∣ , ∣ y ∣ ) ) / 3 z = |x| + |y| - (2 * min(|x|, |y|)) / 3 z=∣x∣+∣y∣−(2∗min(∣x∣,∣y∣))/3
代码实现:
int16_t ax = abs(x);
int16_t ay = abs(y);
int16_t z = ax + ay - ((2 * (ax < ay ? ax : ay)) / 3);
效果说明:当摇杆沿对角线移动时(x=y),公式简化为 ( 4 / 3 ) ∗ x (4/3) * x (4/3)∗x,接近真实平方根 2 ∗ x ≈ 1.414 x \sqrt2*x ≈ 1.414x 2∗x≈1.414x当摇杆沿单轴移动时(如 x=0),公式退化为 y,与真实值一致平衡了计算效率和准确性
2. 动态响应补偿(预测释放动作)
物理需求:
当用户松开摇杆时,光标会因惯性继续移动,需要模拟「减速回弹」效果。
数学实现:
通过差分计算摇杆速度变化:
z i = z + 6 ∗ ( z − z p r e v ) zi = z + 6*(z - z_{prev}) zi=z+6∗(z−zprev)
( z p r e v z_{prev} zprev是上一帧的 z 值)
代码实现:
int16_t zi = (z - z_prev) * 6 + z; // 放大变化量的影响
效果说明:
- 快速释放时(z 急剧减小): z i zi zi会远小于 z z z,导致 x c a l c / y c a l c x_{calc}/y_{calc} xcalc/ycalc分母增大,光标减速
- 保持摇杆时(z 稳定): z i ≈ z zi ≈ z zi≈z,光标匀速移动
- 推动摇杆时(z 增大): z i > z zi > z zi>z,分母增大,光标加速更平缓
3. 非线性速度映射
物理需求:
摇杆偏移量与光标速度呈非线性关系(小偏移精细控制,大偏移快速移动)。
数学实现:
速度公式:
x_calc = (x * z * MAX_CURSOR_SPEED) / (zi * SPEED_REGULATOR)
代码实现:
int16_t x_calc = (zi == 0) ? 0 : (x * z * MAX_CURSOR_SPEED) / (zi * SPEED_REGULATOR);
参数控制:
| 参数 | 作用 | 调整建议 |
|---|---|---|
MAX_CURSOR_SPEED | 最大移动速度 | 值越大光标移动越快 |
SPEED_REGULATOR | 整体灵敏度调节 | 值越大光标移动越慢 |
6 (zi的系数) | 惯性响应强度 | 值越大释放时的减速越明显 |
4. 死区处理与噪声过滤
物理需求:
消除摇杆中心位置的微小抖动。
数学实现:
当 z ≤ 4 时强制归零:
if (z > 4) { ... } else { *x_out=0; *y_out=0; }
效果说明:
- 过滤摇杆电阻器的噪声
- 提供明确的中心死区
整体算法流程图
原始输入 (x,y) ↓
计算近似矢量长度 z ↓
动态补偿 → 计算 zi (包含惯性预测) ↓
非线性映射 → 输出 (x_calc, y_calc) ↓
保存 z 值 → 供下一帧使用
实际调试技巧
-
参数联动调整:
- 先固定
SPEED_REGULATOR=1,调整MAX_CURSOR_SPEED确定最大速度 - 然后增大
SPEED_REGULATOR微调灵敏度 - 最后调整
zi的系数(代码中的6)控制惯性效果
- 先固定
-
边界保护:
添加范围限制防止溢出:
x_calc = MAX(-127, MIN(x_calc, 127)); // 确保在鼠标协议范围内
这个算法通过巧妙的近似和差分计算,在极低的计算开销下实现了符合人体工学的光标控制特性,正是这种「用简单数学模拟复杂物理直觉」的设计,让它成为经典。

相关文章:
QMK启用摇杆和鼠标按键功能
虽然选择了触摸屏,我仍选择为机械键盘嵌入摇杆模块,这本质上是对"操作连续性"的执着。 值得深思的是,本次开发过程中借助DeepSeek的代码生成与逻辑推理,其展现的能力已然颠覆传统编程范式,需求描述可自动…...
计算机网络-SSH基本原理
最近年底都在忙,然后这两天好点抽空更新一下。前面基本把常见的VPN都学习了一遍,后面的内容应该又继续深入一点。 一、SSH简介 SSH(Secure Shell,安全外壳协议)是一种用于在不安全网络上进行安全远程登录和实现其他安…...
yolov11模型在Android设备上运行【踩坑记录】
0) 参考资料: https://github.com/Tencent/ncnn?tabreadme-ov-file https://github.com/pnnx/pnnx https://github.com/nihui/ncnn-android-yolov5 https://github.com/Tencent/ncnn?tabreadme-ov-file 1) :将xxx.pt模型转化成 xxx.onnx ONNX(Ope…...
Linux在x86环境下制作ARM镜像包
在x86环境下制作ARM镜像包(如qemu.docker),可以通过QEMU和Docker的结合来实现。以下是详细的步骤: 安装QEMU-user-static QEMU-user-static是一个静态编译的QEMU二进制文件,用于在非目标架构上运行目标架构的二进制文…...
win编译openssl
一、perl执行脚本 1、安装perl脚本 perl安装 2、配置perl脚本 perl Configure VC-WIN32 no-asm no-shared --prefixE:\openssl-x.x.x\install二、编译openssl 1、使用vs工具编译nmake 如果使用命令行nmake编译会提示“无法打开包括文件: “limits.h”“ 等错误信息 所以…...
为什么说,在IT行业中长期从事外包岗位没有前途?
文章目录 前言一、职业发展与技能提升受限二、工作稳定性和归属感缺失三、薪资待遇和福利差异四、行业声誉和求职歧视总结 前言 在IT行业中,由于企业要降低成本、优化资源、分散风险以及满足市场需求和技术需求等原因,存在大量的外包岗位。很多人都说长…...
【B站保姆级视频教程:Jetson配置YOLOv11环境(七)Ultralytics YOLOv11配置】
Jetson配置YOLOv11环境(7)Ultralytics YOLOv11环境配置 文章目录 1. 下载YOLOv11 github项目2. 安装ultralytics包3. 验证ultralytics安装3.1 下载yolo11n.pt权重文件3.2 推理 1. 下载YOLOv11 github项目 创建一个目录,用于存放YOLOv11的项目…...
硬核技术:小程序能够调用手机的哪些传感器
一、加速度传感器 小程序可以调用手机的加速度传感器来检测设备的运动状态。加速度传感器能够测量设备在三个轴(X、Y、Z)上的加速度变化。通过分析这些数据,小程序可以实现一些功能,如运动检测、步数统计、游戏中的动作感应等。 健…...
Day 31 卡玛笔记
这是基于代码随想录的每日打卡 491. 非递减子序列 给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素,如出现两个整数相等࿰…...
【蓝桥杯嵌入式】4_key:单击+长按+双击
全部代码网盘自取 链接:https://pan.baidu.com/s/1PX2NCQxnADxYBQx5CsOgPA?pwd3ii2 提取码:3ii2 1、电路图 将4个按键的引脚设置为input,并将初始状态设置为Pull-up(上拉输入) 为解决按键抖动的问题,我们…...
Synchronized和ReentrantLock面试详解
前言 接下来为大家带来的是 Java 中的两个典型锁代表:Synchronized 和 ReentrantLock 的详解 面试题:谈一谈AQS 在说 ReentrantLock 时,有必要先了解一下 AQS,因为 ReentrantLock 就是基于 AQS 实现的 分析: 共享…...
1.2 学习驱动(Driver)分为几步?
文章目录 前言一、什么是UVM中的驱动(Driver)?二、如何理解Driver?三、如何使用Driver?第一步:定义Driver类第二步:实现run_phase任务第三步:实现驱动任务第四步:实例化和…...
【MySQL篇】事务的认识以及四大特性
何为事务? 事务(Transaction)是指一组操作的集合,这些操作要么全部执行成功,要么全部不执行。事务通常用于保证数据库的一致性、完整性和可靠性,确保数据的完整性与正确性。 有效避免部分执行,…...
2.7日学习总结
深入探究栈、队列与二叉树 一、栈的深度剖析 进阶特性:除了常规的入栈、出栈操作,栈在处理函数调用、表达式求值等场景时,展现出独特的递归模拟能力。利用栈可以将递归算法转化为非递归形式,有效避免因递归过深导致的栈溢出问题。…...
SQL带外注入
SQL 带外注入(Out-of-Band SQL Injection, OOB SQLi) 是 SQL 注入的一种特殊类型,主要用于以下情况: 数据库没有直接返回错误信息(比如被防火墙拦截了)。无法使用常规注入手法(如 UNION、错误信…...
Nginx进阶篇 - nginx多进程架构详解
文章目录 1. nginx的应用特点2. nginx多进程架构2.1 nginx多进程模型2.2 master进程的作用2.3 进程控制2.4 worker进程的作用2.5 worker进程处理请求的过程2.6 nginx处理网络事件 1. nginx的应用特点 Nginx是互联网企业使用最为广泛的轻量级高性能Web服务器,其特点是…...
【算法专场】分治(下)
目录 前言 归并排序 思想 912. 排序数组 算法思路 算法代码 LCR 170. 交易逆序对的总数 算法思路 算法代码 315. 计算右侧小于当前元素的个数 - 力扣(LeetCode) 算法思路 算法代码 493. 翻转对 算法思路 算法代码 好久不见~时隔多日&…...
OSPF基础(2):数据包详解
OSPF数据包(可抓包) OSPF报文直接封装在IP报文中,协议号89 头部数据包内容: 版本(Version):对于OSPFv2,该字段值恒为2(使用在IPV4中);对于OSPFv3,该字段值恒为3(使用在IPV6中)。类型(Message Type):该OSPF报文的类型。…...
ubuntu直接运行arm环境qemu-arm-static
qemu-arm-static 嵌入式开发有时会在ARM设备上使用ubuntu文件系统。开发者常常会面临这样一个问题,想预先交叉编译并安装一些应用程序,但是交叉编译的环境配置以及依赖包的安装十分繁琐,并且容易出错。想直接在目标板上进行编译和安装&#x…...
Docker Desktop安装kubernetes时一直在Starting:Kubernetes failed to start
原因:由于墙的问题,导致拉取国外的K8s镜像失败 解决: 下载 k8s-for-docker-desktop 选中自己的kubernetes 版本 下载zip包 PowerShell运行load_images.ps1文件 重启docker kubernetes运行成功...
beyond the ‘PHYSICAL‘ memory limit.问题处理
Container [pid5616,containerIDcontainer_e50_1734408743176_3027740_01_000006] is running 507887616B beyond the ‘PHYSICAL’ memory limit. Current usage: 4.5 GB of 4 GB physical memory used; 6.6 GB of 8.4 GB virtual memory used. Killing container. 1.增大map…...
AI大模型零基础学习(1):大模型使用篇
一、大模型是什么?为什么你需要它? 一句话理解:大模型像一个能听懂人话的"超级智能助手",它能写文章、解数学题、翻译语言、写代码…只要你会打字提问,它就能给出答案。 典型场景: 学生党&…...
StarSpider 星蛛 爬虫 Java框架 可以实现 lazy爬取 实现 HTML 文件的编译,子标签缓存等操作
StarSpider 星蛛 爬虫 Java框架 开源技术栏 StarSpider 能够实现 针对 HTML XSS SQL 数学表达式等杂乱数据的 爬取 解析 提取 需求! 目录 文章目录 StarSpider 星蛛 爬虫 Java框架目录介绍如何获取?maven配置 架构是什么样的?结果对象的类…...
【翻译+论文阅读】DeepSeek-R1评测:粉碎GPT-4和Claude 3.5的开源AI革命
目录 一、DeepSeek-R1 势不可挡二、DeepSeek-R1 卓越之处三、DeepSeek-R1 创新设计四、DeepSeek-R1 进化之路1. 强化学习RL代替监督微调学习SFL2. Aha Moment “啊哈”时刻3. 蒸馏版本仅采用SFT4. 未来研究计划 部分内容有拓展,部分内容有删除,与原文会有…...
先进制造aps专题二十八 生产排程仿真引擎和工厂生产仿真引擎的设计
一 排产仿真引擎的设计 主要分为仿真模型,仿真模型逻辑和仿真框架这三个部分 1 仿真模型 和算法排产不一样,在算法排产里,机器对应的是数据库记录,排产逻辑是写在整体的算法里的,而仿真排产,机器对应的是…...
WPF模板
WPF模板深度解析:打造个性化UI的利器 在WPF(Windows Presentation Foundation)的世界里,模板(Template)是构建个性化用户界面(UI)不可或缺的工具。它们允许开发者将控件的逻辑功能与…...
动态规划LeetCode-121.买卖股票的最佳时机1
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的最大利润。…...
pytest+request+yaml+allure 接口自动化测试全解析[手动写的跟AI的对比]
我手动写的:Python3:pytest+request+yaml+allure接口自动化测试_request+pytest+yaml-CSDN博客 AI写的:pytest+request+yaml+allure 接口自动化测试全解析 在当今的软件开发流程中,接口自动化测试扮演着至关重要的角色。它不仅能够提高测试效率,确保接口的稳定性和正确性…...
单片机通讯中的时序图:初学者的入门指南
一、什么是时序图? 在单片机的世界里,时序图是一种非常重要的工具,它用于描述信号在时间上的变化规律。简单来说,时序图就像是信号的“时间线”,它展示了各个信号线在不同时间点上的电平状态。通过时序图,我…...
#渗透测试#批量漏洞挖掘#微商城系统 goods SQL注入漏洞
免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…...
