Encoder Front Page
SRS Home | Front Page | Monthly Issue | Index
Google
Search WWW Search seattlerobotics.org

How to Build a Spinning Sign and Other Free-Rotating Active Mechanisms

Theodore Johnson

 

image002.jpg (5512 bytes)

Introduction

 

I recently became interested in the possibility of building a spinning sign – one in which an array of LEDs are spun by a motor and lit at carefully timed positions to create the illusion of a sign floating in mid-air. Bob Blick has built such a sign (which he calls a “propeller clock”) and describes his design on his web page, http://www.bobblick.com/bob/projects/mclock/. This posting has been the inspiration for many other designs, which can be found by following the links on Bob Blick’s page.

A spinning sign is an interesting project because it combines all the fun things of robotics (programming, electronics, mechanics), and also requires hacking with the internals of motors. Building a spinning sign seemed to be beyond my capabilities, though. The construction techniques described on Bob Blick’s page, and on others (e.g. see http://www3.sympatico.ca/surfin.dude/blickweb/propclk.html, http://home.wxs.nl/~luberth/propklok.htm) require precision metal working. I live in a small New York apartment and do not have space for a machine shop. Besides, I have clumsy fingers.

 

Still, I persisted in trying to build a spinning sign and eventually developed a construction method which is simple enough that even I could carry it out. In this article, I describe the construction method, which requires only hand tools and a hand-held drill. The resulting mechanism has some advantages over those produced by previously described designs – the sign is powered independently of the motor, and I can communicate with the sign using an UART at 4800 baud. As a result, the design can be used for new purposes, such as creating a platform for a freely rotating sonar ranging module.

 

Background

To build a spinning sign, we need to take apart and hack a DC motor. In this section, I describe the internal components of a DC motor (but see http://www.howstuffworks.com/motor.htm for an illustrated tutorial).

 

Inside a DC motor, the power leads to two brushes, which are springs mounted to the shell of the motor. The brushes are mounted so that they rest against a commutator, which is a cylinder attached to the axle of the motor and which has several electrical contacts on its surface. A DC motor also has several electromagnets attached to the axle, called armatures (a simple DC motor will have three armatures). The magnet wire coiled around the armatures is connected to armature coil terminals, which are also connected to the contacts on the commutator. Normally, there is one terminal (and one contact on the commutator) per armature. As the motor spins, the direction of the current though the armature coils alternates, so that the armatures are continually pushing against the field magnets surrounding the spinning armatures.

 

Powering The Sign

The most significant problem to be overcome in developing a spinning sign is that of supplying power to the rotating active component of the sign (e.g. the microcontroller, the LEDs, etc.). Obviously, we cannot just attach a couple of wires.

 

Bob Blick’s idea was to exploit the fact that the armature coil terminals receive power while they spin. The designs described on his web page involve running wires from the spinning platform into the shell of the motor, and soldering them to the armature coil terminals. Because the wires must spin with the axle, some precision machining is required. Some designs drill through the axle and route wires through the hollow axle and into the motor shell. Other designs create a special bearing for the motor axle, which can accommodate the wires. Either way, I have neither the tools nor the skill to follow this design.

 

Another approach has been to create a slip ring to supply the power. A slip ring is similar to a commutator, except that there is only one contact extending around the entire outer cylinder. Power is supplied to the slip ring using a brush. Only one slip ring is required, as the metal axle of the motor can also be used (normally, there is no electrical connection between the case or the axle and the power to the armatures). However, I was not able to find small and inexpensive slip rings for sale, and building one seems to require precision machining (though with soft materials) that I am not capable of.

 

The slip ring approach seemed promising if I could only find a source of slip rings, perhaps removed from other equipment. Now for a stroke of the obvious. Every motor has a commutator and a set of brushes already precision machined and matched to each other. If remove the commutator, brushes, and brush mounts from one motor, I can attach them to another and thereby supply power to the rotating component. Small 12 volt DC motors are inexpensive and readily available, hence they are an ideal source of parts.

 

Building The Platform

 

To build a platform for a spinning sign, you will need the following:

 

Finding the right DC motors is critical to the success of the project. It cannot be a brushless motor (or you won’t get the brushes). It should have a long shaft, as we will be attaching significant amount of hardware to the motor and shaft. It should be powerful enough to spin the sign at about 20 revolutions per second or more. It should be a simple motor, preferably with three armatures (and preferably not helical). And finally, it should be cheap.

 

Fortunately, it is easy to find such a motor. I found several that are nearly ideal for the project at Argo Electronics Corp., 393 Canal St. They have a couple of crates of old audio motors, which I think were meant for installation in turntables. The one I used to build my prototype spinning sign spins at 20 RPS at 9 volts, has a long shaft, and a wide and stable base. I recognize that many people have the misfortune of not living in New York City, so for the example in this article I purchased a pair of motors from Electronic Goldmine, for a total cost of about $4.00. Similar motors are advertised in the Jameco catalog.

 

The plastic disc is the platform for the spinning part of the spinning sign. The platform is permanently attached to the axle, so I chose a disc that is large enough to attach mounting hardware. Of course, you may choose any other shape for your platform. These disks are inexpensive and readily available from a plastics supply store (there are five of them on Canal street).

 

I start off with the two Johnson Electric motors shown in Figure 1. I will cannibalize the commutator and brushes from one motor and mount them on the other. Using identical motors increases the likelihood of a good fit between the commutator and the shaft, and between the brush mounting and the exterior of the second motor. The parts from a simpler and less expensive motor will probably work well as long as the shaft diameters are the same.

image004.jpg (7345 bytes)

Figure 1. The two motors.

 

The next step is to remove the case from the motor. Figure 2 shows the case, the axle of the motor with the armatures and the commutator, and the brushes mounted in the base of the motor. Notice the white washers on both ends of the axle. They serve to insulate the commutator from the motor housing, and act as bearings so that the axle can spin freely. Remove them from the axle and save them.

image006.jpg (10282 bytes)

Figure 2. After opening the case of the first motor.

 

To remove the commutator, we first need to remove the magnet wire winding from the armatures (doing some makes removing the commutator much easier). Figure 3 shows the axle and the magnet wire that I’ve removed.

image008.jpg (10983 bytes)

Figure 3. The armatures unwound.

 

Next, pull the commutator off of the axle, as shown in Figure 4. This one is composed of a piece of plastic with five copper plates crimped to the exterior. The commutator has a small cylinder that extends past the commutator terminals, which I will use to glue the commutator to the plastic disk platform. Different commutators have different shapes; you will need to figure out an attachment strategy when you finally see the commutator’s shape.

image010.jpg (9359 bytes)

Figure 4. Remove the commutator.

 

Next, I prepare motor base to be attached to the second motor. Figure 5 shows the base’s front and profile. Because the shaft is a little short, I will need to file down this base to make it shorter and ensure that the plastic disk I use as a platform will clear the base when I make the final assembly.

image012.jpg (10901 bytes)

Figure 5. The brushes and the motor base.

 

I need to attach the motor base so that the commutator can spin smoothly while making good contact with the brushes. The strategy for making the attachment depends on the shape of the motor and its base. In this case, I decided to remove the sleeve bearing at the canter of the motor base and widen the hole until it fits the small cylindrical protrusion around the axle of the second motor. Figure 6 shows the base with the enlarged hole. I have also filed several millimeters from the height of the base.

image014.jpg (9525 bytes)

Figure 6. The motor base after filing to fit the motor.

 

Next, I attach the motor base to the second motor by supergluing the center hole to the small protrusion around the axle (which is visible in Figure 1). The result is shown in Figure 7. I have also screwed the brush mounting on. However, the screws interfere with the movement of the brushes. This joint will not bear a load, so using just the glue is sufficient. I later removed the screws.

image016.jpg (10247 bytes)

Figure 7. The brush mounting glued to the second motor.

 

Next, I start work on the commutator and the platform that will support the spinning sign. I start by drilling a center hole in the plastic disk that I will use for the platform, and also four holes for mounting a circuit board. Figure 8 shows the result, along with the commutator. The grey plastic extension of the commutator will mount in the center hole. I used a CAD program to draw a template of the disk and the screw holes, printed it, and used the printout to mark where to drill. It is a lot easier than measuring the disk.

image018.jpg (6885 bytes)

Figure 8. The platform with the center hole and the mounting holes drilled.

 

Before mounting the commutator in the disk, I need to solder wires to what used to be the armature coil terminals. The result is shown in Figure 9. Next, I will drill holes in the disk for the commutator wires. I have marked drill holes in Figure 9.

image020.jpg (8969 bytes)

Figure 9. Power wires soldered to the commutator, and drill hole marks.

 

Drill the wire holes, then glue the commutator to the plastic disk. I used a space-filling epoxy. Be certain to pull the wires tight, and to ensure that the commutator assembly is flush against the plastic disk (bits of glue or wire which are not flush may snag against the brushes).

 

The last step is to attach the spinning platform to the motor shaft. Before using any glue, make certain that when the platform is mounted on the axle, the brushes make contact with the commutator and that the whole assembly spins freely. Use the washers you saved when you opened up the motor (see Figure 2) to shim the assembly. You will need to use at least one of these washers to insulate the commutator from the sleeve bearing of the motor. Front and side views of the completed assembly are in Figure 10 and Figure 11.

 

The green wire is mechanically attached to the motor axle. Before gluing the commutator to the disk, I cut a small slot in the mounting extension of the commutator. After gluing the disk assembly to the shaft of the second motor, I worked the conductor of the green wire into the slot, then glued it into place using epoxy.

image022.jpg (11394 bytes)

Figure 10. Top view of the completed assembly.

image024.jpg (8538 bytes)

Figure 11. Side view of the completed assembly.

 

Testing the Assembly

After building the spinning sign assembly, I test it to verify that it can be used to power a sign. I built a small test board, shown in Figure 12. A ground is formed by the diodes, and LEDs are attached to the power lines from the commutator. I built this test board for a 3-phase commutator, so there are only three LEDs, not five. I added the terminal blocks and two additional diodes for this test.

image026.jpg (14911 bytes)

Figure 12. Test board the spinning sign assembly.

 

I powered up the motor and the spinning circuit, as is shown in Figure 13. As expected, three segments are lit each covering one fifth of a revolution. There is a small overlap between segments, so if the spinning sign assembly is properly constructed there is never any loss of power. This assembly has a small flaw, which can be seen in the left-hand green LED segment (its thickness varies). The glue that attaches the commutator to the disk is not entirely flush with disk, and at one point in the revolution the glue rubs against one of the brushes, breaking the contact. I later corrected this problem by shaving off some drooping globs of glue, and by moving the brushes lower on the commutator.

image028.jpg (5455 bytes)

Figure 13. The test results.

 

Spinning Sign Circuit

I have a better motor for my spinning sign, so for the remainder of the article I will use it instead of the motor assembly that I have just built. The motor assembly with a circuit board attached is shown in Figure 14. One very nice feature of this motor is that it stands upright, making testing a lot easier. I used this device to create the image at the start of the article.

image030.jpg (9654 bytes)

Figure 14. My actual spinning sign motor.

 

The actual spinning circuit board is fairly simple, and is shown in Figure 15. Three power wires from the commutator connect to the circuit board to the left of the center. I built a bridge with six diodes, feeding power to the voltage regulator (a 7805) at top. I use a 10 microfarad input capacitor to overcome any power glitches. Immediately to the right of the center is the microcontroller, an Atmel AT90S2313. Farther to the right is an array of seven LEDS. To the far left is a crude counterweight to balance out vibrations.

image032.jpg (12049 bytes)

Figure 15. The spinning sign circuit board.

 

Towards the bottom is the timing circuit for the sign. In order to display text, the program needs an interrupt each time the motor completes one revolution. The simplest way to get a timing signal is take it from one of the commutator power lines (see Figure 13). I use a 4n26 optoisolator to protect the 2313 from the unregulated power. Unfortunately, the power lines are somewhat glitchy, so that the microcontroller was getting spurious interrupts. I solved this problem by using a .1 microfarad capactor to filter out the glitches (I aimed to filter a one half degree power interruption at 20 RPS). I’m planning on replacing the capacitor with a larger one. Because the commutator power lines are AC, I protect the RC circuit with a diode.

 

I did not know what the important design issues would be when I started this project, so I built this board as a prototype. I would not expect a perf board to be strong enough for extended use, although this one has held up well. One lesson I learned is that the LEDs in the sign must be very bright. I used very inexpensive LEDs in this prototype, and the sign is barely visible in normal light. Another mistake I made is to not connect all of the LEDs to the B port of the 2313 (which makes the program a little more cumbersome).

 

Communications

After powering the spinning circuitry, the next hard problem in building a spinning sign is to devise a way to signal it. Again, you can’t just run a wire to the microcontroller. One method of communicating with the spinning circuit is to use an array of LEDs at the motor base, with a matching set of light detectors on the spinning circuit. This technique has been used to emulate a couple of buttons. If the non-rotating circuit can receive a signal on every revolution of the spinning component, the rotation of the sign can be used as a clock, allowing synchronous 20 bits per second communication (assuming 20 RPS).

 

Actually, there is a simpler way to communicate. The axle of a motor is usually electrically connected to the case of the motor (e.g. because of the sleeve bearings), but is isolated from the from the motor power circuit. Therefore, we have one wire available for a communication line (the commutator assembly provides the common ground). In Figure 15, the green wire in the center is connected both to the axle of the motor and to the RXD line of the 2313’s UART. In Figure 14, a gray wire connected to the case of the motor is visible. I can reliably communicate at 4800 baud over this line.

 

Programming

The program that drives the sign is written in Codevision C, with the interrupt routines written in assembly. The EXT_INT1 interrupt is triggered on a low-to-high change of state from the optoisolator. The main action of the EXT_INT1 ISR is to record the number of ticks in the cycle, and to inform the main routine when a new cycle has started. The commutator used in the assembly supplied power to six armatures, but the armatures at opposite sides were tied together. As a result, INT1 is triggered twice per revolution. The PHASE bit allows the ISR to trigger a new cycle once every two interrupts.

 

The TIM1_OVF triggers when the 16-bit timer 1 overflows. The INT1 ISR resets the timer on every second interrupt, so an overflow means that the motor is not spinning. The timer 1 overflow ISR sets a couple of bits to signal this fact. I had planned to use this mechanism to implement a low power mode, but have not used it.

 

The main routine divides a revolution of the motor into NSLOTS slots. A different bit pattern can be loaded into each slot, using the load_pattern procedure. The main routine waits until the time for the next slot pattern, or for a new cycle indicator. If the INT1 interrupt has detected a new cycle, the current slot is reset to zero (the new cycle indicator is also set to zero, a race condition but a very unlikely one). The load_pattern procedure is called, and the time for the next slot pattern to be loaded is computed, using the last cycle time as an estimate for the current cycle time.

 

The load_pattern procedure determines the current character to display from display buffer, outbuf, finds the bitmap to display from the character map, then displays the bit pattern (the display routine would be simpler and faster if I used all of port B for the display LEDs).

 

The main routine also polls the UART for incoming characters. USR.7 is set when a new character has arrived, and USR.4 is set if there is a framing error. I use a newline character (‘\n’) as a control character, to switch the input and display buffers (i.e., display the transmitted text).

 

/*********************************************
This program was produced by the
CodeWizardAVR V1.0.2.1 Standard
Automatic Program Generator
© Copyright 1998-2001
Pavel Haiduc, HP InfoTech S.R.L.
http://infotech.ir.ro
e-mail:dhptechn@ir.ro , hpinfotech@xnet.ro
 
Project : spinner_2
Version : 2
Date    : 11/25/2001
Author  : Theodore Johnson
Company : New York USA
Comments: 
Make the spinning sign act as a serial port.
 
Chip type           : AT90S2313
Clock frequency     : 10.000000 MHz
Memory model        : Tiny
Internal SRAM size  : 128
External SRAM size  : 0
Data Stack size     : 32
*********************************************/
 
#include <90s2313.h>
 
#include<stdio.h>
 
#define BAUD 4800
#define XTAL 10000000L
 
 
//            Define the LED pins,
//            LED0 is innermost, LED6 is outermost.
 
#define LED0 PORTB.0
#define LED1 PORTB.1
#define LED2 PORTB.2
#define LED3 PORTB.3
#define LED4 PORTB.4
#define LED5 PORTD.5
#define LED6 PORTD.6    
 
//     Parameters of the sign
 
#define NSLOTS 128    // number of columns in the display 
#define BUFLEN 23      // buffer length : longer than
                       // NSLOTS / (charwidth+1)
 
/////////////////////////////////////////
//            display management.
 
unsigned char inbuf1[23] = "New York City";
unsigned char inbuf2[23] = "";
unsigned char *outbuf = inbuf1;
unsigned char *currbuf = inbuf2;
 
unsigned char outbuflen = 13;
unsigned char inbuflen = 0;
 
flash unsigned char charmap[475] = {
       0x00, 0x00, 0x00, 0x00, 0x00,     // " "
       0x00, 0x00, 0x7b, 0x00, 0x00,     // "!"
       0x70, 0x40, 0x00, 0x70, 0x40,     // """
       0x14, 0x7f, 0x14, 0x7f, 0x14,     // "#"
       0x32, 0x49, 0x7f, 0x49, 0x26,     // "$"
       0x42, 0x24, 0x49, 0x12, 0x21,     // "%"
       0x36, 0x49, 0x49, 0x36, 0x05,     // "&"
       0x00, 0x00, 0x70, 0x00, 0x00,     // "'"
       0x1c, 0x22, 0x41, 0x41, 0x00,     // "("
       0x00, 0x41, 0x41, 0x22, 0x1c,     // ")"
       0x2a, 0x1c, 0x08, 0x1c, 0x2a,     // "*"
       0x08, 0x08, 0x3e, 0x08, 0x08,     // "+"
       0x00, 0x01, 0x03, 0x07, 0x00,     // ","
       0x08, 0x08, 0x08, 0x08, 0x08,     // "-"
       0x00, 0x00, 0x06, 0x06, 0x00,     // "."
       0x03, 0x04, 0x08, 0x10, 0x60,     // "/"
       0x1c, 0x63, 0x41, 0x63, 0x1c,     // "0"
       0x21, 0x21, 0x7f, 0x01, 0x01,     // "1"
       0x21, 0x43, 0x45, 0x49, 0x31,     // "2"
       0x61, 0x49, 0x49, 0x59, 0x66,     // "3"
       0x0c, 0x14, 0x24, 0x7f, 0x04,     // "4"
       0x73, 0x51, 0x51, 0x51, 0x4e,     // "5"
       0x3e, 0x49, 0x49, 0x49, 0x26,     // "6"
       0x40, 0x43, 0x44, 0x48, 0x70,     // "7"
       0x26, 0x59, 0x49, 0x59, 0x26,     // "8"
       0x30, 0x09, 0x49, 0x4b, 0x3c,     // "9"
       0x00, 0x00, 0x36, 0x36, 0x00,     // ":"
       0x00, 0x00, 0x31, 0x37, 0x00,     // ";"
       0x08, 0x14, 0x22, 0x41, 0x00,     // "<"
       0x14, 0x14, 0x14, 0x14, 0x14,     // "="
       0x00, 0x41, 0x22, 0x14, 0x08,     // ">"
       0x20, 0x40, 0x4d, 0x48, 0x70,     // "?"
       0x3c, 0x42, 0x59, 0x69, 0x16,     // "@"
       0x1f, 0x24, 0x44, 0x24, 0x1f,     // "A"
       0x7f, 0x49, 0x49, 0x49, 0x36,     // "B"
       0x3e, 0x41, 0x41, 0x41, 0x26,     // "C"
       0x7f, 0x41, 0x41, 0x41, 0x3e,     // "D"
       0x7f, 0x49, 0x49, 0x49, 0x41,     // "E"
       0x7f, 0x50, 0x50, 0x50, 0x40,     // "F"
       0x3e, 0x41, 0x49, 0x49, 0x2e,     // "G"
       0x7f, 0x08, 0x08, 0x08, 0x7f,     // "H"
       0x41, 0x41, 0x7f, 0x41, 0x41,     // "I"
       0x42, 0x41, 0x41, 0x7e, 0x40,     // "J"
       0x7f, 0x08, 0x14, 0x22, 0x41,     // "K"
       0x7f, 0x01, 0x01, 0x01, 0x01,     // "L"
       0x7f, 0x20, 0x18, 0x20, 0x7f,     // "M"
       0x7f, 0x40, 0x30, 0x08, 0x7f,     // "N"
       0x3e, 0x41, 0x41, 0x41, 0x3e,     // "O"
       0x7f, 0x48, 0x48, 0x48, 0x30,     // "P"
       0x3c, 0x4a, 0x4a, 0x46, 0x3d,     // "Q"
       0x7f, 0x48, 0x48, 0x48, 0x37,     // "R"
       0x22, 0x51, 0x49, 0x45, 0x22,     // "S"
       0x40, 0x40, 0x7f, 0x40, 0x40,     // "T"
       0x7e, 0x01, 0x01, 0x01, 0x7e,     // "U"
       0x7c, 0x02, 0x01, 0x02, 0x7c,     // "V"
       0x7f, 0x02, 0x0c, 0x02, 0x7f,     // "W"
       0x43, 0x24, 0x18, 0x24, 0x43,     // "X"
       0x60, 0x10, 0x0f, 0x10, 0x60,     // "Y"
       0x43, 0x45, 0x49, 0x51, 0x61,     // "Z"
       0x7f, 0x41, 0x41, 0x41, 0x41,     // "["
       0x60, 0x10, 0x08, 0x04, 0x03,     // "\"
       0x41, 0x41, 0x41, 0x41, 0x7f,     // "]"
       0x10, 0x20, 0x40, 0x20, 0x10,     // "^"
       0x01, 0x01, 0x01, 0x01, 0x01,     // "_"
       0x00, 0x00, 0x40, 0x20, 0x00,     // "`"
       0x1e, 0x21, 0x21, 0x21, 0x3f,     // "a"
       0x7f, 0x09, 0x09, 0x09, 0x06,     // "b"
       0x1e, 0x21, 0x21, 0x21, 0x21,     // "c"
       0x0e, 0x11, 0x11, 0x11, 0x7f,     // "d"
       0x1e, 0x25, 0x25, 0x25, 0x19,     // "e"
       0x08, 0x3f, 0x48, 0x48, 0x20,     // "f"
       0x30, 0x4a, 0x49, 0x49, 0x3f,     // "g"
       0x7f, 0x08, 0x08, 0x08, 0x07,     // "h"
       0x00, 0x11, 0x5f, 0x01, 0x00,     // "i"
       0x02, 0x01, 0x11, 0x11, 0x5e,     // "j"
       0x7f, 0x08, 0x14, 0x22, 0x01,     // "k"
       0x00, 0x41, 0x7f, 0x01, 0x00,     // "l"
       0x3f, 0x20, 0x18, 0x20, 0x1f,     // "m"
       0x3f, 0x20, 0x20, 0x20, 0x1f,     // "n"
       0x1e, 0x21, 0x21, 0x21, 0x1e,     // "o"
       0x3f, 0x24, 0x24, 0x24, 0x18,     // "p"
       0x18, 0x24, 0x24, 0x24, 0x3f,     // "q"
       0x3f, 0x20, 0x20, 0x20, 0x10,     // "r"
       0x12, 0x29, 0x21, 0x25, 0x12,     // "s"
       0x10, 0x10, 0x3e, 0x11, 0x12,     // "t"
       0x00, 0x1e, 0x01, 0x01, 0x1f,     // "u"
       0x30, 0x0c, 0x03, 0x0c, 0x30,     // "v"
       0x1e, 0x01, 0x06, 0x01, 0x1e,     // "w"
       0x11, 0x0a, 0x04, 0x0a, 0x11,     // "x"
       0x38, 0x05, 0x05, 0x05, 0x3f,     // "y"
       0x21, 0x23, 0x25, 0x29, 0x31,     // "z"
       0x00, 0x08, 0x3e, 0x41, 0x41,     // "{"
       0x00, 0x00, 0x7f, 0x00, 0x00,     // "|"
       0x41, 0x41, 0x36, 0x08, 0x00,     // "}"
       0x20, 0x10, 0x20, 0x10, 0x20      // "~"
};
 
void load_pattern(unsigned int slot){
unsigned char pos;
int chr_i;
unsigned char col;
unsigned char val;
unsigned int cmap_i;
       pos = slot / 6;
       if(pos < outbuflen){
              val = 0;
              chr_i = outbuf[pos];
              chr_i -= 32;
              if(chr_i >= 0  && chr_i < 94){
                     col = slot % 6;      
                     if(col == 5){
                            val = 0;
                     }else{
                           cmap_i = 5*(unsigned int)chr_i + col;
                           val = charmap[cmap_i];
                     }
              }
       }else{
              val = 0;
       }
       
//     if(PIND.0)
//            val = 0xff - val;
 
       if(val & 0x0001)     LED0 = 1;
       else                 LED0 = 0;
       if(val & 0x0002)     LED1 = 1;
       else                 LED1 = 0;
       if(val & 0x0004)     LED2 = 1;
       else                 LED2 = 0;
       if(val & 0x0008)     LED3 = 1;
       else                 LED3 = 0;
       if(val & 0x0010)     LED4 = 1;
       else                 LED4 = 0;
       if(val & 0x0020)     LED5 = 1;
       else                 LED5 = 0;
       if(val & 0x0040)     LED6 = 1;
       else                 LED6 = 0;
}
 
/////////////////////////////////////////
// For communication with the ISRs.
 
register unsigned char state @15; // bitvector of state vars
#define NOT_MOVING 0x01
#define TIMER_OVF 0x02 
#define PHASE 0x04
 
register unsigned char new_cycle @14;
register unsigned int cycle_time @12;
 
// External Interrupt 1 service routine  
#pragma savereg-
 
interrupt [EXT_INT1] void ext_int1_isr(void)
{
#asm   
.EQU TCNT1L = 0x2c
.EQU TCNT1H = 0x2d
       push r16
       in r16,SREG
       push r16
 
;             Test the phase of the rotor 
       bst r15, 2
       brts ROTATION_DONE
 
;             Half phase, set the phase bit and jump to return.      
       set
       bld r15, 2
       rjmp RETURN_INT1
       
ROTATION_DONE: 
;             reset the phase bit.
       clt
       bld r15, 2              
 
;             Check if the rotor is moving (no timer overflow)       
       bst r15, 1
       brts NO_OVERFLOW
;             No overflow, clear "not moving" flag.    
       clt
       bld r15, 0
                                                
NO_OVERFLOW:
;             If not moving, cycle time=0xffff, else the time1 val.
       bst r15, 0
       brtc ROTOR_MOVING
       
       ldi r16, 0xff
       mov r13, r16
       mov r12, r16
       rjmp FINAL_STEPS
       
ROTOR_MOVING:
       in r12, TCNT1L       
       in r13, TCNT1H
       
FINAL_STEPS:
;             reset timer overflow flag
       clt
       bld r15, 1
;             set the new cycle indicator
       ldi r16, 0xff
       mov r14, r16
;             Clear timer1                             
       ldi r16, 0x00
       out TCNT1H, r16
       out TCNT1L, r16
               
;;            Restore registers for the interrupt return.            
RETURN_INT1:  
    pop r16
    out SREG,r16
    pop r16   
#endasm
}
#pragma savereg+
 
#pragma savereg-
// Timer 1 overflow interrupt service routine
interrupt [TIM1_OVF] void timer1_ovf_isr(void)
{
#asm
       push r16
       in r16,SREG
       push r16
;             Set the overflow and not moving bits.
       set
       bld r15, 0
       bld r15, 1
 
       pop r16
       out SREG,r16
       pop r16       
#endasm
}
#pragma savereg+
 
//            Related to slot timeing
unsigned int curr_slot; 
unsigned int next_slot_time;
unsigned long int tmp_l; 
unsigned int timer1_val;
 
void main(void)
{
unsigned char  iter;
unsigned char c;
 
// Input/Output Ports initialization
// Port B
PORTB=0x00;
DDRB=0x1F;
 
// Port D
PORTD=0x00;
DDRD=0x60;
 
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
// Mode: Output Compare
// OC0 output: Disconnected
TCCR0=0x00;
TCNT0=0x00;
 
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 156.250 kHz
// Mode: Output Compare
// OC1 output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
TCCR1A=0x00;
TCCR1B=0x03;
TCNT1H=0x00;
TCNT1L=0x00;
OCR1H=0x00;
OCR1L=0x00;
 
// External Interrupt(s) initialization
// INT0: Off
// INT1: On
// INT1 Mode: Falling Edge
GIMSK=0x80;
MCUCR=0x08;
GIFR=0x80;
 
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x80;
 
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
 
state = 0;                 // moving, no overflow, phase 0
cycle_time = 8192;   // 52 ms at 6.4 us/tick
new_cycle = 1;       // force a cycle initialization
curr_slot = 0;       // beginning of the sequence
next_slot_time = cycle_time / NSLOTS;
 
/* initialize the UART's baud rate */
UCR = 0x18;  
UBRR=XTAL/16/BAUD-1;
 
// Global enable interrupts
#asm("sei") 
 
iter = 0;
while (1){  
       timer1_val = TCNT1;
       while( (timer1_val < next_slot_time) && (! new_cycle)){
              if(USR.7 == 1){            // byte has arrived
                     c = UDR;
                     if(! USR.4){
                           switch(c){
                           case '\n':      // change buffer
                                  if(currbuf == inbuf1){
                                         outbuf = inbuf1;
                                         currbuf = inbuf2;
                                  }else{
                                         outbuf = inbuf2;
                                         currbuf = inbuf1;
                                  }
                                  outbuflen = inbuflen;
                                  inbuflen = 0;
                                  break;
                           default:             // not a control character                                    
                                  if(inbuflen < BUFLEN){
                                         currbuf[inbuflen] = c;
                                         inbuflen++;
                                  }
                                  break;
                           }
                     }
              }
              timer1_val = TCNT1;
       }
              
       if(new_cycle){
              curr_slot = 0;
              new_cycle = 0;       // race condition, but extremely unlikely.
       }
       
       if(curr_slot < NSLOTS){
              load_pattern(curr_slot);          
              curr_slot++;                      
//                   do 32 bit math                    
              tmp_l = cycle_time;
              tmp_l = tmp_l * curr_slot;
              tmp_l = (tmp_l / NSLOTS) ;                      
              next_slot_time = tmp_l;    
       }else{
              next_slot_time = 0xffff;
       }
}
}

Making a clock, sign, thermometer, etc.

 

I started out trying to make a propeller clock; I wound up making an unusual serial terminal. Separating the display function and the drive function makes a lot of sense. The spinning circuit board can be made more simply, and hopefully more reliably, if its functionality is limited. For example, there is no need to mount a supercap to provide backup current to a real-time clock on the spinning circuit board itself. Furthermore, it is easier to provide new and more elaborate functions (e.g. a thermometer, a calendar, etc) in the competed device.

 

The design of the spinning sign lends itself to robotic applications. The platform does not need to be spinning rapidly to provide power to the free-rotating circuit, and there is a wire available for moderately high-speed communications. Instead of mounting the commutator-platform assembly on a motor, it can be mounted on a shaft whose direction is precisely controlled. One simple application is a free-rotating sonar module, which continually reports its distance measurements. More sophisticated applications require a protocol for duplex communication over a single wire. Dallas Semiconductor produces a variety of 1-wire chips, including a 1-wire to UART converter.