美文网首页程序员装X指北我爱编程
黑科技:程序员如何打造属于自己的分体键盘

黑科技:程序员如何打造属于自己的分体键盘

作者: VellBibi | 来源:发表于2016-04-19 21:38 被阅读1428次

    前言

    作为一名程序员,键盘在手,天下我有啊,不整把高大上的键盘怎么提升B格。之前一直想买个机械键盘,听说机械键盘敲代码和玩游戏都特别爽,也是装B神器。同时也觉得普通的键盘打字打久了手腕会有点酸酸的,因为敲键盘时都是要弯着手腕的,一点也不符合人体工程学。于是乎就想买一个分体的机械键盘,结果找了半天都没有比较中意的,找到几个人体工程学键盘,都是薄膜的,而且价格高得离谱,不就多个人体工程学光环嘛。。。身为程序员中的屌丝,岂能被金钱这种东西降低自身B格呢

    普通机械键盘



    带 * “人体工程学光环” * 键盘


    为了不要这么纠结,就自己diy一个咯,正好有台3D打印机(又一个装B神器,你值得拥有,O(∩_∩)O哈哈~),全部外壳自己打印,控制板用Arduino Leonardo,原生支持键盘鼠标驱动,轴体考虑到成本,先买了80颗国产黑轴做实验,键帽也简单打印一下,说干就干

    3D打印的分体黑轴机械键盘

    符合人体工程学的分体式设计,全键无冲,可任意自定义快捷键,甚至可任性滴修改键位布局哦(重新设计外壳即可)

    准备

    • 工具
      • 3D打印机 (打印外壳)
      • 电烙铁
      • 热熔胶 (固定按键用)
      • 万用表
    • 硬件
      • Arduino Leonardo板 (驱动板)
      • 黑轴轴体
      • 二极管+电阻+杜邦线+万能板
      • 小螺丝 (外壳装配)
    • 软件
      • SolidWorks (3D建模,设计外壳,可用任何3D建模软件代替哦)
      • Arduino IDE (写键盘驱动程序)

    是时候上点图了。。。
    自己组装的三角洲式3D打印机



    淘宝买的工具



    步骤

    先设计键位排布,使用 ** SolidWorks ** (任何3D建模软件都ok的,只是我比较熟系SolidWorks哦)画出简单的按键布局,先从左手开始,先完整的搞定左手能使用了,再做右手。设计好布局后制作支撑轴体的面板,然后设计电路,用飞线焊接,写代码测试按键是否都正常。电路正常后设计整个外壳,然后整体组装。这就完工啦

    • 键位设计

    右手之所以外形扭曲,是因为3D打印机打印面积有限

    • 电路设计

      由于Arduino板io口有限,必须使用矩阵扫描来实现按键。扫描方式就是:定义n个io口为扫描口,m个接收口,组成一个n*m的矩阵。扫描口默认全部都是低电压,然后依次将每个扫描口单独置为高电压(即扫描动作),当这个高电压的扫描口上连接的某个按键有按下时,对应的接收口电压就也是高电压,这时就可以定位到是哪个按钮按下了,矩阵如图所示:


    Arduino有6个模拟口,14个数字口。我要做的键盘不超过80个键,所以使用8个数字口进行脉冲扫描,6个模拟口加4个数字口用来接收脉冲来定位按键,这样就实现了8*10的矩阵,支持80个键。还有2个数字键是空闲的,可以用于特殊按键定制。

    • 按键冲突处理

    如果按上图简单实现会存在冲突问题,当接收口上有多个按键被按下时,会存在回路,高电压的扫描口和低电压的扫描口发生短路,就不知道是哪个按键被按下了。一般键盘都是5键左右不冲突,也就是这个键盘有5个接收口,只要保证在同一个接收口上的按键不会同时按下就不会有冲突。

    使用矩阵扫描方式就会有按键冲突问题,我使用二极管来处理冲突,保证不会出现回路问题,如图(R是扫描口,C是接收口):


    • 电压动荡处理
      二极管解决了冲突问题,但是不能解决电压动荡,电压不稳定有两方面,第一就是当扫描口高电压变为低电压时,接收口电压不会立即变成低电压,所以在接收口都需要加一个下拉电阻,让电压立马降下来。第二就是按键按下时接触片碰撞时导致的电压不稳,这个最好是通过加电容(和按键并联)去过滤波动电压,买元件时忘了买电容了,这里就简单粗暴了

    • 最终电路设计


    • 外壳设计(第一期简单点,不把电路板放到外壳内)

      • 左手


      • 右手


      • 侧面


    • 键帽设计


    • 程序设计

    #include "Keyboard.h"
    #include "HID.h"
    
    #define scanPin_len 8
    int scanPin[] = {4,5,6,7,0,1,2,3}; // 扫描pin,(默认低电平,逐个输出高电平)
    int scanPos = 0; // 当前扫描位
    
    #define btnPinA_len 6
    #define btnPinD_len 4
    int btnPinA[] = {5,4,3,2,1,0}; // 按钮pin,模拟端口
    int btnPinD[] = {8,9,10,11}; // 按钮pin,数字端口
    
    #define btn_len 10
    byte btn[scanPin_len][btn_len]; // 按钮状态
    byte btnTmp[btn_len]; // 临时按钮状态
    
    #define KEY_FN KEY_RIGHT_SHIFT // FN键
    // 8*10的按键映射矩阵
    uint8_t keyMap[scanPin_len][btn_len] = 
    {
    {'y','n','7','8',KEY_F6,'h','m','u','j',' '},
    {'o','.','0','9',KEY_F7,'l',',','i','k',KEY_FN},
    {'p','/','-',KEY_LEFT_ARROW,KEY_F8,';',KEY_UP_ARROW,'[','\'',KEY_DOWN_ARROW},
    {KEY_F10,KEY_DELETE,'=',KEY_BACKSPACE,KEY_F9,KEY_F11,KEY_RETURN,']','\\',KEY_RIGHT_ARROW},
    {KEY_ESC,KEY_LEFT_GUI,'`',KEY_LEFT_CTRL,KEY_TAB,'a','q','z',KEY_CAPS_LOCK,KEY_LEFT_SHIFT},
    {KEY_F1,KEY_LEFT_ALT,'1',KEY_F2,'2','s','w','x','d','c'},
    {KEY_F3,' ','4',KEY_F4,'3','e','r','b','f','v'},
    {KEY_F5,'6','5',0,0,0,'t','g',0,0}
    }; 
    
    void setup() {
      Keyboard.begin();
      Keyboard.releaseAll();
    
      // 初始化扫描pin
      for(int i=0; i<scanPin_len; i++) {
        pinMode(scanPin[i], OUTPUT);
      }
    
      // 初始化按钮pin
      for(int i=0; i<btnPinD_len; i++) {
        pinMode(btnPinD[i], INPUT);
      }
    
      // 初始化按钮状态
      for(int i=0; i<scanPin_len; i++) {
        for(int j=0; j<btn_len; j++) {
          btn[i][j] = 0;
        }
      }
    
      for(int j=0; j<btn_len; j++) {
        btnTmp[j] = 0;
      }
    }
    
    void loop() {
      // 轮询设置scanPin
      for(int i=0; i<scanPin_len; i++) {
        if(i == scanPos) {
          digitalWrite(scanPin[i], HIGH);
        } else {
          digitalWrite(scanPin[i], LOW);
        }
      }
    
      delay(5);
    
      // 读取按键信息
      readBtn();
    
      // 处理状态有改变的btn
      for(int i=0; i<btn_len; i++) {
        if(btn[scanPos][i] != btnTmp[i]) {
          btn[scanPos][i] = btnTmp[i];
    
          if(btnTmp[i] == 1) {
            Keyboard.press(keyMap[scanPos][i]);
          } else {
            Keyboard.release(keyMap[scanPos][i]);
          }
        }
      }
     scanPos = (scanPos + 1) % scanPin_len; // 下一个
    }
    
    void readBtn() {
      // 先读模拟口,再读数字口
      // 5 -> 0
      int index = 0;
      for(int i = 0; i < btnPinA_len; i ++) {
        int val = analogRead(btnPinA[i]);
        if(val > 600) {
          btnTmp[index] = 1;
        } else {
          btnTmp[index] = 0;
        }
        index ++;
      }
    
      for(int i = 0; i < btnPinD_len; i ++) {
        btnTmp[index] = digitalRead(btnPinD[i]);
        index ++;
      }
    }
    
    

    制作图集

    • 第一版键帽(3D打印机精度还是有点欠缺,特别是处理弧线):


    • 打印机底板想换成玻璃的,结果新买的毛玻璃打印时受热不均,碎了。。。还是乖乖用回美纹纸吧


    • 在打印中,加热头松动掉落,还好机器有自动保护,没有造成火灾。幸亏代码不是我写的,要不然肯定会火灾的,_

    • 打印过程中底座脱落,都打成鸟窝了。。。一路坎坷啊


    • 轴体安装,再次简单粗暴的没有使用卫星轴


    • 飞线,这是第一版没加二极管的连线,没有做pcb,直接飞线连,简单高效,适合屌丝程序员,O(∩_∩)O哈哈~


    • ArduinoLeonardo还没到货,拿uno测试按键


    • 外壳组装效果


    • 第一版键帽(字母是用美纹纸贴上去的,就是这么简单粗暴)


    • Leonardo到货,测试驱动


    • 右手组装成功(黑色更有感觉啊)


    • 新版键帽(白色),美纹纸弱爆了,必须整高端点,然后发现白色材料打印效果很赞,光滑度也高了不少,打印材料还是相当重要的


    • 电路板,裸露在键盘外还是挺有极客范的哦


    • 最后效果,键盘在手,天下我有


    总结

    做完整个键盘感觉非常有成就感,也非常实用,截止目前已经用了快半年时间了,已经非常喜欢机械键盘的按键反馈,也非常习惯分体键盘的布局,手腕也不再弯着,没有酸过了。最后来大致汇总下花费吧(屌丝程序员装B有望啦。。。)

    • 80颗国产黑轴 104元
    • 元器件 24元(邮费贵,还有很多二极管和万能板没用到)
    • Leonardo 30元
      大概材料花费在160元左右,比普通的机械键盘便宜多了,而且还是分体的,所有按键可随意自定义(驱动程序都自己写的,还有啥不能改的,O(∩_∩)O哈哈~)

    最后晒一下我在小黑屋的办公桌(这键盘是不是B格最高的? O(∩_∩)O~~)


    相关文章

      网友评论

      • 6049f1d4dd98:太强了
      • YangYuer:哇,看起来真不错,请问跟你买一个行不行?
        VellBibi:做得比较粗糙啦,没啥时间去搞量产,哈哈,欢迎一起diy【ps:其实钱给够也不是不可以:sunglasses:

      本文标题:黑科技:程序员如何打造属于自己的分体键盘

      本文链接:https://www.haomeiwen.com/subject/zrkzlttx.html