User Tools

Site Tools


hidkey_gpio

AVR Programmer built from a USB Keyboard

Recently, my keyboard became louder and louder while typing, so I decided to buy a new one. But what to do with the old one? Ever since I saw the AVR programmer that uses a USB hub, I thought of abusing a USB keyboard for that.

Theory

Most keyboards have at least three indicator LEDs (Num-, Caps- and Scroll-Lock), which can be controlled from the host using a HID Set_Report request, and thus can be used as general purpose outputs. The inputs are a bit more tricky, since the keyboard uses a scan matrix divided in rows and columns. Most keyboards also do some debouncing and detect rows that are 'stuck', which means that we need to simulate the keypress of a single key. If a key has been pressed, the keyboard triggers an interrupt transfer with 8 bytes of data, containing the current state of all keys. The first byte reflects the state of the modifier keys (shift, ctrl, alt, etc.) which I'll be using as inputs. Additionally, the keys can be polled using a Get_Report request to the control endpoint, but the interrupt transfers need to be handled either way, since my keyboard just locked up after the first keypress when I didn't handle them first.

Hardware

The keyboard I've been using is a Microsoft Digital Media Keyboard 3000, which contains the following PCB:

The fourth LED (function lock) can't be controlled from the host (although the HID specification allows two more LEDs, 'Compose' and 'Kana').

For emulating a single keypress, I'm using an optocoupler to connect the row and column of that key. Most switching PSUs use an optocoupler for the feedback loop, so I salvaged mine (KB817-B) from an old mobile phone charger:

And that's basically all that's needed, here's the finished setup:

You can see the keyboard controller PCB, the optocoupler, an Atmel ATtiny2313 microcontroller, and an LED for testing the simple blink program I flashed. I desoldered the LEDs and directly connected the 5V outputs (since there were already 330Ω current limiting resistors in place) as follows:

KeyboardATtinydirection
Num lockSCLout
Caps lockMOSIout
Scroll lockResetout
row/column of right shift keyMISO (through optocoupler)in

For the optocoupler I added a 470Ω current limiting resistor.

Software

First I wrote a small libusb-based test utility for experimenting with the keyboard. I'm directly communicating with the keyboard from userspace, unloading the kernel driver first.

Speed

Just toggling the LEDs in a while-loop gives a signal fluctuating between 500 Hz and 1.2 kHz on the output, which is faster than I assumed. Unfortunately, the inputs are much slower, since the rows are only scanned at 250 Hz on my keyboard:

In addition to that, the keyboard seems to debounce the keys, which means that in the worst case it takes 40ms for the host to know the state of the input. That's quite a bummer, since it drastically limits the not-so-bad output speed.

AVR Programmer

After some initial tests, I wrote an interface driver for avrdude. The code can be found on github: https://github.com/steve-m/avrdude, and can be cloned with:

git clone git://github.com/steve-m/avrdude.git

So far this has only been tested with Linux, but it should work fine on Windows and OS X as well.

Results

$ ./avrdude -c hidkey -C avrdude.conf -p attiny2313 -U flash:w:main.hex:i -V

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 4.17s

avrdude: Device signature = 0x1e910a
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "/home/steve/dev/hid_gpio/avr_demo/main.hex"
avrdude: writing flash (86 bytes):

Writing | ################################################## | 100% 123.79s

avrdude: 86 bytes of flash written
avrdude: verifying flash memory against /home/steve/dev/hid_gpio/avr_demo/main.hex:
[skipped]

Well, as you can see, programming takes quite a while, mainly because of the slow input speed I mentioned before, but is very reliable. Since I assume that many people have some old USB keyboard lying around collecting dust, it still could be used as a bootstrap flasher for solving the old chicken-and-egg problem when building a faster programmer (USBasp, USBtinyISP) or as low-speed general purpose output for other applications. For example, many outputs could be driven using a simple serial-in, parallel-out shift register.

Update #1

Since reading the MISO input is much slower than writing the outputs, as I mentioned above, I took a closer look at the avrdude code. As it turns out, the inputs are read every time, even if the data isn't used at all afterwards (like when writing a byte). I applied a crude hack, so that MISO is only being read if the data is used afterwards. I pushed that change to a new speedup branch on github.

With those changes, flashing my 86 byte demo application is now over ten times faster (11.75s vs. 123.79s)!

$ ./avrdude -c hidkey -C avrdude.conf -p attiny2313 -U flash:w:main.hex:i

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 4.33s

avrdude: Device signature = 0x1e910a
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "/home/steve/dev/hid_gpio/avr_demo/main.hex"
avrdude: writing flash (86 bytes):

Writing | ################################################## | 100% 11.75s

