今年入了焊板子这个好玩的坑,刚开始是用 Arduino 的 starter kit 学习一些入门教程,并用别人做的 PCB 和不太详细的组装指南成功组装了给 Procreate 用的遥控器,那个时候连 PCB 是什么都搞不清楚(我以为电路板里是有复杂集成电路自己实现一个完整芯片的功能)。之后又莫名入了定制键盘的坑,更加一发不可收拾,做了第一个手工焊接的键盘,然后又焊了分离式键盘,定制 OLED 显示屏之类的。其实在焊完第一个键盘对整个流程有了大致的了解之后就有意想要什么时候设计自己的键盘了。只是一直并没有动手去做的原因是并没有找到特别有说服力的理由,毕竟我也不想只是为了设计一个键盘而去设计一个键盘,而网上有各种各样的定制键盘的设计,有不少是开源的,基本上包含了各种你感兴趣的设计方案。

直到最近一个偶然的契机 N 对小键盘和 Colemak 布局产生兴趣,于是新键盘的想法很快成型了。刚才我说入了焊板子的坑其实并不准确,因为到目前为止我一个针脚都没有焊过,全靠首席焊接工 N 的鼎力支持这些项目才得以完成,所以突然有了回馈工人的一个好手段。下面分享一下我设计键盘的过程。

布局设计

设计键盘的第一步通常是设计键盘布局,键盘布局需要考虑的两个主要限制条件一个是微控制器(MCU)的通用 IO 针脚数量,这会决定你能使用的行数和列数,从而决定了你能够支持的按键数目上限,不过一般不是做 Full Size 或者更大的话,这都不是太大的问题。另一个主要限制是键盘帽的兼容性,可以参考市面上常见的键盘帽套装了的各地按键的大小,大致可以知道什么样的布局是最比较可行的。下面是经过很久不断迭代之后的最终结果(当然不断迭代并不是因为不停地有更好地方案,而是各种方案都有各自不同的取舍,来回反复无法确定下来的缘故)。

这个布局里字母键、方向键和最底下一排的修饰键基本是任意键帽套装里都有的,向下、向上切换键通常需要在 Ortho 套件里才有,不过 1U 的键也很容易找到其他的替代品(例如 Fn 键)。左右两边的 1.5U 的键是非标准的,但是在带有 Ergo 套件的键帽里也都能找到。稍微麻烦一点的是两个 1.5U 的空格键,一般在 Ortho 套件和 Ergo 套件中都比较容易找到 2U 的空格键,不过在一些套件中也有 1.5U 的空格键(例如 KAT Refined 的 ortho 套件,或者 MT3 Susuwatari 的 Ergo 套件)。另外字母键如果要使用 Colemak 布局的话,有一些键帽是有专门的 Colevrak 套件提供支持的。

当然其实这些限制也不是卡死的规则,退一步讲可以用 XDA、DSA 之类的 uniform profile 的键帽,甚至是用空白键帽,就只要能找到长度匹配的就可以了。所以网上其实有一些布局非常非标准的键盘,比如 QAZ 三个键都不是 1U 的 QAZ 键盘。而且由于最终定案是使用 Kailh Choc Low Profile 键轴,和 Cherry MX 系列的键帽并不兼容,基本上只能使用空白的 MBK 键帽,反而没有那么多忧虑了。

总之如果对键盘布局有一个大致的想法,就可以使用 Keyboard Layout Editor 这个在线工具(通常称作 KLE)进行布局设计了。这个工具在定制键盘社区里很有名气,而且在 Raw Data 一栏可以导出键盘布局的信息,这个信息是基于 json 的,虽然感觉格式的设计有点随机,但是在社区里已经非常通用了,例如通过 KLE Render 可以渲染出上面那样的键盘图,网站 1网站 2网站 3 都可以通过 KLE 导出的数据来自动生成定位板甚至是其他中间板的 CAD 设计图,可以将这个图导入 PCB 软件辅助 PCB 设计,或者直接发送到激光切割厂商切割一个定位板出来,通过手工连线的方式无需 PCB 就可以做键盘原型。在 KLE 里如果登录到 github 的话,还可以把自己编辑的键盘布局保存下来,或者可以直接复制右上角 Permalink 处的链接,这个链接直接把所有信息嵌入在 URL 中,打开就可以看到自己之前编辑的布局。

我的布局方案是在 Planck 的 4x12 正交格子的方案上出发修改的,因为我比较喜欢这个布局方案,而且正交格子设计起来非常简单。主要的改动有以下几个:

  1. 把两侧的按键从 1U 加宽到 1.5U。因为我发现 N 在习惯了比较大的键盘的时候有时候按边缘的键会因为按的太远自己按出去。而且我自己也发现在按一下比较复杂的组合键,特别是需要同时按上下两个键的时候,由于手指是错开的,如果两个键都是 1U 的话就很难按,但是宽一些的键就有很大的自由度(例如上面的布局里假设我需要同时按 Esc 和 Shift 键)。最后就是边缘的按键(特别是左边的 Shift 和右边的回车两个键)宽一些的话按的时候手腕的扭动角度可以稍微小一些,感觉也会更舒服。
  2. 中间空出一列空间来。主要有两个作用,一个是留一个比较合适的空间出来放 MCU,在上一篇博客中有讲过我焊的最近两个 kit 都通过把 MCU 放在 PCB 正面而减小了键盘的整体厚度,放在正中央感觉从美观程度还是方便程度(USB 口的位置)都是非常不错的选择。另一个左右则是细微地增加两手之间的间隔,这也是从使用分离式键盘得到的体会:间隔增加之后也可以帮助减小手腕弯曲的负担。
  3. 1.5U 的空格键基本上是由上一条设计所决定的。最下面一行的设计是改动最多的,包括使用 1.25U 或者 1.5U 的修饰键,或者合并两个空格使用 3U 空格,或者在方向键左边留空帮助更方便地找到方向键等等,也很难说哪个方案绝对优于另外的方案,总之最后确定了这个很工整的方案。

