Stop motion animation is a very laborious process, but it has been made a lot easier by software such as DragonFrame. Camera settings, lighting level and camera position can all be preset and interpolated automatically, saving the animator a lot of time and effort.
DZED (the authors of DragonFrame) supply a range of hardware which their software will talk to, including several USB to DMX interfaces. Mains-voltage lamps, fixtures and lighting dimmers are then used, which makes the cost of 8 or more channels of lighting considerable.
Such powerful lighting (the lamps/dimmers are mainly aimed at theatre/stage use) is overkill for stop-motion, where long exposures and relatively weak lights can be used. In addition they can be hot, and the voltage hazard necessitates bulky wiring.
An alternative is to use 12V lights. 12V halogen lamps are very cheap indeed, can run over thinly insulated wire, and can run cool enough to allow primitive twisted-wire fixtures. In addition, 12V power supplies are readily obtainable as IT surplus.
What is needed therefore is a device which can interface over USB with DragonFrame, and directly drive a number of 12V lights.
The basic specification was as follows:
Host-powered USB interface
16 independently dimmable channels
10 bit (or greater) resolution
Up to 24V operation (to allow for more powerful halogens if necessary)
Current capacity of 15A per channel
I chose an ATSAM3S2A microcontroller - full speed USB interface, plenty of Flash, and the SAM-BA bootloader which makes programming easy. They're also fairly cheap.
The microcontroller could drive a number of PWM outputs directly, either using the (numerous) internal counters or bit-banging in software. However, producing 16 glitch-free PWM outputs whilst maintaining USB communication with the host seemed like an unnecessary challenge, and there exist several 16-channel PWM drivers (meant for driving LEDs) such as the PCA9685 and the TLC5940. I chose the the PCA9685 (a choice which I later regretted!).
The digital circuitry could be powered off the USB bus, so I chose a NX1117C 3V regulator (and abundance of decoupling capacitors). The 12V side was fed by a HP47A server PSU off eBay (see here), and a woefully inadequate number of decoupling capacitors (see later)
It seemed wise to keep the sensitive digital electronics and high current switching isolated, so used a bunch of EL208 optocouplers to carry the PWM signals across an isolation barrier.
I picked the SI42??DY MOSFET, with 30V Vds and 34A Icont and impressively low 3mR on-resistance. The SO-8 package made it easy to mount too (though with correspondingly low maximum power dissipation, which I intended to avoid by driving it hard with a MOSFET driver).
I considered various ways of driving the MOSFET. Combined optoisolator/MOSFET drivers were nice, but expensive. There are numerous small and cheap MOSFET drivers, but the slow optocoupler transition time meant I needed one with a comparator or schmitt trigger input, which few seemed to offer. The TC4428 dual driver did, so despite costing more than the MOSFET I chose that.
Design and layout
The design is almost self-explanatory - the AT91SAM3 is powered off the USB port, and drives the PCA9685 via I2C. The outputs of the PCA9685 each go to an optocoupler, which span an isolation gap on the board. The outputs of each pair of optocouplers enter both sides of the MOSFET drivers, which then drive the MOSFETs themselves. The MOSFET sources are grounded, and the drains connected to a terminal, such that the lamps are connected between 12V and the MOSFET drains.
Issues I encountered
Toner-transfer PCB making
Making toner-transfer PCBs turns out to be trickier than I thought - particularly the choice of transfer medium. Glossy magazine paper works fine for larger trackwidth designs, but I was unable to get a good transfer of the PCB. After a lot of experimentation, I finally discovered Inkjet HP Superior Paper 180 glossy, which worked perfectly first time.
It took quite a while to get the populated board to respond over USB. In theory all that the AT91SAM3 needs is power and a USB connection, and the SAM-BA bootloader will run and enumerate over USB. However, mine completely refused to. After some guesswork I established that the crystal wasn't oscillating (I wish I had an oscilliscope!) and replaced it with an integrated oscillator, and it promptly started working. I presume that either the board layout was unsuitable, or I had damaged the crystal trying to make it fit my slightly undersized drill holes.
The datasheet for the PCA9685 suggests setting PWM periods which span over the clock overflow. As you can set the on and off point anywhere in the PWM cycle, it should be possible to stagger the on-periods of the different channels, to minimise current ripple.
However, this doesn't work. I encountered strange glitchy behaviour when the on-period includes the clock overflow, so was unable to implement the ripple-minimisation scheme. I wish I had gone for the TLC5940!
Woefully inadequate decoupling / suicidal MOSFET drivers
I quickly learned, after burning my finger on a couple of the TC4428A MOSFET drivers, that despite their datasheet's boastful claims of being latchup-immune, they were very dependent on a smooth supply. I am unable to gauge quite how bad the ripple was (without a scope) but after the addition of a lot more 2.2uF, 10uF and 50uF capacitors (including 2.2uFs spanning the top of the chips themselves!) the rest of the drivers behaved properly. The latched-up ones sadly never recovered!
Software (the interesting bit)
The purpose of this device was to interface with DragonFrame, so I first had to figure out the (undocumented) protocol it would need to implement.
This was made much easier by the discovery that the official devices implement the USB serial device class, so as well as making the AT91SAM3 implementation easier, I could easily create a fake device in software on a computer (using Serial-over-Bluetooth, for some reason) to get DragonFrame to talk to.
Additionally, DZED provide a Arduino sketch for a DIY-camera-slider-controller, which DragonFrame will talk to. It turns out that the protocols are very similar.
I hooked up a serial console on a virtual serial device, and told DragonFrame to connect to it. The following appeared in the console (over the course of several seconds):
It seemed to be some initialisation request, to which the device was supposed to respond. DF quickly established that there wasn't a working device attached, so clearly I needed to send something back in response.
At this point I brought out the big guns and attacked the Java version of DF itself with a decompiler. The code appeared to have been obfuscated with ProGuard, but it wasn't especially hard to reconstruct something resembling readable Java with decrypted strings.
I quickly established that the correct response was (to pretend to be a DDMX-S2):
dx2 ver 1.0\r\n
DF reported that the device had been recognised! The rest was easy, since DF didn't demand a response from further commands, sending various interesting things when buttons were pressed in the interface.
The rest of the protocol is as follows:
Set DMX channel values
dx <start channel> <channel 1 level> <channel 2 level> ...
(The 'dy' command also had the same syntax, and seemed to do the same thing). The levels were values between 0 and 255. DF expects the response:
dx <number of channels set> 1\r\n
DF expects the device to respond:
mm 0 0\r\n
My device was always ready, so replied: