Tiny (still-flakey) USB keyboard with ATMEGA 32u4

After making a USB keyboard with an ATTINY85 and noticing it wasn’t the “yellow of the egg”, I decided to try at making an ATMEGA 32u4 version.

Overview

What I was looking to make was (recap from previously):

  • a simple USB keyboard with 1 key
  • reprogrammable
  • tiny
  • mechanical keyboard key
  • cheap enough to give away
  • actually works
  • debuggable

The “cheap” aspect was mostly to justify making & buying some :). These costs a bit more than the ATTINY85’s, but still not super expensive.

Spoiler: this design doesn’t work consistently, the next revision fixes some of the issues from here. Don’t copy this design.

Hardware design

I took the schematic of the Pro Micro schematic, compared with other similar designs, and the ATMEGA 32u4 datasheet/manual to put mine together. I ended up with this:

ATMEGA 32U4 USB keyboard schematic

Laying this out, I increased the size of my board to 28mm diameter and switched to 0402 components (since I was having it all made by JLCPCB again).

The layout ended up being:

top of the PCB

(top side)

bottom of the PCB

(bottom side)

The layout is significantly more complex, I was pretty stoked to get it all to fit. EasyEDA has an auto-router, but it’s a pain to use the online version, and routing it yourself is a good practice (and not super hard once you get a grip on it). I used the same USB ports, they worked well. I considered USB-C, but OMG SO MANY PINS. Let’s keep things simple for now.

The ATMEGA 32u4 has an internal clock, but apparently it’s only stable for low-speed USB functionality. Low-speed USB won’t work for USB keyboards. So, time for a 16MHz clock.

I had to improvise a little bit since the crystal used on the Pro Micro isn’t available at JLCPCB as a part. I ended up using a different crystal. I used the same 22nF caps though, people on the line seemed to suggest that 22pF is a great match for any crystal (turns out it’s not).

Price-wise, these ended up at $4 for 5 PCBs + $35 for parts & assembly (ca $8/piece), cheap enough to try.

final board photo

(ignore the solder mess at the bottom, read on)

Software

To use these, you have to first flash the Pro Micro bootloader on them (with the ICSP port). This enables the USB port, so it’s then easy to check if they “work”, even without flashing any additional code. The basic test code is here - but unnecessary for the next steps.

Flashing the Caterina bootloader was pretty straight-forward. Connecting it to USB and flashing the Arduino code was super hit & mostly miss.

Checking the logs on my Ubuntu machine, I see it try to connect, and usually fail:

Feb  7 21:47:49 johannes /usr/lib/gdm3/gdm-x-session[2089]: (**) Option "xkb_model" "pc105"
Feb  7 21:47:49 johannes /usr/lib/gdm3/gdm-x-session[2089]: (**) Option "xkb_layout" "us,ch"
Feb  7 21:47:49 johannes /usr/lib/gdm3/gdm-x-session[2089]: (**) Option "xkb_variant" ","
Feb  7 21:47:49 johannes /usr/lib/gdm3/gdm-x-session[2089]: (**) Option "xkb_options" "grp_led:scroll"
Feb  7 21:47:49 johannes /usr/lib/gdm3/gdm-x-session[1202]: (II) systemd-logind: got fd for /dev/input/event20 13:84 fd 27 paused 1
Feb  7 21:47:49 johannes /usr/lib/gdm3/gdm-x-session[1202]: (II) systemd-logind: releasing fd for 13:84
Feb  7 21:47:49 johannes /usr/lib/gdm3/gdm-x-session[2089]: (II) event20 - SparkFun SparkFun Pro Micro: is tagged by udev as: Keyboard
Feb  7 21:47:49 johannes /usr/lib/gdm3/gdm-x-session[2089]: (II) event20 - SparkFun SparkFun Pro Micro: device is a keyboard
Feb  7 21:47:49 johannes acpid: input device has been disconnected, fd 24
Feb  7 21:47:49 johannes kernel: [517540.201477] usb 1-8: new low-speed USB device number 45 using xhci_hcd
Feb  7 21:47:49 johannes kernel: [517540.605776] usb 1-8: device descriptor read/64, error -71
Feb  7 21:47:50 johannes kernel: [517540.841495] usb 1-8: device descriptor read/64, error -71
Feb  7 21:47:51 johannes kernel: [517541.977506] usb 1-8: new low-speed USB device number 46 using xhci_hcd
Feb  7 21:47:51 johannes kernel: [517542.109746] usb 1-8: device descriptor read/64, error -71
Feb  7 21:47:51 johannes kernel: [517542.345754] usb 1-8: device descriptor read/64, error -71
Feb  7 21:47:51 johannes kernel: [517542.453584] usb usb1-port8: attempt power cycle
Feb  7 21:47:52 johannes kernel: [517543.109656] usb 1-8: new low-speed USB device number 47 using xhci_hcd
Feb  7 21:47:52 johannes kernel: [517543.110367] usb 1-8: Device not responding to setup address.
Feb  7 21:47:52 johannes kernel: [517543.317783] usb 1-8: Device not responding to setup address.
Feb  7 21:47:52 johannes kernel: [517543.525636] usb 1-8: device not accepting address 47, error -71
Feb  7 21:47:53 johannes kernel: [517543.861487] usb 1-8: new low-speed USB device number 48 using xhci_hcd
Feb  7 21:47:53 johannes kernel: [517543.862234] usb 1-8: Device not responding to setup address.
Feb  7 21:47:53 johannes kernel: [517544.070243] usb 1-8: Device not responding to setup address.
Feb  7 21:47:53 johannes kernel: [517544.277648] usb 1-8: device not accepting address 48, error -71
Feb  7 21:47:53 johannes kernel: [517544.277734] usb usb1-port8: unable to enumerate USB device
Feb  7 21:47:53 johannes /usr/lib/gdm3/gdm-x-session[2089]: (II) config/udev: removing device SparkFun SparkFun Pro Micro
Feb  7 21:47:53 johannes /usr/lib/gdm3/gdm-x-session[2089]: (**) Option "fd" "65"
Feb  7 21:47:53 johannes /usr/lib/gdm3/gdm-x-session[2089]: (II) event20 - SparkFun SparkFun Pro Micro: device removed
Feb  7 21:47:53 johannes /usr/lib/gdm3/gdm-x-session[2089]: (II) UnloadModule: "libinput"
Feb  7 21:47:53 johannes /usr/lib/gdm3/gdm-x-session[2089]: (II) systemd-logind: releasing fd for 13:84
Feb  7 21:47:53 johannes /usr/lib/gdm3/gdm-x-session[2089]: (EE) systemd-logind: failed to release device: Device not taken