有一个比较麻烦的问题是中间留一列的空隙够不够放一个 MCU 板。其实我手边有 Elite-C 板子可以测量尺寸,大致上也就是 1U 的宽度,但是还是不能完全确定,有时候可能是小于 1mm 的差距,如果到时候定位板上切割的宽度不够宽放不下,或者键帽按下去会碰到微控制器上,就非常不好了。有一个比较正面的支持证据是 Helix 键盘 确实在 1U 的空间里放了 MCU 板子,不够它那个是在边缘上,感觉可操作空间大一些。总之最后我在拿到 PCB 板的实物的时候第一时间就是用 Elite-C 试了一下,发现几乎是刚好 fit,只能说是非常幸运了!其实设计整个 PCB 的过程中电路这些感觉都是很简单的,一直让我各种心里没谱的反而主要是各种机械上的东西,比如钻孔要钻多大,上下板子的形状和孔如何报道对齐之类的。

由于是 Planck 键盘的 4x12 布局拉宽之后的设计,所以我把这个键盘取了一个非常不酷的名字叫做 Flanck (Fat Planck)。除了拉宽之外,另一个最大的设计改变就是我决定弃用 Cherry MX 系列兼容的键轴,而采用 Kailh Choc 键轴,后者的特点在于 low profile,从轴体到键帽的设计,整个加在一起可以将键盘高度降的极低。突然想尝试这个的原因是我发现 N 打字的姿势跟我不太一样:我是手腕悬空,用肘部做支撑,而 N 大部分时候是使用手掌底部支撑的,所以如果键盘比较高而又不使用 wrist rest 的话,感觉手腕弯曲的负担很大,所以 low profile 的键盘应该是一个很不错的尝试。另外 Choc 键轴还有 20gf 超轻弹簧的线性轴 (Light Blue),有点好奇是什么样的手感(网上甚至还有更轻的定制 12g 弹簧卖)。

Flanck 和普通键盘(NiZ Plum Atom68)的高度对比。

PCB 设计概览和参考资料

有了合适的布局之后就可以开始设计 PCB 了,当然并不是所有的键盘都需要 PCB,有不少人直接通过上面提到的那些网站生成一个定位板文件,切割一个定位板出来然后通过手工连线的方式来做键盘,毕竟键盘的矩阵电路其实是很简单的。不过使用 PCB 可以更整洁,省空间,而且可以使用 hotswap socket,也是学习一门好玩的手艺。

PCB 的全称是 Printed Circuit Board,根据我的粗浅理解,它和集成电路的一个重要区别是前者只有导线,描述了电流的走向,而后者除了电路之外还集成了晶体管这样的电子元件,可以实现与或非门从而构造出更复杂的逻辑电路。所以 PCB 是简单得多的东西,但是说 PCB 上没有电子元件也不完全准确,其实在设计 PCB 的时候我们考虑的也是各种不同的元件如何连接之类的,只是打印出来的 PCB 上并不包括这些元件,而是它们的“footprint”,描述了每个元件的形状、大小,以及提供对应的针脚可以让元件和 PCB 内部的电路连接起来。而将各种电子元件通过焊锡连到 PCB 上的过程就叫做焊板子,虽然有一些 PCB 制造商也提供 PCBA (A 代表 Assembly) 的服务可以自动把(支持的)元件焊到板子上,但是这些元件也都是在板子外部的,所以和集成电路还是很不一样。

PCB 使用简单的分层结构,最简单的配置是正反两层,如果只有一层的话在布线的时候很难处理线交错的情况,两层就可以解决大部分比较简单的电路布线问题(疯狂使用 via 的话可能可以解决所有交错的问题,但是 via 应该是要越少越好的),更复杂的电路还可以用更多层来做。当然 PCB 中还有其他一些辅助层和层间结构,中国的 PCB 制造似乎很繁荣,Youtube 上有各种中国的 PCB 制造商的工厂参观视频,可以对 PCB 的具体结构和制作流程有一个不错的了解。

PCB 的设计主要分为 Schematic 设计(有点像算法伪代码)和物理布局和连线两个部分。 好几个商用软件可以使用,价格非常昂贵,不过在键盘设计社区里比较常见的是一个叫做 KiCad 的开源软件。网上有不少 KiCad 的 tutorial(例如这个),而键盘 PCB 设计相关的资料,目前最完整又没有过时的是 ai03 的 PCB 设计指南,从头到尾教你做一个 2x2 的小键盘 PCB,不仅帮助了解 PCB 设计的基本概念,而且包含诸如配置 library 的相对路径、格子的尺寸之类的主要从经验来的东西。如果对设计键盘 PCB 感兴趣的话,一定要看。还有一个更复杂的设计一个完整键盘的 tutorial 是 Masterzen 的博客,不过截至到我开始做 PCB 为止这个博客系列才只写了两部分,希望在之后会逐渐补完。最后是 hadi 的键盘 PCB 设计的 Youtube 系列视频,截至到目前也还没有出完,而且他主要使用的是一个叫做 Eagle 的商业软件,在讲解过程中有时候会在一些细节处挖得过于深有点不知所云(可能主要是我背景知识不太够),但是也是不错的参考资料。

再就是直接打开网上的开源键盘的项目查看,不过网上的开源的键盘虽然不少,但是组织得清楚明了的项目会让你学习过程顺畅很多,甚至有一些项目可能由于路径配置或者缺少相应的库,打开会是一堆问号元件。我在设计 PCB 的时候主要参考下面这两个开源项目,刚好也是我在上一篇博客中组装的两个键盘,同时有项目文件和实物做参考感觉非常有帮助:

另外一些看起来比较好玩,或者比较干净的开源键盘项目还有 keyseebeetornferris 等。

组件库

