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 1

static 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) {  // 消抖时间 5ms
            debounce = 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(2min(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 x1.414x当摇杆沿单轴移动时(如 x=0),公式退化为 y,与真实值一致平衡了计算效率和准确性


2. 动态响应补偿(预测释放动作)

物理需求
当用户松开摇杆时,光标会因惯性继续移动,需要模拟「减速回弹」效果。

数学实现
通过差分计算摇杆速度变化:
z i = z + 6 ∗ ( z − z p r e v ) zi = z + 6*(z - z_{prev}) zi=z+6(zzprev)
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 ziz,光标匀速移动
  • 推动摇杆时(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 值 → 供下一帧使用

实际调试技巧

  1. 参数联动调整

    • 先固定 SPEED_REGULATOR=1,调整 MAX_CURSOR_SPEED 确定最大速度
    • 然后增大 SPEED_REGULATOR 微调灵敏度
    • 最后调整 zi 的系数(代码中的 6)控制惯性效果
  2. 边界保护
    添加范围限制防止溢出:

  x_calc = MAX(-127, MIN(x_calc, 127)); // 确保在鼠标协议范围内

这个算法通过巧妙的近似和差分计算,在极低的计算开销下实现了符合人体工学的光标控制特性,正是这种「用简单数学模拟复杂物理直觉」的设计,让它成为经典。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/964173.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Rust 所有权特性详解

Rust 所有权特性详解 Rust 的所有权系统是其内存安全的核心机制之一。通过所有权规则&#xff0c;Rust 在编译时避免了常见的内存错误&#xff08;如空指针、数据竞争等&#xff09;。本文将从堆内存与栈内存、所有权规则、变量作用域、String 类型、内存分配、所有权移动、Cl…

基于深度学习的视觉检测小项目(十七) 用户管理后台的编程

完成了用户管理功能的阶段。下一阶段进入AI功能相关。所有的资源见文章链接。 补充完后台代码的用户管理界面代码&#xff1a; import sqlite3from PySide6.QtCore import Slot from PySide6.QtWidgets import QDialog, QMessageBoxfrom . import user_manage # 导入使用ui…

ubuntuCUDA安装

系列文章目录 移动硬盘制作Ubuntu系统盘 前言 根据前篇“移动硬盘制作Ubuntu系统盘”安装系统后&#xff0c;还不能够使用显卡。 如果需要使用显卡&#xff0c;还需要进行相关驱动的安装&#xff08;如使用的为Nvidia显卡&#xff0c;就需要安装相关的Nvidia显卡驱动&#xff…

Docker Compose的使用

文章首发于我的博客&#xff1a;https://blog.liuzijian.com/post/docker-compose.html 目录 Docker Compose是什么Docker Compose安装Docker Compose文件Docker Compose常用命令案例&#xff1a;部署WordPress博客系统 Docker Compose是什么 Docker Compose是Docker官方的开源…

【深度分析】DeepSeek 遭暴力破解,攻击 IP 均来自美国,造成影响有多大?有哪些好的防御措施?

技术铁幕下的暗战&#xff1a;当算力博弈演变为代码战争 一场针对中国AI独角兽的全球首例国家级密码爆破&#xff0c;揭开了数字时代技术博弈的残酷真相。DeepSeek服务器日志中持续跳动的美国IP地址&#xff0c;不仅是网络攻击的地理坐标&#xff0c;更是技术霸权对新兴挑战者的…

使用python实现与本地ollama部署的deepseek对话

专栏总目录 按照ollama官方doc的example操作&#xff0c;没有成功与本地ollama上的deepseek-r1:1.5b通讯后&#xff0c;发现vscode可以调用本地ollama上的deepseek模型。 为了实现与ollama上的deepseek模型通讯&#xff0c;我使用wireshark对本地回环地址进行侦听后&#xff0c…

基于STM32的智能健康监测手环

1. 引言 随着可穿戴设备的普及&#xff0c;健康监测技术正逐步融入日常生活。本文设计了一款基于STM32的智能健康监测手环&#xff0c;能够实时采集用户心率、血氧饱和度、体温及运动数据&#xff0c;并通过低功耗蓝牙&#xff08;BLE&#xff09;与手机APP交互。该系统结合了…

OpenGL学习笔记(六):Transformations 变换(变换矩阵、坐标系统、GLM库应用)

文章目录 向量变换使用GLM变换&#xff08;缩放、旋转、位移&#xff09;将变换矩阵传递给着色器坐标系统与MVP矩阵三维变换绘制3D立方体 & 深度测试&#xff08;Z-buffer&#xff09;练习1——更多立方体 现在我们已经知道了如何创建一个物体、着色、加入纹理。但它们都还…

麦芯(MachCore)应用开发教程5 --- 工位和晶圆传输

麦芯是构建在windows系统上的设备应用操作系统&#xff0c;利用该系统可以快速高效的开发一款设备专用软件。希望进一步了解请email: acloud163.com 黄国强 2025/02/03 一、工位与子设备的关系 想象工厂中的流水线工作站&#xff0c;每个工位&#xff08;Station&#xff09…

冰蝎v3.0 beta7来啦

我用了一台kali&#xff0c;一台centos&#xff0c;一台windows&#xff0c;做了一个文件上传和一个反弹shell实验&#xff0c;载荷是AES加密的&#xff0c;终于感受到了对加密流量的无可奈何~ kali&#xff08;php8.1&#xff09;centos&#xff08;php7.1&#xff09;window…

Golang 并发机制-5:详解syn包同步原语

并发性是现代软件开发的一个基本方面&#xff0c;Go&#xff08;也称为Golang&#xff09;为并发编程提供了一组健壮的工具。Go语言中用于管理并发性的重要包之一是“sync”包。在本文中&#xff0c;我们将概述“sync”包&#xff0c;并深入研究其最重要的同步原语之一&#xf…

【PyQt】超级超级笨的pyqt计算器案例

计算器 1.QT Designer设计外观 1.pushButton2.textEdit3.groupBox4.布局设计 2.加载ui文件 导入模块&#xff1a; sys&#xff1a;用于处理命令行参数。 QApplication&#xff1a;PyQt5 应用程序类。 QWidget&#xff1a;窗口基类。 uic&#xff1a;用于加载 .ui 文件。…

Flutter Scaffold 页面结构

Material是一套设计风格&#xff0c;提供了大量的小部件&#xff0c;这里用Material风格搭建一个常见的应用页面结构。 创建Material应用 import package:flutter/material.dart;class App extends StatelessWidget {overrideWidget build(BuildContext context) {return Mat…

2月3日星期一今日早报简报微语报早读

2月3日星期一&#xff0c;农历正月初六&#xff0c;早报#微语早读。 1、多个景区发布公告&#xff1a;售票数量已达上限&#xff0c;请游客合理安排行程&#xff1b; 2、2025春节档总票房破70亿&#xff0c;《哪吒之魔童闹海》破31亿&#xff1b; 3、美宣布对中国商品加征10…

大模型本地化部署(Ollama + Open-WebUI)

文章目录 环境准备下载Ollama模型下载下载Open-WebUI 本地化部署的Web图形化界面本地模型联网查询安装 Docker安装 SearXNG本地模型联网查询 环境准备 下载Ollama 下载地址&#xff1a;Ollama网址 安装完成后&#xff0c;命令行里执行命令 ollama -v查看是否安装成功。安装成…

10 Flink CDC

10 Flink CDC 1. CDC是什么2. CDC 的种类3. 传统CDC与Flink CDC对比4. Flink-CDC 案例5. Flink SQL 方式的案例 1. CDC是什么 CDC 是 Change Data Capture&#xff08;变更数据获取&#xff09;的简称。核心思想是&#xff0c;监测并捕获数据库的变动&#xff08;包括数据或数…

【25考研】南开软件考研复试复习重点!

一、复试内容 复试采取现场复试的方式。复试分为笔试、机试和面试三部分。三部分合计100分&#xff0c;其中笔试成绩占30%、机试成绩占30%、面试成绩占40%。 1.笔试&#xff1a;专业综合基础测试 考核方式&#xff1a;闭卷考试&#xff0c;时长为90分钟。 笔试考查内容范围…

【Cadence仿真技巧学习笔记】求解65nm库晶体管参数un, e0, Cox

在设计放大器的第一步就是确定好晶体管参数和直流工作点的选取。通过阅读文献&#xff0c;我了解到L波段低噪声放大器的mos器件最优宽度计算公式为 W o p t . p 3 2 1 ω L C o x R s Q s p W_{opt.p}\frac{3}{2}\frac{1}{\omega LC_{ox}R_{s}Q_{sp}} Wopt.p​23​ωLCox​Rs…

【leetcode练习·二叉树拓展】归并排序详解及应用

本文参考labuladong算法笔记[拓展&#xff1a;归并排序详解及应用 | labuladong 的算法笔记] “归并排序就是二叉树的后序遍历”——labuladong 就说归并排序吧&#xff0c;如果给你看代码&#xff0c;让你脑补一下归并排序的过程&#xff0c;你脑子里会出现什么场景&#xff…

[ESP32:Vscode+PlatformIO]新建工程 常用配置与设置

2025-1-29 一、新建工程 选择一个要创建工程文件夹的地方&#xff0c;在空白处鼠标右键选择通过Code打开 打开Vscode&#xff0c;点击platformIO图标&#xff0c;选择PIO Home下的open&#xff0c;最后点击new project 按照下图进行设置 第一个是工程文件夹的名称 第二个是…