在 Ferris Sweep 上玩转 Rust
在 Ferris Sweep 上玩转 Rust
2025-3-29 · Gabe Venberg
目录
前几天,我偶然发现了 RMK,一个用 Rust 编写的键盘固件。考虑到我的 Ferris Sweep 的丝印上有一个 Ferris 螃蟹的标志,感觉用 RMK 刷写它才算名副其实。 自从我第一次构建它以来,我的 Ferris Sweep 一直在运行 QMK,这是一个非常成熟的基于 C 的键盘固件。 QMK 是一个很棒的项目,为已经支持的键盘进行基本的按键映射非常简单,并且有很好的文档记录。但是,如果您正在设计自己的键盘,或者想使用某些高级 QMK 功能,您将无法使用 QMK 基于 JSON 的“数据驱动”功能。相反,您将必须使用其基于 C 宏的配置,这可能令人生畏,并且可能需要了解 QMK 复杂的构建系统。
RMK 是一个比 QMK 新得多的项目。 QMK 提供了超过 1,000 个键盘的预制配置,允许您构建别人的设计并直接设计按键映射,而无需担心矩阵或引脚映射。 RMK 没有这样的定义,只有关于如何编写它们的文档和一些示例项目,这使得将其放在我的 Ferris Sweep 上的旅程足够有趣,值得写下来。
配置固件 #
现在,RMK 和 QMK 之间的设置有点不同。在 QMK 中,您克隆整个 QMK 仓库,进行任何您想要的按键映射修改,然后编译。 RMK 使用一种具有良好包管理器的语言编写,只需要您从一个依赖于 RMK crate 的小型模板创建一个仓库。 RMK 提供了一个工具 rmkit
来帮助您设置此仓库。
我首先通过 cargo 安装 rmkit
和一些嵌入式工具,运行:
cargo install rmkit flip-link elf2uf2-rs probe-rs-tools cargo-make cargo-binutils llvm-tools
copy
然后我运行 rmkit init
并回答了关于我的键盘的一些问题,这生成了我的初始模板。 从那里,我修改了 .cargo/config.toml
以使用 elf2uf2-rs
,因为我的键盘没有暴露的调试头,这将是 probe-rs
所需要的。 文档提到我可能需要修改一个名为 memory.x
的文件,该文件定义了微控制器的内存映射,但我发现模板已经具有 RP2040 的正确内存映射。
keyboard.toml
#
这是已经完成的容易的部分。 下一步是配置一个名为 keyboard.toml
的文件。 RMK 使用此文件来配置所有内容,从我们使用的微控制器的类型,到定义微控制器上的哪些引脚对应于哪些键,甚至在编译时配置初始按键映射。 与 QMK 类似,RMK 确实为您提供了直接使用 Rust 代码配置键盘的选项,但我没有做任何足够花哨的事情来证明这一点。
keyboard.toml
的第一部分包含关于您的键盘的一些基本元数据,例如它的名称、USB ID 和它使用的微控制器。 为了保持一致性,我从 QMK 的 Ferris Sweep info.json 中提取了所有这些信息。
然后我必须配置引脚映射,定义哪个引脚对应于每个键(在 Ferris Sweep 的情况下,因为它是一个直接连接,其中每个引脚恰好对应于 1 个键),或者定义哪个引脚对应于键盘矩阵的行和列(在大多数更大的键盘的情况下)。 这被证明比预期的更困难,因为我计划从 QMK 的引脚映射配置文件 keyboard.json
中获取引脚映射。 但是,它是无法使用的,因为 keyboard.json
给出了不同微控制器 ATmega32U4 的引脚映射,特别是对于 Aurdino Pro-Micro 板。 QMK 在编译时做了一些黑魔法,以便将这些引脚映射重写为 RP2040 等效项,但我无法弄清楚所说的魔法,以便手动执行相同的操作。 最后,我克隆了 Ferris Sweep 本身的 仓库,并在 Kicad 中查看了 PCB 设计,追踪了每个引脚的去向,并与 elite-pi(我特定的 RP2040 板)使用指南 进行交叉引用以获取引脚编号。
我必须做的另一件事是定义键盘每半部分的 2 个微控制器如何相互通信,并定义该通信中使用的引脚。 由于是“直接连接”设计,Ferris Sweep 在每个微控制器上只有一个不专用于键的 IO 引脚。 这意味着它必须使用所谓的“半双工” UART,其中单根线用于双向通信。 幸运的是,RMK 通过 RP2040 的 PIO 功能支持这种通信模式。 不幸的是,这种通过 PIO 使用半双工的能力仅在 RMK 的 master 分支上,并且截至撰写本文时,尚未进入稳定版本。 这仅仅意味着我必须在 Cargo.toml
文件中进行一些修改,以指示 cargo 从 git 获取最新的提交,而不是获取该软件包的最新发布版本。
在 痛苦的 追踪每个引脚并定义引脚映射的过程之后,我们可以定义按键映射。 我的 按键映射 不是很花哨,除了它广泛使用图层和 tap-hold Keybinds。 (按键绑定在按住时执行与点击时不同的操作) 我使用的所有功能都由 RMK 详细记录,因此虽然移植按键映射很乏味,但它并不是特别困难或值得注意。
vial.json
#
下一步也是最复杂的一步是创建 vial.json
。 VIAL 软件使用此文件,允许您在不重新刷写微控制器的情况下重新映射键盘,并且由于“原因”,如果您的 vial.json
与您的按键映射不匹配,键盘将无法启动。 via 项目提供的 JSON 文件是错误的,因为它将键盘布局为 8x5,而不是我在 RMK 中完成的 4x10。 所以我必须获取该文件,将其加载到 keyboard layout editor 中,并按照 vials 指南 以正确的布局重新制作 vial.json
。 老实说,我仍然不完全理解我在做什么,我只是瞎搞直到它起作用。
刷写和调试 #
最后,我可以刷写我的键盘了。 我刷写了它,然后……它不起作用。 左侧,即插入 USB 的一侧,工作正常,所有键都可以工作。 但是右侧,通过 TRRS 连接到主控,没有任何反应。 经过一两天调查发现,半双工串行实现仅使用 RP2040 的内部上拉电阻,对于我的键盘和 TRRS 电缆,该电阻不足以支持 115200 的波特率。 这(暂时)通过在 RMK 源代码中设置较低的波特率来解决,但从长远来看,我制作了一个 PR,镜像了 QMK 的解决方案,自那以后已被合并。
但是,经过所有这些之后,我有一个运行 Rust 的 Ferris Sweep,正如丝印所要求的那样。 当然,在 PR 合并并发布新版本之前,它运行的是我自己的 fork 中的 RMK 修改版本,但它仍然是 Rust。
如果您想将其用于您自己的 RP2040 有线 Ferris Sweep,或者只是想要一个示例来帮助您,请访问 此处 查看最终固件仓库。
使用 RMK #
然后我继续使用 RMK 完成了一些任务,包括撰写本文。 它的感觉与 QMK 不完全相同,我认为 tap-hold 和按键防抖的某些时序可能不同,但是,除了一些重复的键(一旦可配置的防抖 生效,应该可以修复),它是一个非常实用的键盘固件。 显然,作为一个比 QMK 新得多的项目,它尚未实现 QMK 的一些更高级的功能,例如对显示器、每个键的 RGB 或指点设备的支持。 下一个版本侧重于输入设备,因此将来 RMK 还将支持带有编码器和指点设备(如轨迹球和触摸板)的键盘。
最终想法 #
由于我在设置 RMK 时遇到的困难,我不建议将其推荐给任何不熟悉配置另一个键盘固件的人,并且您至少应该具备一些关于微控制器的知识,例如如何确定键盘的引脚排列。 但是,如果您感兴趣并且有一些先前的经验,它有一些非常酷的想法,例如在微控制器中使用异步运行时(感谢 embassy),以及使用 proc-macro 编译时解析其 keyboard.toml
文件,以生成固件代码,而无需强制用户编写 Rust。 它也是使用 Embassy 的复杂 Rust 代码库的一个很好的例子。
至于我自己,我计划继续将 RMK 用于我的 Ferris Sweep,并且可能会将其用于我构建的任何其他支持它的键盘。 我还计划继续为该项目做出贡献,修复我发现的问题。