设计之前第一步需要解决的问题是找到合适的组件库。Kicad 的组件库主要分为两种,一种是符号库,提供设计 Schema 的时候使用的逻辑符号,主要信息是有多少个针脚之类的,这个就算自己做也比较方便;另一种是 footprint 库,包含了元件的实物大小,针脚的位置和布局之类的,如果自己做的话需要严格参考元件厂商发布的规格,或者有时候可能需要自己用实物进行测量。不过幸运的是网上有不少 KiCad 的键盘相关的元件库可以参考和使用,例如 ai03 的键盘组件库daprice 的键轴库keebio 的键盘组件库等等。 keyboardio 还有一个各种键轴的规则文档的合集,如果需要自己制作 footprint 的话可以参考。 Corne 用了自己的组件库,由于我主要参考 Corne 的 PCB 文件,所以也直接用了它的组件库。

Schematic Design

下面是 Flanck 键盘的 Schematic 一览,清晰的 PDF 版也可以从 github 的 Release 页面下载到。其实我在一开始学习 PCB 设计的时候看网上的教程就很不明白 Schema 这一步是用来做什么的,因为虽然在这里指定了各个组件以及它们是如何连接的,但是到第二步物理布局的时候还是要在合适的位置放置各个组件,并指定它们之间的连接关系。不过在自己做过一次设计并有过几次迭代之后就很清楚了:PCB 的物理布局主要是给生产 PCB 的机器看的东西,而 Schema 则主要是给人看的东西。

Schema 里虽然大致给定了一些元件的相对位置和逻辑上的连接关系,但是元件都是以一个符号的形式画出来,而并不是其真实的形状,而且其位置也只是起到一个示意图的作用,所以各个元件如何摆放有很大的自由度,于是就可以尽可能地做的(让人看的时候)容易理解。举几个例子,我们可以用标签来标明逻辑上的连接关系,例如上图中的 MCU 针脚有诸如 col4col5 之类的标签,这代表对应的针脚应该和下方的按键矩阵里对应标签的导线连接起来。通过标签我们就可以把 MCU 放在任意位置,而且也不需要画满屏幕的线把对应的针脚连接起来。此外,键盘矩阵里的导线在有交叉的时候默认是不相互连接的,所以矩阵里的连线也能画成逻辑上一目了然的样子。这些都是在做物理 PCB 布局的时候无法享受的待遇:事实上 PCB 的物理布局设计中很大的一部分内容就是如何布线使得他们不相交短路。

这样的逻辑上清楚的设计图纸的好处就在于容易理解,在你需要修改别人的设计或者迭代自己的设计的时候尤其有帮助,这比直接去盯着画了物理布线的 PCB 文件找每条线最后都连到了哪里容易太多了。与此同时 Schema 还提供一定程度的自动验证作用:在设计 PCB 的物理布局的时候很容易漏掉某个元件或者漏连某一根线,漏掉的东西在 Schema 里很容易看出来,但是在 PCB 物理布局上就比较难了,于是设计软件可以对比 Schema 和物理布局,给出相应的错误提示。

从上面的 Schema 中可以看出 Flanck 键盘的设计非常简单,矩阵是一个规整的 4x12 的布局,由于 MCU 有足够的针脚,我也不需要留多余的针脚来做 RGB LED 或者 OLED 屏,所以我也没有为了优化把它做成诸如 6x7 之类的逻辑排布(参见 Reviung41 的设计)。除了按键矩阵之外的元件就是一个 reset 按钮、一个 MCU 和一些螺丝孔。所以(前面也提过)其实设计过程中线路的布局之类的都非常容易,反而是具体实物上的东西(比如钻孔钻多大,元件之间留的空间之类的)相关的问题让人很没谱。

另一个让我们的 PCB 设计很简单的原因是我使用了 MCU 板。在实际组装的时候我使用的是 Elite-C 板,这是一系列的兼容 Arduino Pro Micro 板针脚的板子中比较容易买到同时又有 USB-C 口的板子,另一个例子是有蓝牙模块的 nice!nano。他们之所以叫 MCU 板是因为他们本身就是一个(很小的)PCB,除了 MCU 本身之外,让 MCU 能正常运作的相应的电阻、电容等原件,以及和外部交换数据的 USB 接口都已经正确地焊在板子上了,所以我们在使用的时候只需要设计按键矩阵,然后把对应的行和列连到 MCU 板暴露出来的数据针脚上就可以了。使用 MCU 板的一大好处当然是简单,而且模块化,同一个 PCB 设计可以使用针脚兼容的不同 MCU 板,比如使用 nice!nano 板的话就可以变成一个蓝牙键盘。坏处的话应该价格更高一些(但这必须是在考虑大数量的键盘平均价格的情况下),而且 MCU 板比单独的 MCU 要更占空间,所以在键盘设计时必须仔细考虑把 MCU 板放在那里。所以另一个做法就是不使用现有的 MCU 板,而是直接将 MCU 集成到键盘自己的 PCB 中。例如下面是 sparkfun 的 Pro Micro 的 Schematic,可以看到实际的 MCU 是叫做 ATmega32U4 的 8 位 AVR 控制器,如果要做集成化设计的话基本上就是要把下图中 MCU 和相关的元件全部集成到键盘的 PCB 中去。

集成化的 PCB 可能在使用 PCBA 服务大规模自动焊接 MCU 的情况下会很方便,但是如果是要 DIY 在家自己手工焊一个 ATmega32U4 到 PCB 上的话,难度就大到有点不太合适了。

Physical Layout

PCB 设计中最费劲的一步应该就是物理布局了,这是 PCB 的具体实现,将决定 PCB 内部的连线如何分布,以及表面连接的电子元件如何摆放,以及板子的外形如何,再哪里钻孔如何组装等等。为了从 Schematic 到物理布局,我们首先要做以下两件事情:

  1. 给 Schematic 里的每个元件取一个名字,这样可以通过名字确定物理布局中的元件和 Schematic 里的逻辑符号之间的对应关系。KiCad 似乎是采用字母加数字的方式来命名的,例如 R1R2 表示电阻、D1D2 表示二极管等。KiCad 工具栏上有一个叫做 Annotate Schematic Symbols 的按钮可以自动按照从左到右从上到下等方式进行命名,如果对于自动命名不满意也可以手工编辑每个元件人肉命名。
  2. 给每个元件指定一个 Footprint,同样在工具栏上有一个叫做 Assign PCB Footprints to Schematic Symbols 的按钮会打开一个对话框可以批量对相同类型的元件的 footprint 进行指定。这一步很重要,因为搞错的话元件就无法正确焊接到 PCB 上。比如使用的二极管是什么尺寸的,使用的按键是 Cherry MX 兼容的吗?还是 Kailh Choc 的?是 1U 的按键还是 1.5U 的按键?是焊接还是使用 Hotswap socket 亦或者是两者兼容?虽然网上基本都能找到各自元件的 footprint 库,但是有时候库里会有好几个类似的 footprint,并且几乎没有任何文档说明,要搞清楚使用拿一个有时候也比较麻烦。我使用的 Kailh Choc Hotswap 的 footprint 里有一个多余的针脚,在键轴上并没有,但是看了网上几个 footprint 库里都是那样设计的,我也就直接用了,不过直到最后实物键盘组装起来我其实也没搞清楚那个针脚到底是做什么用的。

