><前回、CH9329をモジュール化して、シリアル入力からUSBのキー入力ができるようになったので、これに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; } } }