The ChromoDisk is very similar to the Aurora LED. It has the same 9 rings of 18 LEDs and every ring has to be the same color and brightness due to the multiplexing approach. This device uses pulse-width modulation (PWM) instead of resistors to limit the power fed to the LEDs, so while it takes less assembly time and components, you need to be a little careful about how you write the software. This is a good illustration of the tradeoff you need to make when you design with microcontrollers. You need to strike a balance between what you do in hardware and what you do in software. I am NOT saying that LED Artist's approach with all the resistors is bad, it's just a design choice and this is one alternative. More about this later.
Let's start with the design parameters:
- Easily available, low cost components
- Low component count
- Fits within a low-cost tier for PCB manufacture
- Multiple power supply options
- Easily programmable
The components I've chosen barely fit into the tight space. I picked the largest SMT components that I could to facilitate manual handling and ease of soldering. In the end, because of the limited real estate, I was not able to allow both battery pack and DC power jack, so you need to choose which one you'll use. Also, everything fits within a 100 mm square, a 4 inch disk, which is one of the pricing tiers typical of most PCB manufacturers. Anything bigger bumps you to the next pricing tier. Since area and cost goes up as the square of the radius, it's a good idea to limit the size. Routing out the circular shape is normally included in the board price.
The AVR micros are fairly easy to program. The code I've provided is entirely interrupt-driven and written in assembly language. It might be a bit less readable than C or some other language, but it's about as efficient as you can get. I don't claim to be the best programmer, but it seems to work pretty well and I was able to derive some new modes using code from other modes. It's designed to be hacked!
Here is the list of parts for the ChromoDisk, with Mouser P/N, description, and quantity:
|667-ERJ-3EKF1201V||Thick Film Resistors-0603 1.2K ohms 1%||13|
|667-ERJ-3EKF6800V||Thick Film Resistor-0603 680 ohms 1%||3|
|667-ERJ-3EKF1002V||Thick Film Resistor-0603 10K ohms 1% Tol||1|
|81-GRM188R71H104KA93||Capacitor (MLCC)-0603 0.1uF 50volts X7R 10%||1|
|512-FDN338P||MOSFET Small Signal SSOT-3 P-CH -20V||3|
|771-PMST2369115||Bipolar Small Signal NPN 15v 200mA 500MHz||12|
|556-ATTINY4313-SU||Microcontroller AVR 4KB FL 256B SRAM 1.8-5.5V||1|
|612-TL3315NF250Q||Tactile Switch LOPRO 250GF SMD||1|
|611-KSC741GLFS||Tactile Switch 4.3mm IP67 3N Soft Actuator||1|
|798-DF1BZ-6DP-2.5DSA||2.5MM V DBL ROW HDR||1|
|Common cathode RGB LEDS||162|
|Custom PC Board||1|
|598-AVE227M16X16T-F||Al Electrolytic Cap - 220uF 16V 85C Case 6.3 x 7.7||1|
|163-5030-E||DC PWR JACK 2.0MM X 5.5MM SMT||0/1|
|12BH331P-GR||Battery Holder 3 AA PC LEADS||1/0|
|In-System Programmer for AVR microcontrollers||1|
A couple of comments here. First, you'll notice that you have to choose either the DC power jack or the solder-on battery pack (you can use one with wire leads if you like, but I designed it to use the version with pins). There's a mounting hole in the center for whatever you want, but the battery pack will obscure it. I used it to secure battery packs before I added the PC mount pack. Second, I don't have a spec for the RGB LEDs. It's entirely up to you which ones you choose. Since I eliminated the current-limiting resistors on the LEDs by using PWM in software, you can adjust the brightness of the LEDs over a wide range by adjusting a couple of simple parameters at the top of the code. This allows you to accommodate LEDs with various current specs as long as they can take the surge current of the PWM approach.
The pinout for the LEDs is red / cathode / green / blue. I have tried assembling boards with diffused and water-clear LEDs. Diffused give more uniform color and brightness; clear give brighter light that floods farther and they have interesting effects with angle of view, but non-uniformity in LEDs can result in color hot-spots. The PWM approach has some limitations there.
I've ordered the parts in large enough quantities that I can provide the kit of parts and the custom board (but not the ISP programmer). Let me know if you're interested. Given the time involved, I'm not going to make any money on it. That wasn't really the point. It was intended to be a challenge and something fun for people to experiment with.
Step 1: The Build - Layer 1
- Solder on the small passive SMT components and transistors
- Solder on the larger SMT components
- Program the microcontroller
- Solder on the LEDs
- Solder on the power source
The idea here is to put on components that don't interfere with each other in layers. The board is very crowded, so it's essential to do this to make the build as easy as possible. Here's a picture of the board you're starting with. Let's go!
Layer 1 - SMD Components ... Most of them
The passive components go on first because they are the smallest and easiest to lose if you tilt the board. You need as much clear area as possible when you're soldering these. You can use the “skillet” / oven method, or hand solder them. I did it all by hand. SparkFun has a good tutorial on hand-soldering SMT components, so I won't try to duplicate that here. Solder on all of the resistors and the bipolar transistors first. The MOSFETs are static-sensitive, so adding in the other components first will help protect them. Don't put on the microcontroller, the large electrolytic cap, the 6-pin connector, the LEDs, the power connector / battery pack, or the capacitor on the back yet. Solder on the MOSFETs last in this layer.
Step 2: Layer 2 Â Large SMD Components
Step 3: Layer 3 Â Programming
Step 4: Layer 4 – the LEDs
Here's my approach to the order of soldering. Once you've got all of the LEDs in place, solder one pin on each LED in the outer-most ring in place on the bottom side of the board. Make sure that as you solder, the LED is fully inserted so they are all flush with the top side of the board. You can also use the shoulders on the leads to space the LEDs up off the board, but this is a little more difficult to get consistent height. The choice is yours. Thicker overall board with the LEDs up off the board, or slightly thinner and more compact with them all flush.
Next, go around that outer-most ring and clip the leads, leaving a little nub for soldering the other leads later. Then proceed to each of the rings, soldering one lead on each LED and clipping leads. Once you've got all of the LEDs tacked in place, go around and solder all of the remaining leads. This goes a lot faster. Make sure you don't miss any, or you'll be trying to figure out why some of your LEDs are acting funny.
At this point, you need to test all of the LEDs and make sure they are working, because if you're using the battery pack it will cover up some of the leads! The DC power jack is not as problematic, since it does not obscure the pins. It's sometimes very difficult to find bridges, and I don't have any wise words or troubleshooting steps for you. However, I have found that the board is thin enough that you can shine a light through from the top and inspect the bottom side to find some bridges. If you don't see a complete ring of light around each LED on the bottom side, there's likely a bridge there.
Use the one-color-per-ring mode as a final test though, to make sure every color and every LED is working. Here's what it should look like:
Step 5: Layer 5 – the Power Source
Step 6: What Next?
I showed a completed disk to some friends just before Christmas, holding it at the top of their tree. They asked if I could make one that looks like a star, so I went home and stuffed a board with just the LEDs to form a 6-pointed star (the number of LEDs in a ring lends itself to this layout best). They loved the result! So feel free to try different arrangements of LEDs – you do not have to populate the whole board. However, remember that if you are using the battery pack, empty LED spots that you might want to fill later will be covered up!
If you have any questions, please post them to the Instructable. Please also feel free to hack and try different things. I've provided source code, programming file, schematic, board layout, and Gerber files. This is an open source device and you are free to use any of the files, with attribution. I hope you enjoy it!
Step 7: A Walk Through the Code
The code is structured at a high level into parameters, code, and data. the parameters section is meant to bring all of the key contacts that affect how the color modes work to the top where you can easily modify them without diving into the code. The first part of the parameter section adapts the code to the hardware and, although you're welcome to design your own board as I did, I'm going to assume that you want to use it for this board and either change one of the modes that's already there, or write your own. So skip down to:
.equ initialMode = 4
.equ maxModes = 6
The first EQU sets the mode the ChromoDisk jumps into when first powered on or when you press the Reset button. Mode 4 is the rippling color mode. The next EQU is just the number of modes implemented in the code. Remember to change this if you add a mode of your own design.
The rest of the EQUs are specific to each mode. At first I wanted to write a generic program that allowed me to generate new modes just by changing a few parameters. I didn't figure out a way to do that, so each mode has its own special code section you'll see later. I've provided comments that i hope are pretty easy to understand, once you know how the mode works.
You'll see that there are arithmetic equations in some of the EQUs. There are two things to remember here and throughout the code: the microcontroller is an integer machine, and it knows two basic ways to work with numbers. If you write an equation that results in a number with a fraction, it's going to be truncated. Don't expect 2/3 to give you 0.666667. It will give you 0. This may seem obvious, but at some point you're going to forget and wonder why some piece of code is not working right. With respect to number systems: you can interpret an 8-bit number as an absolute number (0-255) or as a signed integer (0 - 127, and -128 to -1). I know this well, and yet I still sometimes get tripped up on it. An even more subtle issue is that the processor will treat an integer as absolute or signed depending upon which instruction you use. Branching is a great example. You would expect that if you read the timer and get 132, that if you compared 120 with 132 and branch if it's lower, BRLT (Branch if less than), it would take the branch. Not so fast. BRLT assumes the numbers are signed, so 132 is actually -125. For unsigned numbers, as in this case, you need to use BRLO (Branch if lower). Keep this in mind as you read through the code.
The executable section starts with the .cseg statement. The code contains, in order:
- the interrupt vectors that point to the interrupt service routines (ISRs)
- the initialization code, executed after a reset
- the main code
- the initialization code for each mode
- the ISRs
- the data area, which starts with .dseg
I don't want to make this too long, so I'll try to summarize. The program is completely interrupt-driven, so everything happens in the interrupt service routines. The main code just puts the processor to sleep. It wakes either when the Mode button is pressed, or when a timer times out. Timer 0 is used to debounce the Mode switch. Timer 1 is used to make the colors change using a single PWM control line and the color on/off outputs.
The Mode switch illustrates the hardware / software trade-off mentioned earlier. Switch contacts bounce when you press them, so you need to be able to handle a series of short pulses, rather than just a high or low input. I ended up doing this in software because I could not find an easy way to do it with the space and hardware that I had. Basically, the mode switch resets Timer 0. If timer 0 times out before it gets reset, the code assumes the contacts have stopped bouncing and the code can take action. This is a bit of a hack and you have to carefully pick the timeout value. You'll see that the device sometimes skips a mode, so the timer setting isn't perfect. It's a trade-off between response time to pressing the button and how often it skips a mode.
If you want to see how each mode is implemented, look at the Timer 1 ISR. There's a variable in memory that keeps track of which mode it's in. The ISR looks at that number and jumps to the right part of the code, kind of like a CASE or SWITCH statement. Each mode needs to keep track of it's own state, stored in RAM, such as which color it's on. Most modes turn on the red, then the green, then the blue, and back to red. In general, they cannot all be turned on at the same time because there's only one PWM output controlling everything and he width depends upon the brightness of the color that's on.
So that's a quick overview, without writing a book. I'l try to answer more detailed questions if you post them. Have fun!