做完这两步之后就可以打开 KiCad 里的 pcbnew 程序,通过工具栏上的 Update PCB from Schematic 来导入 Schematic 里的所有的元件对应的 footprint 了。这个时候首先看到的是所有元件全部挤在一堆,因为 KiCad 不知道在物理上要如何摆放这些元件。所以接下来要做的事情首先是决定如何摆放这些元件。对应布局比较复杂的键盘(例如分离式键盘、各行或者各列错开的键盘等),可以导入最开始布局设计那里生成出来的定位板的 DXF 文件来作为按键摆放位置的参考。幸运的是我们这里设计的键盘是非常规整的正交格子,并且我们使用的 footprint 库里按键的 footprint 包含了每个按键的参考边框:因为键盘上按键之间的间隔其实是有一个标准或者说约定俗成的值的,对于 Cherry MX 键轴兼容的键盘,每个 1U 按键占用的宽度应该是 19.05mm (为什么这么奇怪的值?我想可能是从 3/4 inch 来的,是的,现在世界上还有某些国家不使用国际单位制),如果小于这个值可能就会导致某些键帽会互相卡住之类的。Choc Switch 和 keycap 尺寸都要小一些,可以使用 18mm x 17mm 的尺寸,这样会让键盘更小巧紧凑一些。不过也有继续使用 MX 的尺寸来做 PCB 设计的,因为有个别 Choc 的键帽是对 MX 尺寸设计的,而且 Kailh 推出了兼容 MX 键帽的 Choc V2 键轴,如果要使用 MX 兼容键帽,就必须要使用这个尺寸了。

总之按键的 footprint 按照合适的尺寸提供了外形边框,而我们的键盘又是规整排列的,所以在布局方面非常简单,只要把每个键挨个排列起来就行了(注意和 Schematic 之间的顺序对应),之后再在每个按键附近摆好对应的二极管,以及决定好放 MCU 板的地方就好了,下面是所有元件都摆好时的样子:

由于形状规整,摆好按键之后其实 PCB 的外形也跟着决定了,使用工具栏上的直线和弧线工具就可以画出外形。把外形上的角用弧线做成圆角不仅是为了美观,而且也避免 PCB 上尖角划伤自己。画外形的时候注意要选中 Edge.Cut 这个层,这个层是专门用来描述 PCB 切割形状的。PCB 设计软件里还可以看到许多其他的层,其中有一些层是会发送到生产商那里的,比如 F.Cu 和 B.C 两个分别是正面和反面的铜线层,如果是多层布线的 PCB,还可以有更多铜线层,还有 F.SilkS 和 B.Silk 分别是正面和反面的 Silkscreen 层,分别给出板子正反表面的装饰性涂装,可以在里面画自己的 Logo、写版本信息之类的。也有一些是展示辅助信息的层,并不需要发送到制造商那里,例如我使用 Corne 的 footprint 库的时候按键的外形轮廓是放在 Dwgs.Use 层里的,例如我可以把这个信息导出出去用于定位板或者外壳的制作(保证和 PCB 上元件的形状和对齐)。Masterzen 的博客 里有一个 KiCad 里主要的层的用途的介绍表格。

注意 KiCad 默认是所有层一起显示的,并且如果是放在 PCB 反面的东西是自己镜面显示的(所以写在背面的文字是反过来的),特别是在有 Copper Pour 的情况下,整个 PCB 几乎是没法看的,所以设计和查看的时候可以选择性的打开和关闭一些层。不过我这个 PCB 非常简单,而且也没有使用 Copper Pour (其实当时没搞明白怎么用),所以所有层一起显示也一目了然。

除了按键和 MCU 板之外,我还在板子钻了一些孔用来组装键盘,我在下一小节再讲钻多大的孔。事实上我在 Schematic 里也加了孔对应的符号,这样做是方便我在从 Schematic 导入到 PCB 的时候可以选择自动删除 Schematic 里没有对应的元件,这样让我在初期迭代的时候很方便地在 PCB 和 Schematic 之间来回做修改并保持同步。有一些观点认为像钻孔这种东西不应该出现在 Schematic 里,而是要在 PCB 设计的时候(通过右边工具栏的 Add Footprint 按钮)直接添加 Mounting Hole。具体怎么做就看个人喜好了吧。

把所有的物理元件和孔的位置都决定好之后就可以开始布线了,这个一定要是最后一步,因为如果中间你再移动元件的话,布线就得重新搞。如果你不小心把布线搞得太乱了或者突然意识到换一下针脚的对应关系会让整个布线更简单,在 Edit 菜单项下面有一个 Global Deletions 项可以一次清空所有已有的布线。

用 tracespace.io 生成的 Flanck PCB 背面渲染图。

