After various people showing interest, and after having put this project on the back-burner for a long time, I have finally taken the time to do a proper update, and release the new firmware. There is support for both ATmega8 and ATmega16. The idea was to use the extra I/O on the ATmega16 for scanning two joysticks, and reporting these as a USB game device. Unfortunately I have not been able to make this work as a composite device (with both keyboard and gaming device in one USB device). The current state of the version with joystick support is released as well, but this does not currently work, and no schematics are released for this either. I do not know when I will have the time to look at this again, but if you take a look at it and do some discoveries, please let me know.
This page describes how to build a USB-keyboard in the form-factor of an old C64 breadbox. Actually, with little modification it can be used for any kind of keyboard, since the source code is available, and all that needs to be done is to modify the key-to-scancode mapping.
The c64key is based on Objective Development's AVR-USB firmware only USB stack for Atmel AVR microcontrollers, and the simple HIDkeys example they provide. The implementation presented here has a more elaborate keyboard scanning routine, and allows multiple keys to be pressed (and detected) simlutaneously.
This all started when some friends from Press Play on Tape (the Commodore 64 Revival Band) asked if I could help them create a special input-device for controlling their stage show (mostly a laptop controlling a video projector). They would like to have an old-style C64 (breadbox) on stage, and being able to control their stage show with it. My first idea was to use the keyboard controller I have been working on for my MAME cabinet SpiffMAME. This implements the PS/2 keyboard protocol by bit-banging in an Atmel AVR microcontroller. Unfortunately it is not finished yet, but I promise I will make a page about it when it is done. It turned out that PPOT were not too keen on a PS/2 device, since the laptop they use does not have a PS/2 port.
In other words I was looking at a USB solution. Further more PPOT were more keen on a game controller device (gamepad) than a keyboard, mainly because they had already written software for this, and because the keyboard solution requires the software to be focused to receive the events, while for gamepad and joystick devices, any application that opens the device can receive the events, even if it is minimized or in the background.
So looking arround for a way to implement USB with an Atmel microcontroller, I first looked breifly at the AT90USB-series, which have hardware USB controllers. Unfortunately these were probably expensive, and for sure quite difficult to get a hold of. Then I remembered having seen the IgorPlug-USB, which implements low-speed USB on simple AVR-devices by bit-banging. All in all, I found this quite impressive, but the IgorPlug was not easy to customize, and also needed special drivers (and of course such drivers will never be digitally signed by Microsoft.
For all the bit-banging USB solutions, it is important to mention that they are not fully up to spec, neither on the hardware side (should use differential signalling), nor on the error handling. That said, the applications I have been looking at seem to work flawlessly, and I adore the possibility of creating a device speaking USB for such a low cost.
Browsing arround on the 'Net for something unrelated, I came across an article describing the MJoy game controller, which implements a joystick with 6 analogue axes, a hat-switch and 24 buttons, all by bitbanging the USB-interface, just like the IgorPlug. But this is a HID-device. The nice thing about a HID-compliant unit is that no special drivers are needed, since these are available in the operating system (both on Linux and Windows).
I had the MJoy application running on my prototype hardware setup within a short time, and even started hooking up the keyboard of an old C64, and doing keyboard decoding. I did, however, find the different versions of the MJoy slightly confusing, with different pins being used for different things in various versions, and I was also a little reluctant to use this, mainly due to the fact that source code was only available for some versions, while the larger version running on ATmega16 is only available as a HEX-file. Further more the code does not seem to be quite as portable and customizable as the option I will describe next.
Since I had originally started looking at a keyboard controller, I decided to investigate this option further (I still think a C64-style keyboard for use with a PC is a pretty neat idea). Looking arround on the 'Net, I discovered Objective Development's HIDKeys, which also implements a USB HID-device, but this one is a keyboard instead of a game controller. What thrilled me the most was that the firmware was very modular (in fact written as a stand-alone USB stack called AVR-USB, that can be used to implement all kinds of different low-speed USB devices). The source code for the firmware was available, and with a very resonable license (basically you can use it for free, but have to make available any modified firmware and hardware design). This suits me fine, since I was planning on making it publicly available anyways.
I will attempt to use the AVR-USB stack for implementing a game controller device. I think it should basically be a matter of creating the correct HID report descriptor table, and adapting the code that sends the HID reports. When I get on with this project, I will make a separate page for it.
Messing arround with the HIDKeys application, I was able to make the basic functions work, allowing keypresses to be sent to the computer. I re-wrote the keyboard scanning to work with a keyboard matrix, and a lookup table converts the key-code to the appropriate scan-code, which can be sent to the computer.
Over time the circuit (PNG or PDF) evolved, and I am quite pleased with the current result, although the limited amount of available I/O-pins left on the ATmega8 has meant that I had to refrain from some of the features I had otherwise planned. During the development I had a setup on a breadboard, which allowed me to quickly make changes to the circuit (although most of the "circuit" is the connections between the microcontroller and the C64 keyboard matrix.
In the current release, there is also an ATmega16-based version (PNG or PDF). This has the extra I/O needed for scanning two joysticks as well, but so far I have not been able to make the composite device work, so the working ATmega16-version has no benefits over the ATmega8-version. Hopefully at some point I will find the time to pick up this project again, and make a composite device based on the ATmega16-version, but with scanning of two joysticks as well.
The schematics are quite simple, and I have therefore decided not to make any printed circuit board (or layout) for it. Instead I have gone for a perf-board solution, as depicted below (most of the connections are on the bottom side with wire wrap). The schematics should provide the needed information if anyone should feel the need to make one.
One thing that took me some time to get right was implementing multiple keys pressed at the same time. It turns out that the HID reports used in the HIDKeys sample applications described only one scan-code being sent, and so had to be modified. But I thought the problem was with my own code for scanning the keyboard matrix, so the time spent debugging was somewhat longer than it should have been.
After reading that a keyboard (HID usage page 0x06) should implement a lot of different functions (in order to be compliant with the boot protocol), I tried changing the usage page to 0x07 (keypad), which seems more appropriate since the C64 keyboard does not have 101 keys. For some time this worked, but that turned out to be Windows caching the usage page of the old device, so after the device was removed from the hardware manager it would no longer work. I therefore had to go back to the keyboard usage page, although my c64key does not fully comply with the specification. The primary issue is that it works.
When working with these firmware-only USB solutions, it is important to note that they are not fully up to spec, in terms of the electrical interface (should be differential), and the CRC error handling, which would be handled appropriately if a hardware USB chip had been used. That said, I have not had any problems with the firmware-only solutions, and I am impressed that it is possible to achieve the strict timing requirements in software only.
Some of the missing features, which are mandatory if the specs should be followed exactly are actualy possible to achieve. For instance the specification says that the device must be able to go into sleep-mode. This is not implemented in the current version, but should be possible by using one of the sleep-modes of the microcontroller. Similarly, the missing CRC check could be implemented in software (the reason that it is not handled on the fly in the USB stack is simply that there is not enough time to do so).
Although there are certainly some drawbacks of the firmware-only approach to USB devices, it actually works, and does so with a minimum of components, which are all easy to get a hold of, and at a price that is impressively low. When everything is up and running you can enjoy the following the first time the device is plugged in:
Since the keys of the C64 keyboard matrix have a lot of bounce, I needed to make a sufficiently effective debouncing algorithm. After trying various things, I ended up with a solution where all the keys are scanned and compared to their last values (saved in a buffer). When a change is detected a debounce counter is started, which will count the number of successive scans where the keys do not change. When this count reaches a preset value (10 in this case), the keys have settled, and a USB HID report is generated. This approach is very effective, and has totally eliminated the key bounce. With the current version of the code, the keyboard matrix is scanned at a rate of about 2.2kHz, which is fast enough that the delay of 10 scans does not impose a noticable delay.
The keyboard matrix scanning does not implement blocking, and is therefore prone to ghosting (i.e. when pressing 3 keys, a fourth keystroke is recorded, although this key was never pressed). While it should be possible to implement blocking in a more sophisticated keyboard scanning algorithm, there are also benefits of this simple approach. For instance it allows a hardware solution to the problem by placing diodes in series with all the keys. Of course this is not done on the C64 keyboard, but could be done if one wants to use the USB keyboard in a different configuration. However, it is important to remember that USB HID reports are limited to 8 bytes, and it is therefore not possible to make a USB HID keyboard that handles more than 7 simultaneous keystrokes apart from the 8 modifier keys. For most USB keyboards the number is 6, since one byte is reserved for LED states. In other words, if one wants to handle the maximum number of simultaneous keystrokes on a USB HID device, the eight modifier keys (Ctrl, Alt, Shift and Windows keys) should be used (and 6-7 others).
For this project it was deemed sufficient if the keyboard would behave as expected when used as a normal keyboard, i.e. the important thing is that the individual keys work, and also that handling of modifier keys must work.
In fact, this turned out to be somewhat tricky, since some of the keys on the C64 should generate different USB keycodes based on the state of modifier keys. This is especially true in the configuration used here, where a C64 keyboard with a Danish layout (some keytops replaced) should map to a PC with a Danish keyboard layout. The implementation therefore handles some keys in a special way, where a lookup-table converts the keypress to the appropriate USB keycode, based on the modifier states, and which also allows the modifier states to be changed. Unfortunately this may mess up the modifier states if a special key is pressed simultaneously with a different key, but when used as a regular keyboard (i.e. one key at a time plus modifiers), it performs flawlessly.
If someone wants to use the c64key in a different configuration, either with an unmodified (or non-danish) keyboard configuration, or with a PC that does not use Danish keyboard mapping (I assume this would be very likely if anyone wants to use it), it should be a simple matter of updating the keycodes.h with the appropriate mapping of the keyboard, including the handling of special keys. If anyone attempts this task, please send me the updated file, and I will include it in the distribution. At some point I may make a few more configurations (e.g for a US keyboard), but so far you are on your own.
The archive c64key.zip contains all the source code for the project, including the needed AVR-USB driver. Also included is the circuit diagram for the hardware and some documentation on various bits and pieces which may come in handy if one wants to study the working of the system.
This new version has the following improvements over the original release:
The entire project is licensed under the same terms that Objective Development put forward for their use of the AVR-USB stack. Basically the terms are similar to the GNU GPL, but some additional requirements in terms of making available the hardware documentation are added. The terms are very resonable for hardware/firmware projects, and hopefully will allow more people to see the possibilities of the AVR-USB stack, and create simple devices that can be connected to any PC equipped with a USB port.