Saturday, 29 September 2012

Making a POV globe

I've wanted to make a “globe POV” for a while after seeing a totally amazing hi-res one on YouTube a while back. The mechanical side of it (motor drive and power supply) always put me off a bit – while I think I can design a PCB and feel pretty confident it's going to work, motors, gears and bearings are still a matter of kludge and guesswork for me.

I decided to make a 24 LED proof of concept project using the largest size board my free version of EAGLE would let me work to. I originally intended to use 0805 SMT LEDs mounted on their sides but when I realised what a bitch they are to solder like that I decided to go with good 'ole 3mm thru holes of which I have – ahem – about 3,000 blue ones (don't ask).

The electronics was pretty straightforward – since I have made a few similar things before (basically this was an Arduino project with a custom designed PCB) The 24 LEDs are driven by 3 x 74HC595D shift registers through 100 ohm 0805 resistors. I used an Atmega328 in QPF32 package with a miniature (Nano styley) resonator.

I usually use an FTDI USB-TTL serial lead for in circuit programming, so I simply added pads for the the six ICSP connections I needed for burning the bootloader and temporarily soldered wires to them. In the past I have routed in a proper 2x3 ICSP header but I don't think I'll bother any more - after all, burning the bootloader is a one-off procedure and routing in the header is a pain.

The board is single sided FR4. After etching and drilling it I tinplated the tracks (I do this with all my boards now as it makes soldering easier, looks cooler, doesn't take long, and isn't so expensive). After adding the wire jumpers to the back I worked in stages to add the components and test things.

I think its always a good idea to test at each stage in case you need to junk the board due to a design problem. First I soldered in the Atmega and the resonator and burned the bootloader. When that worked I added the serial programming header, diagnostic LED and resistor and made sure I could get sketches to run on the Atmega. Only then did I add all the other components when was confident the brain was alive. I soldered all the components with an iron, with the exception of the resonator where I put solder and flux on the pads and used a hot air tool make the joints.

The rest of it I made up as I went along, convinced it might go wrong at any moment. I shaped the board to a disk with a stanley knife, steel rule, pliers (for snapping) and sandpaper (for smoothing). Then I added in the slots, top and bottom, to accept a 2mm drive shaft using my new diamond edge cutting disk on my Proxxon table saw (in retrospect this blade is fricking awesome and I should have used it to cut out the circular PCB... you live and learn).

Aligning the 2mm drive shafts top and bottom was pure guesswork. I both soldered them and used copious quanitites of cyanoacrylate superglue (with activator spray) to hold them in place, with the help of a couple of plastic hub wheels from a model kit supplier.

The drive shafts go through miniature model bearings in my crudely fashioned frame (made from MDF I salvaged from an old CD rack, cut with a plain old Bosch jigsaw) and at the bottom there is a simple reduction gear from a DC motor to drive it. There is a crude joinery job holding it all together - I am not proud of it,

What I am more pleased with was the electrical transfer method I used. In a previous project I
“borrowed” (saw it on a Youtube video) the idea of using an axially mounted, graphite-lubricated, 3.5mm jack plug/socket connection to take power to the rotating LED board while also acting as a bearing. This time I used some chunky graphite brushes (intended for drill equipment I think) and drilled a 2.5mm hole in each and passed the 2mm drive shafts through them, then crudely superglued the end of the brush springs to my frame. And it worked! In fact it works rather well and I think I will be using this approach again.

As I expected, some balancing was needed. I superglued a couple of nuts to the back of the board to counterbalance the LEDs on the opposite edge. Its not perfect but it was a big improvement.

I saved soldering the Hall Sensor until the board was mounted in the frame, so I could make sure I cut the legs to the right length. A small Neodymium magnet triggers the sensor.

In hindsight it might have been better to somehow mount the magnet on the front of the frame (opposite the vertical part of the frame, so 180 degrees around the axis from where it is now). The reason is that when the sensor passes the magnet, a new drawing cycle starts. So this is the point where the previous draw cycle might be ended early or overrun (depending on rounding errors due to the timer resolution and variations in spin speed). With the position of the magnet in my design these display “glitches” happen right at the front of the globe and are quite easy to see – it would be better to hide them around the back by moving the trigger point through 180 degrees, or by placing the Hall sensor on the same edge as the LEDs rather than opposite them.

