-
Notifications
You must be signed in to change notification settings - Fork 6
Home
tldr: professional quality PS/2-keyboard-to-USB for my US English 101 key keyboards.
One day my shiny new computer lacked any PS/2 port to which to connect my fine old trusty and comfortable PS/2 keyboards. It had to happen someyear, and 2014 was the year.
In anticipation I had bought a commercial no-name (really nothing printed on it except made in China) PS/2-to-USB adapter. It didn't cost much. However it also didn't work. It would miss about every 50th keystroke. When it missed an UP (key release) keystroke the results were terrible, especially in vi!
So I bought a 2nd, different PS/2-to-USB adapter. That too had the same problem.
Apparently many of the manufacturers in China are using the same broken design and/or firmware :)
Well I'm an embedded software engineer. A quick glance at the PS/2 spec convinced me it couldn't be that complicated to do myself, and in the process maybe I'd learn what it was which troubled the commercial adapters.
I looked at some existing USB and PS/2 libraries for embedded processors. The LUFA library on an ATmega32u4 seemed like the USB solution with the most chance of functioning correctly. The many existing PS/2 keyboard libraries I found were all GPIO based, usually using the Clock signal to generate an interrupt. They also either ignored the parity bit, or dropped bytes with bad parity. So I glued one of those PS/2 libraries to LUFA to see what I got, and what I got worked less well than the commercial adapters. It missed keys left and right. (Note that later I discovered that part of my troubles were because of unequal ground voltages between the test PS/2 machine and the USB development machine. Fixing that helped a lot, as did reducing the length of the probe wires and the area they swept out. It wasn't all due to bad software.)
I implemented sending commands to the keyboard (using the timing I measured by watching on an oscilloscope and an older motherboard with a PS/2 port), the parity check, and issuing resend (0xFE) commands to have the keyboard resend bytes received with bad parity. That helped, getting me fewer errors than the commercial adapters, but still an occasional missed keystroke. Looking at the received byte and the intended byte I saw the trouble was usually that the start bit was missed. It seems the LUFA library interrupt sometimes take longer than half a bit-time (~50 usec). I needed a hardware FIFO to receive the PS/2 bits while the USB was chugging.
The ATmega32u4's UART can be reconfigured in a variety of ways to implement RS-232, SPI, and other serial protocols. Looking at the datasheet it seemed that if I enabled external clocking on falling edges and moved the PS/2 clock and data signals to the right pins that the UART would receive and buffer 2 bytes before dropping, giving the CPU 2 msec or more to come around. In addition it would check the parity for free. And so it was. It almost worked on the first try except I had a pin mixed up :)
The UART RX did the trick. Now the adapter was 100% reliable.
I'm releasing this under the GPLv3 license. I know how little licenses mean to a Chinese manufacturer. But hey, it took me a long weekend to implement, and ultimately it would be a better the world if they stole this code rather than whatever they have been using.
Surmountable limitations:
-
the key map from PS/2 to USB key codes is, at present, only for US English keyboards because that's all I have at hand. Feel free to add your own and send a patch or pull request.
-
the key map only covers the 101 original keys. That is again because that's all I have at hand. Historically keyboards made after the GUI keys (the "Windows keys") were added are, by and large, no better than easily available contemporary USB keyboards (in other words, crap). The exception might be the original MS ergonomic keyboard, which has its aficionados.
See [https://github.com/nsd20463/ps2_kbd_to_usb_adapter/blob/master/README] for more general info, and the long comment at the top of [https://github.com/nsd20463/ps2_kbd_to_usb_adapter/blob/master/main.c] for PS/2 signal traces and timings.