CH9329 でPS/2キーボードをUSBキーボードとして現役復帰
>
PS/2キーボードのデータの読み取りは、こちらのサイトを参考にさせていただき、ほとんどそのまま使わせて頂いています。
説明も詳しく、キーボードのLEDも点灯できて、とても助かりました。
okiraku-camera.tokyo
接続は、PS/2キーボード→5v/3.3vレベル変換→ATOM Lite→CH9329→USB/PC の順で、
間にATOM Liteをかましていますが、オーバースペックだし、3.3Vなので、5Vからの変換も必要なので、ゆくゆくは、PICかAVRのようなマイコンに置き換えようと思っています。5Vのマイコンを使えば、レベル変換もいらなくなりますしね。
接続のピンは、以下のような感じ。

レベル変換は、FETで以下のような回路を組んでいます。
レベル変換も、CH9329も安いモジュールが沢山出ているから、それらを利用してもいいですね。

ATOM Liteのスケッチは、前述のPS/2のキーコードを受け取り、受け取ったデータに応じて該当するコードに変換してシリアルでCH9329に送るだけです。
一応、手持ちのキーボードでは、全部のキーコードがカバーできていると思います。
古いPCのPS2キーボードをATOM Lite とCH9329でUSBキーボードとして復活。ATOM Liteでは、少々オーバースペックですがとりあえず手元にあったので…。
— Munehiro_Fuku (@munehiro_fuku) 2024年2月25日
あっ! Worldのスペルまちごうてるやん!
いやん…。 pic.twitter.com/0i9MzU4Ifs
スペルミスは、ご愛敬ということで…。
以下、ATOM Liteのスケッチ。(余計なコードやら、エラーがあるかもしれませんがとりあえず、こんな感じ…。)
お決まりのお約束ですが、追試される方は、自己責任で…。よろしく
#define TXPIN 19
#define RXPIN 22
#define PS2_DATA 21
#define PS2_CLK 25
#define CLK_INT 25
#define LED 23
void show_error(int ms) {
digitalWrite(LED, 1);
delay(ms);
digitalWrite(LED, 0);
delay(ms);
}
static const uint8_t KBD_BUFFER_SIZE = 12;
volatile uint8_t kbd_in, kbd_out;
uint8_t kbd_buffer[KBD_BUFFER_SIZE]; // fifo key buffer
void clear_buffer() {
cli();
kbd_in = kbd_out = 0;
sei();
}
bool put_buffer(uint8_t s) {
uint8_t rx = kbd_in + 1;
if (rx >= KBD_BUFFER_SIZE) rx = 0;
if (rx == kbd_out) return false; // buffer is full.
kbd_buffer[kbd_in] = s;
kbd_in = rx;
return true;
}
uint8_t get_buffer() {
if (kbd_in == kbd_out) return 0; // empty.
uint8_t s = kbd_buffer[kbd_out];
cli();
if (++kbd_out >= KBD_BUFFER_SIZE) kbd_out = 0;
sei();
return s;
}
uint8_t cmd_to_send;
typedef enum { None = 0, Idle, Receiving, WaitForStart, Sending } bus_state_t;
volatile bus_state_t bus_state;
void clk_interrupt() {
volatile static uint8_t data = 0;
volatile static uint8_t clocks = 0;
volatile static uint8_t par = 0;
switch (bus_state) {
case Idle:
clocks = 0;
data = 0;
digitalWrite(LED, 1);
bus_state = Receiving; // found Start bit
break;
case Receiving:
clocks++;
if (clocks < 9) {
data = data >> 1 | (digitalRead(PS2_DATA) ? 0x80 : 0);
if (clocks == 8) // パリティとストップビットは無視。
put_buffer(data); // とりあえずバッファフルは見ない。
} else if (clocks == 10) { // STOP bit.
bus_state = Idle;
digitalWrite(LED, 0);
}
break;
case WaitForStart: // Start bit will be fetched. (DATA is LOW)
par = 0;
clocks = 0;
data = cmd_to_send;
bus_state = Sending;
break;
case Sending:
clocks++;
if (clocks == 9) // parity
digitalWrite(PS2_DATA, par & 1 ? LOW : HIGH);
else if (clocks == 10) // STOP bit timing.
pinMode(PS2_DATA, INPUT); // release DATA
else if (clocks == 11) // ACK bit from keyboard.
bus_state = Idle;
else if (clocks > 0 && clocks < 9) { // send data bits.
if (data & 1) {
digitalWrite(PS2_DATA, HIGH);
par++;
} else
digitalWrite(PS2_DATA, LOW);
data = data >> 1;
}
break;
}
}
bool send_command(uint8_t cmd, uint8_t resp_count = 1, uint8_t* resp = 0) {
int timeout = 15;
unsigned long start = millis();
while (bus_state != Idle && millis() - start < timeout)
delayMicroseconds(200);
clear_buffer();
cmd_to_send = cmd;
bus_state = WaitForStart;
pinMode(PS2_CLK, OUTPUT); //
digitalWrite(PS2_CLK, 0); // drive LOW.
delayMicroseconds(100); // at least 100usec.
pinMode(PS2_DATA, OUTPUT); // drive LOW (START bit)
digitalWrite(PS2_DATA , 0);
delayMicroseconds(50);
pinMode(PS2_CLK, INPUT); // release clock. CLK goes to HIGH.
start = millis();
while (bus_state != Idle && millis() - start < timeout)
delayMicroseconds(100);
uint8_t ret = 0;
for (int8_t i = 0; i < resp_count; i++) {
char tmp[20];
start = millis();
if (cmd == 0xff && i == 1)
timeout = 500; // for Basic Assuarance Test.
while ((ret = get_buffer()) == 0 && millis() - start < timeout) // タイムアウトチェックする
;
if (resp)
resp[i] = ret;
sprintf(tmp, "cmd=%02X, resp=%02X", cmd, ret);
Serial.println(tmp);
}
return (ret == 0xfa);
}
bool kbd_reset() {
uint8_t tmp[2];
send_command(0xff, 2, tmp); // reset keyboard.
if (tmp[0] == 0xfa && tmp[1] == 0xaa)
return true;
return false;
}
#define PS2_LED_CAPSLOCK 4
#define PS2_LED_NUMLOCK 2
#define PS2_LED_SCRLOCK 1
uint8_t kbd_led_state = 0;
bool kbd_led(uint8_t led) {
bool f = send_command(0xed); // LED
if (f) {
kbd_led_state = led;
send_command(led); // LED parameter.
} else
kbd_reset();
return f;
}
void toggle_led(uint8_t led) {
uint8_t new_led = kbd_led_state;
if (new_led & led) new_led &= ~led;
else new_led |= led;
kbd_led(new_led);
}
const uint8_t pattern[] = {0, 2, 4, 1, 0, 1, 4, 2, 0, 2, 6, 7, 5, 1, 0, 7};
void led_demo() {
kbd_reset();
delay(50);
for (int j = 0; j < 5; j++) {
for (uint8_t i = 0; i < sizeof(pattern); i++) {
kbd_led(pattern[i] & 7);
delay(200);
}
}
kbd_led(0);
}
void sendKeyPacket(uint8_t ckey, uint8_t ukey) {
uint8_t sum = 0x10C + ckey + ukey;
uint8_t packet[14] = {0x57, 0xAB, 0x00, 0x02, 0x08, ckey, 0x00,
ukey, 0x00, 0x00, 0x00, 0x00, 0x00, sum};
for(int i = 0; i < 14; i++) {
Serial2.write(packet[i]);
}
}
void press1key(uint8_t ckey, uint8_t ukey) {
sendKeyPacket(ckey, ukey);
// __delay_ms(1);
sendKeyPacket(0x00, 0x00);
}
void setup() {
Serial.begin(115200);
delay(100);
Serial.println("CH9329_PS2_Kyboard converter");
Serial2.begin(9600, SERIAL_8N1, RXPIN, TXPIN);
pinMode(LED, OUTPUT);
digitalWrite(LED, 0);
pinMode(PS2_DATA, INPUT);
pinMode(PS2_CLK, INPUT);
attachInterrupt(CLK_INT, clk_interrupt, FALLING);
delay(50);
if (!kbd_reset())
show_error(300);
toggle_led(PS2_LED_NUMLOCK);
}
uint8_t pckey = 0x00;
void loop() {
uint8_t scan_code = get_buffer();
static bool e0_prefix = false;
static bool f0_prefix = false;
static bool e1_prefix = false;
if (scan_code) {
char tmp[20];
sprintf(tmp, "%02X ", scan_code);
Serial.println(tmp);
switch(scan_code) {
case 0x1C : //a
if(!f0_prefix)
sendKeyPacket(pckey, 0x04);
break;
case 0x32 : //b
if(!f0_prefix)
sendKeyPacket(pckey, 0x05);
break;
case 0x21 : //c
if(!f0_prefix)
sendKeyPacket(pckey, 0x06);
break;
case 0x23 : //d
if(!f0_prefix)
sendKeyPacket(pckey, 0x07);
break;
case 0x24 : //e
if(!f0_prefix)
sendKeyPacket(pckey, 0x08);
break;
case 0x2B : //f
if(!f0_prefix)
sendKeyPacket(pckey, 0x09);
break;
case 0x34 : //g
if(!f0_prefix)
sendKeyPacket(pckey, 0x0A);
break;
case 0x33 : //h
if(!f0_prefix)
sendKeyPacket(pckey, 0x0B);
break;
case 0x43 : //i
if(!f0_prefix)
sendKeyPacket(pckey, 0x0C);
break;
case 0x3B : //j
if(!f0_prefix)
sendKeyPacket(pckey, 0x0D);
break;
case 0x42 : //k
if(!f0_prefix)
sendKeyPacket(pckey, 0x0E);
break;
case 0x4B : //l
if(!f0_prefix)
sendKeyPacket(pckey, 0x0F);
break;
case 0x3A : //m
if(!f0_prefix)
sendKeyPacket(pckey, 0x10);
break;
case 0x31 : //n
if(!f0_prefix)
sendKeyPacket(pckey, 0x11);
break;
case 0x44 : //o
if(!f0_prefix)
sendKeyPacket(pckey, 0x12);
break;
case 0x4D : //p
if(!f0_prefix)
sendKeyPacket(pckey, 0x13);
break;
case 0x15 : //q
if(!f0_prefix)
sendKeyPacket(pckey, 0x14);
break;
case 0x2D : //r
if(!f0_prefix)
sendKeyPacket(pckey, 0x15);
break;
case 0x1B : //s
if(!f0_prefix)
sendKeyPacket(pckey, 0x16);
break;
case 0x2C : //t
if(!f0_prefix)
sendKeyPacket(pckey, 0x17);
break;
case 0x3C : //u
if(!f0_prefix)
sendKeyPacket(pckey, 0x18);
break;
case 0x2A : //v
if(!f0_prefix)
sendKeyPacket(pckey, 0x19);
break;
case 0x1D : //w
if(!f0_prefix)
sendKeyPacket(pckey, 0x1A);
break;
case 0x22 : //x
if(!f0_prefix)
sendKeyPacket(pckey, 0x1B);
break;
case 0x35 : //y
if(!f0_prefix)
sendKeyPacket(pckey, 0x1C);
break;
case 0x1A : //z
if(!f0_prefix)
sendKeyPacket(pckey, 0x1D);
break;
case 0x16 : //1
if(!f0_prefix)
sendKeyPacket(pckey, 0x1E);
break;
case 0x1E : //2
if(!f0_prefix)
sendKeyPacket(pckey, 0x1F);
break;
case 0x26 : //3
if(!f0_prefix)
sendKeyPacket(pckey, 0x20);
break;
case 0x25 : //4
if(!f0_prefix)
sendKeyPacket(pckey, 0x21);
break;
case 0x2E : //5
if(!f0_prefix)
sendKeyPacket(pckey, 0x22);
break;
case 0x36 : //6
if(!f0_prefix)
sendKeyPacket(pckey, 0x23);
break;
case 0x3D : //7
if(!f0_prefix)
sendKeyPacket(pckey, 0x24);
break;
case 0x3E : //8
if(!f0_prefix)
sendKeyPacket(pckey, 0x25);
break;
case 0x46 : //9
if(!f0_prefix)
sendKeyPacket(pckey, 0x26);
break;
case 0x45 : //0
if(!f0_prefix)
sendKeyPacket(pckey, 0x27);
break;
case 0x4E : //-
if(!f0_prefix)
sendKeyPacket(pckey, 0x2D);
break;
case 0x55 : //^
if(!f0_prefix)
sendKeyPacket(pckey, 0x2E);
break;
case 0x6A : // back slash
if(!f0_prefix)
sendKeyPacket(pckey, 0x89);
break;
case 0x54 : //@
if(!f0_prefix)
sendKeyPacket(pckey, 0x2F);
break;
case 0x5B : //[
if(!f0_prefix)
sendKeyPacket(pckey, 0x30);
break;
case 0x4C : //;
if(!f0_prefix)
sendKeyPacket(pckey, 0x33);
break;
case 0x52 : //:
if(!f0_prefix)
sendKeyPacket(pckey, 0x34);
break;
case 0x5D : //]
if(!f0_prefix)
sendKeyPacket(pckey, 0x31);
break;
case 0x41 : //,
if(!f0_prefix)
sendKeyPacket(pckey, 0x36);
break;
case 0x49 : //.
if(!f0_prefix)
sendKeyPacket(pckey, 0x37);
break;
case 0x4A : ///
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x38);
else
sendKeyPacket(pckey, 0x54);
}
break;
case 0x51 : // back slash
if(!f0_prefix)
sendKeyPacket(pckey, 0x87);
break;
case 0x29 : //Space
if(!f0_prefix)
sendKeyPacket(pckey, 0x2C);
break;
case 0x76 : //ESC
if(!f0_prefix)
sendKeyPacket(pckey, 0x29);
break;
// Function Key
case 0x05 : //F1
if(!f0_prefix)
sendKeyPacket(pckey, 0x3A);
break;
case 0x06 : //F2
if(!f0_prefix)
sendKeyPacket(pckey, 0x3B);
break;
case 0x04 : //F3
if(!f0_prefix)
sendKeyPacket(pckey, 0x3C);
break;
case 0x0C : //F4
if(!f0_prefix)
sendKeyPacket(pckey, 0x3D);
break;
case 0x03 : //F5
if(!f0_prefix)
sendKeyPacket(pckey, 0x3E);
break;
case 0x0B : //F6
if(!f0_prefix)
sendKeyPacket(pckey, 0x3F);
break;
case 0x83 : //F7
if(!f0_prefix)
sendKeyPacket(pckey, 0x40);
break;
case 0x0A : //F8
if(!f0_prefix)
sendKeyPacket(pckey, 0x41);
break;
case 0x01 : //F9
if(!f0_prefix)
sendKeyPacket(pckey, 0x42);
break;
case 0x09 : //F10
if(!f0_prefix)
sendKeyPacket(pckey, 0x43);
break;
case 0x78 : //F11
if(!f0_prefix)
sendKeyPacket(pckey, 0x44);
break;
case 0x07 : //F12
if(!f0_prefix)
sendKeyPacket(pckey, 0x45);
break;
// Ten Key
case 0x70 : //0
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x62);
else
sendKeyPacket(pckey, 0x49); //Insert
}
break;
case 0x69 : //1
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x59);
else
sendKeyPacket(pckey, 0x4D); //End
}
break;
case 0x72 : //2
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x5A);
else
sendKeyPacket(pckey, 0x51); //Down
}
break;
case 0x7A : //3
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x5B);
else
sendKeyPacket(pckey, 0x4E); //PgDn
}
break;
case 0x6B : //4
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x5C);
else
sendKeyPacket(pckey, 0x50); //Left
}
break;
case 0x73 : //5
if(!f0_prefix)
sendKeyPacket(pckey, 0x5D);
break;
case 0x74 : //6
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x5E);
else
sendKeyPacket(pckey, 0x4F); //Right
}
break;
case 0x6C : //7
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x5F);
else
sendKeyPacket(pckey, 0x4A); //Home
}
break;
case 0x75 : //8
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x60);
else
sendKeyPacket(pckey, 0x52); //Up
}
break;
case 0x7D : //9
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x61);
else
sendKeyPacket(pckey, 0x4B); //PgUp
}
break;
case 0x71 : //.
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x63);
else
sendKeyPacket(pckey, 0x4C); //Delete
}
break;
case 0x7C : //*
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x55);
else
sendKeyPacket(pckey, 0x46); //Prt Sc
}
break;
case 0x7B : //-
if(!f0_prefix)
sendKeyPacket(pckey, 0x56);
break;
case 0x79 : //+
if(!f0_prefix)
sendKeyPacket(pckey, 0x57);
break;
case 0x77 : //NumLk
if(!f0_prefix && !e1_prefix)
sendKeyPacket(pckey, 0x53);
break;
case 0x7E : //Scr Lk
if(!f0_prefix)
sendKeyPacket(pckey, 0x47);
break;
//other Key
case 0x58 : //CAPS
if(!f0_prefix)
sendKeyPacket(pckey, 0x39);
break;
case 0x0E : //半角全角
if(!f0_prefix)
sendKeyPacket(pckey, 0x35);
break;
case 0x0D : //Tab
if(!f0_prefix)
sendKeyPacket(pckey, 0x2B);
break;
case 0x5A : //Enter
if(!f0_prefix) {
if(!e0_prefix)
sendKeyPacket(pckey, 0x28);
else
sendKeyPacket(pckey, 0x58);
}
break;
case 0x66 : //BackSpace
if(!f0_prefix)
sendKeyPacket(pckey, 0x2A);
break;
case 0x1F : //L_Win
if(!f0_prefix) {
if(e0_prefix)
sendKeyPacket(pckey, 0xE3);
pckey = pckey | 0x08; //L_Win
}
else {
pckey = 0;
}
break;
case 0x27 : //R_Win
if(!f0_prefix) {
if(e0_prefix)
sendKeyPacket(pckey, 0xE7);
pckey = pckey | 0x08;
}
else {
pckey = 0;
}
break;
case 0x67 : //無変換
if(!f0_prefix)
sendKeyPacket(pckey, 0x8B);
break;
case 0x64 : //変換
if(!f0_prefix)
sendKeyPacket(pckey, 0x8A);
break;
case 0x13 : //カタひらローマ
if(!f0_prefix)
sendKeyPacket(pckey, 0x88);
break;
case 0x2F : //Application
if(!f0_prefix) {
if(e0_prefix)
sendKeyPacket(pckey, 0x65);
}
break;
case 0xE1 : //Pause
if(!f0_prefix) {
sendKeyPacket(pckey, 0x48);
}
break;
//Special Key
case 0x12 : //L_shift
if(!f0_prefix) {
if(!e0_prefix)
pckey = pckey | 0x02;
}
else
pckey = 0;
break;
case 0x59 : //R_Shift
if(!f0_prefix)
pckey = pckey | 0x20;
else
pckey = 0;
break;
case 0x14 :
if(!e1_prefix){
if(!f0_prefix) {
if(!e0_prefix)
pckey = pckey | 0x01; //L_Ctrl
else
pckey = pckey | 0x10; //R_Ctrl
}
else {
pckey = 0;
}
}
break;
case 0x11 :
if(!f0_prefix) {
if(!e0_prefix)
pckey = pckey | 0x04; //L_Alt
else
pckey = pckey | 0x40; //R_Alt
}
else {
pckey = 0;
}
break;
case 0xf0 : //Key Release
f0_prefix = true;
sendKeyPacket(0x00, 0x00);
break;
}
if (scan_code == 0xf0)
f0_prefix = true;
else if (scan_code == 0xe0)
e0_prefix = true;
else if (scan_code == 0xe1){
e1_prefix = true;
uint8_t scan_code = get_buffer();
if (scan_code) {
sprintf(tmp, ": %02X : ", scan_code);
Serial.println(tmp);
}
}
else if (!f0_prefix && !e0_prefix && !e1_prefix) { // LED.
if (scan_code == 0x77 ) // Num Lock
toggle_led(PS2_LED_NUMLOCK);
else if (scan_code == 0x58) // CAPS
toggle_led(PS2_LED_CAPSLOCK);
else if (scan_code == 0x7E) // SCROLL
toggle_led(PS2_LED_SCRLOCK);
//else if (scan_code == 0x77) // Num Lock
//led_demo();
}
if (scan_code != 0xe0 && scan_code != 0xf0 && scan_code != 0xe1) {
if (e0_prefix) e0_prefix = false;
if (f0_prefix) f0_prefix = false;
if (e1_prefix && scan_code == 0x77) e1_prefix = false;
}
}
}