Monday, December 7, 2015

PS/2 Keyboard Emulator

One of my computers had a CMOS checksum error and waited for me to press a key to proceed.  I usually talked to this computer running Linux through the network or the serial console from my laptop.  In these days of smart phones, tablets and laptops, a regular keyboard has become scarce.  I did not have a regular keyboard around.  This computer is old enough to still have a PS/2 keyboard connector.  I figured that I should just build a PS/2 keyboard emulator.  This idea was to have on-screen keyboard that sent the keyboard scan code through a USB serial device to a microcontroller which in turn would generate the PS/2 keyboard signals.

The first thing was to get a 6-pin mini-DIN connector.  I found one on the USB mouse to min-DIN adapter.  The adapter is entirely passive; the USB mouse evidently switches the mode when it detects it is not connected to a USB host.  I pulled the adapter apart and had to remove the metal housing so I could solder wires to the pins.  I wired to the keyboard/mouse combo connector pinouts.
Pin # Signal Name
1 Mouse data
2 Keyboard data
3 Ground
4 Vcc
5 Mouse Clock
6 Keyboard Clock 
Pin 2 and 6 are normally NC for standard keyboard and mouse.


I chose to use the CY8CKIT-094-42xx PSOC prototyping kit, which only costed $3.99 and had a USB serial converter built-in.  And it is a 5V device, so it is compatible to the PS/2 interface.  The PS/2 keyboard/mouse interface consists of two open-drain signals, CLOCK and DATA.  When I measured the pull-up resistance, I was surprised to find they were 1K Ohms on the computer.  The communication is bidirectional.  The data is 11-bit: 1 start bit 0, 8 data bits lsb first, 1 odd parity bit and 1 stop bit 1.  First check if the clock and the data are idle (high).  The data bit is driven when the clock is high and the computer samples it when the clock goes low.  The clock period should be 60 to 100us and generated by the device.  I chose 80us period.  It seemed rather simple to implement.  I would ignore the PC to the keyboard communication for now.  The great thing about developing for the PSOC microcontroller is the PSoC Creator which takes care of the hardware peripherals graphically.  The low level code is generated automatically without having to find out the register definitions.  I quickly dragged out a UART component, a timer and a few I/O ports and configured them graphically.  And the PSOC architecture is the most flexible in the components allocations and pin assignments.  I did have to look at the API definitions or the code to find what functions to call but it was easier than writing the code myself.  In a short time, I had the UART communication, could toggle the I/O ports and had a 1KHz timer interrupt.  The timer counts would provide an 1us resolution clock and be used for the timing of the CLOCK.  The micrcontroller would be only responsible for clocking out the data received from the serial port.  The CY8CKIT comes with a boot loader; my application would have to configure as bootloadable.  The CY8CKIT enters the boot loader when it is powered up with the button pressed.  The Bootloader Host program downloads the code through the USB serial port.

Next I needed a way to get the keyboard scan code to the microcontroller.  I decided that I would use python with tkinter to get the keyboard event.  The event provides a keycode to every key event.  But the keycode is not the same as the keyboard scan code, so has to be translated.  And an extra code F0 has to sent for the key release event.  The majority of the keyboard scan code is just one byte; a few have an extra byte E0 prefix, which has to be sent before F0 for the key release.  I used PySerial for the serial I/O.

I verified the serial data were transmitted correctly by echoing them back.  I coded the keyboard clock and data I/O.  It was ready to go.  I plugged in and hit the key, but nothing happened.   So it was debugging time.  I checked the wiring again and found nothing wrong.  When probed the signals, I was surprised to find the signals were only at 2.5V.  So it was not just straight 1KOhms pull up.  I added 2KOhms pull-up resistors to both signals and they went up to 5V.  I also noticed that the computer kept pulling the CLK low for 180us every 100ms.  When I looked at the signals that the microcontroller generated, the timing was not correct.  I expected to see the CLK period to be 80us, but I saw 2ms.   So the timer was not doing what I expected.  I changed settings for the timer and tried other counters.  But I was not able to change timing.  I decided to forgo the timer component and use the ARM SysTick timer.  Still the timing did not change.  I was using the timer to interrupt at 1KHz and reading the counter value for the microsecond resolution.  The interrupt worked but the counter value did not seem change.  I decided to change the interrupt to 100KHz for 10us resolution and used it as the time base.  All the sudden, it worked: the computer responded to my keystrokes.  I thought I might have to to handle the PC to keyboard commands and apparently it could work without it for BIOS.  But Linux probably does keyboard detection and the keyboard would have to respond to commands.

Why the counter value was not read correctly was rather puzzling.  I would have to investigate later.

No comments:

Post a Comment