Sometimes it would connect, I’d be able to flash the Arduino code on it, and then it wouldn’t work the next time. Annoying. No idea what’s wrong. Software, hardware?

Debugging

With a little help from the internal makers group at Google, I learned a few things:

  • The crystal needs to be right next to the MCU. Even on a tiny device.
  • The caps need to be dimensioned
  • Watch out for differential pairs on the USB port (D+/D-) - I think mine is fine though
  • I need an oscilloscope (this didn’t actually help, but who needs two excuses to buy more hardware?)

Here’s the layout for the crystal:

location of tracks to the crystal

The crystal I used (Yangxing Tech X322516MLB4SI mentions 9pF (which is probably the load capacitance). To dimension the caps, I followed this guide (and others) that suggest C1, C2 = 2CL – 2Cstray . I initially thought my Cstray would be low (it feels short, so 1pF?), but folks suggested it’s more like 4-5pF.

Recalculating based on that, I ended up with

C1, C2 = 2 * 9pF - 2 * 4.5)pF = 9 pF

Of course, 0402 components are now a bit of a disadvantage (this says they’re 1.0mm x 0.5mm). I managed to swap them out and these boards now work too.

Reflections

The MCU choice is fine. The size of the MCU package is a bit of a pain, as is the size of the crystal package – I can’t hand-solder these, so I have to order them made. The ATMEGA 32u4 is in the “extended parts” list at JLCPCB, so it costs a little bit more to have these made.

The layout routing needs to be improved. While I managed to make these work in the end by swapping out the caps, it would be better to have the crystal closer to the MCU.

Component sizes for parts you may need to swap out are a bit painful at 0402. It would have been easier to have the load caps for the crystal at 0603 size. I also noticed that R200 for the LED is too low – it’s too bright (I was wondering if this higher current draw was resetting the MCU, but I doubt it). It would have been easier to swap out the resistor if it were 0603. (There’s enough room here to use 0603 parts.)

You can’t view the clock output directly at the crystal, since measuring throws off the capacitance. However, you can enable a clock output on the MCU by using a fuse setting:

(from the ATMEGA 32u4 datasheet)

This enables the clock to be output on pin 32 / PC7 / CLK0 of the chip. I can kinda reach it with the oscilloscope probe, but it would be easier to have it broken out a tiny bit with a via.

Setting the fuses is fairly easy, again with the ICSP port. I don’t know if resetting the fuse is critical, but I did that. Watch your clocks.

The scope

I still have no idea what I’m doing on my oscilloscope, but it’s cool. I also didn’t know how to capture screenshots from the scope initially, so these are phone shots while holing the probes to the board. Not great.

I picked up a Siglent SGS1104 X-E, 100MHz, 4-channel, with some serial port debugging features. I hacked it (as you do) with the information on EEVBlog, extending to 200MHz (which I don’t really need), and enabling Wifi (where I could reuse a wifi dongle I had - but which it turns out has bad reception where I wanted to use the scope). It was mostly a toss between this one & the Rigol 100MHz series.

FWIW I wasn’t able to measure a significant difference on the clock with either the old caps or the new ones. The only difference I saw was that it was insignificantly closer to 16MHz (before: 15.9992 MHz, after: 16.0001 MHz). I can’t imagine that this difference would be enough to throw off the USB port. Swapping out the caps fixed it though, so I’m possibly measuring it incorrectly.

Before:

After:

I did manage to get a cleaner screenshot later on, but this is with the replacements in place:

The frequency looks ok, but I have no idea how to judge the quality of this signal. The CLK0 is supposed to be a square wave at the clock frequency. One day I’ll spend some time to figure out some of the things I can do with the scope, but so far it’s mostly just been cool to use.

Next steps

Things to keep:

  • ATMEGA 32u4 - seems to work well, reasonably supported, lots of pins
  • Some 0402-sized parts
  • Micro-USB port
  • ICSP port

Things to change:

  • Layout - move crystal closer
  • Make replaceable parts 0603 (though longer-term I suspect I can make them smaller if they end up not being replaced: the LED resistor, the crystal caps)
  • Increase resistor value for LED
  • Find/make key layout that works for Kailh low-profile switches too
  • Add a trace & via for CLK0 / PC7

Things to try:

  • Install QMK firmware
  • Try touch-sensor code

Some of these have been implemented in the meantime. Check out rev 2.

Comments / questions

There's currently no commenting functionality here. If you'd like to comment, please use Twitter and @me there. Thanks!

Tweet about this - and/or - search for latest comments / top comments

Related pages