avrdude: 86 bytes of flash written
avrdude: verifying flash memory against /home/steve/dev/hid_gpio/avr_demo/main.hex:
avrdude: load data flash data from input file /home/steve/dev/hid_gpio/avr_demo/main.hex:
avrdude: input file /home/steve/dev/hid_gpio/avr_demo/main.hex contains 86 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 124.25s

avrdude: verifying ...
avrdude: 86 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

Programming the whole flash of the ATiny2313 now only takes 4 minutes 37 seconds:

$ ./avrdude -c hidkey -C avrdude.conf -p attiny2313 -U flash:w:/tmp/usbtiny.hex:i
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 4.33s

avrdude: Device signature = 0x1e910a
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "/tmp/usbtiny.hex"
avrdude: writing flash (2046 bytes):

Writing | ################################################## | 100% 276.90s

avrdude: 2046 bytes of flash written
avrdude: verifying flash memory against /tmp/usbtiny.hex:
avrdude: load data flash data from input file /tmp/usbtiny.hex:
avrdude: input file /tmp/usbtiny.hex contains 2046 bytes
avrdude: reading on-chip flash data:

Reading | ##########                                         | 20% 593.63s^C

I'd say that's quite an improvement, and very well acceptable for a chicken-and-egg bootstrap flasher :-)

This project was featured on Hackaday.

Reference Literature

Discussion

Eduardo Capocchi, 2012/11/26 14:45

Great stuff, thanks for sharing. Will try this out!

Miklós Márton, 2012/11/26 19:27

That is brilliant man! Congratulations!

Mauro Scomparin, 2012/11/26 20:12

Really nice! Great idea!

Ondrej Kolonicny, 2012/11/29 08:50

Hi, great project. Can you post somewhere your libusb-based test utility ??

Thanks…

Steve M., 2012/11/30 19:04

Yes, I cleaned it up a bit and backported the new code I wrote for avrdude. You can get it here. The VID/PID of your keyboard need to be defined at compile time though, see the USB_VID and USB_PID defines in main.c.

Ondrej Kolonicny, 2012/12/03 15:53

Hi Steve, thank you, but i can't compile it on Ubuntu. I got:

cc -Wall -O2 `pkg-config –cflags libusb-1.0` -c -o main.o main.c main.c:125: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘irq_cb’ main.c: In function ‘hidkey_alloc_transfers’: main.c:150: error: ‘irq_cb’ undeclared (first use in this function) main.c:150: error: (Each undeclared identifier is reported only once main.c:150: error: for each function it appears in.)

Any idea ??

Steve M., 2012/12/03 16:23

Seems that you have an older version of libusb-1.0 which lacks the LIBUSB_CALL macro. I pushed a small change, can you try to build it again?

Esad Delic-Ibukic, 2012/12/06 18:07

Great idea! Congratulations!

zex, 2012/12/08 19:22

Since the optocoupler is rated at 20 miliamps and between 1.2v and 1.4v as ideal operating condition, isn't a 470ohm resistor waaay to much? Shouldn't it be in the 180 to 190 range?

Steve M., 2012/12/12 20:17

Well, I had a bunch of 470Ω nearby already, so I just used that one ;) But according to Fig. 11 in the datasheet everything is fine with that, the phototransistor reaches saturation even for If=7.5mA, and that's about what you get assuming Vf=1.2V and the output voltage of the AVR being 5V.

vishu, 2012/12/19 18:10

I need to ask you a question. I have a Atmega 328P lying around since they are not bootloaded. Can I load a bootloader on to them and using your method? I have all the parts you have used (USB keyboard and Phone Charger).

Steve M., 2012/12/22 16:24

Sure, either build a programmer first, like USBasp, so you can flash the other chips faster, or install a USB or serial bootloader, there are quite a few around. As USB bootloader, I can recommend BootloadHID.

nag, 2013/01/24 13:27

Hi Steve,would you please send the programmer suited for windows xp

Steve M., 2013/01/24 14:12

Sorry, I don't have a prebuilt version for Windows, you have to build it yourself. A quick search on Google returned the following link, which might be helpful: http://tomeko.net/other/avrdude/building_avrdude.php

matrixstorm, 2013/06/11 02:33

Hi.

Great idea. Recently I had some pretty similar idea using a keyboard.

It is not limited to USB only and is used by controling keyboard LEDs:

http://matrixstorm.com/avr/tinyusbboard/#chickenoregg (http://matrixstorm.com/avr/tinyusbboard/bootloader/keyboardasp_v2.zip)

Best regards

max, 2013/09/05 22:25

I am trying to reapeat your experiment. Could you also post what to do after cloning github? Trying to compile it with command line provided by Ondrej Kolonicny complains about missing file “ac_cfg.h”. Thanx

Enter your comment
R M N N Y
 
hidkey_gpio.txt · Last modified: 2013/02/27 01:10 by steve_m