PCB 程序会根据 Schematic 里的连线信息显示哪些针脚需要通过布线联系起来,并且当你把鼠标放在一个 pad 或者针脚上按 x 开始布线的时候,需要连接但还未连接起来的目的针脚就会高亮出来。默认情况下红色的线是在 PCB 正面,绿色在反面,需要的时候(比如眼前有一根障碍的线绕不开)可以按 v 加入一个 via 转移到另一边,从而绕开障碍,不过通常建议是 via 尽量少用,还有一些约定俗成比如行的布线一边在反面,列的布线在正面等等。总之具体怎么布线感觉很看个人经验和喜好,也可以完全使用自动布线功能,不过许多爱好者都更偏好手动布线,并把这当做是 PCB 设计的重要一环。具体布线过程中还有许多小细节要注意,比如布线的粗细选择,线路之间以及线路与其它元件之间的距离等等,不仅和最终 PCB 制造商的生产能力有关,有时候也和连线的用途(例如是电源线还是数据线)相关,具体可以参见我前面列的那几个 PCB 设计的教程,我在这里就不多说了。

用 tracespace.io 生成的 Flanck PCB 正面渲染图。

布线之后可以添加 Copper Pour,我看到 Corne Light 里没有使用,于是也没有用,在组装之后也没有碰到什么异常。最后非常重要的一步是运行 Inspect 菜单下的 Design Rules Checker,这是一个非常简单的检查工具,比如 Schematic 里指定的哪里的连线在 PCB 里没有连起来的话,这里就会指出来。

Keyboard Plate Design

最后想要提一下外壳设计,说是外壳其实是另外两块不带电路设计的 PCB,也就是上一篇博客中提到的 Sandwich Case 设计,由于 PCB 材料本身坚硬柔软程度都适中,厚度也差不多合适(一般是 1.6 毫米左右),而且 PCB 生产商能够做各种切割和钻孔,一次定多个 PCB 也有助于降低运费成本等等,所以用 PCB 材料来做定位板和底板是 DIY 键盘是很不错的选择。要做一个 PCB 材料的定位板和底板也就是需要设计额外的两个板子,我在网上几乎没有找到教程讲这个,所以这里稍微多讲一点,虽然我的方法很可能不是最标准的。在那之前先介绍一下 Sandwich Case 的具体结构,下面是一个螺丝孔附近的示意图:

可以看到基本组装方式是在定位板和底板上钻比较小的孔,让 M2 螺丝能通过。注意 PCB 制造商只能钻孔,并不能做螺纹,所以中间需要用一个有对应 M2 大小螺纹的 Spacer 来将两个螺丝组合到一起。所以 PCB 上需要钻一个更大的孔来让 Spacer 可以通过,M2 Spacer 的内径虽然一定对应 M2 螺丝的大小(约 2mm),但是外径并没有标准,我发现网上能买到的圆柱形的 Spacer 的外径一般比六边形柱体的要小,前者可能能到 3mm 左右,后者甚至能到 5mm 上下,我这里钻的是 4.8mm 的孔,结果差不多刚刚好能 fit 我买到的所有类型的 Spacer。

定位板和 PCB 之间一般靠键轴来固定住,在使用 Hotswap socket 的情况下如果想让他额外稳定的话可以用图中所示的小垫片 Washer。这个垫片的大小就取决于你使用的 Spacer 的外径了,上一篇博客有介绍如何使用 Nylon Washer,后来我发现橡胶的 O Ring (例如经常用来给机械键盘静音的那种) 也很好用。具体加多少垫片取决于 PCB 和底板之间空间大小,在使用 Choc Hotswap socket 的时候大约是 2mm。如果不是使用 Choc Switch 而是使用 Cherry MX 兼容的键轴的话,这里大概是 3mm 左右,并且 PCB 和定位板之间也不会紧贴在一起,而是有另外的 3mm 左右的间隔(也可以通过垫片来固定)。

固定方法搞清楚之后就是如何来生成定位板和底板的文件了,主要的难点在于几个板子尺寸要匹配,钻孔要对准,特别是定位板,除了要钻螺丝孔之外,还需要切割用于固定键轴的空隙。我们之前提到我使用的 Footprint 库在 PCB 的 Dwgs.Use 层里放了键轴的外形轮廓信息,理论上我们可以把这些信息导出来,然后导入到定位板的项目文件中,再根据这些信息来画键轴框架。

Flanck 的定位板渲染图。

不过我发现 Corne 的 Footprint 库里有一个叫做 SW_Hole 的 Footprint,于是我在 PCB 完成之后,直接把 PCB 项目文件整个复制了一份,然后把 Schematic 里的所有二极管去掉,再把所有按键的 Footprint 重新指定为 SW_Hole,然后在 PCB 那里清除所有布线,再根据 Schmatic 更新一下——由于更新了 Footprint 之后元件原来的位置信息还是没有变的,于是瞬间就有了所有按键的切割信息,同时对应的螺丝孔也在,只要编辑每个螺丝孔把直径改小即可。最后根据 MCU 板的 Footprint 画出针对 MCU 的留空,再在 Schematic 和 PCB 里都删掉 MCU 就可以了——虽然 KiCad 会抱怨说 Schematic 里的按键的两个针脚在 Footprint 里没有找到对应关系,但是我们这里反正没有电路,就直接忽略了。用同样的办法可以制作底板。在这一步还可以在 Silkscreen 上加入 Logo 信息之类的,不过由于我整个项目需要迅速做完,并且希望留有时间能处理如果有设计失误时的二次迭代,就没有去搞这些。

我不知道“正确”的做法或者更标准的做法应该是怎样的,我看网上有人会用 Inkscape 甚至 OpenSCAD 去画定位板,特别是当目的是要用塑料或者金属切割而不是用 PCB 材料来制作定位板的时候。不过即使开源项目提供了所有的源文件,但是也不知道作者是通过怎样的流程来制作这些文件,以及通过怎样的方式保持各个板子之间的尺寸对应关系同步的。可能这只有靠经验积累了吧。

Manufacturing

最后一步是实际 PCB 生产。在网上搜索的话可以找到很多 PCB 生产厂商,国内在这方面似乎很有竞争力,在键盘爱好者社区比较常用的是 JLCPCB。在我刚开始在博客里写键盘相关的文章是就有网友联系我并介绍了他们自己的 PCB 生产服务 WellPCB,我本来是打算试一试他们的服务的,但是这次的项目由于时间上比较紧急,看到 JLCPCB 网站能非常直观的上传和估算生产所需的时间(绿色 PCB 只需要 1~2 天,定制颜色也只需要 3 天),最后还是决定先用 JLCPCB 了。

