ꜱʟᴜɢ
2022-07-25T06:00:16+00:00
最近休假闲得无聊天天高强度刷nga,正好稚晖君的新键盘出来,作为一个cmmk玩家随便谈谈这个项目好了。
一方面这个圈子实在太小,可能过两天热度就没了,另一方面他的项目还没开源,现在出来谈省得之后再说大家喷我马后炮。
我这里只谈传统键盘部分,而且是仅限于电路实现的部分。至于体验相关的增强模块、键帽、结构等等这些就不多说了。
先说我自己的判断,如果稚晖君的项目开源了,很可能会有不少客制化键盘采用他的设计,这个影响远比那些增强模块要大。
稚晖君的视频是以展示的思路向大家做介绍,但并没有说他为什么这样做设计。我在之前做了几乎一样的事情,所以一下子就看明白了他的想法。
他的设计思路涉及很多方面,我慢慢做解释。
首先是pcb布线要简单。
常见的pcb布线其实没有什么难度,所以有非常多m*n扫描矩阵加二极管的实现,毕竟还要rgb加灯,顺便的事情。
但是在“切割pcb保证手感”这个需求的前提下,布线就蛋疼了。不是不可以,在我看来更多是强迫症受不了。
稚晖君在视频里解释了,m*n扫描矩阵需要二极管来消除ghost keys,而1*n矩阵可以无视这个问题。
这个设计更多地是为全键无冲服务的,鬼键的产生是扫描矩阵无法分辨在一个矩阵中的按键组合。
在很多老旧键盘的线路设计里可以看到一些诡异的走线,主要是为了让有可能被同时按下的键处于不同的扫描组合里。不然就会出现某些特定的按键组合没有响应的情况。
很难说这个方案的原创性,因为至少在2018年我就见过用位移寄存器做扫描的思路了,这个技巧叫PISO并进串出,两年前就有不少公开项目在用了,我也照猫画虎模仿着做过。
之前我借鉴别人的设计用的是74HC595,他这里用的是74HC165,一个三状态输出,一个两状态输出,没有本质区别。
这个东西一个8bit,假如用1bit代表1个按键的通断状态的话,串联16个就足够常规104键盘使用了。
用位移寄存器还有个好处,这个等到协议部分再说。
然后是他说的稍微复杂一点的事情,滤波防抖。
这个事情也非常简单。物理开关信号转成电信号的时候,并不是理想中的,瞬间接通瞬间断开。用示波器看的话,就是视频里很短暂的时间里,键盘开关反复接通断开。
这个事情有硬件和软件的解决方式。硬件上有SR电路,现在基本没人用了,还有RC电路,这个用的比较多。软件上稚晖君用的方案是二次扫描,效果很好。
二次扫描的原理是基于一个假定,就是正常人手速再快,两次按键之间的间隔也会大于接触抖动,所以直接过滤掉高频抖动就行了。很多年之前我的鼠标双击了,就写了个类似的程序,过滤掉不可能是我自己造成的双击,熬过了买到新鼠标的时间。
但滤波这个话题不是那么简单,以我电路设计和固件开发的经验来说,还有要考虑的因素。稚晖君视频里只是一句话带过了。
一个是自锁,比如ctrl/alt/shift按键,按下之后要保持锁定状态。换句话说就是,正确处理按键按下和松开两个事件。
第二个是滤波是延迟操作,要根据处理能力和设计目标来匹配元件。比如要达到1000Hz的polling rate汇报率,扫描到滤波要在0.5/1000秒以内。
然后就是固件内对于扫描和汇报是两个异步逻辑,这个有点类似于显示器刷新率的意思。玩家追求显卡输出的fps尽量高于显示器的实际刷新率,扫描部分就是fps,汇报部分就是刷新率。
常见的QMK/ZMK在这方面做得其实不太好,原因不是开发能力不行,而是它们的设计初衷是兼容已有的键盘,而非为特定键盘设计。
关于性能方面的话题,还是等到后面协议部分在细说。
再说说全键无冲。
这个几乎是误解最深的了。大概十多年前就有说法,什么ps/2才能全键无冲,usb只能六键无冲。
再后来有人做了创新,一个键盘连上,系统里看见一串HID设备,“模拟”全键无冲。
再后来QMK/ZMK流行了之后,又有了所谓的“开关”,需要在全键无冲和六键无冲之间做切换。
实际上,只要认真读一遍USB协议关于HID设备的规范,很容易就会发现,根本不存在所谓的“冲突”。
稍微简化一点说,HID协议里的通信方式是,设备端可以自定义信号事件的数据结构,宿主端负责解析。
比如键盘上一般有104个键盘,可以定义一个128bit的数组,每个bit用来表示键盘上一个按键的通断状态。然后这个问题就解决了。
这里就能看出电路设计时用PISO位移寄存器的好处了,省去了后续处理,物理信号和数字信号一一对应。
所谓的“六键无冲”其实是个历史遗留问题。在usb协议刚出的时候,主板bios很难在有限的rom空间里实现完整的HID解析。
于是HID标准提出了一个叫boot的简易协议,这个协议使用了六组映射用来解析按键事件,这样bios只要支持boot协议就可以在有限环境中提供对usb键盘的支持。
然后开发键盘的就偷懒把这个boot协议拿来用了,然后usb六键无冲就这么流传了下来。
现在开发cmmk固件的话,都是实现一个自己的NKRO全键无冲协议,然后再为兼容性实现一个boot协议,键盘主动汇报优先使用NKRO协议。
如果宿主端如bios只支持boot协议的话,会自动切换到boot协议。等到进入系统之后就会正常切换到NKRO协议了。那个NKRO开关也是不必要的。
接着说性能方面的问题。稚晖君提到了“性能拉满”,那我就解释一下吧。
这里的性能拉满应该分两个维度,一个是mcu对于按键触发的响应,另一个是操作系统对键盘设备的响应。整个流程的性能取决于二者中较低的那个。
前者的设计指标其实不用太高,高桥名人的手速够快了吧,一秒16次,相较于常见的125Hz甚至1000Hz汇报率,都是小事。
早期QMK/ZMK在某些mcu上的应用不太好,主要是这个按键触发是同步操作,即使有1000Hz的汇报率,也可能只有50Hz的触发率。当然相对16Hz的按键频率来说够不够用,那就见仁见智了。
这个汇报率主要的用途是决定延迟,1000Hz下按键最晚在1/1000秒之后被处理,125Hz下就变成了8/1000秒。我了解的比较夸张的音游判定有到8ms的,125Hz算是性能的实际下限了,当然输出设备最好也要120Hz以上才能不成为瓶颈。
第二点polling rate汇报率,它是一个usb 2.0的概念。因为2.0时代用的是同步polling模式,或者说叫时分复用,而3.0之后usb拓扑变成了点对点,然后支持异步polling,所以就不存在这个概念了。
当然物理信号层面还是有限制的,总线信号间隔最小是125us,所以理论上最高的汇报率是8000Hz。
那为什么常见的输入设备最高就1000Hz呢?因为真的够用了,而且厂家多年的宣传让用户先入为主产生了最高1000Hz的印象。
键盘这种输入设备没有必要用3.0,一方面是真的够用了,另一方面2.0控制器无论集成度、成本和功耗都远好于3.0版本的。
至于说高性能arm处理器,这个还是别吹了,大家都是stm32,而且键盘这个应用场景和arm性能一点不沾边。
这里用stm32或者说arm的主要原因是便宜又方便,像固件设计、代码移植都有大把现成的方案可以用,省去了大量二次开发的成本。
抛开稚晖君的项目不谈,电路设计和固件开发层面,我的感觉是这个事情已经到头了。所以客制化圈子才产生了科技以换壳为本的现象……
剩下的研发方向,大概就是蓝牙和2.4G。蓝牙因为协议限制基本上也到了瓶颈,而2.4G就不是区区爱好者可以涉足的领域了。
还是做下修改吧,上面就不动了。万一有谁看了我这帖子被误导了……主要是我太急着码字了,要是我这帖子在开源之后才写出来就一点说服力没有了。
一方面这个圈子实在太小,可能过两天热度就没了,另一方面他的项目还没开源,现在出来谈省得之后再说大家喷我马后炮。
我这里只谈传统键盘部分,而且是仅限于电路实现的部分。至于体验相关的增强模块、键帽、结构等等这些就不多说了。
先说我自己的判断,如果稚晖君的项目开源了,很可能会有不少客制化键盘采用他的设计,这个影响远比那些增强模块要大。
稚晖君的视频是以展示的思路向大家做介绍,但并没有说他为什么这样做设计。我在之前做了几乎一样的事情,所以一下子就看明白了他的想法。
他的设计思路涉及很多方面,我慢慢做解释。
首先是pcb布线要简单。
常见的pcb布线其实没有什么难度,所以有非常多m*n扫描矩阵加二极管的实现,毕竟还要rgb加灯,顺便的事情。
但是在“切割pcb保证手感”这个需求的前提下,布线就蛋疼了。不是不可以,在我看来更多是强迫症受不了。
稚晖君在视频里解释了,m*n扫描矩阵需要二极管来消除ghost keys,而1*n矩阵可以无视这个问题。
这个设计更多地是为全键无冲服务的,鬼键的产生是扫描矩阵无法分辨在一个矩阵中的按键组合。
在很多老旧键盘的线路设计里可以看到一些诡异的走线,主要是为了让有可能被同时按下的键处于不同的扫描组合里。不然就会出现某些特定的按键组合没有响应的情况。
很难说这个方案的原创性,因为至少在2018年我就见过用位移寄存器做扫描的思路了,这个技巧叫PISO并进串出,两年前就有不少公开项目在用了,我也照猫画虎模仿着做过。
之前我借鉴别人的设计用的是74HC595,他这里用的是74HC165,一个三状态输出,一个两状态输出,没有本质区别。
这个东西一个8bit,假如用1bit代表1个按键的通断状态的话,串联16个就足够常规104键盘使用了。
用位移寄存器还有个好处,这个等到协议部分再说。
然后是他说的稍微复杂一点的事情,滤波防抖。
这个事情也非常简单。物理开关信号转成电信号的时候,并不是理想中的,瞬间接通瞬间断开。用示波器看的话,就是视频里很短暂的时间里,键盘开关反复接通断开。
这个事情有硬件和软件的解决方式。硬件上有SR电路,现在基本没人用了,还有RC电路,这个用的比较多。软件上稚晖君用的方案是二次扫描,效果很好。
二次扫描的原理是基于一个假定,就是正常人手速再快,两次按键之间的间隔也会大于接触抖动,所以直接过滤掉高频抖动就行了。很多年之前我的鼠标双击了,就写了个类似的程序,过滤掉不可能是我自己造成的双击,熬过了买到新鼠标的时间。
但滤波这个话题不是那么简单,以我电路设计和固件开发的经验来说,还有要考虑的因素。稚晖君视频里只是一句话带过了。
一个是自锁,比如ctrl/alt/shift按键,按下之后要保持锁定状态。换句话说就是,正确处理按键按下和松开两个事件。
第二个是滤波是延迟操作,要根据处理能力和设计目标来匹配元件。比如要达到1000Hz的polling rate汇报率,扫描到滤波要在0.5/1000秒以内。
然后就是固件内对于扫描和汇报是两个异步逻辑,这个有点类似于显示器刷新率的意思。玩家追求显卡输出的fps尽量高于显示器的实际刷新率,扫描部分就是fps,汇报部分就是刷新率。
常见的QMK/ZMK在这方面做得其实不太好,原因不是开发能力不行,而是它们的设计初衷是兼容已有的键盘,而非为特定键盘设计。
关于性能方面的话题,还是等到后面协议部分在细说。
再说说全键无冲。
这个几乎是误解最深的了。大概十多年前就有说法,什么ps/2才能全键无冲,usb只能六键无冲。
再后来有人做了创新,一个键盘连上,系统里看见一串HID设备,“模拟”全键无冲。
再后来QMK/ZMK流行了之后,又有了所谓的“开关”,需要在全键无冲和六键无冲之间做切换。
实际上,只要认真读一遍USB协议关于HID设备的规范,很容易就会发现,根本不存在所谓的“冲突”。
稍微简化一点说,HID协议里的通信方式是,设备端可以自定义信号事件的数据结构,宿主端负责解析。
比如键盘上一般有104个键盘,可以定义一个128bit的数组,每个bit用来表示键盘上一个按键的通断状态。然后这个问题就解决了。
这里就能看出电路设计时用PISO位移寄存器的好处了,省去了后续处理,物理信号和数字信号一一对应。
所谓的“六键无冲”其实是个历史遗留问题。在usb协议刚出的时候,主板bios很难在有限的rom空间里实现完整的HID解析。
于是HID标准提出了一个叫boot的简易协议,这个协议使用了六组映射用来解析按键事件,这样bios只要支持boot协议就可以在有限环境中提供对usb键盘的支持。
然后开发键盘的就偷懒把这个boot协议拿来用了,然后usb六键无冲就这么流传了下来。
现在开发cmmk固件的话,都是实现一个自己的NKRO全键无冲协议,然后再为兼容性实现一个boot协议,键盘主动汇报优先使用NKRO协议。
如果宿主端如bios只支持boot协议的话,会自动切换到boot协议。等到进入系统之后就会正常切换到NKRO协议了。那个NKRO开关也是不必要的。
接着说性能方面的问题。稚晖君提到了“性能拉满”,那我就解释一下吧。
这里的性能拉满应该分两个维度,一个是mcu对于按键触发的响应,另一个是操作系统对键盘设备的响应。整个流程的性能取决于二者中较低的那个。
前者的设计指标其实不用太高,高桥名人的手速够快了吧,一秒16次,相较于常见的125Hz甚至1000Hz汇报率,都是小事。
早期QMK/ZMK在某些mcu上的应用不太好,主要是这个按键触发是同步操作,即使有1000Hz的汇报率,也可能只有50Hz的触发率。当然相对16Hz的按键频率来说够不够用,那就见仁见智了。
这个汇报率主要的用途是决定延迟,1000Hz下按键最晚在1/1000秒之后被处理,125Hz下就变成了8/1000秒。我了解的比较夸张的音游判定有到8ms的,125Hz算是性能的实际下限了,当然输出设备最好也要120Hz以上才能不成为瓶颈。
第二点polling rate汇报率,它是一个usb 2.0的概念。因为2.0时代用的是同步polling模式,或者说叫时分复用,而3.0之后usb拓扑变成了点对点,然后支持异步polling,所以就不存在这个概念了。
当然物理信号层面还是有限制的,总线信号间隔最小是125us,所以理论上最高的汇报率是8000Hz。
那为什么常见的输入设备最高就1000Hz呢?因为真的够用了,而且厂家多年的宣传让用户先入为主产生了最高1000Hz的印象。
键盘这种输入设备没有必要用3.0,一方面是真的够用了,另一方面2.0控制器无论集成度、成本和功耗都远好于3.0版本的。
至于说高性能arm处理器,这个还是别吹了,大家都是stm32,而且键盘这个应用场景和arm性能一点不沾边。
这里用stm32或者说arm的主要原因是便宜又方便,像固件设计、代码移植都有大把现成的方案可以用,省去了大量二次开发的成本。
抛开稚晖君的项目不谈,电路设计和固件开发层面,我的感觉是这个事情已经到头了。所以客制化圈子才产生了科技以换壳为本的现象……
剩下的研发方向,大概就是蓝牙和2.4G。蓝牙因为协议限制基本上也到了瓶颈,而2.4G就不是区区爱好者可以涉足的领域了。
还是做下修改吧,上面就不动了。万一有谁看了我这帖子被误导了……主要是我太急着码字了,要是我这帖子在开源之后才写出来就一点说服力没有了。
点击展开 ...
要修改的地方主要还是那个位移寄存器的实现原理,前面有个回复差点把我带沟里去,其实这里面是完全不同的两种思路的体现。
第一个思路是稚晖君的做法,我现在有100多个按键,如果用传统扫描矩阵,至少要20pin,但是mcu上没有这么多,所以我把100个输入串行化。这个逻辑叫PISO并进串出。
HC165的datasheet里就有参考实现,应用场景就是键盘。这个方案最大的好处是简单直观。
不过一个单元只有8bit,串联一个无所谓,硬怼都没关系。但对于100+按键来说,串联十多个就需要需要同步时钟。这个不麻烦,mcu只要有SPI接口这个事情就非常简单了。
另一个优点是这个方案对编程十分友好,因为所有按键是同时采样的,然后8bit对齐输出。
要说缺点的话,也不是很影响。一是中断只能由mcu通过spi触发,二是还是要配8个拉升电阻,相对于扫描矩阵用二极管的优势就不大了。
另一个思路是顺着全键无冲和独立rgb控制的思路下来的。这个思路是,mcu上pin很少,但我需要和非常多的独立开关交互,所以我要把控制指令串行化,通过位移寄存器映射到指定位置,逻辑上应该叫做SIPO串入并出。
这个方案可以用HC595或者HC164,但datasheet里没有很详细的样例。一般用595而不用164,因为164少一条latch线不能手动锁存。
主要的优点是两方面,一是不需要额外spi去同步各个寄存器,它会自动overflow到下一个。然后就是你可以用相同的逻辑控制按键对应的led。
其他方面的小优点也有,不太重要的比如前一种方案需要3pin而这个方案只需要2pin,然后不需要额外的元件。
我觉得比较重要的还有两方面,一是按键可以触发中断,二是按键输出可以选择8bit对齐模式,也可以选择非对齐模式。
缺点的话,这个思路不是那么直观,非对齐模式下需要一些额外代码来处理。
第一个思路是稚晖君的做法,我现在有100多个按键,如果用传统扫描矩阵,至少要20pin,但是mcu上没有这么多,所以我把100个输入串行化。这个逻辑叫PISO并进串出。
HC165的datasheet里就有参考实现,应用场景就是键盘。这个方案最大的好处是简单直观。
不过一个单元只有8bit,串联一个无所谓,硬怼都没关系。但对于100+按键来说,串联十多个就需要需要同步时钟。这个不麻烦,mcu只要有SPI接口这个事情就非常简单了。
另一个优点是这个方案对编程十分友好,因为所有按键是同时采样的,然后8bit对齐输出。
要说缺点的话,也不是很影响。一是中断只能由mcu通过spi触发,二是还是要配8个拉升电阻,相对于扫描矩阵用二极管的优势就不大了。
另一个思路是顺着全键无冲和独立rgb控制的思路下来的。这个思路是,mcu上pin很少,但我需要和非常多的独立开关交互,所以我要把控制指令串行化,通过位移寄存器映射到指定位置,逻辑上应该叫做SIPO串入并出。
这个方案可以用HC595或者HC164,但datasheet里没有很详细的样例。一般用595而不用164,因为164少一条latch线不能手动锁存。
主要的优点是两方面,一是不需要额外spi去同步各个寄存器,它会自动overflow到下一个。然后就是你可以用相同的逻辑控制按键对应的led。
其他方面的小优点也有,不太重要的比如前一种方案需要3pin而这个方案只需要2pin,然后不需要额外的元件。
我觉得比较重要的还有两方面,一是按键可以触发中断,二是按键输出可以选择8bit对齐模式,也可以选择非对齐模式。
缺点的话,这个思路不是那么直观,非对齐模式下需要一些额外代码来处理。