Thursday, May 19, 2011

Playpause 1: Design

Ever needed to quickly pause the music or movie that's playing on your computer? Maybe someone is calling you, or you're trying to listen to something else. Hunting around for the pause button with a mouse takes several seconds. What if you could instantly pause with a single button press?
When a USB Doodad gets shipped to someone, it will have a presoldered AVR chip. Somehow I need to get the bootloader into the AVR chip without soldering anything else to the board. The natural way of doing this is via a bed of nails, as I mentioned in Doodad 4.

I have been messing around with bed-of-nails recently, via a little subproject I've been working on called "Playpause":

On top is the Playpause board.
Below is the board for doing bed-of-nails testing.

While the USB Doodad is designed to be a general purpose reprogrammable device, Playpause just does one job: It is a tiny USB device which pretends to be a HID keyboard. But unlike most keyboards, playpause only knows how to send one keycode: The play/pause keycode you'll get from pressing the play/pause button on a multimedia keyboard. Thus with one of these plugged in, you can play and pause your music or movie player without having to get busy with the mouse.  It's small, inexpensive, and easy to build.

(You can find the source code and the KiCad design files here.  TODO: I'm currently working out how to do git submodules so I can park the V-USB code inside playpause, because that's how V-USB likes to be used).

Because it sends a standard USB HID Keyboard keycode, PlayPause will work with any operating system, and doesn't need to have drivers installed. (Although at present it doesn't work with the Mac. I wish I knew why).

Playpause uses the V-USB software USB stack. This is a good choice for AVR projects that you want to be USB-capable, even if the AVR micro you're using doesn't have support for USB in hardware. But there's a catch: V-USB implements USB by using software, which means that when processing USB traffic, the micro can't do anything else.  In practice, this is fine if the task you want your micro to do isn't timing or latency critical, and for this V-USB works very nicely.

Playpause uses surface mount soldering, and the micro I'm using is the 8-pin ATtiny85 in a SOIC-8 package . The pitch on these pins is less than half of an ATtiny85 in PDIP format, which is why Playpause can be made so conveniently small.

While the USB port provides 5V for your device to run on, the USB signalling is done at 3.3V.  This means that every USB project needs to handle this difference somehow.  There are two basic ways of doing this.  The first is to regulate the 5V down to 3.3V, then feed your micro on 3.3V.  This can be done with a linear regulator, or by using the voltage drop across two ordinary diodes.  The second is to run your micro at 5V, but clamp the USB signals to 3.3V.  This can be done with two 3.6V zener diodes, or I've seen some designs (for example the SparkFun AVRStick) that use the forward voltage drop of blue LEDs.  The reason why 3.6V zener diodes are used instead of 3.3V, is that at the frequencies USB runs at, and given the capacitance of a zener diode, the 3.6V zener effectively clamps at 3.3V.  For Playpause, I'm taking the second approach, as shown in “solution B” on the V-USB hardware design page.

Playpause has no external crystal or resonator.  Instead, the clock for the AVR chip is generated onboard using the ATtiny85's RC oscillator.  This presents two problems.  The first is that the RC oscillator is uncalibrated, and may not produce the accurate or stable frequency needed to bit-bang USB packets.  The second problem is that the minimum frequency at which the controller can run V-USB is 12MHz, which is above the nominal 8MHz of the RC oscillator.  How is this situation resolved?

The first problem can be solved by noting that certain parts of the USB protocol take a precise amount of time.  The V-USB software in Playpause can count how many CPU cycles this known time period takes, then from this calculate a calibration value for the RC oscillator. That calibration value then makes sure the RC oscillator produces an exact frequency.

The solution to the second problem lies in the ATtiny85's built-in digital PLL. This circuit has a multiply-by-8 function, so that an input of 8.25MHz from the RC oscillator (8MHz but tweaked up a bit via calibration) gets boosted by the PLL to 66MHz. This frequency is then fed through the clock prescaler, which divides it by 4, to give 16.5MHz, which a frequency supported by V-USB. Curiously, only certain Atmel chips have a PLL, including the ATtinyx5, but excluding the ATmegax8 range. Very useful indeed!

Things to do


For some reason, Playpause currently doesn't work with Mac computers. My Mac-using friends tell me that the Playpause is able to identify itself correctly, but for some reason MacOS doesn't believe it has a driver to handle the multimedia keypresses Playpause is sending. To try to solve this problem, my plan is to plug in a commercial multimedia keyboard, and sniff the codes it sends when the pause button is sent. That's what I originally did when developing the software, but perhaps there's something I missed, particularly in how multimedia keyboards identify themselves. Either way, it seems that only a software change would be needed to fix the problem.


The USB standard says that when the computer suspends, USB devices should put themselves into a low power mode. I'm not currently doing that, so my board is using more power than is strictly necessary. To fix this, I'd set up the processor to sleep until a USB interrupt is received, and if it's not the wakeup condition, go back to sleep.

Code cleanup

The code that's in Git is a direct offshoot of my work on the USB Doodad. As such, it's cluttered with a bunch of things which are not relevant to Playpause. Some janitorial work here would be good.

Coming next: Making PCBs at home


  1. Great Work and great tutorial.
    Due to space, time and because I managed to find a combination that works, I now use toner transfer for one side and try to "send for Fab" on double.

  2. You mention source code but I only see the KiCad files in the repo. Would you be willing to add the source code also?