通常 PCB 制造商需要打包的 Garber 文件,JLCPCB 网站上有如何从 KiCad 导出 Garber 文件的详细指南。通常(在设计 PCB 之前)还需要看一下你想要使用的制造商的生产能力和规则限制,例如 JLCPCB 的页面在这里,从而搞清楚各种尺寸的最小最大限制之类的。一般开源的键盘也都会提供 Zip 打包的 Garber 文件下载,Flanck 键盘的 PCB 和定位板、底板对应的 Garber 文件可以在 Releases 页面下载到。

在 JLCPCB 网站上上传 Garber 文件,就会生成简单的预览和价格估计了,价格会跟板子的尺寸还有一些用料等基本参数有关,JLCPCB 上每个 Garber 文件至少是 5 块板子起生产,不过价格也很便宜,打印 5 块板子也就十美元多一点。PCB、定位板和底板各五份一共才三十几美元,不过用中国的生产商的一大问题是如果不想等待一个多月的话,快递费会比较贵,我选了 DHL 的 3~5 天送货大约要 28 美元的快递费。我后来还了解到一些美国的生产商,例如 PCBs.ioOSHPark 等,以后可以试一试。

总之对 JLCPCB 的服务还是很满意的,美国时间晚上发过去,第二天就收到了邮件回复希望确认一些东西,同时我意识到我在 PCB 上钻的孔的大小是不对的,于是更新了 PCB 让它们使用了新的 Garber 文件。它们让我确认的问题主要有两个:

  1. PCB 上有许多 0.3mm 的 slot hole,而他们的生产能力要求最小是 0.65mm。在对方给的示意图上确认了一下,发现这是之前我提到过的 Choc Switch 的 footprint 上那个不知道是做什么用的额外的针脚,其实它也没和 PCB 的其他部分连在一起,所以可能怎样都无所谓。而且 0.3mm 和 0.65mm 之间的差别其实非常小,所以我就全部修改了。
  2. 有许多针脚在 PCB 正面都没有 solder mask opening。我查了半天这个 solder mask opening 是什么东西,发现似乎 solder mask 是把 PCB 内部的铜线和外部隔开的一层绝缘材料,而 opening 则是留出的一部分 pad,好让你在焊接的时候能把焊锡粘上去。对比了一下 Corne 里不同 PCB 的版本,发现它是最近才开始用这样的设计的:因为 MCU 板其实是针脚插到背面去焊,所以正面并不需要放 solder mask opening。虽然不知道这样做有什么好处,但是似乎也没有什么问题,于是我就让它们按这个制作了。

确认之后就进入生产环节,基本上是 3 天生产完成,在网站上能看到 PCB 生产的实时进度,非常人性化(似乎前期很快,但是最终检查需要很久),最后快递也是三天左右,不过 DHL 最后不知道是送错了 apartment 号还是什么情况,导致我无法打开小区的 locker,最后又折腾了一天才拿到,顿时对 DHL 印象变差很多。

Keyboard Firmware

定制 PCB 还需要定制自己的键盘固件,PCB 只是一个按键的矩阵,在之前 debug 5x12 键盘时介绍过 controller 是如何从这个有二极管的键盘矩阵中识别出哪个按键被按下的,这就是键盘固件代码做的事情。识别出按键之后,需要根据用户配置的 Keymap 来决定给电脑发送哪个 keycode,如果键盘要支持比较复杂的比如分层之类的功能的话,决定发送什么 keycode 的步骤本身就不是一件 trivial 的事情,此外和电脑进行交互的接口——例如(极其复杂的)USB-HID 或者(没有最复杂只有更复杂的)HID over GATT Profile 蓝牙低功耗规格等——通常也是很麻烦的东西,所以理论上来讲固件要做的事情还挺多的。不过幸运的是有诸如 QMK 之类的非常成熟的开源固件可以使用,如果是使用支持的控制器的话,那么大部分代码就都不用自己写了。

由于我使用的 Elite-C 这个 MCU 板子是 QMK 支持的,所以定制固件其实非常简单,主要需要做的事情就是指定好二极管的连接方向,以及键盘矩阵有多少行多少列,每一行分别对应 MCU 的哪一个针脚,然后在定制一个默认的按键映射就可以使用 QMK 的代码进行编译了。具体可以参见 Flanck 键盘的固件代码,QMK 关于添加新键盘的部分文档可以在 Keyboard Guidelines 找到,也可以选一个你熟悉的类似的键盘的固件代码做参考。下面只展示一下 config.h 里的内容:

  1. #pragma once
  2. #include "config_common.h"
  3. /* USB Device descriptor parameter */
  4. #define VENDOR_ID 0x4E4B // NK
  5. #define PRODUCT_ID 0x0001
  6. #define DEVICE_VER 0x0001
  7. #define MANUFACTURER pluskid
  8. #define PRODUCT flanck
  9. #define DESCRIPTION 4x12 fat-planck
  10. /* key matrix size */
  11. #define MATRIX_ROWS 4
  12. #define MATRIX_COLS 12
  13. #define MATRIX_ROW_PINS { F4, F5, F6, F7 }
  14. #define MATRIX_COL_PINS { B1, B3, B2, B6, D3, D2, D4, C6, D7, E6, B4, B5 }
  15. #define DIODE_DIRECTION COL2ROW
  16. /* Set 0 if debouncing isn't needed */
  17. #define DEBOUNCE 5
  18. /* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
  19. #define LOCKING_SUPPORT_ENABLE
  20. /* Locking resynchronize hack */
  21. #define LOCKING_RESYNC_ENABLE
  22. #define TAPPING_FORCE_HOLD
  23. #define IGNORE_MOD_TAP_INTERRUPT
  24. #define TAPPING_TERM 130

