The people at rc2014.co.uk (read more here: https://rc2014.co.uk/history/) came up with this little Universal Micro Keyboard. It has the classic 8 x 5 matrix of the ZX81, ZX Spectrum, etc. and can be interfaced directly as a matrix keyboard.
With the addition of an ATMEGA328P it can be hooked up as plain old serial or as USB (via FTDI).
As you can see this keyboard layout was chosen to closely mimic the ZX Spectrum and friends:
I had other ideas:
- a more general layout than the ZX Spectrum emulation
- an easier protocol to decode : PS/2 does not require an accurate clock
I'm working on a project that involves running a reasonably familiar development environment on a homebrew Z80 machine so my needs are not aligned with the existing layout of the RC2014 version of this keyboard. The most striking change in my layout is to move the extra modifier key from the right side of the bottom row to the left: "symbol shift" becomes "control" in my layout. This is based on a personal preference: I prefer using the modifier keys with my left hand.
I used a vector drawing product called inkscapeto draw this overlay. Powerful enough to do the job while still being simple to use. And free.
The colour coding explains what you can expect from a <ctrl>
modified keypress and from a <shift>
modified keypress (obviously the alphabetic keys are lowercase and then uppercase when shifted).
With just 40 keys, we now have the entire printable ASCII set except for one character (nobody cares about 0x60 `
).
In the early years of my computer experience I crossed paths with several memorable products on the CP/M-80 operating system but the two that had the biggest impact on me were MicroPro's WordStar and early versions of Borland's Turbo Pascal (starting with Version 1). What these two products shared was the same keyboard shortcuts for navigating and editing files. Who else out there remembers what comes shortly after <ctrl><K><B>
?
Attempting to emulate these old shortcuts exactly would be pointless: we've all come some distance since then and expect certain keys like <ctrl><C>
, <ctrl><V>
, <ctrl><Z>
and <ctrl><Y>
to do very obvious things. I'm a pragmatist, not a purist - so I'll make some compromises in both directions.
Back then, editors didn't typically handle files the way they do today: you simply opened a single file in an editor application and then, on exiting the editor (<ctrl><K><D>
for "done") you would be prompted to save or abandon changes. Nowadays, at a bare minimum, we expect our editor to have all the usual functionality of the regular File
and Edit
menus (and their corresponding shortcuts).
So, how do we get all of ASCII, the File menu functionality, the Edit menu functionality and rich navigation functionality into just 40 keys?
Hopper Function KeysThis is my first draft and while I do have specific applications in mind I am also trying to be reasonably generic about this.
NavigationI kept the navigation key grid true to the original WordStar and Turbo Pascal keys despite the fact that I've shifted the bottom row across by one key to accommodate my <ctrl>
on the left. I anticipate that I'll move that bottom row left one key in a redesign of the PCB at some future date when Hopper is a success. The only real annoyance here will be for playing games (where the same keys are used to navigate - that offset bottom row is going to get old).
I also accept that we're now familiar with having keys like <home>
, <end>
, <pgdn>
and <pgup>
and should favour their expected behaviour over the original WordStar behaviour. I worked that into the design.
<ctrl><E>
cursor up one line
<ctrl><X>
cursor down one line
<ctrl><S>
left one character
<ctrl><D>
right one character
<ctrl><A>
start of current line (like modern <home>)
<ctrl><F>
end of current line (like modern <end>)
<ctrl><R>
page up (like modern <pgup>)
<ctrl><C>
page down (like modern <pgdn>)
Now comes the beauty of the WordStar design: just re-use those keys with a navigation prefix (<ctrl><Q>
) to get the extreme versions of each of them:
<ctrl><Q><E>
scroll up one line
<ctrl><Q><X>
scroll down one line
<ctrl><Q><S>
left of current word
<ctrl><Q><D>
right of current word
<ctrl><Q><A>
start of document (like modern <ctrl><home>)
<ctrl><Q><F>
end of document (like modern <ctrl><end>)
<ctrl><Q><R>
top of current screen/page (like Visual Studio)
<ctrl><Q><C>
bottom of current screen/page (like Visual Studio)
Some additional navigation keys outside the basic grid:
<ctrl><Q><B>
go to beginning of block/selection
<ctrl><Q><K>
go to end of block/selection
<ctrl><Q><F>
find..
<ctrl><Q><R>
find & replace..
And we may as well have some function keys:
<ctrl><Q><1>
- <F1>
<ctrl><Q><2>
- <F2>
<ctrl><Q><3>
- <F3>
<ctrl><Q><4>
- <F4>
<ctrl><Q><5>
- <F5>
- run<ctrl><Q><6>
- <F6>
<ctrl><Q><7>
- <F7>
- probably "build" or "compile"<ctrl><Q><8>
- <F8>
-<ctrl><Q><9>
- <F9>
- set or clear breakpoint<ctrl><Q><10>
- <F10>
- single step
These are the functions that use the block prefix (<ctrl><K>
). This is based on WordStar and Turbo Pascal but also modified to include things we'd expect from the typical Edit menu. I use the words "select" or "selection" which are the modern equivalent of what WordStar called "block".
<ctrl><K><B>
start the selection at current cursor position
<ctrl><K><K>
end the selection at current cursor position
<ctrl><K><A>
select all (the entire file)
<ctrl><K><T>
select the current word
<ctrl><K><H>
hide/clear the selection
<ctrl><K><C>
copy the selection to the clipboard
<ctrl><K><X>
cut (remove) the selection to the clipboard
<ctrl><K><V>
replace the current selection with the content of the clipboard
<ctrl><K><G>
delete the selection (without modifying the clipboard contents)
<ctrl><K><O>
open/import a file and replace the current selection with it
<ctrl><K><S>
save/export the current selection to a file
<ctrl><K><Z>
undo (like Edit | Undo)
<ctrl><K><Y>
redo (like Edit | Redo)
I've added my own <ctrl><Z>
prefix to support what we've come to expect from the typical File menu:
<ctrl><Z><O>
open file
<ctrl><Z><S>
save current file
<ctrl><Z><A>
save current file under new name (Save As..)
<ctrl><Z><N>
create a new file
<ctrl><Z><P>
print the current file (or save it to PDF..)
<ctrl><Z><X>
exit the program
I won't bother listing out all the printable characters (shifted, controlled or unmodified). Those should be obvious. What remains are a few single function keys which are all modified by <ctrl>
:
<ctrl><I>
tab key (I didn't include the reverse tab)
<ctrl><G>
deletes the key right of the current cursor position
<ctrl><H>
backspace: deletes the key left of the current cursor position
<ctrl><F>
find next: similar to what we usually expect <F3>
to do
<ctrl><space>
break: interrupt the current program and launch the debugger
Assuming you've bought the RC2014 Universal Micro Keyboard kit, just follow their instructions to assemble the board:
Even though I'm using the serial interface pins I chose to install the matrix breakout pins too. I did this because it made things a lot easier when working on and debugging my version of the firmware: I just hooked a regular Arduino Uno up to the matrix pins (based on the schematic on this page) then developed and tested that way first.
Once I had a final debugged version of my code, I swapped my ATMEGA328P from the Uno into the keyboard and then proceeded to test via the serial interface pins.
PS/2 ProtocolAs mentioned earlier, the legacy RS-232 protocol requires an accurate clock on both the keyboard and the computer (and an agreed set of options like 9600 baud, 8 bits, no parity, one stop bit). Other serial protocols like SPI, I2C and also the PS/2 protocol include a clock signal as one of the wires.
The clock issue is tedious for 8 bit microcomputers because you have to decide if you are going to use one crystal for both the CPU clock and the UART clock. If so, then it must be one of the magic frequencies (1.8432MHz, 3.6864MHz, 7.3728Mhz) that multiply or divide perfectly to support the baud rate speeds of RS-232. Now calculating real time delays for the rest of your software using clock cycles on your microprocessor becomes a bit of a pain. You could also choose to use two crystals: a nice zippy 20MHz crystal for your CPU clock and a 2nd 3.6864MHz for the UART's clock. Or just use a serial protocol for your keyboard that doesn't require an accurate clock.
RS-232 pros:
- works over longer distances
- as few as 2 conductors :
Gnd
andTx
for unidirectional communication
RS-232 cons:
- accurate clock required on both sides
- limited to ASCII character range (certainly limited to 8 bits - yes we could use UTF-8 sequences..)
As a follow-on to Ben Eater's series on building a breadboard 6502 computer he explores the PS/2 protocol, decoding it with shift registers and sharing interrupt driven 6502 assembly to echo the key presses. This helped me see the benefit of the protocol but it also opened my eyes to the advantages of the PS/2 key codes rather than just sending ASCII through serial. Ben's keyboard videos:
So how does a PS/2 keyboard interface work?
PS/2 Protocol pros:
- no need for accurate crystal-based clock to decode
- works well with other PS/2 software (like a keyboard test app on an Arduino)
- distinct events for key down and key up means great support for multiple concurrent keys pressed
- raw PS/2 keycodes means my layout choices are not hard-coded - make the layout your own without updating the firmware...(the only hard-coded layout constraints are the
<ctrl>
and<shift>
modifier keys because they behave differently with respect to repeating when held down - they don't repeat) - In the 80's most of the industry ignored the non-English speaking world. By encoding keys rather than characters we can support more diverse character sets in the decoding software (with alternate layout overlays of course) rather than hardcoding this limitation into the keyboard device.
Fortunately the two pins used by the ATMEGA328P for RX and TX for the RS-232 version can also be used as regular digital IO pins. This means we can reconfigure the RC2014 Micro Keyboard to be a PS/2 keyboard without making any wiring changes on the board and still using the same serial breakout pins. My PS/2 interface uses GND and 5V the same way but repurposes TX
as the Data
pin and RX
as the Clock
pin for the PS/2 interface.
My PS/2 version of the Arduino firmware sketch is in the attachments of this project. Debugging a simple serial protocol like this one is an ideal place to improve your oscilliscope skills. My version of the firmware bitbangs the clock and data signals at a relatively slow speed (it only needs to be faster than we can type so approximately 1ms per key code seems more than enough). Slower means more likely to be easily decoded on the target 8 bit computer.
In case you are curious the PS/2 protocol is a start bit, 8 bits of data, a parity bit and a stop bit. My clock pulses are each roughly 100 microseconds (100us = 0.1ms, 11 bits per packet = 1100us = 1.1ms). And did I mention that the timing doesn't need to be accurate at all in order to work perfectly?
Case AssemblyIf you've been waiting for an excuse to get a 3D printer, now is your chance. I use a Prusa i3 MK3S+ and can only say good things about it.
The case could have been made in two parts: one below the board and one above the board. I chose to break the top part into two pieces: the complex part with recesses for components and solder joints and then a much simpler thin wafer to top it off.
I did this so that I could experiment with different approaches to the keyboard membrane. At one point I thought a super thin 3D printed plastic layer might work. I ended up just using plastic stickers for the final layer so in future, now that I know this works, I may remodel the top of the case into a single part.
To assemble the parts I used these neat little M3 Nylon fasteners with hex shaped nuts. The bottom of the case has countersunk recesses for the nuts so you get a flush base for the keyboard (no wobbling on the desk).
I tested my 3D printed case with a simple laser printed paper overlay stuck on with glue to confirm that I had the correct depth above the switches for comfortable clicking. Works like a dream!
Once I was happy with the overlay, I took it down to the local print shop to make some colour stickers for me. I figured no harm in getting a few more than I needed in case I needed a few layers to make the overlay thick enough.
This was simply printed on a colour laser printer at the local print shop using A4 glossy sticker paper ("one sticker per sheet" kind). I could fit 4 to a sheet so I have spares..
Project HistoryI'll update this project and add release notes here as my layout and my firmware evolve over time. I also plan to release driver firmware for both 65C02 and Z80A in future:
- 2022-08-02 - first draft released.
Comments