欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~

本文由VellHe发表于云+社区专栏

前言

作为一名程序员,键盘在手,天下我有啊,不整把高大上的键盘怎么提升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~~)

问答

文字识别在格式上有什么要求?

相关阅读

超简单实现WEB页面顶部阅读进度条

FLV视频文件格式图示

基于日志分析的母机故障定位 ——机器学习应用

【每日课程推荐】机器学习实战!快速入门在线广告业务及CTR相应知识

此文已由作者授权腾讯云+社区发布,更多原文请点击

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

海量技术实践经验,尽在云加社区

DIY党的福利!鹅厂程序员教你200元以内制作专属分体键盘的更多相关文章

  1. Github上安卓榜排名第2的程序员教你如何学习【转载,侵删】

    来自:峰瑞资本(微信号:freesvc)文章作者:代码家(微信 ID:daimajia_share) 软件早已吞噬整个世界,程序员是关键角色.过去 40 年中,许多伟大的公司都由程序员缔造,比如比尔· ...

  2. 一个.net程序员教你使用less

    我是一个.net 程序员,虽然说一直做后台,但是web 前端也会去学,虽然说技术只是层窗户纸,但是像我这种多动症患者,不捅破我心难受啊! 好!废话不多提,下面直接正题,至于less 是什么这里不多讲因 ...

  3. 【好程序员笔记分享】——iOS开发之纯代码键盘退出

    -iOS培训,iOS学习-------型技术博客.期待与您交流!------------ iOS开发之纯代码键盘退出(非常简单)     iOS开发之纯代码键盘退出 前面说到了好几次关于键盘退出的,但 ...

  4. 学Java的前景与就业,资深程序员教你怎么开始学Java!

    IT行业一直是就业的热门岗位,程序员这个职业稳定性和收入比都有着不错的前景,那么学Java的前景和就业是什么样的呢?随着入行Java的准程序员越来越多,各种学习Java的流派也层出不穷!其实在编程的世 ...

  5. 资深程序员教你如何实现API自动化测试平台!附项目源码!

    原文链接: 1.平时测试接口,总是现写代码,对测试用例的管理,以及测试报告的管理持久化做的不够, 2.工作中移动端开发和后端开发总是不能并行进行,需要一个mock的依赖来让他们并行开发. 3.同时让自 ...

  6. 大厂程序员教你如何学习C++(内附学习资料)

    目前准备面试同学都知道,C++是百度和腾讯的主流开发语言,而java是阿里的主流开发语言. 对于初学者来说,也不用纠结究竟学习c++还是java 其实只要好好掌握好一门即可,另一门即可融会贯通 因为我 ...

  7. 《疯狂Java:突破程序员基本功的16课》读书笔记-第一章 数组与内存控制

    很早以前就听过李刚老师的疯狂java系列很不错,所以最近找一本拿来拜读,再此做下读书笔记,促进更好的消化. 使用Java数组之前必须先对数组对象进行初始化.当数组的所有元素都被分配了合适的内存空间,并 ...

  8. java程序员笑不死的经历ส้้้้้้้้้

    ส้้้้้้้้้้ส้้้้้้้้้้ส้้้้้้้้้ 1.程序猿最烦两件事,第一件事是别人要求他给自己的代码写文档,第二件呢?是别人的程序没有留下文档. 2.宪法顶个球!中国的法律都是.t ...

  9. Appstore排名前十的程序员应用软件

    程序员又名程序猿,苦逼劳累的代名词,曾经一个朋友这么开玩笑说,如果你是富二代,你当程序员就是脑残,如果你是穷二代,当程序员的话,死的时候一定是趴键盘. 程序员 哦,可怜的程序员.在那山的这边海的那边有 ...

随机推荐

  1. Postgresql 日志审计

    配置log_destination = 'csvlog'logging_collector = off log_directory = 'pg_log'log_connections = onlog_ ...

  2. Kafka与.net core(二)zookeeper

    1.zookeeper简单介绍 1.1作用 zookeeper的作用是存储kafka的服务器信息,topic信息,和cunsumer信息.如下图: 而zookeeper是个什么东西呢?简单来说就是一个 ...

  3. hdu X问题 (中国剩余定理不互质)

    http://acm.hdu.edu.cn/showproblem.php?pid=1573 X问题 Time Limit: 1000/1000 MS (Java/Others)    Memory ...

  4. 【cocos2d-x 手游研发小技巧(5)获取网络图片缓存并展示】

    今天是年前最后一天上班了,最后一天上班,祝大家马上有各种东西,最后一天也给写一点干货,就是获取网络图片: 经过自己简单封装了一下,实现了获取网络图片,按照比例展示出来,实现方法是cocos2dx - ...

  5. GO学习笔记 - 用":="实现短声明变量!

    对于Delphi程序员,":="这个符号再熟悉不过了,表示对变量赋值!在GO语言中,同样表示赋值,但是和Delphi有些不同,不同就在于不需要var变量名称了!GO语言中的&quo ...

  6. 446. Arithmetic Slices II - Subsequence

    A sequence of numbers is called arithmetic if it consists of at least three elements and if the diff ...

  7. 【OCP-12c】2019年CUUG OCP 071考试题库(77题)

    77.Which two statements are true about sequences created in a single instance database? (Choose two. ...

  8. LOJ#6504. 「雅礼集训 2018 Day5」Convex(回滚莫队)

    题面 传送门 题解 因为并不强制在线,我们可以考虑莫队 然而莫队的时候有个问题,删除很简单,除去它和前驱后继的贡献即可.但是插入的话却要找到前驱后继再插入,非常麻烦 那么我们把它变成只删除的回滚莫队就 ...

  9. robot framework接口测试之一-完整的测试用例

    *** Settings *** Library Collections Library json Library requests Library RequestsLibrary Library H ...

  10. 理解 atime,ctime,mtime (上)

    理解 atime,ctime,mtime (上) Unix文件系统会为每个文件存储大量时间戳.这意味着您可以使用这些时间戳来查找任意时间访问到的任何文件或目录(读取或写入),更改(文件访问权限更改)或 ...