第 5 到 11 行的内容是 USB 标识相关的,基本上可以自己随便写。第 13 行到第 19 行是最重要的需要写对的东西,但是其实也很简单,可以看到我们这里指明了键盘有 4 行和 12 列——这是和 PCB 布线相关的,前面提到即使你的实际布局是 4x12,你也可以把它搞成 7x7 之类的来节省两个针脚用作其它用途。不过我们这里为了简单就用了和物理布局一样的 4x12 的布局。接下来需要指定具体每行每列对应哪个针脚。我使用的是 Elite-C 这个 MCU 板,所以参考了 Deskthority 的 Elite-C 页面里的针脚图,如下图最左边(QMK 使用 Arduino 名,也就是绿色的标签),似乎所有 Pro Micro 兼容的 MCU 板的针脚名字都是通用的,但是网上找的的 Pro Micro 的针脚图也各有细微的差别,具体我也不知道是怎么回事。总之这个图和我 PCB 上用的 footprint 里的 silkscreen 上显示的符号(下图中间)是差不多的——注意左图显示的是 Elite-C 的正面,而我们焊接的时候是把 Elite-C 反过来焊在 PCB 上的(这样把元件隐藏保护起来),所以针脚是左右对称的。最右边是 Elite-C 板子的正面和反面的实物图,上面也写了针脚名。

对比 Schematic 上各行各列与针脚连接的状况,就可以写出固件代码里的针脚名。最后那些代码是 QMK 的一些行为配置,直接留空使用默认值也可以。其他比较重要的,一个是在 rules.mk 里定义好 MCU 和 Bootloader:

  1. # MCU name
  2. MCU = atmega32u4
  3. # Bootloader selection
  4. # Teensy halfkay
  5. # Pro Micro caterina
  6. # Atmel DFU atmel-dfu
  7. # LUFA DFU lufa-dfu
  8. # QMK DFU qmk-dfu
  9. # ATmega32A bootloadHID
  10. # ATmega328P USBasp
  11. BOOTLOADER = atmel-dfu

默认好像是使用 Pro Micro 的 caterina,Elite-C 应该选择 atmel-dfu,不过默认值使用起来好像也没有什么问题,只是 Keymap 里的 Reset 键并不能触发 MCU 进入可以烧固件的状态,必须要按 PCB 上的 reset 键才行,指定了正确的 bootloader 就可以解决这个问题。最后需要定义一个默认的 Keymap。由于 Flanck 的按键数目和布局和 Planck 是一样的,所以最简单的办法是通过 QMK Configurator 配置一个 Planck 的 keymap,导出为 json 文件,然后在通过 qmk json2c 工具生成一个 keymap.c 出来。

对于我的情况,在上一篇博客中已经说明过了,由于我有好几个布局比较类似的键盘,所以用了一个统一的脚本来管理和生成 keymap 文件。当然,其实手写 keymap 也是非常容易的啦。如果要手写 keymap 的话,可以参考 QMK 的文档了解一下各种 keycode 和复杂行为的写法。

QMK 本身是一个通用的键盘固件框架,提供了各种高度复杂的功能和灵活的可定制性,可以看到如果是使用 QMK 支持的 MCU 的话,定制一个固件其实非常方便。所以绝大多数(非无线)定制键盘都使用 QMK 来构建固件。而蓝牙无线键盘则由于非常复杂的开源 license 冲突方面的原因几乎无法使用官方的 QMK 作为固件,从而有许多其他选择,不过整个蓝牙固件社区感觉还在发展中,整体来说还不如 QMK 成熟。事实上在做好第一个 Flanck 键盘之后我们又用多余的 PCB 加上 nice!nano 这个和 Pro Micro 兼容但是有蓝牙模块的 MCU 板子做了一个无线版的 Flanck 键盘,这个如果有机会下次再细说。

总之 QMK 虽然是一个通用固件“库”一样的存在,但是它使用了一种比较奇怪的代码组织方式,就是所有用 QMK 来定制的固件代码也全都要 commit 到 QMK 的主仓库下面。于是 QMK 有一个无比巨大的 keyboards 文件夹,并且由于各种 prototype 乃至 handwired 键盘太多,还单独有一个 keyboards/handwired 目录来放这些键盘。如果你有多个键盘,需要互相共享一些代码或者配置之类的的话,你还可以在 users 下面有一个你自己的目录放自己的一些代码。总之感觉非常混乱,不过这样的组织方式也有它的好处,比如 QMK Configurator 这个在线工具就直接依赖主仓库里有的键盘的元信息可以在线预览和配置各个不同键盘的 keymap,并且用户不需要在本地安装 QMK 开发工具就能用它在线编译出固件来,还是很方便的——虽然也许单独用一个仓库来放键盘定义可能是更合适的选择?

总之 Flanck 的固件代码原则上也应该要 commit 到 QMK 的主仓库里去的,不过看到 QMK 的 github 上 300 个左右的 Pull Requests 多少有点劝退的感觉,如果我以后有需要可能会尝试把代码 push 上去,目前的状态是我直接在本地做一个软链接到 QMK 的目录里去进行编译,唯一不方便的地方就是不能直接用 QMK Configurator 直接在线生成固件,但是如果是我自己用的话其实也无妨。

固件编译好之后会得到一个 .hex 文件,按 MCU 上的 reset 键让它进入 Bootloader 模式,就可以通过 QMK Toolbox 来把 .hex 格式的固件写到 MCU 上了,具体如何编译和烧固件直接参考 QMK 的文档即可。

Keyboard Assembly

收到 PCB 的时候是非常兴奋的,JLCPCB 的真空包装看起来很结实,还送了一个柯基的小吉祥物。本来是打算周末再组装的,结果也没有忍住兴奋当天就动手组装了。除了 PCB 之外还需要一些其他可以买到的组件提前就准备好了,一个 Elite-C MCU 板,48 个 SMD 的二极管,48 个 Kailh Choc 键轴和 Hotswap Socket,48 个 Choc 键帽(40 个 1U 和 8 个 1.5U),一个 reset 按钮,M2 螺丝和 spacer,USB-C 线等。我在 github 页面给出了一个详细列表和我使用的购买来源,总的来说还是比较容易搞到的组件。

