It has been nearly two years since I mentioned the USB keyboard controller project. For most of that time, my primary keyboard at home has been driven by one or another bread-boarded incarnation of it. A few major points on the hardware side:
- I’ve given up on the PIC and gone back to AVR. I ended up with some bugs which I couldn’t resolve, and I’m not sure they weren’t in the USB stack; I also wasn’t at all sure what the licensing would and wouldn’t allow me to publish.
- This will mean learning to solder TQFP packages, and getting boards manufactured. Oh well.
- I’ve decided to target the ATmega32U2 chip – less pins to solder the the U4. It gives up a handful of IO pins, the ADC, 1k of RAM, and the ability to sense if the USB is plugged in when the device is externally powered. None of which matter for this project.
- I’m using a pair of 74HC595 shift registers to drive the columns on the keyboard matrix. 16 pins driven for 3 pins on the AVR is a win. It does mean using a diode per column rather than just setting the undriven AVR pins to inputs (to avoid having outputs fight when multiple keys on the same row are held down) but again, I can live with that.
- Being able to develop on the Adafruit 32U4 board means I have spare outputs for connecting debug LEDs to and more RAM for debug statements.
The software side has made massive advances over the last 3 days (yay for a long weekend!) and I think is now pretty much final. It presents a dual-interface USB device with a “boot keyboard” interface (all the standard keys) plus a generic HID interface for some some non-standard ones (I’ve assigned one to the “System Sleep” code which does exactly the right thing). Things I’ve learnt:
- LUFA is awesome. Especially when you consider it was mostly written when the author was a student, and responds to bug requests very quickly (and the ones I found were extremely minor!). I found it so much easier to use than the Microchip USB stack as well. Oh, and the demos are brilliant for getting started.
- Being able to use GCC is good. I don’t like proprietary development environments.
- Using the hardware serial port for debugging messages is generally good (and you can run it at silly baudrates like 921600, although I’m not sure if that just ends up meaning bigger gaps between characters on the wire…), BUT:
- Too much debugging output can cause oddness. I spent several hours chasing a “bug” where Linux would often wait 5 seconds between finding the first interface and the second one. Wireshark was showing “malformed packet” coming back from the device. I took out one debug statement which fired every time the device received a Control Request – and the problem vanished.
- Oh, wireshark can dump the USB bus. Really handy!
- Git is good. Should have started using it ages ago (I’ve been using RCS. Clearly I’m too old 🙂 )
Next steps?
- I’ve ordered some 32U2 chips. I’ve also got some Adafruit 32 pin TQFP breakout boards. Hopefully I should end up with one successfully soldered to the other and integrated into the breadboard in place of the current 32U4 board.
- Experiment with a bootloader. I think I’ll want one that only triggers if you hold a pin in a specific state when plugging the device in. Working with the bare chip I’ll have access to the HWB line, which I don’t on the board I’m using now.
- Start working on a board layout in something like Eagle or DesignSpark PCB. Don’t think I’m masochistic enough for GEDA. I’ll see how I go soldering the TQFP chips before deciding if I go SOIC for the shift registers (and maybe a diode pack?).
If I can I’ll lay out the board such that it would make a good development board for other purposes. I might add another optional shift register for driving the LEDs – this keyboard only has 3, but even fairly normal keyboards can have 5, and the kernel source seems to recognize another 6 beyond that…
If you are crazy enough to want to build something similar, the code is up on github. The readme should give you a rough idea of how to set up the hardware – main thing to remember is that the scanning is done active low so I can utilize the pull-up resistors built into the chip rather than having to supply pull-down resistors, so the diodes go in backwards to what you might expect!