如何把老式键盘转换成USB键盘并且自己编程?
什么是PS/2接口PS/2是IBM所推出的用于PC的接口,连接器共有6个孔,圆形设计,在1990~2000左右时间的台式机键盘是PS/2接口的,尽管在1996年已经出现了USB接口,但是受限于芯片成本和设计方案,当时的许多机械键盘依然采用PS/2接口。因为没有识别协议和描述符,所以键盘鼠标必须插到以颜色划分的特定接口才能工作。PS/2接口和现在的USB接口具有许多共性,比如实际使用的线缆数是4根,
什么是PS/2接口
PS/2是IBM所推出的用于PC的接口,连接器共有6个孔,圆形设计,在1990~2000左右时间的台式机键盘是PS/2接口的,尽管在1996年已经出现了USB接口,但是受限于芯片成本和设计方案,当时的许多机械键盘依然采用PS/2接口。
因为没有识别协议和描述符,所以键盘鼠标必须插到以颜色划分的特定接口才能工作。
PS/2接口和现在的USB接口具有许多共性,比如实际使用的线缆数是4根,5V供电,但是在传输形式上有区别,PS/2接口是一根时钟线和一根数据线,而USB数据传输是D- D+差分线。
和USB相比较,PS/2接口数据处理难度低,频率低,兼容性好,按下按键后,键盘会拉低CLK电平,传输12bit的DATA信号,而拉低这个动作同时会让主板芯片组发生一个中断信号触发中断处理,所以理论上反应会比USB更快。
如何自己编程解码
PS/2接口的数据读取属于比较简单的,但是在编程上依然会有一定难度,PS/2接口传输的是11bit数据,第12Bit一定为0。这里的难度主要在于按键判别并通过USB传输上。
模块可以选择Arduino Leonardo,在成本和编程难度上有比较好的平衡,多数时候我比较支持使用低代码(简单调用就可以实现复杂功能的代码)来创作和改装。
使用模组和专用芯片转换成USB
要在内部修改成USB,首要选的就是可以使用的芯片,我购置了一些WIT122UH用来测试,它的价格只有几块钱。
我在设计上考虑到多数时候是用于内置,所以这一个模块只配置了最少的元件。
然后我还设计了一个更复杂一点的,用上了一个紫色的PS/2母口座,型号为DS1093-01-PN60
此外还有一个增强设计增加了16Pin的Type-C接口,另外还有一个母座,如果要从焊接难度出发,可以把Type-C接口替换成4pin的Micro-USB接口。
模块设计上,考虑两种可能,一种是外置设计一种是内置设计
如果你要简化DIY流程可以直接使用这些常见的PS/2接口转USB,将它们拆开可以很好的压缩体积。
举一反三
因为PS/2信号容易模拟,很简单就可以使用8Bit的MCU生成,因此,如果制作键盘,那么使用PS/2转USB信号的芯片,就能够以一个相对于使用原生USB接口的芯片来说更低的多的成本制作出一把USB接口的键盘。
用编程来解决
我是用Leonardo和Arduino IDE来实现这种操作,拥有很大的自由度,应用库是PS2KeyAdvanced https://github.com/techpaul/PS2KeyAdvanced
对应的键盘是一把Cherry G84-4400,PS2接口
在使用上我用的是PS2KeyAdvanced库,和另一个PS2Keyboard库比较来说,这一个库可以支持点亮CAPS LOCK灯,而这个灯是需要控制器可以支持数据写入的。也就是往键盘写数据。
线路连接上,我把DATA连接在D2,CLK连接在D3。
关于各类函数的说明
keyboard.available()这一函数的作用很简单,就是在获取到数据的时候触发其他时候返回0,因此在loop中不断的去判断是否有返回值1。当然实际上数据是已经被缓冲存储的,因为一开始的时候设置了中断,中断函数中对键盘发送的数据进行处理。
按键值的查询
//sample以A为例:按下代码 释放代码
//一般: 41 8041
//CAPS LOCK下: 1041 9041
//SHIFT按住情况下: 4041 C041
//SHIFT按键:4106 8106
完整代码
#include "Keyboard.h"
#include <PS2KeyAdvanced.h>
//最新的,用于PS2转换到USB
//sample以A为例:按下代码 释放代码
//一般: 41 8041
//CAPS LOCK下: 1041 9041
//SHIFT按住情况下: 4041 C041
//SHIFT按键:4106 8106
/* Keyboard constants Change to suit your Arduino
define pins used for data and clock from keyboard */
#define DATAPIN 2
#define IRQPIN 3
#define UPPER 40
#define RELEASE_UPPER C0
#define SHIFTMODE 0x40
#define FUNCTION 0x80
uint16_t c;
PS2KeyAdvanced keyboard;
void setup( )
{
// Configure the keyboard library
keyboard.begin( DATAPIN, IRQPIN );
Serial.begin( 115200 );
Serial.println( "PS2 Advanced Key Simple Test:" );
Keyboard.begin();
}
void loop( )
{
if ( keyboard.available( ) )
{
// read the next key
c = keyboard.read( );
//For DEBUG
if ( c > 0 )
{
// DisValue(c);//DEBUG用的,获取c的具体值,状态符号和按键值
}
//ACTION
if ((c & 0xFF) > 0) //判断获取的按键值c有没有大于0
{
DisValue(c);
switch (c >> 8)
{
case 0x10://大写锁定下按下字母
UpperKey(c);
break;
case 0x90://大写锁定下释放字母
UpperKey_Release(c);
break;
case 0x40://SHIFT大写按下
UpperKey(c);
break;
case 0xC0://SHIFT大写释放
UpperKey_Release(c);
break;
case 0x11:
if (c == 0x111D)
{
Keyboard.press(KEY_TAB);
}
break;
case 0x91:
if (c == 0x911D)
{
Keyboard.release(KEY_TAB);
}
break;
case 0x00://一般按键按下
standardKey(c);
case 0x80://一般按键释放
standardKey_Release(c);
break;
case 0x01://功能按键
FuncKey(c);
break;
case 0x81://功能键和SHIFT键释放
Serial.println( "FUNC RELEASE" );
FuncKey_Release(c);
break;
case 0x41://SHIFT键按下
break;
break;
case 0x20://CTRL Press And
Keyboard.press(KEY_LEFT_CTRL);
standardKey(c);
break;
case 0xA0://CTRL Release
Keyboard.release(KEY_LEFT_CTRL);
standardKey_Release(c);
break;
case 0x21: //CTRL
Serial.println( "CTRL PRESS" );
Keyboard.press(KEY_LEFT_CTRL);
break;
case 0x9: //LEFT-ALT
Serial.println( "L-ALT PRESS" );
Keyboard.press(KEY_LEFT_ALT);
break;
case 0x5: //RIGHT-ALT
Serial.println( "R-ALT PRESS" );
Keyboard.press(KEY_RIGHT_ALT);
break;
default:
Serial.print( " Unknow Higher code: " );
Serial.println( c >> 8, HEX );
}
// Keyboard.press(c);
}
}
}
///
/*
Descript: For DEBUG
*///
void DisValue(unsigned int c)
{
Serial.print( "Value " );
Serial.print( c, HEX );
Serial.print( " - Status Bits " );
Serial.print( c >> 8, HEX );
Serial.print( " Code " );
Serial.println( c & 0xFF, HEX );
}
///
/*
Descript: abcde and so on,main key
*///
#define LEFT_WIN 0x008B//press
#define CAPELOCK 0x00FA//press
void standardKey(unsigned int c)
{
int key = c << 8;
Serial.print("Stand press :");
if(remap(c)!=0)
{
Keyboard.press(remap(c));
return;
}
if (((key >> 8) >= '@') && ((key >> 8) <= 'Z'))
{
Serial.write((key >> 8) + (97 - 65)); //输出小写字符
Keyboard.press(c + ' ');
}
else
{
Serial.write((key >> 8)); //输出其他的字符
Keyboard.press(c);
}
Serial.println("");
}
//return 0: not found
unsigned char remap(unsigned int c)
{
unsigned int key = c << 8;
unsigned char remap_char=0;
switch (key>>8)
{
case 0x3E:
remap_char = '/';
break;
case 0x3B:
remap_char = ',';
break;
case 0x3A: //按键[']
remap_char = 0x27;
break;
case 0x8B:
remap_char = KEY_LEFT_GUI;
break;
case 0x3D:
remap_char = '.';
break;
case 0xFA:
remap_char = KEY_CAPS_LOCK;
break;
default:
break;
}
return remap_char;
}
//CODE:0x 80??
void standardKey_Release(unsigned int c)
{
int key = c << 8;
Serial.print("Stand Release :");
if(remap(c)!=0)
{
Keyboard.release(remap(c));
return;
}
if (((key >> 8) >= 'A') && ((key >> 8) <= 'Z'))
{
Serial.write((key >> 8) + (97 - 65)); //输出小写字符
Keyboard.release(c + ' ');
}
else
{
Serial.write((key >> 8)); //输出其他的字符
Keyboard.release(c);
}
Serial.println("");
}
void UpperKey(unsigned int c)
{
int key = c << 8;
unsigned int chara;
Serial.print("Upper press :");
if (c == 0x403A) //["]
{
Keyboard.press(0x22);
return;
}
if ((key >> 8) >= '0' && (key >> 8) <= '9')
{
switch (key >> 8)
{
case '0':
chara = ')';
break;
case '1':
chara = '!';
break;
case '2':
chara = '@';
break;
case '3':
chara = '#';
break;
case '4':
chara = '$';
break;
case '5':
chara = '%';
break;
case '6':
chara = '^';
break;
case '7':
chara = '&';
break;
case '8':
chara = '*';
break;
case '9':
chara = '(';
break;
default:
break;
}
Serial.write(chara);
Serial.println("");
Keyboard.press(chara);
}
else
{
Serial.write(key >> 8);
Serial.println("");
Keyboard.press(key >> 8);
}
}
void UpperKey_Release(unsigned int c)
{
int key = c << 8;
Serial.print("Upper release :");
Serial.write(key >> 8);
if (c == 0xC03A) //["]
{
Keyboard.release(0x22);
return;
}
Serial.println("");
Keyboard.releaseAll();
}
void rep(String s)
{
Serial.print("Func press:");
Serial.println(s);
}
void FuncKey(unsigned int c)
{
int key = c << 8;
switch (key >> 8)
{case 0x1B :
rep("ESC");
Keyboard.press(KEY_ESC);
break;
case PS2_KEY_ENTER :
rep("ENTER");
Keyboard.press(0x28);
break;
case PS2_KEY_SPACE:
rep("SPACE");
Keyboard.press(0x20);
break;
case PS2_KEY_TAB :
rep("TAB");
Keyboard.press(KEY_TAB);
break;
case PS2_KEY_DELETE :
rep("DELETE");
Keyboard.press(KEY_DELETE);
break;
case PS2_KEY_INSERT :
Keyboard.press(KEY_INSERT);
rep("INSERT");
break;
case PS2_KEY_UP_ARROW :
rep("UP");
Keyboard.press(KEY_UP_ARROW);
break;
case PS2_KEY_DN_ARROW :
Keyboard.press(KEY_DOWN_ARROW );
rep("DOWN");
break;
case PS2_KEY_L_ARROW :
Keyboard.press(KEY_LEFT_ARROW );
rep("L_ARROW");
break;
case PS2_KEY_R_ARROW :
Keyboard.press(KEY_RIGHT_ARROW);
rep("R_ARROW");
break;
case PS2_KEY_PGDN :
Keyboard.press(KEY_PAGE_DOWN );
rep("PGDN");
break;
case PS2_KEY_PGUP :
Keyboard.press(KEY_PAGE_UP );
rep("PGUP");
break;
case PS2_KEY_END :
Keyboard.press(KEY_END );
rep("END");
break;
case PS2_KEY_HOME:
Keyboard.press(KEY_HOME );
rep("HOME");
break;
case PS2_KEY_SYSRQ :
Keyboard.press(PS2_KEY_SYSRQ);
rep("SYSRQ");
break;
case PS2_KEY_BACK:
Keyboard.press(PS2_KEY_BACK);
rep("BACK");
break;
case 0x04:
rep("PRINT SCREEN");
break;
case PS2_KEY_F1 :
rep("F1");
Keyboard.press(KEY_F1);
break;
case PS2_KEY_F2 :
rep("F2");
Keyboard.press(KEY_F2);
break;
case PS2_KEY_F3 :
rep("F3");
Keyboard.press(KEY_F3);
break;
case PS2_KEY_F4 :
rep("F4");
Keyboard.press(KEY_F4);
break;
case PS2_KEY_F5 :
rep("F5");
Keyboard.press(KEY_F5);
break;
case PS2_KEY_F6 :
rep("F6");
Keyboard.press(KEY_F6);
break;
case PS2_KEY_F7 :
rep("F7");
Keyboard.press(KEY_F7);
break;
case PS2_KEY_F8 :
rep("F8");
Keyboard.press(KEY_F8);
break;
case PS2_KEY_F9 :
rep("F9");
Keyboard.press(KEY_F9);
break;
case PS2_KEY_F10 :
rep("F10");
Keyboard.press(KEY_F10);
break;
case PS2_KEY_F11 :
rep("F11");
Keyboard.press(KEY_F11);
break;
case PS2_KEY_F12 :
rep("F12");
Keyboard.press(KEY_F12);
break;
case PS2_KEY_F13 :
rep("F13");
break;
case 0x1C:
Keyboard.press(KEY_BACKSPACE);
break;
default:
Serial.print("UNKNOW CODE:");
Serial.println(c, HEX);
}
}
void FuncKey_Release(unsigned int c)
{
// Keyboard.release(c);
Keyboard.releaseAll();
}
void KeyBoard_Press(unsigned int c)
{
Keyboard.press(c);
}
void KeyBoard_Release(unsigned int c)
{
Keyboard.release(c);
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)