组装过程也不复杂,特别是如果有其他键盘 kit 的组装经验的话。首先是 PCB 背面的二极管和 Hotswap socket。每个按键都有一个附带的二极管,PCB 上有类似于 ⏄ 这样的符号,三角形所指的方向(有一条线的那一边)就是二极管应该指向的方向,具体二极管本身的方向怎么认跟你买到的二极管的包装有关系,通常有黑色线条的一边是和 PCB 上三角形所指的那个线对应。SMD 的二极管在焊接之前先在 Pad 上涂一些 Flux 会让焊接容易一些(不需要严格地只涂在 Pad 上,周围涂上也没有关系)。

焊完二极管之后就可以焊 Hotswap socket 了。如下图所示,PCB 背面每个 socket 都有白色图示标识,把 socket 放上去然后用焊锡固定住两个针脚即可(下图左边和右边是还未焊 Hotswap socket 和已经焊上了的情况的对比)。

焊完背面反过来,PCB 正面只有 Elite-C 和一个小的 reset button 需要焊,和其他 kit 的组装过程是差不多的,只要注意 Elite-C 板是反过来(元件隐藏在内部)放的——检查 Elite-C 上能看到的针脚文字和 PCB 上写的文字能对应起来而不是镜面对称即可。具体的步骤可以参考 BoardSource 的 Ortho 组装指南的对应部分以及 Kyria 组装指南的 Controller 部分Reset Button 部分(它的 reset 按钮是从侧面按的,但是针脚是一样的)。

组装 MCU 板的时候可以使用 Socket,这样 MCU 也是可以是比较容易拆卸的。虽然其实需要拆卸的机会也并不多了。我使用了 Mill-Max 的 Super Low Profile Sip Socket,使用 ID 号 315-47-112-41-004000Mouser 之类的地方能找到。这个 ID 号中间的 -112- 代表 12 个针脚的版本,我在 Mouser 没有看到有现货,于是买了 -104--108- 的,组合起来使用。我还买了可以和这个 socket 配合使用的针脚(ID 是 315-47-112-41-004000),但是直接使用 through hole 的二极管剪下来的多余针脚也是可以的,不过注意这个 socket 的孔是圆的,所以 Elite-C 自带的 header pin 的那种方形 pin 是无法插入进去的。其实如果不需要超级 low profile 的话,可能 Amazon 或者其他地方能找到的那种便宜很多的普通的 Machine Pin Female Header (例如这个) 可能也是可以的,不过我还没有试过了。使用 socket 的具体组装方法可以参考 Kyria 的文档

使用 Mill-Max Super Low Profil Sip Socket 安装 Elite-C 的示意图,这是可选步骤,直接用 Elite-C 自带的 header pin 焊接上会简便很多。

安装完 Elite-C 之后可以插上 USB 线,把之前做好的固件烧进去,然后用金属镊子或者小导线之类的短路每一个按键的 Hotswap socket 的两个针脚看是否能正常触发按键。如果没有问题的话就可以把定位板放在 PCB 上面对齐,然后开始安装键轴了。安装键轴的时候注意键轴的两个针脚是垂直插入下面的 Hotswap Socket 的两个孔的。装完键轴之后再测试一下按键都没有问题,然后就可以用螺丝和 spacer 把三块板子组装起来了,组装的方式参考之前 PCB 设计时的那张示意图。之后再在底部合适的部分贴上 rubber feet。

组装完板子之后在装上键帽,就大功告成啦!经过之前的多次组装碰到的问题和调试之后,这次居然史无前例的一次成功,没有出任何问题!

Kailh Choc Key Switch

凯华的 Choc 键轴感觉还挺好玩的,确实能做出来比 Cherry MX 兼容的那些键轴的键盘要低很多。手感还不错,因为我都使用线性轴,所以手感的差别主要是弹簧弹性的差别。这次试用了 20g 超轻的键轴,其实感觉和 low profile 的键轴还挺搭的,按起来超级省力,虽然在习惯之前有时候会误按到,但是很快就能习惯。网上还有 12g 重量的弹簧卖,可以替换到键轴里。针对 Choc 键轴设计的键帽非常少,基本上目前质量比较高又比较容易搞到手的就只有 MKB 键帽了,听起来很糟糕,不过似乎反而是件乐事,因为 Cherry MX 键轴的情况下挑选键帽花的大量精力都可以省下来了。

只有一点我不是特别中意的就是 Choc 目前似乎还没有静音轴,Cherry MX 兼容的那些键轴的非静音线性轴我现在已经能接受了,但是 Choc 的非静音线性轴感觉声音真的比较响,也不太好听,可能和 Choc 键帽比较薄也有一定关系,尝试用 O ring 发现几乎没有改善。不过幸运的是我尝试用润滑油(在 1upkeyboard 买的 Tribosys 3204)涂了一下键轴轴体之后就基本没有声音了。这也是我第一次打开键轴,发现其实挺容易打开的,不过每个键轴涂润滑油真是超级费事就是了。

后记

这次的项目感觉从一开始 PCB 设计文件上打孔错误偶然在生产之前被发现,到 MCU 板的留空几乎恰好能放得下 Elite-C,可以说是包含了各种运气因素在里面才没有任何差错地一次成功。不过一些提心吊胆的地方也主要是因为整个过程时间非常短的缘故,原本我也没有打算这么快就要自己定制一个 PCB 的,但是突然有一个好想法的同时意识到 N 的生日也是很接近了,所以如果能赶在那之前做完的话就是最好的。同时由于这是我第一次设计 PCB,而且也是第一次去订购 PCB 的生产,对整个过程的时间概念估计不是特别准确,特别是考虑到如果第一次设计有问题需要修改的话,可能至少还需要留足多一轮的生产加快递的时间,所以整个 PCB 基本上是两天(的下班空余)时间赶完就直接发到 JLCPCB 那边了😅。

总之最后很顺利地提前完成了整个项目,非常好玩,而且自己也学到了许多东西。😃 而且做出来的键盘也非常好用,不输于我的 collection 里的任何其他键盘!