There is a voltage regulator on the board and I power the motor and board from a common supply of about 10 volts. Something that surprised me was that when plugged the USB lead in to the programming header on the board, the motor received power and span up. I didn't think the current could cross the regulator in the opposite direction so it was a bit of a nasty suprise (and presumably it does the regulator no good either). A 1N4001 rectifier diode on the +10V supply going to the board brush solved the problem. I was very glad I found this issue before the motor was attached to the frame or I could have smashed the board.

When it was all fitted together I tested it out with a simple set of vertical and horizontal lines, rendering as a wireframe globe, and it looked great. Next I wanted to display a pixellated map of the world... to get this done I found a suitable Mercator projection image on Google images and resized down to the target 24 x 64 pixels in PaintShop Pro (still my drawing tool of choice for its easy work with small bitmaps). Quite a bit of manual tweaking was then needed to get a decent recognisable monochrome image.

The next step was to calculate the bitmap values to insert in the code. I needed data as 3 bytes for each vertical scan column through the image (64 x 3 bytes of data) and I needed the low bit positioned at the vertical top of each byte.

I usually use a spreadsheet to do this kind of thing (OpenOffice). I wondered how I might import the mono bitmap image data directly into the spreadsheet, but as I was in a hurry and the image was small I just retyped it manually. To help keep track I divided the image into 8x8 squares and highlighted them chessboard fashion in yellow. This meant I could work one square at a time and it only took 15 minutes or so to enter the data into the spreadsheet and use formulas to calculate the bitmap values I needed.

I copy/pasted the values from the spreadsheet into the Arduino sketch, programmed the globe and fired it up – and it worked first time! The Pacific looked a bit empty though, so I went back and added Hawaii.

I put a 32kbit I2C EEPROM on the board so that I can store a decent set of image data to make animations possible. I haven't done anything with it yet.

Here is a brief description of how the code works. Almost all the POV projects I've made work in this way...
The sketch sets up the Atmega328's internal timers 1 and 2 to run at the same rate (1/64 of full clock speed). This means they are counting at a very high rate (I think it works out as 62500 counts a second).

The sketch uses “interrupts” – an interrupt is a way for a specific hardware event (like a pin changing value or an internal timer reaching a certain threshold) to cause a specific piece of program code (called a “service routine”) to be run immediately. This means the program does not need to keep “polling” things like inputs and timers, which would not be very accurate at these timescales. Interrupts are really the only way to get the consistent timing accuracy we need.

We use two interrupts, one is called when the hall sensor fires and the value on pin 2 (Interrupt 0 pin) changes. The service routine for this interrupt captures the value of timer 1 and resets the timer. This value is the number of timer ticks in one complete rotation of the board.

Now we divide this up to get the number of timer ticks in a single “sector” (vertical scan column width). Although there are 64 columns in the image, I actually insert an artificial blank column between every pair of image columns to cleanly seperate the “pixels” rather than letting them run together into streaks (it just looks nicer) so I treat the image like it has 128 columns.

So the timer count is divided by 128 and the result is put into the timer 2 “period register”. Now when timer 2 reaches the value of the period register it is automatically reset and another interrupt fires. The service routine for this interrupt will therefore get regularly called 128 times on each revolution – perfect! Now we just need to load values from the image data into the LEDs. We need to keep a count of how far round the revolution we are and we can add an incrementing offset to make the image appear to spin. It is also here that we turn off all the LEDs on every other call to add the gaps between the pixels.

The LEDs are loaded by simple shift register stuff (Check the Arduino tutorials if you don't know what a shift register is) . Speed is of the essence, so the three shift registers are loaded in parallel using 3 data lines and common clock lines. I avoid using digitalWrite() to drive the output pins on anything like this and go direct to the port registers since it's an order of magnitude faster!

*** Source code, EAGLE files and image data available at